Skip to content

Commit eaf3a1c

Browse files
authored
Merge pull request #3031 from github/koesie10/modeled-method-union
Switch `ModeledMethod` to union of types
2 parents 7adc114 + ccaf2ad commit eaf3a1c

36 files changed

+651
-390
lines changed
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

-2
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/flow-model-queries.ts

+7-6
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import {
77
NotificationLogger,
88
showAndLogExceptionWithTelemetry,
99
} from "../common/logging";
10-
import { getModelsAsDataLanguage } from "./languages";
10+
import {
11+
getModelsAsDataLanguageModel,
12+
ModelsAsDataLanguagePredicates,
13+
} from "./languages";
1114
import { ProgressCallback } from "../common/vscode/progress";
1215
import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders";
13-
import { ModeledMethod, ModeledMethodType } from "./modeled-method";
16+
import { ModeledMethod } from "./modeled-method";
1417
import { redactableError } from "../common/errors";
1518
import { telemetryListener } from "../common/vscode/telemetry";
1619
import { runQuery } from "../local-queries/run-query";
@@ -112,7 +115,7 @@ async function resolveFlowQueries(
112115
}
113116

114117
async function runSingleFlowQuery(
115-
type: Exclude<ModeledMethodType, "none">,
118+
type: keyof ModelsAsDataLanguagePredicates,
116119
queryPath: string | undefined,
117120
queryStep: number,
118121
{
@@ -158,9 +161,7 @@ async function runSingleFlowQuery(
158161
}
159162

160163
// Interpret the results
161-
const modelsAsDataLanguage = getModelsAsDataLanguage(language);
162-
163-
const definition = modelsAsDataLanguage.predicates[type];
164+
const definition = getModelsAsDataLanguageModel(language, type);
164165

165166
const bqrsPath = completedQuery.outputDir.bqrsPath;
166167

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

+17-1
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

+16-10
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,30 @@
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

6-
type GenerateMethodDefinition = (method: ModeledMethod) => DataTuple[];
12+
type GenerateMethodDefinition<T> = (method: T) => DataTuple[];
713
type ReadModeledMethod = (row: DataTuple[]) => ModeledMethod;
814

9-
export type ModelsAsDataLanguageModelType = Exclude<ModeledMethodType, "none">;
10-
11-
export type ModelsAsDataLanguagePredicate = {
15+
export type ModelsAsDataLanguagePredicate<T> = {
1216
extensiblePredicate: string;
1317
supportedKinds: string[];
14-
generateMethodDefinition: GenerateMethodDefinition;
18+
generateMethodDefinition: GenerateMethodDefinition<T>;
1519
readModeledMethod: ReadModeledMethod;
1620
};
1721

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

2329
export type ModelsAsDataLanguage = {
2430
/**

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

+2-2
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,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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+
};
40+
}
41+
42+
function createEmptySourceModeledMethod(
43+
methodSignature: MethodSignature,
44+
): ModeledMethod {
45+
return {
46+
...methodSignature,
47+
type: "source",
48+
output: "",
49+
kind: "",
50+
provenance: "manual",
51+
};
52+
}
53+
54+
function createEmptySinkModeledMethod(
55+
methodSignature: MethodSignature,
56+
): SinkModeledMethod {
57+
return {
58+
...methodSignature,
59+
type: "sink",
60+
input: "",
61+
kind: "",
62+
provenance: "manual",
63+
};
64+
}
65+
66+
function createEmptySummaryModeledMethod(
67+
methodSignature: MethodSignature,
68+
): ModeledMethod {
69+
return {
70+
...methodSignature,
71+
type: "summary",
72+
input: "",
73+
output: "",
74+
kind: "",
75+
provenance: "manual",
76+
};
77+
}
78+
79+
function createEmptyNeutralModeledMethod(
80+
methodSignature: MethodSignature,
81+
): ModeledMethod {
82+
return {
83+
...methodSignature,
84+
type: "neutral",
85+
kind: "",
86+
provenance: "manual",
87+
};
88+
}

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

+75-2
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,85 @@ 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+
}
25+
26+
export interface SourceModeledMethod extends MethodSignature {
27+
readonly type: "source";
28+
readonly output: string;
29+
readonly kind: ModeledMethodKind;
30+
readonly provenance: Provenance;
31+
}
32+
33+
export interface SinkModeledMethod extends MethodSignature {
34+
readonly type: "sink";
35+
readonly input: string;
36+
readonly kind: ModeledMethodKind;
37+
readonly provenance: Provenance;
38+
}
39+
40+
export interface SummaryModeledMethod extends MethodSignature {
41+
readonly type: "summary";
2442
readonly input: string;
2543
readonly output: string;
2644
readonly kind: ModeledMethodKind;
2745
readonly provenance: Provenance;
2846
}
2947

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

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

+4-11
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,11 @@ function canonicalizeModeledMethod(
3737
return {
3838
...methodSignature,
3939
type: "none",
40-
input: "",
41-
output: "",
42-
kind: "",
43-
provenance: "manual",
4440
};
4541
case "source":
4642
return {
4743
...methodSignature,
4844
type: "source",
49-
input: "",
5045
output: modeledMethod.output,
5146
kind: modeledMethod.kind,
5247
provenance: "manual",
@@ -56,7 +51,6 @@ function canonicalizeModeledMethod(
5651
...methodSignature,
5752
type: "sink",
5853
input: modeledMethod.input,
59-
output: "",
6054
kind: modeledMethod.kind,
6155
provenance: "manual",
6256
};
@@ -73,13 +67,11 @@ function canonicalizeModeledMethod(
7367
return {
7468
...methodSignature,
7569
type: "neutral",
76-
input: "",
77-
output: "",
7870
kind: modeledMethod.kind,
7971
provenance: "manual",
8072
};
8173
default:
82-
assertNever(modeledMethod.type);
74+
assertNever(modeledMethod);
8375
}
8476
}
8577

@@ -118,7 +110,8 @@ export function validateModeledMethods(
118110
}
119111

120112
const neutralModeledMethods = consideredModeledMethods.filter(
121-
(modeledMethod) => modeledMethod.type === "neutral",
113+
(modeledMethod): modeledMethod is NeutralModeledMethod =>
114+
modeledMethod.type === "neutral",
122115
);
123116

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

0 commit comments

Comments
 (0)