Skip to content

Commit be9084e

Browse files
committed
Fix error messages for ast viewers and update caching
This commit does two things: 1. Add more appropriate error messages when asts can't be viewed. 2. Make better use of cached operations for asts. In the past, we were not actually using cached operations. Each time an ast view request occurred, we created a new TemplatePrintAstProvider instance. With this change, we reuse the TemplatePrintAstProvider between calls and ensure that an AST that is called once is reused on subsequent calls.
1 parent 57d856f commit be9084e

4 files changed

Lines changed: 60 additions & 41 deletions

File tree

extensions/ql-vscode/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
## [UNRELEASED]
44

55
- Avoid displaying an error when removing orphaned databases and the storage folder does not exist. [#748](https://github.com/github/vscode-codeql/pull/748)
6+
- Add better error messages when AST Viewer is unable to create an AST. [#753](https://github.com/github/vscode-codeql/pull/753)
7+
- Cache AST viewing operations so that subsequent calls to view the AST of a single file will be extremely fast. [#753](https://github.com/github/vscode-codeql/pull/753)
68

79
## 1.4.2 - 2 February 2021
810

extensions/ql-vscode/src/contextual/templateProvider.ts

Lines changed: 47 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import * as vscode from 'vscode';
1+
import {
2+
CancellationToken,
3+
DefinitionProvider,
4+
Location,
5+
LocationLink,
6+
Position,
7+
ProgressLocation,
8+
ReferenceContext,
9+
ReferenceProvider,
10+
TextDocument,
11+
Uri
12+
} from 'vscode';
213

314
import { decodeSourceArchiveUri, encodeArchiveBasePath, zipArchiveScheme } from '../archive-filesystem-provider';
415
import { CodeQLCliServer } from '../cli';
@@ -22,20 +33,20 @@ import { qlpackOfDatabase, resolveQueries } from './queryResolver';
2233
* or from a selected identifier.
2334
*/
2435

25-
export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvider {
26-
private cache: CachedOperation<vscode.LocationLink[]>;
36+
export class TemplateQueryDefinitionProvider implements DefinitionProvider {
37+
private cache: CachedOperation<LocationLink[]>;
2738

2839
constructor(
2940
private cli: CodeQLCliServer,
3041
private qs: QueryServerClient,
3142
private dbm: DatabaseManager,
3243
) {
33-
this.cache = new CachedOperation<vscode.LocationLink[]>(this.getDefinitions.bind(this));
44+
this.cache = new CachedOperation<LocationLink[]>(this.getDefinitions.bind(this));
3445
}
3546

36-
async provideDefinition(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.LocationLink[]> {
47+
async provideDefinition(document: TextDocument, position: Position, _token: CancellationToken): Promise<LocationLink[]> {
3748
const fileLinks = await this.cache.get(document.uri.toString());
38-
const locLinks: vscode.LocationLink[] = [];
49+
const locLinks: LocationLink[] = [];
3950
for (const link of fileLinks) {
4051
if (link.originSelectionRange!.contains(position)) {
4152
locLinks.push(link);
@@ -44,9 +55,9 @@ export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvide
4455
return locLinks;
4556
}
4657

47-
private async getDefinitions(uriString: string): Promise<vscode.LocationLink[]> {
58+
private async getDefinitions(uriString: string): Promise<LocationLink[]> {
4859
return withProgress({
49-
location: vscode.ProgressLocation.Notification,
60+
location: ProgressLocation.Notification,
5061
cancellable: true,
5162
title: 'Finding definitions'
5263
}, async (progress, token) => {
@@ -64,7 +75,7 @@ export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvide
6475
}
6576
}
6677

67-
export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider {
78+
export class TemplateQueryReferenceProvider implements ReferenceProvider {
6879
private cache: CachedOperation<FullLocationLink[]>;
6980

7081
constructor(
@@ -76,13 +87,13 @@ export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider
7687
}
7788

7889
async provideReferences(
79-
document: vscode.TextDocument,
80-
position: vscode.Position,
81-
_context: vscode.ReferenceContext,
82-
_token: vscode.CancellationToken
83-
): Promise<vscode.Location[]> {
90+
document: TextDocument,
91+
position: Position,
92+
_context: ReferenceContext,
93+
_token: CancellationToken
94+
): Promise<Location[]> {
8495
const fileLinks = await this.cache.get(document.uri.toString());
85-
const locLinks: vscode.Location[] = [];
96+
const locLinks: Location[] = [];
8697
for (const link of fileLinks) {
8798
if (link.targetRange!.contains(position)) {
8899
locLinks.push({ range: link.originSelectionRange!, uri: link.originUri });
@@ -93,7 +104,7 @@ export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider
93104

94105
private async getReferences(uriString: string): Promise<FullLocationLink[]> {
95106
return withProgress({
96-
location: vscode.ProgressLocation.Notification,
107+
location: ProgressLocation.Notification,
97108
cancellable: true,
98109
title: 'Finding references'
99110
}, async (progress, token) => {
@@ -112,40 +123,41 @@ export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider
112123
}
113124

114125
export class TemplatePrintAstProvider {
115-
private cache: CachedOperation<QueryWithResults | undefined>;
126+
private cache: CachedOperation<QueryWithResults>;
116127

117128
constructor(
118129
private cli: CodeQLCliServer,
119130
private qs: QueryServerClient,
120131
private dbm: DatabaseManager,
121-
122-
// Note: progress and token are only used if a cached value is not available
123-
private progress: ProgressCallback,
124-
private token: vscode.CancellationToken
125132
) {
126-
this.cache = new CachedOperation<QueryWithResults | undefined>(this.getAst.bind(this));
133+
this.cache = new CachedOperation<QueryWithResults>(this.getAst.bind(this));
127134
}
128135

129-
async provideAst(document?: vscode.TextDocument): Promise<AstBuilder | undefined> {
136+
async provideAst(
137+
progress: ProgressCallback,
138+
token: CancellationToken,
139+
document?: TextDocument
140+
): Promise<AstBuilder | undefined> {
130141
if (!document) {
131-
return;
132-
}
133-
const queryResults = await this.cache.get(document.uri.toString());
134-
if (!queryResults) {
135-
return;
142+
throw new Error('Cannot view the AST. Please select a valid source file inside a CodeQL database.');
136143
}
144+
const queryResults = await this.cache.get(document.uri.toString(), progress, token);
137145

138146
return new AstBuilder(
139147
queryResults, this.cli,
140-
this.dbm.findDatabaseItem(vscode.Uri.parse(queryResults.database.databaseUri!, true))!,
148+
this.dbm.findDatabaseItem(Uri.parse(queryResults.database.databaseUri!, true))!,
141149
document.fileName
142150
);
143151
}
144152

145-
private async getAst(uriString: string): Promise<QueryWithResults> {
146-
const uri = vscode.Uri.parse(uriString, true);
153+
private async getAst(
154+
uriString: string,
155+
progress: ProgressCallback,
156+
token: CancellationToken
157+
): Promise<QueryWithResults> {
158+
const uri = Uri.parse(uriString, true);
147159
if (uri.scheme !== zipArchiveScheme) {
148-
throw new Error('AST Viewing is only available for databases with zipped source archives.');
160+
throw new Error('Cannot view the AST. Please select a valid source file inside a CodeQL database.');
149161
}
150162

151163
const zippedArchive = decodeSourceArchiveUri(uri);
@@ -181,9 +193,9 @@ export class TemplatePrintAstProvider {
181193
this.qs,
182194
db,
183195
false,
184-
vscode.Uri.file(query),
185-
this.progress,
186-
this.token,
196+
Uri.file(query),
197+
progress,
198+
token,
187199
templates
188200
);
189201
}

extensions/ql-vscode/src/extension.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -692,13 +692,18 @@ async function activateWithInstalledDistribution(
692692
);
693693

694694
const astViewer = new AstViewer();
695+
const templateProvider = new TemplatePrintAstProvider(cliServer, qs, dbm);
696+
695697
ctx.subscriptions.push(astViewer);
696698
ctx.subscriptions.push(commandRunnerWithProgress('codeQL.viewAst', async (
697699
progress: ProgressCallback,
698700
token: CancellationToken
699701
) => {
700-
const ast = await new TemplatePrintAstProvider(cliServer, qs, dbm, progress, token)
701-
.provideAst(window.activeTextEditor?.document);
702+
const ast = await templateProvider.provideAst(
703+
progress,
704+
token,
705+
window.activeTextEditor?.document,
706+
);
702707
if (ast) {
703708
astViewer.updateRoots(await ast.getRoots(), ast.db, ast.fileName);
704709
}

extensions/ql-vscode/src/helpers.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,19 +296,19 @@ export async function getPrimaryDbscheme(datasetFolder: string): Promise<string>
296296
* A cached mapping from strings to value of type U.
297297
*/
298298
export class CachedOperation<U> {
299-
private readonly operation: (t: string) => Promise<U>;
299+
private readonly operation: (t: string, ...args: any[]) => Promise<U>;
300300
private readonly cached: Map<string, U>;
301301
private readonly lru: string[];
302302
private readonly inProgressCallbacks: Map<string, [(u: U) => void, (reason?: any) => void][]>;
303303

304-
constructor(operation: (t: string) => Promise<U>, private cacheSize = 100) {
304+
constructor(operation: (t: string, ...args: any[]) => Promise<U>, private cacheSize = 100) {
305305
this.operation = operation;
306306
this.lru = [];
307307
this.inProgressCallbacks = new Map<string, [(u: U) => void, (reason?: any) => void][]>();
308308
this.cached = new Map<string, U>();
309309
}
310310

311-
async get(t: string): Promise<U> {
311+
async get(t: string, ...args: any[]): Promise<U> {
312312
// Try and retrieve from the cache
313313
const fromCache = this.cached.get(t);
314314
if (fromCache !== undefined) {
@@ -329,7 +329,7 @@ export class CachedOperation<U> {
329329
const callbacks: [(u: U) => void, (reason?: any) => void][] = [];
330330
this.inProgressCallbacks.set(t, callbacks);
331331
try {
332-
const result = await this.operation(t);
332+
const result = await this.operation(t, ...args);
333333
callbacks.forEach(f => f[0](result));
334334
this.inProgressCallbacks.delete(t);
335335
if (this.lru.length > this.cacheSize) {

0 commit comments

Comments
 (0)