Use QueryLanguage type for representing query languages

This will make both the `askForLanguage` and `findLanguage` functions
return a `QueryLanguage` instead of a `string`. This will make it harder
to make mistakes when using these functions.

There are also some other changes with regards to `QueryLanguage` such
that we never need to use `as QueryLanguage` explicitly anymore, except
for the new `isQueryLanguage` function. The only remaining place that I
know of where we're using a `string` to represent the `QueryLanguage`
is in a database item's language, but this is harder to change and may
be relied upon by language authors.
This commit is contained in:
Koen Vlaswinkel
2023-05-08 13:56:11 +02:00
parent bf9eb2469b
commit b20a65a3a7
5 changed files with 53 additions and 12 deletions

View File

@@ -4,6 +4,7 @@ import { writeFile } from "fs-extra";
import { dump as dumpYaml } from "js-yaml";
import {
getOnDiskWorkspaceFolders,
isQueryLanguage,
showAndLogExceptionWithTelemetry,
} from "../helpers";
import { TeeLogger } from "../common";
@@ -15,7 +16,6 @@ import { fetchExternalApiQueries } from "./queries";
import { QueryResultType } from "../pure/new-messages";
import { join } from "path";
import { redactableError } from "../pure/errors";
import { QueryLanguage } from "../common/query-language";
export type RunQueryOptions = {
cliServer: Pick<CodeQLCliServer, "resolveQlpacks">;
@@ -41,7 +41,14 @@ export async function runQuery({
// For a reference of what this should do in the future, see the previous implementation in
// https://github.com/github/vscode-codeql/blob/089d3566ef0bc67d9b7cc66e8fd6740b31c1c0b0/extensions/ql-vscode/src/data-extensions-editor/external-api-usage-query.ts#L33-L72
const query = fetchExternalApiQueries[databaseItem.language as QueryLanguage];
if (!isQueryLanguage(databaseItem.language)) {
void showAndLogExceptionWithTelemetry(
redactableError`Unsupported database language ${databaseItem.language}`,
);
return;
}
const query = fetchExternalApiQueries[databaseItem.language];
if (!query) {
void showAndLogExceptionWithTelemetry(
redactableError`No external API usage query found for language ${databaseItem.language}`,

View File

@@ -12,6 +12,7 @@ import {
isFolderAlreadyInWorkspace,
getFirstWorkspaceFolder,
showNeverAskAgainDialog,
isQueryLanguage,
} from "../helpers";
import { ProgressCallback, withProgress } from "../common/vscode/progress";
import {
@@ -32,7 +33,6 @@ import {
setAutogenerateQlPacks,
} from "../config";
import { QlPackGenerator } from "../qlpack-generator";
import { QueryLanguage } from "../common/query-language";
import { App } from "../common/app";
import { existsSync } from "fs";
@@ -739,6 +739,13 @@ export class DatabaseManager extends DisposableObject {
return;
}
if (!isQueryLanguage(databaseItem.language)) {
void this.logger.log(
"Could not create skeleton QL pack because the selected database's language is not supported.",
);
return;
}
const firstWorkspaceFolder = getFirstWorkspaceFolder();
const folderName = `codeql-custom-queries-${databaseItem.language}`;
@@ -769,7 +776,7 @@ export class DatabaseManager extends DisposableObject {
try {
const qlPackGenerator = new QlPackGenerator(
folderName,
databaseItem.language as QueryLanguage,
databaseItem.language,
this.cli,
firstWorkspaceFolder,
);

View File

@@ -25,7 +25,7 @@ import { QueryMetadata } from "./pure/interface-types";
import { telemetryListener } from "./telemetry";
import { RedactableError } from "./pure/errors";
import { getQlPackPath } from "./pure/ql";
import { dbSchemeToLanguage } from "./common/query-language";
import { dbSchemeToLanguage, QueryLanguage } from "./common/query-language";
import { isCodespacesTemplate } from "./config";
import { AppCommandManager } from "./common/commands";
@@ -726,6 +726,10 @@ export async function isLikelyDbLanguageFolder(dbPath: string) {
);
}
export function isQueryLanguage(language: string): language is QueryLanguage {
return Object.values(QueryLanguage).includes(language as QueryLanguage);
}
/**
* Finds the language that a query targets.
* If it can't be autodetected, prompt the user to specify the language manually.
@@ -733,7 +737,7 @@ export async function isLikelyDbLanguageFolder(dbPath: string) {
export async function findLanguage(
cliServer: CodeQLCliServer,
queryUri: Uri | undefined,
): Promise<string | undefined> {
): Promise<QueryLanguage | undefined> {
const uri = queryUri || Window.activeTextEditor?.document.uri;
if (uri !== undefined) {
try {
@@ -743,7 +747,14 @@ export async function findLanguage(
);
const language = Object.keys(queryInfo.byLanguage)[0];
void extLogger.log(`Detected query language: ${language}`);
return language;
if (isQueryLanguage(language)) {
return language;
}
void extLogger.log(
"Query language is unsupported. Select language manually.",
);
} catch (e) {
void extLogger.log(
"Could not autodetect query language. Select language manually.",
@@ -758,7 +769,7 @@ export async function findLanguage(
export async function askForLanguage(
cliServer: CodeQLCliServer,
throwOnEmpty = true,
): Promise<string | undefined> {
): Promise<QueryLanguage | undefined> {
const language = await Window.showQuickPick(
await cliServer.getSupportedLanguages(),
{
@@ -775,7 +786,16 @@ export async function askForLanguage(
"Language not found. Language must be specified manually.",
);
}
return undefined;
}
if (!isQueryLanguage(language)) {
void showAndLogErrorMessage(
`Language '${language}' is not supported. Language must be specified manually.`,
);
return undefined;
}
return language;
}

View File

@@ -41,7 +41,7 @@ export const QUERY_LANGUAGE_TO_DATABASE_REPO: QueryLanguagesToDatabaseMap = {
};
export class SkeletonQueryWizard {
private language: string | undefined;
private language: QueryLanguage | undefined;
private fileName = "example.ql";
private qlPackStoragePath: string | undefined;
@@ -154,6 +154,9 @@ export class SkeletonQueryWizard {
if (this.folderName === undefined) {
throw new Error("Folder name is undefined");
}
if (this.language === undefined) {
throw new Error("Language is undefined");
}
this.progress({
message: "Creating skeleton QL pack around query",
@@ -164,7 +167,7 @@ export class SkeletonQueryWizard {
try {
const qlPackGenerator = new QlPackGenerator(
this.folderName,
this.language as QueryLanguage,
this.language,
this.cliServer,
this.qlPackStoragePath,
);
@@ -181,6 +184,9 @@ export class SkeletonQueryWizard {
if (this.folderName === undefined) {
throw new Error("Folder name is undefined");
}
if (this.language === undefined) {
throw new Error("Language is undefined");
}
this.progress({
message:
@@ -192,7 +198,7 @@ export class SkeletonQueryWizard {
try {
const qlPackGenerator = new QlPackGenerator(
this.folderName,
this.language as QueryLanguage,
this.language,
this.cliServer,
this.qlPackStoragePath,
);

View File

@@ -39,6 +39,7 @@ import {
QLPACK_FILENAMES,
QLPACK_LOCK_FILENAMES,
} from "../pure/ql";
import { QueryLanguage } from "../common/query-language";
export interface QlPack {
name: string;
@@ -76,7 +77,7 @@ async function generateQueryPack(
const targetQueryFileName = join(queryPackDir, packRelativePath);
const workspaceFolders = getOnDiskWorkspaceFolders();
let language: string | undefined;
let language: QueryLanguage | undefined;
// Check if the query is already in a query pack.
// If so, copy the entire query pack to the temporary directory.