diff --git a/src/analyze.test.ts b/src/analyze.test.ts index c73159d1fe..0d0a48d18e 100644 --- a/src/analyze.test.ts +++ b/src/analyze.test.ts @@ -10,7 +10,7 @@ import { runQueries } from "./analyze"; import { setCodeQL } from "./codeql"; import { Config } from "./config-utils"; import * as count from "./count-loc"; -import { Language } from "./languages"; +import { KnownLanguage } from "./languages"; import { getRunnerLogger } from "./logging"; import { setupTests, setupActionsVars } from "./testing-utils"; import * as util from "./util"; @@ -21,8 +21,8 @@ setupTests(test); // and correct case of builtin or custom. Also checks the correct search // paths are set in the database analyze invocation. test("status report fields and search path setting", async (t) => { - const mockLinesOfCode = Object.values(Language).reduce((obj, lang, i) => { - // use a different line count for each language + const mockLinesOfCode = Object.values(KnownLanguage).reduce((obj, lang, i) => { + // use a different line count for each KnownLanguage obj[lang] = i + 1; return obj; }, {}); @@ -35,13 +35,13 @@ test("status report fields and search path setting", async (t) => { const addSnippetsFlag = ""; const threadsFlag = ""; const packs = { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "a/b", version: clean("1.0.0")!, }, ], - [Language.java]: [ + [KnownLanguage.java]: [ { packName: "c/d", version: clean("2.0.0")!, @@ -49,7 +49,7 @@ test("status report fields and search path setting", async (t) => { ], }; - for (const language of Object.values(Language)) { + for (const language of Object.values(KnownLanguage)) { setCodeQL({ packDownload: async () => ({ packs: [] }), databaseRunQueries: async ( @@ -203,7 +203,7 @@ test("status report fields and search path setting", async (t) => { function verifyLineCounts(tmpDir: string) { // eslint-disable-next-line github/array-foreach - Object.keys(Language).forEach((lang, i) => { + Object.keys(KnownLanguage).forEach((lang, i) => { verifyLineCountForFile(path.join(tmpDir, `${lang}.sarif`), i + 1); }); } @@ -249,14 +249,14 @@ test("status report fields and search path setting", async (t) => { version: "2.0.0", }, ]; - for (const lang of Object.values(Language)) { + for (const lang of Object.values(KnownLanguage)) { t.deepEqual(readContents(`${lang}-queries-builtin.qls`), qlsContent); t.deepEqual(readContents(`${lang}-queries-custom-0.qls`), qlsContent); t.deepEqual(readContents(`${lang}-queries-custom-1.qls`), qlsContent2); const packSuiteName = `${lang}-queries-packs.qls`; - if (lang === Language.cpp) { + if (lang === KnownLanguage.cpp) { t.deepEqual(readContents(packSuiteName), qlsPackContentCpp); - } else if (lang === Language.java) { + } else if (lang === KnownLanguage.java) { t.deepEqual(readContents(packSuiteName), qlsPackContentJava); } else { t.false( diff --git a/src/analyze.ts b/src/analyze.ts index 422df5eea9..5df4b7b85f 100644 --- a/src/analyze.ts +++ b/src/analyze.ts @@ -8,7 +8,7 @@ import * as analysisPaths from "./analysis-paths"; import { CODEQL_VERSION_COUNTS_LINES, getCodeQL } from "./codeql"; import * as configUtils from "./config-utils"; import { countLoc } from "./count-loc"; -import { isScannedLanguage, Language } from "./languages"; +import { isScannedLanguage, KnownLanguage, Language } from "./languages"; import { Logger } from "./logging"; import * as sharedEnv from "./shared-environment"; import * as util from "./util"; @@ -124,7 +124,7 @@ async function createdDBForScannedLanguages( ) { logger.startGroup(`Extracting ${language}`); - if (language === Language.python) { + if (language === KnownLanguage.python) { await setupPythonExtractor(logger); } diff --git a/src/config-utils.test.ts b/src/config-utils.test.ts index 6a6ae6d300..1e82249f61 100644 --- a/src/config-utils.test.ts +++ b/src/config-utils.test.ts @@ -9,7 +9,7 @@ import * as sinon from "sinon"; import * as api from "./api-client"; import { getCachedCodeQL, setCodeQL } from "./codeql"; import * as configUtils from "./config-utils"; -import { Language } from "./languages"; +import { KnownLanguage, Language } from "./languages"; import { getRunnerLogger } from "./logging"; import { setupTests } from "./testing-utils"; import * as util from "./util"; @@ -299,7 +299,7 @@ test("load non-empty input", async (t) => { // And the config we expect it to parse to const expectedConfig: configUtils.Config = { - languages: [Language.javascript], + languages: [KnownLanguage.javascript], queries: { javascript: { builtin: [], @@ -974,7 +974,7 @@ test("No detected languages", async (t) => { }); }); -test("Unknown languages", async (t) => { +test("Unknown, unsupported languages", async (t) => { return await util.withTmpDir(async (tmpDir) => { const languages = "rubbish,english"; @@ -998,7 +998,77 @@ test("Unknown languages", async (t) => { } catch (err) { t.deepEqual( err, - new Error(configUtils.getUnknownLanguagesError(["rubbish", "english"])) + new Error( + configUtils.getUnsupportedLanguagesError(["rubbish", "english"]) + ) + ); + } + }); +}); + +test("Unknown, supported languages", async (t) => { + return await util.withTmpDir(async (tmpDir) => { + const languages = "ql"; + + const codeQL = setCodeQL({ + async resolveLanguages() { + return { + ql: ["/tmp/ql-for-ql-pack"], + }; + }, + }); + + let config: configUtils.Config = await configUtils.initConfig( + languages, + undefined, + undefined, + undefined, + undefined, + { owner: "github", repo: "example " }, + tmpDir, + tmpDir, + codeQL, + tmpDir, + gitHubVersion, + sampleApiDetails, + getRunnerLogger(true) + ); + t.deepEqual(config.languages, ["ql"]); + }); +}); + +test("Partially supported languages", async (t) => { + return await util.withTmpDir(async (tmpDir) => { + const languages = "rubbish,ql"; + + const codeQL = setCodeQL({ + async resolveLanguages() { + return { + ql: ["/tmp/ql-for-ql-pack"], + }; + }, + }); + try { + await configUtils.initConfig( + languages, + undefined, + undefined, + undefined, + undefined, + { owner: "github", repo: "example " }, + tmpDir, + tmpDir, + codeQL, + tmpDir, + gitHubVersion, + sampleApiDetails, + getRunnerLogger(true) + ); + throw new Error("initConfig did not throw error"); + } catch (err) { + t.deepEqual( + err, + new Error(configUtils.getUnsupportedLanguagesError(["rubbish"])) ); } }); @@ -1044,7 +1114,7 @@ test("Config specifies packages", async (t) => { getRunnerLogger(true) ); t.deepEqual(packs as unknown, { - [Language.javascript]: [ + [KnownLanguage.javascript]: [ { packName: "a/b", version: clean("1.2.3"), @@ -1102,13 +1172,13 @@ test("Config specifies packages for multiple languages", async (t) => { getRunnerLogger(true) ); t.deepEqual(packs as unknown, { - [Language.javascript]: [ + [KnownLanguage.javascript]: [ { packName: "a/b", version: clean("1.2.3"), }, ], - [Language.python]: [ + [KnownLanguage.python]: [ { packName: "c/d", version: clean("1.2.3"), @@ -1377,8 +1447,8 @@ parsePacksErrorMacro.title = (providedTitle: string) => function invalidPackNameMacro(t: ExecutionContext, name: string) { parsePacksErrorMacro( t, - { [Language.cpp]: [name] }, - [Language.cpp], + { [KnownLanguage.cpp]: [name] }, + [KnownLanguage.cpp], new RegExp( `The configuration file "/a/b" is invalid: property "packs" "${name}" is not a valid pack` ) @@ -1388,8 +1458,8 @@ invalidPackNameMacro.title = (_: string, arg: string) => `Invalid pack string: ${arg}`; test("no packs", parsePacksMacro, {}, [], {}); -test("two packs", parsePacksMacro, ["a/b", "c/d@1.2.3"], [Language.cpp], { - [Language.cpp]: [ +test("two packs", parsePacksMacro, ["a/b", "c/d@1.2.3"], [KnownLanguage.cpp], { + [KnownLanguage.cpp]: [ { packName: "a/b", version: undefined }, { packName: "c/d", version: clean("1.2.3") }, ], @@ -1398,9 +1468,9 @@ test( "two packs with spaces", parsePacksMacro, [" a/b ", " c/d@1.2.3 "], - [Language.cpp], + [KnownLanguage.cpp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "a/b", version: undefined }, { packName: "c/d", version: clean("1.2.3") }, ], @@ -1410,16 +1480,16 @@ test( "two packs with language", parsePacksMacro, { - [Language.cpp]: ["a/b", "c/d@1.2.3"], - [Language.java]: ["d/e", "f/g@1.2.3"], + [KnownLanguage.cpp]: ["a/b", "c/d@1.2.3"], + [KnownLanguage.java]: ["d/e", "f/g@1.2.3"], }, - [Language.cpp, Language.java, Language.csharp], + [KnownLanguage.cpp, KnownLanguage.java, KnownLanguage.csharp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "a/b", version: undefined }, { packName: "c/d", version: clean("1.2.3") }, ], - [Language.java]: [ + [KnownLanguage.java]: [ { packName: "d/e", version: undefined }, { packName: "f/g", version: clean("1.2.3") }, ], @@ -1430,21 +1500,21 @@ test( "no language", parsePacksErrorMacro, ["a/b@1.2.3"], - [Language.java, Language.python], + [KnownLanguage.java, KnownLanguage.python], /The configuration file "\/a\/b" is invalid: property "packs" must split packages by language/ ); test( "invalid language", parsePacksErrorMacro, - { [Language.java]: ["c/d"] }, - [Language.cpp], + { [KnownLanguage.java]: ["c/d"] }, + [KnownLanguage.cpp], /The configuration file "\/a\/b" is invalid: property "packs" has "java", but it is not one of the languages to analyze/ ); test( "not an array", parsePacksErrorMacro, - { [Language.cpp]: "c/d" }, - [Language.cpp], + { [KnownLanguage.cpp]: "c/d" }, + [KnownLanguage.cpp], /The configuration file "\/a\/b" is invalid: property "packs" must be an array of non-empty strings/ ); @@ -1496,8 +1566,8 @@ function parseInputAndConfigErrorMacro( parseInputAndConfigErrorMacro.title = (providedTitle: string) => `Parse Packs input and config Error: ${providedTitle}`; -test("input only", parseInputAndConfigMacro, {}, " c/d ", [Language.cpp], { - [Language.cpp]: [{ packName: "c/d", version: undefined }], +test("input only", parseInputAndConfigMacro, {}, " c/d ", [KnownLanguage.cpp], { + [KnownLanguage.cpp]: [{ packName: "c/d", version: undefined }], }); test( @@ -1505,9 +1575,9 @@ test( parseInputAndConfigMacro, {}, "a/b , c/d@1.2.3", - [Language.cpp], + [KnownLanguage.cpp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "a/b", version: undefined }, { packName: "c/d", version: "1.2.3" }, ], @@ -1519,9 +1589,9 @@ test( parseInputAndConfigMacro, {}, " + a/b , c/d@1.2.3 ", - [Language.cpp], + [KnownLanguage.cpp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "a/b", version: undefined }, { packName: "c/d", version: "1.2.3" }, ], @@ -1533,9 +1603,9 @@ test( parseInputAndConfigMacro, ["a/b", "c/d"], " ", - [Language.cpp], + [KnownLanguage.cpp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "a/b", version: undefined }, { packName: "c/d", version: undefined }, ], @@ -1547,9 +1617,9 @@ test( parseInputAndConfigMacro, ["a/b", "c/d"], " e/f, g/h@1.2.3 ", - [Language.cpp], + [KnownLanguage.cpp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "e/f", version: undefined }, { packName: "g/h", version: "1.2.3" }, ], @@ -1561,9 +1631,9 @@ test( parseInputAndConfigMacro, ["a/b", "c/d"], " +e/f, g/h@1.2.3 ", - [Language.cpp], + [KnownLanguage.cpp], { - [Language.cpp]: [ + [KnownLanguage.cpp]: [ { packName: "e/f", version: undefined }, { packName: "g/h", version: "1.2.3" }, { packName: "a/b", version: undefined }, @@ -1586,7 +1656,7 @@ test( parseInputAndConfigErrorMacro, {}, "c/d", - [Language.cpp, Language.csharp], + [KnownLanguage.cpp, KnownLanguage.csharp], /multi-language analysis/ ); @@ -1595,7 +1665,7 @@ test( parseInputAndConfigErrorMacro, {}, " + ", - [Language.cpp], + [KnownLanguage.cpp], /remove the '\+'/ ); @@ -1604,7 +1674,7 @@ test( parseInputAndConfigErrorMacro, {}, " xxx", - [Language.cpp], + [KnownLanguage.cpp], /"xxx" is not a valid pack/ ); diff --git a/src/config-utils.ts b/src/config-utils.ts index c0881314f1..909946a5f7 100644 --- a/src/config-utils.ts +++ b/src/config-utils.ts @@ -673,8 +673,8 @@ export function getNoLanguagesError(): string { ); } -export function getUnknownLanguagesError(languages: string[]): string { - return `Did not recognise the following languages: ${languages.join(", ")}`; +export function getUnsupportedLanguagesError(languages: string[]): string { + return `Does not support the following languages: ${languages.join(", ")}`; } /** @@ -714,8 +714,10 @@ async function getLanguagesInRepo( * has been set, otherwise it is deduced as all languages in the repo that * can be analysed. * - * If no languages could be detected from either the workflow or the repository + * If no supported languages could be detected from either the workflow or the repository * then throw an error. + * + * The set of supported languages is defined by the `codeql resolve languages` command. */ async function getLanguages( codeQL: CodeQL, @@ -731,11 +733,11 @@ async function getLanguages( .filter((x) => x.length > 0); logger.info(`Languages from configuration: ${JSON.stringify(languages)}`); + const allSupportedLanguages = new Set(Object.keys(await codeQL.resolveLanguages())); if (languages.length === 0) { // Obtain languages as all languages in the repo that can be analysed languages = await getLanguagesInRepo(repository, apiDetails, logger); - const availableLanguages = await codeQL.resolveLanguages(); - languages = languages.filter((value) => value in availableLanguages); + languages = languages.filter((value) => allSupportedLanguages.has(value)); logger.info( `Automatically detected languages: ${JSON.stringify(languages)}` ); @@ -748,21 +750,21 @@ async function getLanguages( } // Make sure they are supported - const parsedLanguages: Language[] = []; - const unknownLanguages: string[] = []; + const supportedLanguages: Language[] = []; + const unsupportedLanguages: Language[] = []; for (const language of languages) { const parsedLanguage = parseLanguage(language); - if (parsedLanguage === undefined) { - unknownLanguages.push(language); - } else if (parsedLanguages.indexOf(parsedLanguage) === -1) { - parsedLanguages.push(parsedLanguage); + if (!allSupportedLanguages.has(parsedLanguage)) { + unsupportedLanguages.push(language); + } else if (supportedLanguages.indexOf(parsedLanguage) === -1) { + supportedLanguages.push(parsedLanguage); } } - if (unknownLanguages.length > 0) { - throw new Error(getUnknownLanguagesError(unknownLanguages)); + if (unsupportedLanguages.length > 0) { + throw new Error(getUnsupportedLanguagesError(unsupportedLanguages)); } - return parsedLanguages; + return supportedLanguages; } async function addQueriesFromWorkflow( @@ -1071,7 +1073,7 @@ export function parsePacksFromConfig( if (!Array.isArray(packsArr)) { throw new Error(getPacksInvalid(configFile)); } - if (!languages.includes(lang as Language)) { + if (!languages.includes(lang)) { throw new Error(getPacksRequireLanguage(lang, configFile)); } packs[lang] = []; @@ -1172,7 +1174,7 @@ function shouldCombinePacks(packsInput?: string): boolean { function combinePacks(packs1: Packs, packs2: Packs): Packs { const packs = {}; for (const lang of Object.keys(packs1)) { - packs[lang] = packs1[lang].concat(packs2[lang] || []); + packs[lang] = packs1[lang]?.concat(packs2[lang] || []); } for (const lang of Object.keys(packs2)) { if (!packs[lang]) { diff --git a/src/count-loc.test.ts b/src/count-loc.test.ts index 3a32ab6e1a..eb2cb69cef 100644 --- a/src/count-loc.test.ts +++ b/src/count-loc.test.ts @@ -3,7 +3,7 @@ import * as path from "path"; import test from "ava"; import { countLoc } from "./count-loc"; -import { Language } from "./languages"; +import { KnownLanguage } from "./languages"; import { getRunnerLogger } from "./logging"; import { setupTests } from "./testing-utils"; @@ -14,7 +14,7 @@ test("ensure lines of code works for cpp and js", async (t) => { path.join(__dirname, "../tests/multi-language-repo"), [], [], - [Language.cpp, Language.javascript], + [KnownLanguage.cpp, KnownLanguage.javascript], getRunnerLogger(true) ); @@ -29,7 +29,7 @@ test("ensure lines of code works for csharp", async (t) => { path.join(__dirname, "../tests/multi-language-repo"), [], [], - [Language.csharp], + [KnownLanguage.csharp], getRunnerLogger(true) ); @@ -43,7 +43,7 @@ test("ensure lines of code can handle undefined language", async (t) => { path.join(__dirname, "../tests/multi-language-repo"), [], [], - [Language.javascript, Language.python, "hucairz" as Language], + [KnownLanguage.javascript, KnownLanguage.python, "hucairz" as KnownLanguage], getRunnerLogger(true) ); @@ -72,7 +72,7 @@ test("ensure lines of code can handle includes", async (t) => { path.join(__dirname, "../tests/multi-language-repo"), ["../../src/testdata"], [], - [Language.javascript], + [KnownLanguage.javascript], getRunnerLogger(true) ); @@ -88,7 +88,7 @@ test("ensure lines of code can handle empty includes", async (t) => { path.join(__dirname, "../tests/multi-language-repo"), ["idontexist"], [], - [Language.javascript], + [KnownLanguage.javascript], getRunnerLogger(true) ); @@ -102,7 +102,7 @@ test("ensure lines of code can handle exclude", async (t) => { path.join(__dirname, "../tests/multi-language-repo"), [], ["**/*.py"], - [Language.javascript, Language.python], + [KnownLanguage.javascript, KnownLanguage.python], getRunnerLogger(true) ); diff --git a/src/count-loc.ts b/src/count-loc.ts index 549f07d703..6d0c6ee9be 100644 --- a/src/count-loc.ts +++ b/src/count-loc.ts @@ -1,19 +1,19 @@ import { LocDir } from "github-linguist"; -import { Language } from "./languages"; +import { KnownLanguage, Language } from "./languages"; import { Logger } from "./logging"; // Map from linguist language names to language prefixes used in the action and codeql -const linguistToMetrics: Record = { - c: Language.cpp, - "c++": Language.cpp, - "c#": Language.csharp, - go: Language.go, - java: Language.java, - javascript: Language.javascript, - python: Language.python, - ruby: Language.ruby, - typescript: Language.javascript, +const linguistToMetrics: Record = { + c: KnownLanguage.cpp, + "c++": KnownLanguage.cpp, + "c#": KnownLanguage.csharp, + go: KnownLanguage.go, + java: KnownLanguage.java, + javascript: KnownLanguage.javascript, + python: KnownLanguage.python, + ruby: KnownLanguage.ruby, + typescript: KnownLanguage.javascript, }; const nameToLinguist = Object.entries(linguistToMetrics).reduce( @@ -24,7 +24,7 @@ const nameToLinguist = Object.entries(linguistToMetrics).reduce( obj[name].push(key); return obj; }, - {} as Record + {} as Record ); /** diff --git a/src/database-upload.test.ts b/src/database-upload.test.ts index de55a0245f..8c61eba2f4 100644 --- a/src/database-upload.test.ts +++ b/src/database-upload.test.ts @@ -10,7 +10,7 @@ import * as apiClient from "./api-client"; import { setCodeQL } from "./codeql"; import { Config } from "./config-utils"; import { uploadDatabases } from "./database-upload"; -import { Language } from "./languages"; +import { KnownLanguage } from "./languages"; import { Logger } from "./logging"; import { RepositoryNwo } from "./repository"; import { setupActionsVars, setupTests } from "./testing-utils"; @@ -36,7 +36,7 @@ const testApiDetails: GitHubApiDetails = { function getTestConfig(tmpDir: string): Config { return { - languages: [Language.javascript], + languages: [KnownLanguage.javascript], queries: {}, pathsIgnore: [], paths: [], diff --git a/src/init-action.ts b/src/init-action.ts index 44402a688a..5faaa35705 100644 --- a/src/init-action.ts +++ b/src/init-action.ts @@ -21,7 +21,7 @@ import { installPythonDeps, runInit, } from "./init"; -import { Language } from "./languages"; +import { KnownLanguage } from "./languages"; import { getActionsLogger } from "./logging"; import { parseRepositoryNwo } from "./repository"; import { @@ -172,7 +172,7 @@ async function run() { ); if ( - config.languages.includes(Language.python) && + config.languages.includes(KnownLanguage.python) && getRequiredInput("setup-python-dependencies") === "true" ) { try { diff --git a/src/languages.test.ts b/src/languages.test.ts index 5837c91f8d..cdb9bac66f 100644 --- a/src/languages.test.ts +++ b/src/languages.test.ts @@ -1,7 +1,7 @@ import test from "ava"; import { - Language, + KnownLanguage, isScannedLanguage, isTracedLanguage, parseLanguage, @@ -12,18 +12,18 @@ setupTests(test); test("parseLanguage", async (t) => { // Exact matches - t.deepEqual(parseLanguage("csharp"), Language.csharp); - t.deepEqual(parseLanguage("cpp"), Language.cpp); - t.deepEqual(parseLanguage("go"), Language.go); - t.deepEqual(parseLanguage("java"), Language.java); - t.deepEqual(parseLanguage("javascript"), Language.javascript); - t.deepEqual(parseLanguage("python"), Language.python); + t.deepEqual(parseLanguage("csharp"), KnownLanguage.csharp); + t.deepEqual(parseLanguage("cpp"), KnownLanguage.cpp); + t.deepEqual(parseLanguage("go"), KnownLanguage.go); + t.deepEqual(parseLanguage("java"), KnownLanguage.java); + t.deepEqual(parseLanguage("javascript"), KnownLanguage.javascript); + t.deepEqual(parseLanguage("python"), KnownLanguage.python); // Aliases - t.deepEqual(parseLanguage("c"), Language.cpp); - t.deepEqual(parseLanguage("c++"), Language.cpp); - t.deepEqual(parseLanguage("c#"), Language.csharp); - t.deepEqual(parseLanguage("typescript"), Language.javascript); + t.deepEqual(parseLanguage("c"), KnownLanguage.cpp); + t.deepEqual(parseLanguage("c++"), KnownLanguage.cpp); + t.deepEqual(parseLanguage("c#"), KnownLanguage.csharp); + t.deepEqual(parseLanguage("typescript"), KnownLanguage.javascript); // Not matches t.deepEqual(parseLanguage("foo"), undefined); @@ -32,21 +32,21 @@ test("parseLanguage", async (t) => { }); test("isTracedLanguage", async (t) => { - t.true(isTracedLanguage(Language.cpp)); - t.true(isTracedLanguage(Language.java)); - t.true(isTracedLanguage(Language.csharp)); + t.true(isTracedLanguage(KnownLanguage.cpp)); + t.true(isTracedLanguage(KnownLanguage.java)); + t.true(isTracedLanguage(KnownLanguage.csharp)); - t.false(isTracedLanguage(Language.go)); - t.false(isTracedLanguage(Language.javascript)); - t.false(isTracedLanguage(Language.python)); + t.false(isTracedLanguage(KnownLanguage.go)); + t.false(isTracedLanguage(KnownLanguage.javascript)); + t.false(isTracedLanguage(KnownLanguage.python)); }); test("isScannedLanguage", async (t) => { - t.false(isScannedLanguage(Language.cpp)); - t.false(isScannedLanguage(Language.java)); - t.false(isScannedLanguage(Language.csharp)); + t.false(isScannedLanguage(KnownLanguage.cpp)); + t.false(isScannedLanguage(KnownLanguage.java)); + t.false(isScannedLanguage(KnownLanguage.csharp)); - t.true(isScannedLanguage(Language.go)); - t.true(isScannedLanguage(Language.javascript)); - t.true(isScannedLanguage(Language.python)); + t.true(isScannedLanguage(KnownLanguage.go)); + t.true(isScannedLanguage(KnownLanguage.javascript)); + t.true(isScannedLanguage(KnownLanguage.python)); }); diff --git a/src/languages.ts b/src/languages.ts index a696908c7f..b4190e907b 100644 --- a/src/languages.ts +++ b/src/languages.ts @@ -1,5 +1,12 @@ -// All the languages supported by CodeQL -export enum Language { +/** + * A lowercase string representing a language. + */ +export type Language = Lowercase; + +/** + * All the languages known to be supported by CodeQL + */ +export enum KnownLanguage { csharp = "csharp", cpp = "cpp", go = "go", @@ -11,20 +18,20 @@ export enum Language { // Additional names for languages const LANGUAGE_ALIASES: { [lang: string]: Language } = { - c: Language.cpp, - "c++": Language.cpp, - "c#": Language.csharp, - typescript: Language.javascript, + c: KnownLanguage.cpp, + "c++": KnownLanguage.cpp, + "c#": KnownLanguage.csharp, + typescript: KnownLanguage.javascript, }; // Translate from user input or GitHub's API names for languages to CodeQL's names for languages -export function parseLanguage(language: string): Language | undefined { +export function parseLanguage(language: string): Language { // Normalise to lower case language = language.toLowerCase(); // See if it's an exact match - if (language in Language) { - return language as Language; + if (language in KnownLanguage) { + return language; } // Check language aliases @@ -32,14 +39,14 @@ export function parseLanguage(language: string): Language | undefined { return LANGUAGE_ALIASES[language]; } - return undefined; + return language; } export function isTracedLanguage(language: Language): boolean { return ( - ["cpp", "java", "csharp"].includes(language) || + [KnownLanguage.cpp, KnownLanguage.java, KnownLanguage.csharp].includes(language as any) || (process.env["CODEQL_EXTRACTOR_GO_BUILD_TRACING"] === "on" && - language === Language.go) + language === KnownLanguage.go) ); } diff --git a/src/tracer-config.test.ts b/src/tracer-config.test.ts index eae544844a..c62814a256 100644 --- a/src/tracer-config.test.ts +++ b/src/tracer-config.test.ts @@ -5,7 +5,7 @@ import test from "ava"; import { setCodeQL } from "./codeql"; import * as configUtils from "./config-utils"; -import { Language } from "./languages"; +import { KnownLanguage } from "./languages"; import { setupTests } from "./testing-utils"; import { concatTracerConfigs, @@ -18,7 +18,7 @@ setupTests(test); function getTestConfig(tmpDir: string): configUtils.Config { return { - languages: [Language.java], + languages: [KnownLanguage.java], queries: {}, pathsIgnore: [], paths: [], @@ -49,7 +49,7 @@ test("getTracerConfigForLanguage - minimal setup", async (t) => { const result = await getTracerConfigForLanguage( codeQL, config, - Language.javascript + KnownLanguage.javascript ); t.deepEqual(result, { spec: "abc", env: { foo: "bar" } }); }); @@ -89,7 +89,7 @@ test("getTracerConfigForLanguage - existing / critical vars", async (t) => { const result = await getTracerConfigForLanguage( codeQL, config, - Language.javascript + KnownLanguage.javascript ); t.deepEqual(result, { spec: "abc", @@ -309,7 +309,7 @@ test("getCombinedTracerConfig - return undefined when no languages are traced la await util.withTmpDir(async (tmpDir) => { const config = getTestConfig(tmpDir); // No traced languages - config.languages = [Language.javascript, Language.python]; + config.languages = [KnownLanguage.javascript, KnownLanguage.python]; const codeQL = setCodeQL({ async getTracerEnv() {