Skip to content

Commit 2f9f2f3

Browse files
committed
Merge remote-tracking branch 'origin/main' into koesie10/generate-model-unify
2 parents 693adb5 + eaf3a1c commit 2f9f2f3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+893
-435
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Remove all readonly modifiers from a type.
3+
*/
4+
export type Mutable<T> = {
5+
-readonly [P in keyof T]: T[P];
6+
};

extensions/ql-vscode/src/databases/local-databases/database-manager.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,10 @@ export class DatabaseManager extends DisposableObject {
248248
const firstWorkspaceFolder = getFirstWorkspaceFolder();
249249
const folderName = `codeql-custom-queries-${databaseItem.language}`;
250250

251+
const qlpackStoragePath = join(firstWorkspaceFolder, folderName);
252+
251253
if (
252-
existsSync(join(firstWorkspaceFolder, folderName)) ||
254+
existsSync(qlpackStoragePath) ||
253255
isFolderAlreadyInWorkspace(folderName)
254256
) {
255257
return;
@@ -276,7 +278,8 @@ export class DatabaseManager extends DisposableObject {
276278
const qlPackGenerator = new QlPackGenerator(
277279
databaseItem.language,
278280
this.cli,
279-
join(firstWorkspaceFolder, folderName),
281+
qlpackStoragePath,
282+
qlpackStoragePath,
280283
);
281284
await qlPackGenerator.generate();
282285
} catch (e: unknown) {

extensions/ql-vscode/src/local-queries/qlpack-generator.ts

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
import { mkdir, writeFile } from "fs-extra";
22
import { dump } from "js-yaml";
3-
import { join } from "path";
3+
import { dirname, join } from "path";
44
import { Uri } from "vscode";
55
import { CodeQLCliServer } from "../codeql-cli/cli";
66
import { QueryLanguage } from "../common/query-language";
7+
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
8+
import { basename } from "../common/path";
79

810
export class QlPackGenerator {
9-
private readonly qlpackName: string;
11+
private qlpackName: string | undefined;
1012
private readonly qlpackVersion: string;
1113
private readonly header: string;
1214
private readonly qlpackFileName: string;
@@ -16,8 +18,9 @@ export class QlPackGenerator {
1618
private readonly queryLanguage: QueryLanguage,
1719
private readonly cliServer: CodeQLCliServer,
1820
private readonly storagePath: string,
21+
private readonly queryStoragePath: string,
22+
private readonly includeFolderNameInQlpackName: boolean = false,
1923
) {
20-
this.qlpackName = `getting-started/codeql-extra-queries-${this.queryLanguage}`;
2124
this.qlpackVersion = "1.0.0";
2225
this.header = "# This is an automatically generated file.\n\n";
2326

@@ -26,6 +29,8 @@ export class QlPackGenerator {
2629
}
2730

2831
public async generate() {
32+
this.qlpackName = await this.determineQlpackName();
33+
2934
// create QL pack folder and add to workspace
3035
await this.createWorkspaceFolder();
3136

@@ -39,6 +44,37 @@ export class QlPackGenerator {
3944
await this.createCodeqlPackLockYaml();
4045
}
4146

47+
private async determineQlpackName(): Promise<string> {
48+
let qlpackBaseName = `getting-started/codeql-extra-queries-${this.queryLanguage}`;
49+
if (this.includeFolderNameInQlpackName) {
50+
const folderBasename = basename(dirname(this.folderUri.fsPath));
51+
if (
52+
folderBasename.includes("codeql") ||
53+
folderBasename.includes("queries")
54+
) {
55+
// If the user has already included "codeql" or "queries" in the folder name, don't include it twice
56+
qlpackBaseName = `getting-started/${folderBasename}-${this.queryLanguage}`;
57+
} else {
58+
qlpackBaseName = `getting-started/codeql-extra-queries-${folderBasename}-${this.queryLanguage}`;
59+
}
60+
}
61+
62+
const existingQlPacks = await this.cliServer.resolveQlpacks(
63+
getOnDiskWorkspaceFolders(),
64+
);
65+
const existingQlPackNames = Object.keys(existingQlPacks);
66+
67+
let qlpackName = qlpackBaseName;
68+
let i = 0;
69+
while (existingQlPackNames.includes(qlpackName)) {
70+
i++;
71+
72+
qlpackName = `${qlpackBaseName}-${i}`;
73+
}
74+
75+
return qlpackName;
76+
}
77+
4278
private async createWorkspaceFolder() {
4379
await mkdir(this.folderUri.fsPath);
4480
}
@@ -56,7 +92,7 @@ export class QlPackGenerator {
5692
}
5793

5894
public async createExampleQlFile(fileName = "example.ql") {
59-
const exampleQlFilePath = join(this.folderUri.fsPath, fileName);
95+
const exampleQlFilePath = join(this.queryStoragePath, fileName);
6096

6197
const exampleQl = `
6298
/**

extensions/ql-vscode/src/local-queries/skeleton-query-wizard.ts

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import { showInformationMessageWithAction } from "../common/vscode/dialog";
3434
import { redactableError } from "../common/errors";
3535
import { App } from "../common/app";
3636
import { QueryTreeViewItem } from "../queries-panel/query-tree-view-item";
37-
import { containsPath } from "../common/files";
37+
import { containsPath, pathsEqual } from "../common/files";
3838
import { getQlPackPath } from "../common/ql";
3939
import { load } from "js-yaml";
4040
import { QlPackFile } from "../packaging/qlpack-file";
@@ -284,25 +284,14 @@ export class SkeletonQueryWizard {
284284
}
285285

286286
private async createQlPack() {
287-
if (this.qlPackStoragePath === undefined) {
288-
throw new Error("Query pack storage path is undefined");
289-
}
290-
if (this.language === undefined) {
291-
throw new Error("Language is undefined");
292-
}
293-
294287
this.progress({
295288
message: "Creating skeleton QL pack around query",
296289
step: 2,
297290
maxStep: 3,
298291
});
299292

300293
try {
301-
const qlPackGenerator = new QlPackGenerator(
302-
this.language,
303-
this.cliServer,
304-
this.qlPackStoragePath,
305-
);
294+
const qlPackGenerator = this.createQlPackGenerator();
306295

307296
await qlPackGenerator.generate();
308297
} catch (e: unknown) {
@@ -313,13 +302,6 @@ export class SkeletonQueryWizard {
313302
}
314303

315304
private async createExampleFile() {
316-
if (this.qlPackStoragePath === undefined) {
317-
throw new Error("Folder name is undefined");
318-
}
319-
if (this.language === undefined) {
320-
throw new Error("Language is undefined");
321-
}
322-
323305
this.progress({
324306
message:
325307
"Skeleton query pack already exists. Creating additional query example file.",
@@ -328,11 +310,7 @@ export class SkeletonQueryWizard {
328310
});
329311

330312
try {
331-
const qlPackGenerator = new QlPackGenerator(
332-
this.language,
333-
this.cliServer,
334-
this.qlPackStoragePath,
335-
);
313+
const qlPackGenerator = this.createQlPackGenerator();
336314

337315
this.fileName = await this.determineNextFileName();
338316
await qlPackGenerator.createExampleQlFile(this.fileName);
@@ -475,6 +453,33 @@ export class SkeletonQueryWizard {
475453
return `[${this.fileName}](command:vscode.open?${queryString})`;
476454
}
477455

456+
private createQlPackGenerator() {
457+
if (this.qlPackStoragePath === undefined) {
458+
throw new Error("QL pack storage path is undefined");
459+
}
460+
if (this.queryStoragePath === undefined) {
461+
throw new Error("Query storage path is undefined");
462+
}
463+
if (this.language === undefined) {
464+
throw new Error("Language is undefined");
465+
}
466+
467+
const parentFolder = dirname(this.qlPackStoragePath);
468+
469+
// Only include the folder name in the qlpack name if the qlpack is not in the root of the workspace.
470+
const includeFolderNameInQlpackName = !getOnDiskWorkspaceFolders().some(
471+
(workspaceFolder) => pathsEqual(workspaceFolder, parentFolder),
472+
);
473+
474+
return new QlPackGenerator(
475+
this.language,
476+
this.cliServer,
477+
this.qlPackStoragePath,
478+
this.queryStoragePath,
479+
includeFolderNameInQlpackName,
480+
);
481+
}
482+
478483
public static async findDatabaseItemByNwo(
479484
language: string,
480485
databaseNwo: string,

extensions/ql-vscode/src/model-editor/auto-modeler.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,6 @@ export class AutoModeler {
218218
{
219219
type: "neutral",
220220
kind: "sink",
221-
input: "",
222-
output: "",
223221
provenance: "ai-generated",
224222
signature: candidate.signature,
225223
packageName: candidate.packageName,

extensions/ql-vscode/src/model-editor/languages/languages.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { QueryLanguage } from "../../common/query-language";
2-
import { ModelsAsDataLanguage } from "./models-as-data";
2+
import {
3+
ModelsAsDataLanguage,
4+
ModelsAsDataLanguagePredicates,
5+
} from "./models-as-data";
36
import { ruby } from "./ruby";
47
import { staticLanguage } from "./static";
58

@@ -18,3 +21,16 @@ export function getModelsAsDataLanguage(
1821
}
1922
return definition;
2023
}
24+
25+
export function getModelsAsDataLanguageModel<
26+
T extends keyof ModelsAsDataLanguagePredicates,
27+
>(
28+
language: QueryLanguage,
29+
model: T,
30+
): NonNullable<ModelsAsDataLanguagePredicates[T]> {
31+
const definition = getModelsAsDataLanguage(language).predicates[model];
32+
if (!definition) {
33+
throw new Error(`No models-as-data predicate for ${model}`);
34+
}
35+
return definition;
36+
}

extensions/ql-vscode/src/model-editor/languages/models-as-data.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,24 @@
11
import { MethodDefinition } from "../method";
2-
import { ModeledMethod, ModeledMethodType } from "../modeled-method";
2+
import {
3+
ModeledMethod,
4+
NeutralModeledMethod,
5+
SinkModeledMethod,
6+
SourceModeledMethod,
7+
SummaryModeledMethod,
8+
} from "../modeled-method";
39
import { DataTuple } from "../model-extension-file";
410
import { Mode } from "../shared/mode";
511
import type { QueryConstraints } from "../../local-queries/query-constraints";
612
import { DecodedBqrs } from "../../common/bqrs-cli-types";
713
import { BaseLogger } from "../../common/logging";
814

9-
type GenerateMethodDefinition = (method: ModeledMethod) => DataTuple[];
15+
type GenerateMethodDefinition<T> = (method: T) => DataTuple[];
1016
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;
1117

12-
export type ModelsAsDataLanguageModelType = Exclude<ModeledMethodType, "none">;
13-
14-
export type ModelsAsDataLanguagePredicate = {
18+
export type ModelsAsDataLanguagePredicate<T> = {
1519
extensiblePredicate: string;
1620
supportedKinds: string[];
17-
generateMethodDefinition: GenerateMethodDefinition;
21+
generateMethodDefinition: GenerateMethodDefinition<T>;
1822
readModeledMethod: ReadModeledMethod;
1923
};
2024

@@ -34,10 +38,12 @@ type ModelsAsDataLanguageModelGeneration = {
3438
) => ModeledMethod[];
3539
};
3640

37-
export type ModelsAsDataLanguagePredicates = Record<
38-
ModelsAsDataLanguageModelType,
39-
ModelsAsDataLanguagePredicate
40-
>;
41+
export type ModelsAsDataLanguagePredicates = {
42+
source?: ModelsAsDataLanguagePredicate<SourceModeledMethod>;
43+
sink?: ModelsAsDataLanguagePredicate<SinkModeledMethod>;
44+
summary?: ModelsAsDataLanguagePredicate<SummaryModeledMethod>;
45+
neutral?: ModelsAsDataLanguagePredicate<NeutralModeledMethod>;
46+
};
4147

4248
export type ModelsAsDataLanguage = {
4349
/**

extensions/ql-vscode/src/model-editor/languages/static/generate.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { BaseLogger } from "../../../common/logging";
22
import {
33
ModelsAsDataLanguage,
4-
ModelsAsDataLanguageModelType,
4+
ModelsAsDataLanguagePredicates,
55
} from "../models-as-data";
66
import { DecodedBqrs } from "../../../common/bqrs-cli-types";
77
import { ModeledMethod } from "../../modeled-method";
88
import { basename } from "../../../common/path";
99

10-
const queriesToModel: Record<string, ModelsAsDataLanguageModelType> = {
10+
const queriesToModel: Record<string, keyof ModelsAsDataLanguagePredicates> = {
1111
"CaptureSummaryModels.ql": "summary",
1212
"CaptureSinkModels.ql": "sink",
1313
"CaptureSourceModels.ql": "source",

extensions/ql-vscode/src/model-editor/languages/static/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { ModelsAsDataLanguage } from "../models-as-data";
2-
import { ModeledMethodType, Provenance } from "../../modeled-method";
2+
import { Provenance } from "../../modeled-method";
33
import { DataTuple } from "../../model-extension-file";
44
import { sharedExtensiblePredicates, sharedKinds } from "../shared";
55
import { filterFlowModelQueries, parseFlowModelResults } from "./generate";
@@ -35,7 +35,7 @@ export const staticLanguage: ModelsAsDataLanguage = {
3535
method.provenance,
3636
],
3737
readModeledMethod: (row) => ({
38-
type: "source" as ModeledMethodType,
38+
type: "source",
3939
input: "",
4040
output: row[6] as string,
4141
kind: row[7] as string,

extensions/ql-vscode/src/model-editor/method-modeling/method-modeling-view-provider.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ export class MethodModelingViewProvider extends AbstractWebviewViewProvider<
137137
}
138138
case "revealInModelEditor":
139139
await this.revealInModelEditor(msg.method);
140+
void telemetryListener?.sendUIInteraction(
141+
"method-modeling-reveal-in-model-editor",
142+
);
140143

141144
break;
142145

0 commit comments

Comments
 (0)