Skip to content

Commit 98b0850

Browse files
committed
Switch ModeledMethod to union of types
This allows more disjointed models to be represented more accurately, such as type models.
1 parent 3399212 commit 98b0850

36 files changed

+643
-358
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/model-editor/auto-modeler.ts

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

extensions/ql-vscode/src/model-editor/flow-model-queries.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import { QueryRunner } from "../query-server";
55
import { CodeQLCliServer } from "../codeql-cli/cli";
66
import { showAndLogExceptionWithTelemetry } from "../common/logging";
77
import { extLogger } from "../common/logging/vscode";
8-
import { getModelsAsDataLanguage } from "./languages";
8+
import {
9+
getModelsAsDataLanguageModel,
10+
ModelsAsDataLanguagePredicates,
11+
} from "./languages";
912
import { ProgressCallback } from "../common/vscode/progress";
1013
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
11-
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
14+
import { ModeledMethod } from "./modeled-method";
1215
import { redactableError } from "../common/errors";
1316
import { telemetryListener } from "../common/vscode/telemetry";
1417
import { runQuery } from "../local-queries/run-query";
@@ -109,7 +112,7 @@ async function resolveFlowQueries(
109112
}
110113

111114
async function runSingleFlowQuery(
112-
type: Exclude<ModeledMethodType, "none">,
115+
type: keyof ModelsAsDataLanguagePredicates,
113116
queryPath: string | undefined,
114117
queryStep: number,
115118
{
@@ -154,9 +157,7 @@ async function runSingleFlowQuery(
154157
}
155158

156159
// Interpret the results
157-
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
158-
159-
const definition = modelsAsDataLanguage.predicates[type];
160+
const definition = getModelsAsDataLanguageModel(language, type);
160161

161162
const bqrsPath = completedQuery.outputDir.bqrsPath;
162163

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 { staticLanguage } from "./static";
47

58
const languages: Partial<Record<QueryLanguage, ModelsAsDataLanguage>> = {
@@ -16,3 +19,16 @@ export function getModelsAsDataLanguage(
1619
}
1720
return definition;
1821
}
22+
23+
export function getModelsAsDataLanguageModel<
24+
T extends keyof ModelsAsDataLanguagePredicates,
25+
>(
26+
language: QueryLanguage,
27+
model: T,
28+
): NonNullable<ModelsAsDataLanguagePredicates[T]> {
29+
const definition = getModelsAsDataLanguage(language).predicates[model];
30+
if (!definition) {
31+
throw new Error(`No models-as-data definition for ${model}`);
32+
}
33+
return definition;
34+
}

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,23 +1,29 @@
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

5-
type GenerateMethodDefinition = (method: ModeledMethod) => DataTuple[];
11+
type GenerateMethodDefinition<T> = (method: T) => DataTuple[];
612
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;
713

8-
export type ModelsAsDataLanguageModelType = Exclude<ModeledMethodType, "none">;
9-
10-
export type ModelsAsDataLanguagePredicate = {
14+
export type ModelsAsDataLanguagePredicate<T> = {
1115
extensiblePredicate: string;
1216
supportedKinds: string[];
13-
generateMethodDefinition: GenerateMethodDefinition;
17+
generateMethodDefinition: GenerateMethodDefinition<T>;
1418
readModeledMethod: ReadModeledMethod;
1519
};
1620

17-
export type ModelsAsDataLanguagePredicates = Record<
18-
ModelsAsDataLanguageModelType,
19-
ModelsAsDataLanguagePredicate
20-
>;
21+
export type ModelsAsDataLanguagePredicates = {
22+
source?: ModelsAsDataLanguagePredicate<SourceModeledMethod>;
23+
sink?: ModelsAsDataLanguagePredicate<SinkModeledMethod>;
24+
summary?: ModelsAsDataLanguagePredicate<SummaryModeledMethod>;
25+
neutral?: ModelsAsDataLanguagePredicate<NeutralModeledMethod>;
26+
};
2127

2228
export type ModelsAsDataLanguage = {
2329
createMethodSignature: (method: MethodDefinition) => string;

extensions/ql-vscode/src/model-editor/languages/static.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

@@ -34,7 +34,7 @@ export const staticLanguage: ModelsAsDataLanguage = {
3434
method.provenance,
3535
],
3636
readModeledMethod: (row) => ({
37-
type: "source" as ModeledMethodType,
37+
type: "source",
3838
input: "",
3939
output: row[6] as string,
4040
kind: row[7] as string,
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { ModeledMethod, SinkModeledMethod } from "./modeled-method";
2+
import { MethodSignature } from "./method";
3+
import { assertNever } from "../common/helpers-pure";
4+
5+
export function createEmptyModeledMethod(
6+
type: ModeledMethod["type"],
7+
methodSignature: MethodSignature,
8+
) {
9+
const canonicalMethodSignature: MethodSignature = {
10+
packageName: methodSignature.packageName,
11+
typeName: methodSignature.typeName,
12+
methodName: methodSignature.methodName,
13+
methodParameters: methodSignature.methodParameters,
14+
signature: methodSignature.signature,
15+
};
16+
17+
switch (type) {
18+
case "none":
19+
return createEmptyNoneModeledMethod(canonicalMethodSignature);
20+
case "source":
21+
return createEmptySourceModeledMethod(canonicalMethodSignature);
22+
case "sink":
23+
return createEmptySinkModeledMethod(canonicalMethodSignature);
24+
case "summary":
25+
return createEmptySummaryModeledMethod(canonicalMethodSignature);
26+
case "neutral":
27+
return createEmptyNeutralModeledMethod(canonicalMethodSignature);
28+
default:
29+
assertNever(type);
30+
}
31+
}
32+
33+
function createEmptyNoneModeledMethod(
34+
methodSignature: MethodSignature,
35+
): ModeledMethod {
36+
return {
37+
...methodSignature,
38+
type: "none",
39+
provenance: "manual",
40+
};
41+
}
42+
43+
function createEmptySourceModeledMethod(
44+
methodSignature: MethodSignature,
45+
): ModeledMethod {
46+
return {
47+
...methodSignature,
48+
type: "source",
49+
output: "",
50+
kind: "",
51+
provenance: "manual",
52+
};
53+
}
54+
55+
function createEmptySinkModeledMethod(
56+
methodSignature: MethodSignature,
57+
): SinkModeledMethod {
58+
return {
59+
...methodSignature,
60+
type: "sink",
61+
input: "",
62+
kind: "",
63+
provenance: "manual",
64+
};
65+
}
66+
67+
function createEmptySummaryModeledMethod(
68+
methodSignature: MethodSignature,
69+
): ModeledMethod {
70+
return {
71+
...methodSignature,
72+
type: "summary",
73+
input: "",
74+
output: "",
75+
kind: "",
76+
provenance: "manual",
77+
};
78+
}
79+
80+
function createEmptyNeutralModeledMethod(
81+
methodSignature: MethodSignature,
82+
): ModeledMethod {
83+
return {
84+
...methodSignature,
85+
type: "neutral",
86+
kind: "",
87+
provenance: "manual",
88+
};
89+
}

extensions/ql-vscode/src/model-editor/modeled-method.ts

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,87 @@ export type Provenance =
1919
// Entered by the user in the editor manually
2020
| "manual";
2121

22-
export interface ModeledMethod extends MethodSignature {
23-
readonly type: ModeledMethodType;
22+
export interface NoneModeledMethod extends MethodSignature {
23+
readonly type: "none";
24+
// Provenance is always propagated
25+
readonly provenance: Provenance;
26+
}
27+
28+
export interface SourceModeledMethod extends MethodSignature {
29+
readonly type: "source";
30+
readonly output: string;
31+
readonly kind: ModeledMethodKind;
32+
readonly provenance: Provenance;
33+
}
34+
35+
export interface SinkModeledMethod extends MethodSignature {
36+
readonly type: "sink";
37+
readonly input: string;
38+
readonly kind: ModeledMethodKind;
39+
readonly provenance: Provenance;
40+
}
41+
42+
export interface SummaryModeledMethod extends MethodSignature {
43+
readonly type: "summary";
2444
readonly input: string;
2545
readonly output: string;
2646
readonly kind: ModeledMethodKind;
2747
readonly provenance: Provenance;
2848
}
2949

50+
export interface NeutralModeledMethod extends MethodSignature {
51+
readonly type: "neutral";
52+
readonly kind: ModeledMethodKind;
53+
readonly provenance: Provenance;
54+
}
55+
56+
export type ModeledMethod =
57+
| NoneModeledMethod
58+
| SourceModeledMethod
59+
| SinkModeledMethod
60+
| SummaryModeledMethod
61+
| NeutralModeledMethod;
62+
3063
export type ModeledMethodKind = string;
64+
65+
export function modeledMethodSupportsKind(
66+
modeledMethod: ModeledMethod,
67+
): modeledMethod is
68+
| SourceModeledMethod
69+
| SinkModeledMethod
70+
| SummaryModeledMethod
71+
| NeutralModeledMethod {
72+
return (
73+
modeledMethod.type === "source" ||
74+
modeledMethod.type === "sink" ||
75+
modeledMethod.type === "summary" ||
76+
modeledMethod.type === "neutral"
77+
);
78+
}
79+
80+
export function modeledMethodSupportsInput(
81+
modeledMethod: ModeledMethod,
82+
): modeledMethod is SinkModeledMethod | SummaryModeledMethod {
83+
return modeledMethod.type === "sink" || modeledMethod.type === "summary";
84+
}
85+
86+
export function modeledMethodSupportsOutput(
87+
modeledMethod: ModeledMethod,
88+
): modeledMethod is SourceModeledMethod | SummaryModeledMethod {
89+
return modeledMethod.type === "source" || modeledMethod.type === "summary";
90+
}
91+
92+
export function modeledMethodSupportsProvenance(
93+
modeledMethod: ModeledMethod,
94+
): modeledMethod is
95+
| SourceModeledMethod
96+
| SinkModeledMethod
97+
| SummaryModeledMethod
98+
| NeutralModeledMethod {
99+
return (
100+
modeledMethod.type === "source" ||
101+
modeledMethod.type === "sink" ||
102+
modeledMethod.type === "summary" ||
103+
modeledMethod.type === "neutral"
104+
);
105+
}

extensions/ql-vscode/src/model-editor/shared/validation.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ModeledMethod } from "../modeled-method";
1+
import { ModeledMethod, NeutralModeledMethod } from "../modeled-method";
22
import { MethodSignature } from "../method";
33
import { assertNever } from "../../common/helpers-pure";
44

@@ -37,16 +37,12 @@ function canonicalizeModeledMethod(
3737
return {
3838
...methodSignature,
3939
type: "none",
40-
input: "",
41-
output: "",
42-
kind: "",
4340
provenance: "manual",
4441
};
4542
case "source":
4643
return {
4744
...methodSignature,
4845
type: "source",
49-
input: "",
5046
output: modeledMethod.output,
5147
kind: modeledMethod.kind,
5248
provenance: "manual",
@@ -56,7 +52,6 @@ function canonicalizeModeledMethod(
5652
...methodSignature,
5753
type: "sink",
5854
input: modeledMethod.input,
59-
output: "",
6055
kind: modeledMethod.kind,
6156
provenance: "manual",
6257
};
@@ -73,13 +68,11 @@ function canonicalizeModeledMethod(
7368
return {
7469
...methodSignature,
7570
type: "neutral",
76-
input: "",
77-
output: "",
7871
kind: modeledMethod.kind,
7972
provenance: "manual",
8073
};
8174
default:
82-
assertNever(modeledMethod.type);
75+
assertNever(modeledMethod);
8376
}
8477
}
8578

@@ -118,7 +111,8 @@ export function validateModeledMethods(
118111
}
119112

120113
const neutralModeledMethods = consideredModeledMethods.filter(
121-
(modeledMethod) => modeledMethod.type === "neutral",
114+
(modeledMethod): modeledMethod is NeutralModeledMethod =>
115+
modeledMethod.type === "neutral",
122116
);
123117

124118
const neutralModeledMethodsByKind = new Map<string, ModeledMethod[]>();

0 commit comments

Comments
 (0)