Skip to content

Commit 0fb50de

Browse files
josefsCopilot
andcommitted
Refresh AST and contextual query caches when reimporting test databases
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 4007728 commit 0fb50de

4 files changed

Lines changed: 364 additions & 6 deletions

File tree

‎extensions/ql-vscode/src/language-support/contextual/cached-operation.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ export class CachedOperation<S extends unknown[], U> {
7777
}
7878
}
7979

80+
has(t: string): boolean {
81+
return this.cached.has(t);
82+
}
83+
8084
reset() {
8185
this.cached.clear();
8286
this.lru.length = 0;

‎extensions/ql-vscode/src/language-support/contextual/template-provider.ts‎

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
} from "../../common/vscode/archive-filesystem-provider";
1818
import type { CodeQLCliServer } from "../../codeql-cli/cli";
1919
import type { DatabaseManager } from "../../databases/local-databases";
20+
import { DatabaseEventKind } from "../../databases/local-databases";
2021
import { CachedOperation } from "./cached-operation";
2122
import type { ProgressCallback } from "../../common/vscode/progress";
2223
import { withProgress } from "../../common/vscode/progress";
@@ -59,16 +60,25 @@ export class TemplateQueryDefinitionProvider implements DefinitionProvider {
5960
private queryStorageDir: string,
6061
) {
6162
this.cache = new CachedOperation(this.getDefinitions.bind(this));
63+
this.dbm.onDidChangeDatabaseItem(({ kind }) => {
64+
if (kind === DatabaseEventKind.Remove) {
65+
this.cache.reset();
66+
}
67+
});
6268
}
6369

6470
async provideDefinition(
6571
document: TextDocument,
6672
position: Position,
6773
token: CancellationToken,
6874
): Promise<LocationLink[]> {
75+
const uriString = document.uri.toString();
76+
if (this.shouldUseCache() && this.cache.has(uriString)) {
77+
await maybeReimportTestDatabase(this.dbm, document.uri);
78+
}
6979
const fileLinks = this.shouldUseCache()
70-
? await this.cache.get(document.uri.toString(), token)
71-
: await this.getDefinitions(document.uri.toString(), token);
80+
? await this.cache.get(uriString, token)
81+
: await this.getDefinitions(uriString, token);
7282

7383
const locLinks: LocationLink[] = [];
7484
for (const link of fileLinks) {
@@ -119,6 +129,11 @@ export class TemplateQueryReferenceProvider implements ReferenceProvider {
119129
private queryStorageDir: string,
120130
) {
121131
this.cache = new CachedOperation(this.getReferences.bind(this));
132+
this.dbm.onDidChangeDatabaseItem(({ kind }) => {
133+
if (kind === DatabaseEventKind.Remove) {
134+
this.cache.reset();
135+
}
136+
});
122137
}
123138

124139
async provideReferences(
@@ -127,9 +142,13 @@ export class TemplateQueryReferenceProvider implements ReferenceProvider {
127142
_context: ReferenceContext,
128143
token: CancellationToken,
129144
): Promise<Location[]> {
145+
const uriString = document.uri.toString();
146+
if (this.shouldUseCache() && this.cache.has(uriString)) {
147+
await maybeReimportTestDatabase(this.dbm, document.uri);
148+
}
130149
const fileLinks = this.shouldUseCache()
131-
? await this.cache.get(document.uri.toString(), token)
132-
: await this.getReferences(document.uri.toString(), token);
150+
? await this.cache.get(uriString, token)
151+
: await this.getReferences(uriString, token);
133152

134153
const locLinks: Location[] = [];
135154
for (const link of fileLinks) {
@@ -193,6 +212,11 @@ export class TemplatePrintAstProvider {
193212
private queryStorageDir: string,
194213
) {
195214
this.cache = new CachedOperation(this.getAst.bind(this));
215+
this.dbm.onDidChangeDatabaseItem(({ kind }) => {
216+
if (kind === DatabaseEventKind.Remove) {
217+
this.cache.reset();
218+
}
219+
});
196220
}
197221

198222
async provideAst(
@@ -205,9 +229,13 @@ export class TemplatePrintAstProvider {
205229
"Cannot view the AST. Please select a valid source file inside a CodeQL database.",
206230
);
207231
}
232+
const uriString = fileUri.toString();
233+
if (this.shouldUseCache() && this.cache.has(uriString)) {
234+
await maybeReimportTestDatabase(this.dbm, fileUri);
235+
}
208236
const completedQuery = this.shouldUseCache()
209-
? await this.cache.get(fileUri.toString(), progress, token)
210-
: await this.getAst(fileUri.toString(), progress, token);
237+
? await this.cache.get(uriString, progress, token)
238+
: await this.getAst(uriString, progress, token);
211239

212240
const queryResults = Array.from(completedQuery.results.values());
213241
if (queryResults.length !== 1) {
@@ -296,6 +324,11 @@ export class TemplatePrintCfgProvider {
296324
private dbm: DatabaseManager,
297325
) {
298326
this.cache = new CachedOperation(this.getCfgUri.bind(this));
327+
this.dbm.onDidChangeDatabaseItem(({ kind }) => {
328+
if (kind === DatabaseEventKind.Remove) {
329+
this.cache.reset();
330+
}
331+
});
299332
}
300333

301334
async provideCfgUri(
@@ -369,3 +402,20 @@ export class TemplatePrintCfgProvider {
369402
return [queryUri, templates];
370403
}
371404
}
405+
406+
async function maybeReimportTestDatabase(
407+
dbm: DatabaseManager,
408+
uri: Uri,
409+
): Promise<void> {
410+
if (uri.scheme !== zipArchiveScheme) {
411+
return;
412+
}
413+
const zippedArchive = decodeSourceArchiveUri(uri);
414+
const sourceArchiveUri = encodeArchiveBasePath(
415+
zippedArchive.sourceArchiveZipPath,
416+
);
417+
const db = dbm.findDatabaseItemBySourceArchive(sourceArchiveUri);
418+
if (db) {
419+
await dbm.maybeReimportTestDatabase(db.databaseUri);
420+
}
421+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { CachedOperation } from "../../../src/language-support/contextual/cached-operation";
2+
3+
describe("CachedOperation", () => {
4+
it("caches results and tracks cached keys with has()", async () => {
5+
const operation = jest.fn(async (key: string, suffix: string) => {
6+
return `${key}:${suffix}`;
7+
});
8+
const cache = new CachedOperation<[string], string>(operation);
9+
10+
expect(cache.has("a")).toBe(false);
11+
12+
const res1 = await cache.get("a", "1");
13+
expect(res1).toBe("a:1");
14+
expect(cache.has("a")).toBe(true);
15+
expect(operation).toHaveBeenCalledTimes(1);
16+
17+
const res2 = await cache.get("a", "2");
18+
expect(res2).toBe("a:1");
19+
expect(operation).toHaveBeenCalledTimes(1);
20+
21+
cache.reset();
22+
expect(cache.has("a")).toBe(false);
23+
24+
const res3 = await cache.get("a", "2");
25+
expect(res3).toBe("a:2");
26+
expect(cache.has("a")).toBe(true);
27+
expect(operation).toHaveBeenCalledTimes(2);
28+
});
29+
});

0 commit comments

Comments
 (0)