Skip to content

Commit a3306da

Browse files
author
Dave Bartolomeo
committed
Implement CodeQL debug adapter
1 parent 0da5aab commit a3306da

11 files changed

Lines changed: 857 additions & 5 deletions

extensions/ql-vscode/package-lock.json

Lines changed: 31 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/ql-vscode/package.json

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,40 @@
7676
"editor.wordBasedSuggestions": false
7777
}
7878
},
79+
"debuggers": [
80+
{
81+
"type": "codeql",
82+
"label": "CodeQL Debugger",
83+
"languages": [
84+
"ql"
85+
],
86+
"configurationAttributes": {
87+
"launch": {
88+
"properties": {
89+
"query": {
90+
"type": "string",
91+
"description": "Path to query file (.ql)",
92+
"default": "${file}"
93+
},
94+
"database": {
95+
"type": "string",
96+
"description": "Path to the target database"
97+
},
98+
"additionalPacks": {
99+
"type": [
100+
"array",
101+
"string"
102+
],
103+
"description": "Additional folders to search for library packs. Defaults to searching all workspace folders."
104+
}
105+
}
106+
}
107+
},
108+
"variables": {
109+
"currentDatabase": "codeQL.getCurrentDatabase"
110+
}
111+
}
112+
],
79113
"jsonValidation": [
80114
{
81115
"fileMatch": "GitHub.vscode-codeql/databases.json",
@@ -444,6 +478,10 @@
444478
"command": "codeQL.setCurrentDatabase",
445479
"title": "CodeQL: Set Current Database"
446480
},
481+
{
482+
"command": "codeQL.getCurrentDatabase",
483+
"title": "CodeQL: Get Current Database"
484+
},
447485
{
448486
"command": "codeQL.viewAst",
449487
"title": "CodeQL: View AST"
@@ -1062,6 +1100,10 @@
10621100
"command": "codeQL.setCurrentDatabase",
10631101
"when": "false"
10641102
},
1103+
{
1104+
"command": "codeQL.getCurrentDatabase",
1105+
"when": "false"
1106+
},
10651107
{
10661108
"command": "codeQL.viewAst",
10671109
"when": "resourceScheme == codeql-zip-archive"
@@ -1434,6 +1476,8 @@
14341476
"@octokit/plugin-retry": "^3.0.9",
14351477
"@octokit/rest": "^19.0.4",
14361478
"@vscode/codicons": "^0.0.31",
1479+
"@vscode/debugadapter": "^1.59.0",
1480+
"@vscode/debugprotocol": "^1.59.0",
14371481
"@vscode/webview-ui-toolkit": "^1.0.1",
14381482
"ajv": "^8.11.0",
14391483
"child-process-promise": "^2.2.1",

extensions/ql-vscode/src/common/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ export type LocalDatabasesCommands = {
180180

181181
// Internal commands
182182
"codeQLDatabases.removeOrphanedDatabases": () => Promise<void>;
183+
"codeQL.getCurrentDatabase": () => Promise<string | undefined>;
183184
};
184185

185186
// Commands tied to variant analysis
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import {
2+
CancellationToken,
3+
DebugConfiguration,
4+
DebugConfigurationProvider,
5+
WorkspaceFolder,
6+
} from "vscode";
7+
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage } from "../helpers";
8+
9+
interface QLDebugArgs {
10+
query: string;
11+
database: string;
12+
additionalPacks: string[] | string;
13+
}
14+
15+
type QLDebugConfiguration = DebugConfiguration & Partial<QLDebugArgs>;
16+
17+
export type QLResolvedDebugConfiguration = DebugConfiguration &
18+
QLDebugArgs & {
19+
additionalPacks: string[];
20+
};
21+
22+
export class QLDebugConfigurationProvider
23+
implements DebugConfigurationProvider
24+
{
25+
public resolveDebugConfiguration(
26+
_folder: WorkspaceFolder | undefined,
27+
debugConfiguration: DebugConfiguration,
28+
_token?: CancellationToken,
29+
): DebugConfiguration {
30+
const qlConfiguration = <QLDebugConfiguration>debugConfiguration;
31+
32+
// Fill in defaults
33+
const resultConfiguration: QLDebugConfiguration = {
34+
...qlConfiguration,
35+
query: qlConfiguration.query ?? "${file}",
36+
database: qlConfiguration.database ?? "${command:currentDatabase}",
37+
};
38+
39+
return resultConfiguration;
40+
}
41+
42+
public async resolveDebugConfigurationWithSubstitutedVariables(
43+
_folder: WorkspaceFolder | undefined,
44+
debugConfiguration: DebugConfiguration,
45+
_token?: CancellationToken,
46+
): Promise<DebugConfiguration | null> {
47+
const qlConfiguration = <QLDebugConfiguration>debugConfiguration;
48+
if (qlConfiguration.query === undefined) {
49+
await showAndLogErrorMessage(
50+
"No query was specified in the debug configuration.",
51+
);
52+
return null;
53+
}
54+
if (qlConfiguration.database === undefined) {
55+
await showAndLogErrorMessage(
56+
"No database was specified in the debug configuration.",
57+
);
58+
return null;
59+
}
60+
61+
const resultConfiguration: QLResolvedDebugConfiguration = {
62+
...qlConfiguration,
63+
query: qlConfiguration.query,
64+
database: qlConfiguration.database,
65+
additionalPacks:
66+
// Fill in defaults here, instead of in `resolveDebugConfiguration`, to avoid the highly
67+
// unusual case where one of the workspace folder paths contains something that looks like a
68+
// variable substitution.
69+
qlConfiguration.additionalPacks === undefined
70+
? getOnDiskWorkspaceFolders()
71+
: typeof qlConfiguration.additionalPacks === "string"
72+
? [qlConfiguration.additionalPacks]
73+
: qlConfiguration.additionalPacks,
74+
};
75+
76+
return resultConfiguration;
77+
}
78+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { DebugProtocol } from "@vscode/debugprotocol";
2+
import { QueryResultType } from "../pure/new-messages";
3+
4+
export type Event = { type: "event" };
5+
6+
export type StoppedEvent = DebugProtocol.StoppedEvent &
7+
Event & { event: "stopped" };
8+
9+
export type InitializedEvent = DebugProtocol.InitializedEvent &
10+
Event & { event: "initialized" };
11+
12+
export type OutputEvent = DebugProtocol.OutputEvent &
13+
Event & { event: "output" };
14+
15+
export interface EvaluationStartedEventBody {
16+
id: string;
17+
outputDir: string;
18+
}
19+
20+
export interface EvaluationStartedEvent extends DebugProtocol.Event {
21+
event: "codeql-evaluation-started";
22+
body: EvaluationStartedEventBody;
23+
}
24+
25+
export interface EvaluationCompletedEventBody {
26+
resultType: QueryResultType;
27+
message: string | undefined;
28+
evaluationTime: number;
29+
}
30+
31+
export interface EvaluationCompletedEvent extends DebugProtocol.Event {
32+
event: "codeql-evaluation-completed";
33+
body: EvaluationCompletedEventBody;
34+
}
35+
36+
export type AnyEvent =
37+
| StoppedEvent
38+
| InitializedEvent
39+
| OutputEvent
40+
| EvaluationStartedEvent
41+
| EvaluationCompletedEvent;
42+
43+
export type Request = DebugProtocol.Request & { type: "request" };
44+
45+
export interface DebugResultRequest extends Request {
46+
command: "codeql-debug-result";
47+
arguments: undefined;
48+
}
49+
50+
export type InitializeRequest = DebugProtocol.InitializeRequest &
51+
Request & { command: "initialize" };
52+
53+
export type AnyRequest = InitializeRequest | DebugResultRequest;
54+
55+
export type Response = DebugProtocol.Response & { type: "response" };
56+
57+
export type InitializeResponse = DebugProtocol.InitializeResponse &
58+
Response & { command: "initialize" };
59+
60+
export type AnyResponse = InitializeResponse;
61+
62+
export type AnyProtocolMessage = AnyEvent | AnyRequest | AnyResponse;
63+
64+
export interface LaunchRequestArguments
65+
extends DebugProtocol.LaunchRequestArguments {
66+
query: string;
67+
database: string;
68+
additionalPacks: string[];
69+
}

0 commit comments

Comments
 (0)