Skip to content

Commit 84b9d9c

Browse files
committed
Contextual queries: Factor common code for resolving and running
Shared by the AST viewer, jump to def, and find references contextual queries. This allows contextual queries to have their dependencies resolved and be run whether the library pack is in the workspace or in the package cache.
1 parent 639487b commit 84b9d9c

File tree

3 files changed

+76
-71
lines changed

3 files changed

+76
-71
lines changed

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

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import { DatabaseManager, DatabaseItem } from '../databases';
55
import fileRangeFromURI from './fileRangeFromURI';
66
import { ProgressCallback } from '../commandRunner';
77
import { KeyType } from './keyType';
8-
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
8+
import { qlpackOfDatabase, resolveQueries, runContextualQuery } from './queryResolver';
99
import { CancellationToken, LocationLink, Uri } from 'vscode';
10-
import { createInitialQueryInfo, QueryWithResults } from '../run-queries-shared';
10+
import { QueryWithResults } from '../run-queries-shared';
1111
import { QueryRunner } from '../queryRunner';
1212

1313
export const SELECT_QUERY_NAME = '#select';
@@ -56,15 +56,7 @@ export async function getLocationsForUriString(
5656

5757
const links: FullLocationLink[] = [];
5858
for (const query of await resolveQueries(cli, qlpack, keyType)) {
59-
const initialInfo = await createInitialQueryInfo(
60-
Uri.file(query),
61-
{
62-
name: db.name,
63-
databaseUri: db.databaseUri.toString(),
64-
},
65-
false
66-
);
67-
const results = await qs.compileAndRunQueryAgainstDatabase(db, initialInfo, queryStorageDir, progress, token, templates);
59+
const results = await runContextualQuery(query, db, queryStorageDir, qs, cli, progress, token, templates);
6860
if (results.successful) {
6961
links.push(...await getLinksFromResults(results, cli, db, filter));
7062
}

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as fs from 'fs-extra';
22
import * as yaml from 'js-yaml';
33
import * as tmp from 'tmp-promise';
4+
import * as path from 'path';
45

56
import * as helpers from '../helpers';
67
import {
@@ -12,6 +13,11 @@ import {
1213
import { CodeQLCliServer } from '../cli';
1314
import { DatabaseItem } from '../databases';
1415
import { QlPacksForLanguage } from '../helpers';
16+
import { logger } from '../logging';
17+
import { createInitialQueryInfo } from '../run-queries-shared';
18+
import { CancellationToken, Uri } from 'vscode';
19+
import { ProgressCallback } from '../commandRunner';
20+
import { QueryRunner } from '../queryRunner';
1521

1622
export async function qlpackOfDatabase(cli: CodeQLCliServer, db: DatabaseItem): Promise<QlPacksForLanguage> {
1723
if (db.contents === undefined) {
@@ -104,3 +110,67 @@ export async function resolveQueries(cli: CodeQLCliServer, qlpacks: QlPacksForLa
104110
void helpers.showAndLogErrorMessage(errorMessage);
105111
throw new Error(`Couldn't find any queries tagged ${tagOfKeyType(keyType)} in any of the following packs: ${packsToSearch.join(', ')}.`);
106112
}
113+
114+
async function resolveContextualQuery(cli: CodeQLCliServer, query: string): Promise<{ packPath: string, lockFilePath?: string }> {
115+
// Contextual queries now live within the standard library packs.
116+
// This simplifies distribution (you don't need the standard query pack to use the AST viewer),
117+
// but if the library pack doesn't have a lockfile, we won't be able to find
118+
// other pack dependencies of the library pack.
119+
120+
// Work out the enclosing pack.
121+
const packContents = await cli.packPacklist(query, false);
122+
const packFilePath = packContents.find((p) => ['codeql-pack.yml', 'qlpack.yml'].includes(path.basename(p)));
123+
if (packFilePath === undefined) {
124+
// Should not happen; we already resolved this query.
125+
throw new Error(`Could not find a CodeQL pack file for the pack enclosing the contextual query ${query}`);
126+
}
127+
const packPath = path.dirname(packFilePath);
128+
const lockFilePath = packContents.find((p) => ['codeql-pack.lock.yml', 'qlpack.lock.yml'].includes(path.basename(p)));
129+
if (!lockFilePath) {
130+
// No lock file, likely because this library pack is in the package cache.
131+
// Create a lock file so that we can resolve dependencies and library path
132+
// for the contextual query.
133+
void logger.log(`Library pack ${packPath} is missing a lock file; creating a temporary lock file`);
134+
await cli.packResolveDependencies(packPath);
135+
// Clear CLI server pack cache before installing dependencies,
136+
// so that it picks up the new lock file, not the previously cached pack.
137+
void logger.log('Clearing the CodeQL CLI server\'s pack cache');
138+
await cli.clearCache();
139+
// Install dependencies.
140+
void logger.log(`Installing package dependencies for library pack ${packPath}`);
141+
await cli.packInstall(packPath);
142+
}
143+
return { packPath, lockFilePath };
144+
}
145+
146+
async function removeTemporaryLockFile(packPath: string) {
147+
const tempLockFilePath = path.resolve(packPath, 'codeql-pack.lock.yml');
148+
void logger.log(`Deleting temporary package lock file at ${tempLockFilePath}`);
149+
// It's fine if the file doesn't exist.
150+
await fs.promises.rm(path.resolve(packPath, 'codeql-pack.lock.yml'), { force: true });
151+
}
152+
153+
export async function runContextualQuery(query: string, db: DatabaseItem, queryStorageDir: string, qs: QueryRunner, cli: CodeQLCliServer, progress: ProgressCallback, token: CancellationToken, templates: Record<string, string>) {
154+
const { packPath, lockFilePath } = await resolveContextualQuery(cli, query);
155+
const initialInfo = await createInitialQueryInfo(
156+
Uri.file(query),
157+
{
158+
name: db.name,
159+
databaseUri: db.databaseUri.toString(),
160+
},
161+
false
162+
);
163+
void logger.log(`Running contextual query ${query}; results will be stored in ${queryStorageDir}`);
164+
const queryResult = await qs.compileAndRunQueryAgainstDatabase(
165+
db,
166+
initialInfo,
167+
queryStorageDir,
168+
progress,
169+
token,
170+
templates
171+
);
172+
if (!lockFilePath) {
173+
await removeTemporaryLockFile(packPath);
174+
}
175+
return queryResult;
176+
}

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

Lines changed: 3 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import * as fs from 'fs-extra';
2-
import * as path from 'path';
31
import {
42
CancellationToken,
53
DefinitionProvider,
@@ -16,17 +14,16 @@ import {
1614
import { decodeSourceArchiveUri, encodeArchiveBasePath, zipArchiveScheme } from '../archive-filesystem-provider';
1715
import { CodeQLCliServer } from '../cli';
1816
import { DatabaseManager } from '../databases';
19-
import { logger } from '../logging';
2017
import { CachedOperation } from '../helpers';
2118
import { ProgressCallback, withProgress } from '../commandRunner';
2219
import AstBuilder from './astBuilder';
2320
import {
2421
KeyType,
2522
} from './keyType';
2623
import { FullLocationLink, getLocationsForUriString, TEMPLATE_NAME } from './locationFinder';
27-
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
24+
import { qlpackOfDatabase, resolveQueries, runContextualQuery } from './queryResolver';
2825
import { isCanary, NO_CACHE_AST_VIEWER } from '../config';
29-
import { createInitialQueryInfo, QueryWithResults } from '../run-queries-shared';
26+
import { QueryWithResults } from '../run-queries-shared';
3027
import { QueryRunner } from '../queryRunner';
3128

3229
/**
@@ -211,61 +208,7 @@ export class TemplatePrintAstProvider {
211208
zippedArchive.pathWithinSourceArchive
212209
};
213210

214-
// The AST viewer queries now live within the standard library packs.
215-
// This simplifies distribution (you don't need the standard query pack to use the AST viewer),
216-
// but if the library pack doesn't have a lockfile, we won't be able to find
217-
// other pack dependencies of the library pack.
218-
219-
// Work out the enclosing pack.
220-
const packContents = await this.cli.packPacklist(query, false);
221-
const packFilePath = packContents.find((p) => ['codeql-pack.yml', 'qlpack.yml'].includes(path.basename(p)));
222-
if (packFilePath === undefined) {
223-
// Should not happen; we already resolved this query.
224-
throw new Error(`Could not find a CodeQL pack file for the pack enclosing the query ${query}`);
225-
}
226-
const packPath = path.dirname(packFilePath);
227-
const lockFilePath = packContents.find((p) => ['codeql-pack.lock.yml', 'qlpack.lock.yml'].includes(path.basename(p)));
228-
if (!lockFilePath) {
229-
// No lock file, likely because this library pack is in the package cache.
230-
// Create a lock file so that we can resolve dependencies and library path
231-
// for the AST query.
232-
void logger.log(`Library pack ${packPath} is missing a lock file; creating a temporary lock file`);
233-
await this.cli.packResolveDependencies(packPath);
234-
// Clear CLI server pack cache before installing dependencies,
235-
// so that it picks up the new lock file, not the previously cached pack.
236-
void logger.log('Clearing the CodeQL CLI server\'s pack cache');
237-
await this.cli.clearCache();
238-
// Install dependencies.
239-
void logger.log(`Installing package dependencies for library pack ${packPath}`);
240-
await this.cli.packInstall(packPath);
241-
}
242-
243-
const initialInfo = await createInitialQueryInfo(
244-
Uri.file(query),
245-
{
246-
name: db.name,
247-
databaseUri: db.databaseUri.toString(),
248-
},
249-
false
250-
);
251-
252-
void logger.log(`Running AST query ${query}; results will be stored in ${this.queryStorageDir}`);
253-
const queryResult = await this.qs.compileAndRunQueryAgainstDatabase(
254-
db,
255-
initialInfo,
256-
this.queryStorageDir,
257-
progress,
258-
token,
259-
templates
260-
);
261-
262-
if (!lockFilePath) {
263-
// Clean up the temporary lock file we created.
264-
const tempLockFilePath = path.resolve(packPath, 'codeql-pack.lock.yml');
265-
void logger.log(`Deleting temporary package lock file at ${tempLockFilePath}`);
266-
// It's fine if the file doesn't exist.
267-
await fs.promises.rm(path.resolve(packPath, 'codeql-pack.lock.yml'), { force: true });
268-
}
211+
const queryResult = await runContextualQuery(query, db, this.queryStorageDir, this.qs, this.cli, progress, token, templates);
269212
return {
270213
query: queryResult,
271214
dbUri: db.databaseUri

0 commit comments

Comments
 (0)