Skip to content

Commit c6c27f4

Browse files
authored
Add access path suggestions to MaD model for Ruby (#3303)
1 parent b7ee9f9 commit c6c27f4

File tree

4 files changed

+131
-0
lines changed

4 files changed

+131
-0
lines changed

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

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
rubyMethodSignature,
1212
rubyPath,
1313
} from "./access-paths";
14+
import { parseAccessPathSuggestionsResults } from "./suggestions";
1415

1516
export const ruby: ModelsAsDataLanguage = {
1617
availableModes: [Mode.Framework],
@@ -168,6 +169,9 @@ export const ruby: ModelsAsDataLanguage = {
168169
},
169170
parseResults: parseGenerateModelResults,
170171
},
172+
accessPathSuggestions: {
173+
parseResults: parseAccessPathSuggestionsResults,
174+
},
171175
getArgumentOptions: (method) => {
172176
const argumentsList = getArgumentsList(method.methodParameters).map(
173177
(argument, index): MethodArgument => {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { BaseLogger } from "../../../common/logging";
2+
import type { DecodedBqrsChunk } from "../../../common/bqrs-cli-types";
3+
import type { ModelsAsDataLanguage } from "../models-as-data";
4+
import type { AccessPathSuggestionRow } from "../../suggestions";
5+
import { isDefinitionType } from "../../suggestions";
6+
import { parseRubyMethodFromPath, rubyMethodSignature } from "./access-paths";
7+
8+
export function parseAccessPathSuggestionsResults(
9+
bqrs: DecodedBqrsChunk,
10+
_modelsAsDataLanguage: ModelsAsDataLanguage,
11+
logger: BaseLogger,
12+
): AccessPathSuggestionRow[] {
13+
return bqrs.tuples
14+
.map((tuple, index): AccessPathSuggestionRow | null => {
15+
const row = tuple.filter(
16+
(value): value is string => typeof value === "string",
17+
);
18+
19+
if (row.length !== 5) {
20+
void logger.log(
21+
`Skipping result ${index} because it has the wrong length`,
22+
);
23+
return null;
24+
}
25+
26+
const type = row[0];
27+
const methodName = parseRubyMethodFromPath(row[1]);
28+
const value = row[2];
29+
const details = row[3];
30+
const definitionType = row[4];
31+
32+
if (!isDefinitionType(definitionType)) {
33+
void logger.log(
34+
`Skipping result ${index} because it has an invalid definition type`,
35+
);
36+
return null;
37+
}
38+
39+
return {
40+
method: {
41+
packageName: "",
42+
typeName: type,
43+
methodName,
44+
methodParameters: "",
45+
signature: rubyMethodSignature(type, methodName),
46+
},
47+
value,
48+
details,
49+
definitionType,
50+
};
51+
})
52+
.filter(
53+
(suggestion): suggestion is AccessPathSuggestionRow =>
54+
suggestion !== null,
55+
);
56+
}

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

+8
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,11 @@ export type AccessPathOption = {
3737
details?: string;
3838
followup?: AccessPathOption[];
3939
};
40+
41+
export function isDefinitionType(
42+
value: string,
43+
): value is AccessPathSuggestionDefinitionType {
44+
return Object.values(AccessPathSuggestionDefinitionType).includes(
45+
value as AccessPathSuggestionDefinitionType,
46+
);
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import type { DecodedBqrsChunk } from "../../../../../src/common/bqrs-cli-types";
2+
import { ruby } from "../../../../../src/model-editor/languages/ruby";
3+
import { createMockLogger } from "../../../../__mocks__/loggerMock";
4+
import { parseAccessPathSuggestionsResults } from "../../../../../src/model-editor/languages/ruby/suggestions";
5+
6+
describe("parseAccessPathSuggestionsResults", () => {
7+
it("should parse the results", async () => {
8+
const bqrsChunk: DecodedBqrsChunk = {
9+
columns: [
10+
{
11+
name: "type",
12+
kind: "String",
13+
},
14+
{
15+
name: "path",
16+
kind: "String",
17+
},
18+
{
19+
name: "value",
20+
kind: "String",
21+
},
22+
{
23+
name: "details",
24+
kind: "String",
25+
},
26+
{
27+
name: "defType",
28+
kind: "String",
29+
},
30+
],
31+
tuples: [
32+
[
33+
"Correctness",
34+
"Method[assert!]",
35+
"Argument[self]",
36+
"self in assert!",
37+
"parameter",
38+
],
39+
],
40+
};
41+
42+
const result = parseAccessPathSuggestionsResults(
43+
bqrsChunk,
44+
ruby,
45+
createMockLogger(),
46+
);
47+
48+
expect(result).toEqual([
49+
{
50+
method: {
51+
packageName: "",
52+
typeName: "Correctness",
53+
methodName: "assert!",
54+
methodParameters: "",
55+
signature: "Correctness#assert!",
56+
},
57+
value: "Argument[self]",
58+
details: "self in assert!",
59+
definitionType: "parameter",
60+
},
61+
]);
62+
});
63+
});

0 commit comments

Comments
 (0)