Skip to content

Commit fee7eae

Browse files
Merge pull request #3377 from github/robertbrignull/disable-automodel-button
Disable the automodel button if there are no methods that can be modeled
2 parents e78f7e6 + 2fdafaf commit fee7eae

File tree

6 files changed

+194
-174
lines changed

6 files changed

+194
-174
lines changed

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

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,59 +5,6 @@ import type { AutoModelQueriesResult } from "./auto-model-codeml-queries";
55
import { assertNever } from "../common/helpers-pure";
66
import type { Log } from "sarif";
77
import { gzipEncode } from "../common/zlib";
8-
import type { Method, MethodSignature } from "./method";
9-
import type { ModeledMethod } from "./modeled-method";
10-
import { groupMethods, sortGroupNames, sortMethods } from "./shared/sorting";
11-
12-
/**
13-
* Return the candidates that the model should be run on. This includes limiting the number of
14-
* candidates to the candidate limit and filtering out anything that is already modeled and respecting
15-
* the order in the UI.
16-
* @param mode Whether it is application or framework mode.
17-
* @param methods all methods.
18-
* @param modeledMethodsBySignature the currently modeled methods.
19-
* @returns list of modeled methods that are candidates for modeling.
20-
*/
21-
export function getCandidates(
22-
mode: Mode,
23-
methods: readonly Method[],
24-
modeledMethodsBySignature: Record<string, readonly ModeledMethod[]>,
25-
processedByAutoModelMethods: Set<string>,
26-
): MethodSignature[] {
27-
// Filter out any methods already processed by auto-model
28-
methods = methods.filter(
29-
(m) => !processedByAutoModelMethods.has(m.signature),
30-
);
31-
32-
// Sort the same way as the UI so we send the first ones listed in the UI first
33-
const grouped = groupMethods(methods, mode);
34-
const sortedGroupNames = sortGroupNames(grouped);
35-
const sortedMethods = sortedGroupNames.flatMap((name) =>
36-
sortMethods(grouped[name]),
37-
);
38-
39-
const candidates: MethodSignature[] = [];
40-
41-
for (const method of sortedMethods) {
42-
const modeledMethods: ModeledMethod[] = [
43-
...(modeledMethodsBySignature[method.signature] ?? []),
44-
];
45-
46-
// Anything that is modeled is not a candidate
47-
if (modeledMethods.some((m) => m.type !== "none")) {
48-
continue;
49-
}
50-
51-
// A method that is supported is modeled outside of the model file, so it is not a candidate.
52-
if (method.supported) {
53-
continue;
54-
}
55-
56-
// The rest are candidates
57-
candidates.push(method);
58-
}
59-
return candidates;
60-
}
618

629
/**
6310
* Encode a SARIF log to the format expected by the server: JSON, GZIP-compressed, base64-encoded

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import type { ModeledMethod } from "./modeled-method";
33
import { load as loadYaml } from "js-yaml";
44
import type { ProgressCallback } from "../common/vscode/progress";
55
import { withProgress } from "../common/vscode/progress";
6-
import { createAutoModelRequest, getCandidates } from "./auto-model";
6+
import { createAutoModelRequest } from "./auto-model";
7+
import { getCandidates } from "./shared/auto-model-candidates";
78
import { runAutoModelQueries } from "./auto-model-codeml-queries";
89
import { loadDataExtensionYaml } from "./yaml";
910
import type { ModelRequest, ModelResponse } from "./auto-model-api";
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import type { Method, MethodSignature } from "../method";
2+
import type { ModeledMethod } from "../modeled-method";
3+
import type { Mode } from "./mode";
4+
import { groupMethods, sortGroupNames, sortMethods } from "./sorting";
5+
6+
/**
7+
* Return the candidates that the model should be run on. This includes limiting the number of
8+
* candidates to the candidate limit and filtering out anything that is already modeled and respecting
9+
* the order in the UI.
10+
* @param mode Whether it is application or framework mode.
11+
* @param methods all methods.
12+
* @param modeledMethodsBySignature the currently modeled methods.
13+
* @returns list of modeled methods that are candidates for modeling.
14+
*/
15+
16+
export function getCandidates(
17+
mode: Mode,
18+
methods: readonly Method[],
19+
modeledMethodsBySignature: Record<string, readonly ModeledMethod[]>,
20+
processedByAutoModelMethods: Set<string>,
21+
): MethodSignature[] {
22+
// Filter out any methods already processed by auto-model
23+
methods = methods.filter(
24+
(m) => !processedByAutoModelMethods.has(m.signature),
25+
);
26+
27+
// Sort the same way as the UI so we send the first ones listed in the UI first
28+
const grouped = groupMethods(methods, mode);
29+
const sortedGroupNames = sortGroupNames(grouped);
30+
const sortedMethods = sortedGroupNames.flatMap((name) =>
31+
sortMethods(grouped[name]),
32+
);
33+
34+
const candidates: MethodSignature[] = [];
35+
36+
for (const method of sortedMethods) {
37+
const modeledMethods: ModeledMethod[] = [
38+
...(modeledMethodsBySignature[method.signature] ?? []),
39+
];
40+
41+
// Anything that is modeled is not a candidate
42+
if (modeledMethods.some((m) => m.type !== "none")) {
43+
continue;
44+
}
45+
46+
// A method that is supported is modeled outside of the model file, so it is not a candidate.
47+
if (method.supported) {
48+
continue;
49+
}
50+
51+
// The rest are candidates
52+
candidates.push(method);
53+
}
54+
return candidates;
55+
}

extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
} from "@vscode/webview-ui-toolkit/react";
1515
import type { ModelEditorViewState } from "../../model-editor/shared/view-state";
1616
import type { AccessPathSuggestionOptions } from "../../model-editor/suggestions";
17+
import { getCandidates } from "../../model-editor/shared/auto-model-candidates";
1718

1819
const LibraryContainer = styled.div`
1920
background-color: var(--vscode-peekViewResult-background);
@@ -186,6 +187,17 @@ export const LibraryRow = ({
186187
return methods.some((method) => inProgressMethods.has(method.signature));
187188
}, [methods, inProgressMethods]);
188189

190+
const modelWithAIDisabled = useMemo(() => {
191+
return (
192+
getCandidates(
193+
viewState.mode,
194+
methods,
195+
modeledMethodsMap,
196+
processedByAutoModelMethods,
197+
).length === 0
198+
);
199+
}, [methods, modeledMethodsMap, processedByAutoModelMethods, viewState.mode]);
200+
189201
return (
190202
<LibraryContainer>
191203
<TitleContainer onClick={toggleExpanded} aria-expanded={isExpanded}>
@@ -205,7 +217,11 @@ export const LibraryRow = ({
205217
{hasUnsavedChanges ? <VSCodeTag>UNSAVED</VSCodeTag> : null}
206218
</NameContainer>
207219
{viewState.showLlmButton && !canStopAutoModeling && (
208-
<VSCodeButton appearance="icon" onClick={handleModelWithAI}>
220+
<VSCodeButton
221+
appearance="icon"
222+
disabled={modelWithAIDisabled}
223+
onClick={handleModelWithAI}
224+
>
209225
<Codicon name="lightbulb-autofix" label="Model with AI" />
210226
&nbsp;Model with AI
211227
</VSCodeButton>
Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import {
22
createAutoModelRequest,
33
encodeSarif,
4-
getCandidates,
54
} from "../../../src/model-editor/auto-model";
65
import { Mode } from "../../../src/model-editor/shared/mode";
76
import { AutomodelMode } from "../../../src/model-editor/auto-model-api";
87
import type { AutoModelQueriesResult } from "../../../src/model-editor/auto-model-codeml-queries";
98
import type { Log } from "sarif";
109
import { gzipDecode } from "../../../src/common/zlib";
11-
import type { Method } from "../../../src/model-editor/method";
12-
import { EndpointType } from "../../../src/model-editor/method";
13-
import type { ModeledMethod } from "../../../src/model-editor/modeled-method";
1410

1511
describe("createAutoModelRequest", () => {
1612
const createSarifLog = (queryId: string): Log => {
@@ -84,118 +80,3 @@ describe("createAutoModelRequest", () => {
8480
expect(parsed).toEqual(result.candidates);
8581
});
8682
});
87-
88-
describe("getCandidates", () => {
89-
it("doesn't return methods that are already modelled", () => {
90-
const methods: Method[] = [
91-
{
92-
library: "my.jar",
93-
signature: "org.my.A#x()",
94-
endpointType: EndpointType.Method,
95-
packageName: "org.my",
96-
typeName: "A",
97-
methodName: "x",
98-
methodParameters: "()",
99-
supported: false,
100-
supportedType: "none",
101-
usages: [],
102-
},
103-
];
104-
const modeledMethods: Record<string, ModeledMethod[]> = {
105-
"org.my.A#x()": [
106-
{
107-
type: "neutral",
108-
kind: "sink",
109-
provenance: "manual",
110-
signature: "org.my.A#x()",
111-
endpointType: EndpointType.Method,
112-
packageName: "org.my",
113-
typeName: "A",
114-
methodName: "x",
115-
methodParameters: "()",
116-
},
117-
],
118-
};
119-
const candidates = getCandidates(
120-
Mode.Application,
121-
methods,
122-
modeledMethods,
123-
new Set(),
124-
);
125-
expect(candidates.length).toEqual(0);
126-
});
127-
128-
it("doesn't return methods that are supported from other sources", () => {
129-
const methods: Method[] = [
130-
{
131-
library: "my.jar",
132-
signature: "org.my.A#x()",
133-
endpointType: EndpointType.Method,
134-
packageName: "org.my",
135-
typeName: "A",
136-
methodName: "x",
137-
methodParameters: "()",
138-
supported: true,
139-
supportedType: "none",
140-
usages: [],
141-
},
142-
];
143-
const modeledMethods = {};
144-
const candidates = getCandidates(
145-
Mode.Application,
146-
methods,
147-
modeledMethods,
148-
new Set(),
149-
);
150-
expect(candidates.length).toEqual(0);
151-
});
152-
153-
it("doesn't return methods that are already processed by auto model", () => {
154-
const methods: Method[] = [
155-
{
156-
library: "my.jar",
157-
signature: "org.my.A#x()",
158-
endpointType: EndpointType.Method,
159-
packageName: "org.my",
160-
typeName: "A",
161-
methodName: "x",
162-
methodParameters: "()",
163-
supported: false,
164-
supportedType: "none",
165-
usages: [],
166-
},
167-
];
168-
const modeledMethods = {};
169-
const candidates = getCandidates(
170-
Mode.Application,
171-
methods,
172-
modeledMethods,
173-
new Set(["org.my.A#x()"]),
174-
);
175-
expect(candidates.length).toEqual(0);
176-
});
177-
178-
it("returns methods that are neither modeled nor supported from other sources", () => {
179-
const methods: Method[] = [];
180-
methods.push({
181-
library: "my.jar",
182-
signature: "org.my.A#x()",
183-
endpointType: EndpointType.Method,
184-
packageName: "org.my",
185-
typeName: "A",
186-
methodName: "x",
187-
methodParameters: "()",
188-
supported: false,
189-
supportedType: "none",
190-
usages: [],
191-
});
192-
const modeledMethods = {};
193-
const candidates = getCandidates(
194-
Mode.Application,
195-
methods,
196-
modeledMethods,
197-
new Set(),
198-
);
199-
expect(candidates.length).toEqual(1);
200-
});
201-
});

0 commit comments

Comments
 (0)