Skip to content

Commit cb707ae

Browse files
committed
Use new raw result types for variant analyses
1 parent cd26f27 commit cb707ae

20 files changed

Lines changed: 801 additions & 393 deletions

File tree

extensions/ql-vscode/src/common/bqrs-result.ts

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,30 @@ import {
44
ColumnKindCode,
55
DecodedBqrsChunk,
66
EntityValue as BqrsEntityValue,
7+
LineColumnLocation,
78
ResultSetSchema,
89
UrlValue as BqrsUrlValue,
10+
WholeFileLocation,
911
} from "./bqrs-cli-types";
1012
import {
1113
CellValue,
1214
Column,
1315
ColumnKind,
1416
EntityValue,
1517
RawResultSet,
16-
Tuple,
18+
Row,
1719
UrlValue,
20+
UrlValueResolvable,
1821
} from "./raw-result-types";
1922
import { assertNever } from "./helpers-pure";
23+
import { isEmptyPath } from "./bqrs-utils";
2024

2125
export function bqrsToResultSet(
2226
schema: ResultSetSchema,
2327
chunk: DecodedBqrsChunk,
2428
): RawResultSet {
2529
const name = schema.name;
26-
const rows = schema.rows;
30+
const totalRowCount = schema.rows;
2731
const nextPageOffset = chunk.next;
2832

2933
const columns = schema.columns.map(
@@ -33,15 +37,15 @@ export function bqrsToResultSet(
3337
}),
3438
);
3539

36-
const tuples = chunk.tuples.map(
37-
(tuple): Tuple => tuple.map((cell): CellValue => mapCellValue(cell)),
40+
const rows = chunk.tuples.map(
41+
(tuple): Row => tuple.map((cell): CellValue => mapCellValue(cell)),
3842
);
3943

4044
return {
4145
name,
42-
rows,
46+
totalRowCount,
4347
columns,
44-
tuples,
48+
rows,
4549
nextPageOffset,
4650
};
4751
}
@@ -98,15 +102,27 @@ function mapEntityValue(cellValue: BqrsEntityValue): EntityValue {
98102
};
99103
}
100104

101-
function mapUrlValue(urlValue: BqrsUrlValue): UrlValue {
105+
function mapUrlValue(urlValue: BqrsUrlValue): UrlValue | undefined {
102106
if (typeof urlValue === "string") {
107+
const location = tryGetLocationFromString(urlValue);
108+
if (location !== undefined) {
109+
return location;
110+
}
111+
103112
return {
104113
type: "string",
105114
value: urlValue,
106115
};
107116
}
108117

109-
if (urlValue.startLine) {
118+
if (isWholeFileLoc(urlValue)) {
119+
return {
120+
type: "wholeFileLocation",
121+
uri: urlValue.uri,
122+
};
123+
}
124+
125+
if (isLineColumnLoc(urlValue)) {
110126
return {
111127
type: "lineColumnLocation",
112128
uri: urlValue.uri,
@@ -117,8 +133,64 @@ function mapUrlValue(urlValue: BqrsUrlValue): UrlValue {
117133
};
118134
}
119135

120-
return {
121-
type: "wholeFileLocation",
122-
uri: urlValue.uri,
123-
};
136+
return undefined;
137+
}
138+
139+
function isLineColumnLoc(loc: BqrsUrlValue): loc is LineColumnLocation {
140+
return (
141+
typeof loc !== "string" &&
142+
!isEmptyPath(loc.uri) &&
143+
"startLine" in loc &&
144+
"startColumn" in loc &&
145+
"endLine" in loc &&
146+
"endColumn" in loc
147+
);
148+
}
149+
150+
function isWholeFileLoc(loc: BqrsUrlValue): loc is WholeFileLocation {
151+
return (
152+
typeof loc !== "string" && !isEmptyPath(loc.uri) && !isLineColumnLoc(loc)
153+
);
154+
}
155+
156+
/**
157+
* The CodeQL filesystem libraries use this pattern in `getURL()` predicates
158+
* to describe the location of an entire filesystem resource.
159+
* Such locations appear as `StringLocation`s instead of `FivePartLocation`s.
160+
*
161+
* Folder resources also get similar URLs, but with the `folder` scheme.
162+
* They are deliberately ignored here, since there is no suitable location to show the user.
163+
*/
164+
const FILE_LOCATION_REGEX = /file:\/\/(.+):([0-9]+):([0-9]+):([0-9]+):([0-9]+)/;
165+
166+
function tryGetLocationFromString(loc: string): UrlValueResolvable | undefined {
167+
const matches = FILE_LOCATION_REGEX.exec(loc);
168+
if (matches && matches.length > 1 && matches[1]) {
169+
if (isWholeFileMatch(matches)) {
170+
return {
171+
type: "wholeFileLocation",
172+
uri: matches[1],
173+
};
174+
} else {
175+
return {
176+
type: "lineColumnLocation",
177+
uri: matches[1],
178+
startLine: Number(matches[2]),
179+
startColumn: Number(matches[3]),
180+
endLine: Number(matches[4]),
181+
endColumn: Number(matches[5]),
182+
};
183+
}
184+
}
185+
186+
return undefined;
187+
}
188+
189+
function isWholeFileMatch(matches: RegExpExecArray): boolean {
190+
return (
191+
matches[2] === "0" &&
192+
matches[3] === "0" &&
193+
matches[4] === "0" &&
194+
matches[5] === "0"
195+
);
124196
}
Lines changed: 66 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
1+
import { createRemoteFileRef } from "../common/location-link-utils";
12
import {
3+
isUrlValueResolvable,
24
UrlValue,
3-
ResolvableLocationValue,
5+
UrlValueResolvable,
6+
} from "./raw-result-types";
7+
import {
48
LineColumnLocation,
9+
UrlValue as BqrsUrlValue,
510
WholeFileLocation,
611
} from "./bqrs-cli-types";
7-
import { createRemoteFileRef } from "../common/location-link-utils";
12+
13+
/**
14+
* Checks whether the file path is empty. If so, we do not want to render this location
15+
* as a link.
16+
*/
17+
export function isEmptyPath(uriStr: string) {
18+
return !uriStr || uriStr === "file:/";
19+
}
820

921
/**
1022
* The CodeQL filesystem libraries use this pattern in `getURL()` predicates
@@ -20,13 +32,25 @@ const FILE_LOCATION_REGEX = /file:\/\/(.+):([0-9]+):([0-9]+):([0-9]+):([0-9]+)/;
2032
* @param loc The location to test.
2133
*/
2234
export function tryGetResolvableLocation(
23-
loc: UrlValue | undefined,
24-
): ResolvableLocationValue | undefined {
25-
let resolvedLoc;
35+
loc: BqrsUrlValue | undefined,
36+
): UrlValueResolvable | undefined {
37+
let resolvedLoc: UrlValueResolvable | undefined;
2638
if (loc === undefined) {
2739
resolvedLoc = undefined;
28-
} else if (isWholeFileLoc(loc) || isLineColumnLoc(loc)) {
29-
resolvedLoc = loc as ResolvableLocationValue;
40+
} else if (isWholeFileLoc(loc)) {
41+
resolvedLoc = {
42+
type: "wholeFileLocation",
43+
uri: loc.uri,
44+
};
45+
} else if (isLineColumnLoc(loc)) {
46+
resolvedLoc = {
47+
type: "lineColumnLocation",
48+
uri: loc.uri,
49+
startLine: loc.startLine,
50+
startColumn: loc.startColumn,
51+
endLine: loc.endLine,
52+
endColumn: loc.endColumn,
53+
};
3054
} else if (isStringLoc(loc)) {
3155
resolvedLoc = tryGetLocationFromString(loc);
3256
} else {
@@ -38,15 +62,17 @@ export function tryGetResolvableLocation(
3862

3963
export function tryGetLocationFromString(
4064
loc: string,
41-
): ResolvableLocationValue | undefined {
65+
): UrlValueResolvable | undefined {
4266
const matches = FILE_LOCATION_REGEX.exec(loc);
4367
if (matches && matches.length > 1 && matches[1]) {
4468
if (isWholeFileMatch(matches)) {
4569
return {
70+
type: "wholeFileLocation",
4671
uri: matches[1],
47-
} as WholeFileLocation;
72+
};
4873
} else {
4974
return {
75+
type: "lineColumnLocation",
5076
uri: matches[1],
5177
startLine: Number(matches[2]),
5278
startColumn: Number(matches[3]),
@@ -68,17 +94,7 @@ function isWholeFileMatch(matches: RegExpExecArray): boolean {
6894
);
6995
}
7096

71-
/**
72-
* Checks whether the file path is empty. If so, we do not want to render this location
73-
* as a link.
74-
*
75-
* @param uri A file uri
76-
*/
77-
export function isEmptyPath(uriStr: string) {
78-
return !uriStr || uriStr === "file:/";
79-
}
80-
81-
export function isLineColumnLoc(loc: UrlValue): loc is LineColumnLocation {
97+
export function isLineColumnLoc(loc: BqrsUrlValue): loc is LineColumnLocation {
8298
return (
8399
typeof loc !== "string" &&
84100
!isEmptyPath(loc.uri) &&
@@ -89,23 +105,36 @@ export function isLineColumnLoc(loc: UrlValue): loc is LineColumnLocation {
89105
);
90106
}
91107

92-
export function isWholeFileLoc(loc: UrlValue): loc is WholeFileLocation {
108+
export function isWholeFileLoc(loc: BqrsUrlValue): loc is WholeFileLocation {
93109
return (
94110
typeof loc !== "string" && !isEmptyPath(loc.uri) && !isLineColumnLoc(loc)
95111
);
96112
}
97113

98-
export function isStringLoc(loc: UrlValue): loc is string {
114+
export function isStringLoc(loc: BqrsUrlValue): loc is string {
99115
return typeof loc === "string";
100116
}
101117

118+
export function tryGetBqrsRemoteLocation(
119+
loc: BqrsUrlValue | undefined,
120+
fileLinkPrefix: string,
121+
sourceLocationPrefix: string | undefined,
122+
): string | undefined {
123+
const resolvedLoc = tryGetResolvableLocation(loc);
124+
125+
return tryGetRemoteLocation(
126+
resolvedLoc,
127+
fileLinkPrefix,
128+
sourceLocationPrefix,
129+
);
130+
}
131+
102132
export function tryGetRemoteLocation(
103133
loc: UrlValue | undefined,
104134
fileLinkPrefix: string,
105135
sourceLocationPrefix: string | undefined,
106136
): string | undefined {
107-
const resolvableLocation = tryGetResolvableLocation(loc);
108-
if (!resolvableLocation) {
137+
if (!loc || !isUrlValueResolvable(loc)) {
109138
return undefined;
110139
}
111140

@@ -115,34 +144,36 @@ export function tryGetRemoteLocation(
115144
// "file:${sourceLocationPrefix}/relative/path/to/file"
116145
// So we need to strip off the first part to get the relative path.
117146
if (sourceLocationPrefix) {
118-
if (!resolvableLocation.uri.startsWith(`file:${sourceLocationPrefix}/`)) {
147+
if (!loc.uri.startsWith(`file:${sourceLocationPrefix}/`)) {
119148
return undefined;
120149
}
121-
trimmedLocation = resolvableLocation.uri.replace(
122-
`file:${sourceLocationPrefix}/`,
123-
"",
124-
);
150+
trimmedLocation = loc.uri.replace(`file:${sourceLocationPrefix}/`, "");
125151
} else {
126152
// If the source location prefix is empty (e.g. for older remote queries), we assume that the database
127153
// was created on a Linux actions runner and has the format:
128154
// "file:/home/runner/work/<repo>/<repo>/relative/path/to/file"
129155
// So we need to drop the first 6 parts of the path.
130-
if (!resolvableLocation.uri.startsWith("file:/home/runner/work/")) {
156+
if (!loc.uri.startsWith("file:/home/runner/work/")) {
131157
return undefined;
132158
}
133-
const locationParts = resolvableLocation.uri.split("/");
159+
const locationParts = loc.uri.split("/");
134160
trimmedLocation = locationParts.slice(6, locationParts.length).join("/");
135161
}
136162

137163
const fileLink = {
138164
fileLinkPrefix,
139165
filePath: trimmedLocation,
140166
};
167+
168+
if (loc.type === "wholeFileLocation") {
169+
return createRemoteFileRef(fileLink);
170+
}
171+
141172
return createRemoteFileRef(
142173
fileLink,
143-
resolvableLocation.startLine,
144-
resolvableLocation.endLine,
145-
resolvableLocation.startColumn,
146-
resolvableLocation.endColumn,
174+
loc.startLine,
175+
loc.endLine,
176+
loc.startColumn,
177+
loc.endColumn,
147178
);
148179
}

extensions/ql-vscode/src/common/interface-types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
} from "../model-editor/shared/view-state";
2626
import { Mode } from "../model-editor/shared/mode";
2727
import { QueryLanguage } from "./query-language";
28+
import { UrlValueResolvable } from "./raw-result-types";
2829

2930
/**
3031
* This module contains types and code that are shared between
@@ -208,7 +209,7 @@ export type FromResultsViewMsg =
208209
*/
209210
interface ViewSourceFileMsg {
210211
t: "viewSourceFile";
211-
loc: ResolvableLocationValue;
212+
loc: ResolvableLocationValue | UrlValueResolvable;
212213
databaseUri: string;
213214
}
214215

extensions/ql-vscode/src/common/raw-result-types.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,20 @@ export type UrlValueLineColumnLocation = {
3131
endColumn: number;
3232
};
3333

34-
export type UrlValue =
35-
| UrlValueString
34+
export type UrlValueResolvable =
3635
| UrlValueWholeFileLocation
3736
| UrlValueLineColumnLocation;
3837

38+
export function isUrlValueResolvable(
39+
value: UrlValue,
40+
): value is UrlValueResolvable {
41+
return (
42+
value.type === "wholeFileLocation" || value.type === "lineColumnLocation"
43+
);
44+
}
45+
46+
export type UrlValue = UrlValueString | UrlValueResolvable;
47+
3948
export type EntityValue = {
4049
url?: UrlValue;
4150
label?: string;
@@ -68,14 +77,14 @@ export type CellValue =
6877
| CellValueString
6978
| CellValueBoolean;
7079

71-
export type Tuple = CellValue[];
80+
export type Row = CellValue[];
7281

7382
export type RawResultSet = {
7483
name: string;
75-
rows: number;
84+
totalRowCount: number;
7685

7786
columns: Column[];
78-
tuples: Tuple[];
87+
rows: Row[];
7988

8089
nextPageOffset?: number;
8190
};

0 commit comments

Comments
 (0)