Skip to content

Commit 51be504

Browse files
authored
Merge branch 'main' into selected-line-column
2 parents 7c47cf0 + cf34378 commit 51be504

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1215
-636
lines changed

.github/codeql/queries/assert-pure.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class PureFile extends File {
1919
this.getRelativePath().regexpMatch(".*/src/pure/.*") or
2020
this.getRelativePath().regexpMatch(".*/src/common/.*")
2121
) and
22-
not this.getRelativePath().regexpMatch(".*/src/common/vscode/.*")
22+
not this.getRelativePath().regexpMatch(".*/vscode/.*")
2323
}
2424
}
2525

extensions/ql-vscode/package.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,10 @@
516516
"title": "Add new list",
517517
"icon": "$(new-folder)"
518518
},
519+
{
520+
"command": "codeQLVariantAnalysisRepositories.importFromCodeSearch",
521+
"title": "Add repositories with GitHub Code Search"
522+
},
519523
{
520524
"command": "codeQLVariantAnalysisRepositories.setSelectedItem",
521525
"title": "Select"
@@ -961,6 +965,11 @@
961965
"when": "view == codeQLVariantAnalysisRepositories && viewItem =~ /canBeOpenedOnGitHub/",
962966
"group": "2_qlContextMenu@1"
963967
},
968+
{
969+
"command": "codeQLVariantAnalysisRepositories.importFromCodeSearch",
970+
"when": "view == codeQLVariantAnalysisRepositories && viewItem =~ /canImportCodeSearch/",
971+
"group": "2_qlContextMenu@1"
972+
},
964973
{
965974
"command": "codeQLDatabases.setCurrentDatabase",
966975
"group": "inline",
@@ -1297,6 +1306,10 @@
12971306
"command": "codeQLVariantAnalysisRepositories.removeItemContextMenu",
12981307
"when": "false"
12991308
},
1309+
{
1310+
"command": "codeQLVariantAnalysisRepositories.importFromCodeSearch",
1311+
"when": "false"
1312+
},
13001313
{
13011314
"command": "codeQLDatabases.setCurrentDatabase",
13021315
"when": "false"

extensions/ql-vscode/src/codeql-cli/cli.ts

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ export class CodeQLCliServer implements Disposable {
218218
private readonly app: App,
219219
private distributionProvider: DistributionProvider,
220220
private cliConfig: CliConfig,
221-
private logger: Logger,
221+
public readonly logger: Logger,
222222
) {
223223
this.commandQueue = [];
224224
this.commandInProcess = false;
@@ -330,6 +330,7 @@ export class CodeQLCliServer implements Disposable {
330330
commandArgs: string[],
331331
description: string,
332332
onLine?: OnLineCallback,
333+
silent?: boolean,
333334
): Promise<string> {
334335
const stderrBuffers: Buffer[] = [];
335336
if (this.commandInProcess) {
@@ -349,7 +350,12 @@ export class CodeQLCliServer implements Disposable {
349350
// Compute the full args array
350351
const args = command.concat(LOGGING_FLAGS).concat(commandArgs);
351352
const argsString = args.join(" ");
352-
void this.logger.log(`${description} using CodeQL CLI: ${argsString}...`);
353+
// If we are running silently, we don't want to print anything to the console.
354+
if (!silent) {
355+
void this.logger.log(
356+
`${description} using CodeQL CLI: ${argsString}...`,
357+
);
358+
}
353359
try {
354360
await new Promise<void>((resolve, reject) => {
355361
// Start listening to stdout
@@ -395,24 +401,30 @@ export class CodeQLCliServer implements Disposable {
395401
const fullBuffer = Buffer.concat(stdoutBuffers);
396402
// Make sure we remove the terminator;
397403
const data = fullBuffer.toString("utf8", 0, fullBuffer.length - 1);
398-
void this.logger.log("CLI command succeeded.");
404+
if (!silent) {
405+
void this.logger.log("CLI command succeeded.");
406+
}
399407
return data;
400408
} catch (err) {
401409
// Kill the process if it isn't already dead.
402410
this.killProcessIfRunning();
403-
// Report the error (if there is a stderr then use that otherwise just report the error cod or nodejs error)
411+
// Report the error (if there is a stderr then use that otherwise just report the error code or nodejs error)
404412
const newError =
405413
stderrBuffers.length === 0
406-
? new Error(`${description} failed: ${err}`)
414+
? new Error(
415+
`${description} failed with args:${EOL} ${argsString}${EOL}${err}`,
416+
)
407417
: new Error(
408-
`${description} failed: ${Buffer.concat(stderrBuffers).toString(
409-
"utf8",
410-
)}`,
418+
`${description} failed with args:${EOL} ${argsString}${EOL}${Buffer.concat(
419+
stderrBuffers,
420+
).toString("utf8")}`,
411421
);
412422
newError.stack += getErrorStack(err);
413423
throw newError;
414424
} finally {
415-
void this.logger.log(Buffer.concat(stderrBuffers).toString("utf8"));
425+
if (!silent) {
426+
void this.logger.log(Buffer.concat(stderrBuffers).toString("utf8"));
427+
}
416428
// Remove the listeners we set up.
417429
process.stdout.removeAllListeners("data");
418430
process.stderr.removeAllListeners("data");
@@ -549,9 +561,11 @@ export class CodeQLCliServer implements Disposable {
549561
{
550562
progressReporter,
551563
onLine,
564+
silent = false,
552565
}: {
553566
progressReporter?: ProgressReporter;
554567
onLine?: OnLineCallback;
568+
silent?: boolean;
555569
} = {},
556570
): Promise<string> {
557571
if (progressReporter) {
@@ -567,6 +581,7 @@ export class CodeQLCliServer implements Disposable {
567581
commandArgs,
568582
description,
569583
onLine,
584+
silent,
570585
).then(resolve, reject);
571586
} catch (err) {
572587
reject(err);
@@ -600,10 +615,12 @@ export class CodeQLCliServer implements Disposable {
600615
addFormat = true,
601616
progressReporter,
602617
onLine,
618+
silent = false,
603619
}: {
604620
addFormat?: boolean;
605621
progressReporter?: ProgressReporter;
606622
onLine?: OnLineCallback;
623+
silent?: boolean;
607624
} = {},
608625
): Promise<OutputType> {
609626
let args: string[] = [];
@@ -614,6 +631,7 @@ export class CodeQLCliServer implements Disposable {
614631
const result = await this.runCodeQlCliCommand(command, args, description, {
615632
progressReporter,
616633
onLine,
634+
silent,
617635
});
618636
try {
619637
return JSON.parse(result) as OutputType;
@@ -739,14 +757,19 @@ export class CodeQLCliServer implements Disposable {
739757
/**
740758
* Finds all available queries in a given directory.
741759
* @param queryDir Root of directory tree to search for queries.
760+
* @param silent If true, don't print logs to the CodeQL extension log.
742761
* @returns The list of queries that were found.
743762
*/
744-
public async resolveQueries(queryDir: string): Promise<ResolvedQueries> {
763+
public async resolveQueries(
764+
queryDir: string,
765+
silent?: boolean,
766+
): Promise<ResolvedQueries> {
745767
const subcommandArgs = [queryDir];
746768
return await this.runJsonCodeQlCliCommand<ResolvedQueries>(
747769
["resolve", "queries"],
748770
subcommandArgs,
749771
"Resolving queries",
772+
{ silent },
750773
);
751774
}
752775

@@ -1050,13 +1073,15 @@ export class CodeQLCliServer implements Disposable {
10501073
resultsPath: string,
10511074
interpretedResultsPath: string,
10521075
sourceInfo?: SourceInfo,
1076+
args?: string[],
10531077
): Promise<sarif.Log> {
10541078
const additionalArgs = [
10551079
// TODO: This flag means that we don't group interpreted results
10561080
// by primary location. We may want to revisit whether we call
10571081
// interpretation with and without this flag, or do some
10581082
// grouping client-side.
10591083
"--no-group-results",
1084+
...(args ?? []),
10601085
];
10611086

10621087
await this.runInterpretCommand(

extensions/ql-vscode/src/codeql-cli/distribution.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@ import * as semver from "semver";
66
import { URL } from "url";
77
import { ExtensionContext, Event } from "vscode";
88
import { DistributionConfig } from "../config";
9-
import {
10-
InvocationRateLimiter,
11-
InvocationRateLimiterResultKind,
12-
showAndLogErrorMessage,
13-
showAndLogWarningMessage,
14-
} from "../helpers";
9+
import { showAndLogErrorMessage, showAndLogWarningMessage } from "../helpers";
1510
import { extLogger } from "../common";
1611
import { getCodeQlCliVersion } from "./cli-version";
1712
import {
@@ -24,6 +19,10 @@ import {
2419
extractZipArchive,
2520
getRequiredAssetName,
2621
} from "../pure/distribution";
22+
import {
23+
InvocationRateLimiter,
24+
InvocationRateLimiterResultKind,
25+
} from "../common/invocation-rate-limiter";
2726

2827
/**
2928
* distribution.ts
@@ -76,7 +75,7 @@ export class DistributionManager implements DistributionProvider {
7675
extensionContext,
7776
);
7877
this.updateCheckRateLimiter = new InvocationRateLimiter(
79-
extensionContext,
78+
extensionContext.globalState,
8079
"extensionSpecificDistributionUpdateCheck",
8180
() =>
8281
this.extensionSpecificDistributionManager.checkForUpdatesToDistribution(),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ export type DatabasePanelCommands = {
275275
"codeQLVariantAnalysisRepositories.openOnGitHubContextMenu": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
276276
"codeQLVariantAnalysisRepositories.renameItemContextMenu": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
277277
"codeQLVariantAnalysisRepositories.removeItemContextMenu": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
278+
"codeQLVariantAnalysisRepositories.importFromCodeSearch": TreeViewContextSingleSelectionCommandFunction<DbTreeViewItem>;
278279
};
279280

280281
export type AstCfgCommands = {

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

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,35 @@
11
import { DisposableObject } from "../pure/disposable-object";
2-
import { extLogger } from "./logging/vscode/loggers";
32
import { getErrorMessage } from "../pure/helpers-pure";
3+
import { Logger } from "./logging";
44

55
/**
66
* Base class for "discovery" operations, which scan the file system to find specific kinds of
77
* files. This class automatically prevents more than one discovery operation from running at the
88
* same time.
99
*/
1010
export abstract class Discovery<T> extends DisposableObject {
11-
private retry = false;
12-
private discoveryInProgress = false;
11+
private restartWhenFinished = false;
12+
private currentDiscoveryPromise: Promise<void> | undefined;
1313

14-
constructor(private readonly name: string) {
14+
constructor(private readonly name: string, private readonly logger: Logger) {
1515
super();
1616
}
1717

18+
/**
19+
* Returns the promise of the currently running refresh operation, if one is in progress.
20+
* Otherwise returns a promise that resolves immediately.
21+
*/
22+
public waitForCurrentRefresh(): Promise<void> {
23+
return this.currentDiscoveryPromise ?? Promise.resolve();
24+
}
25+
1826
/**
1927
* Force the discovery process to run. Normally invoked by the derived class when a relevant file
2028
* system change is detected.
29+
*
30+
* Returns a promise that resolves when the refresh is complete, including any retries.
2131
*/
22-
public refresh(): void {
32+
public refresh(): Promise<void> {
2333
// We avoid having multiple discovery operations in progress at the same time. Otherwise, if we
2434
// got a storm of refresh requests due to, say, the copying or deletion of a large directory
2535
// tree, we could potentially spawn a separate simultaneous discovery operation for each
@@ -36,49 +46,48 @@ export abstract class Discovery<T> extends DisposableObject {
3646
// other change notifications that might be coming along. However, this would create more
3747
// latency in the common case, in order to save a bit of latency in the uncommon case.
3848

39-
if (this.discoveryInProgress) {
49+
if (this.currentDiscoveryPromise !== undefined) {
4050
// There's already a discovery operation in progress. Tell it to restart when it's done.
41-
this.retry = true;
51+
this.restartWhenFinished = true;
4252
} else {
4353
// No discovery in progress, so start one now.
44-
this.discoveryInProgress = true;
45-
this.launchDiscovery();
54+
this.currentDiscoveryPromise = this.launchDiscovery().finally(() => {
55+
this.currentDiscoveryPromise = undefined;
56+
});
4657
}
58+
return this.currentDiscoveryPromise;
4759
}
4860

4961
/**
5062
* Starts the asynchronous discovery operation by invoking the `discover` function. When the
5163
* discovery operation completes, the `update` function will be invoked with the results of the
5264
* discovery.
5365
*/
54-
private launchDiscovery(): void {
55-
const discoveryPromise = this.discover();
56-
discoveryPromise
57-
.then((results) => {
58-
if (!this.retry) {
59-
// Update any listeners with the results of the discovery.
60-
this.discoveryInProgress = false;
61-
this.update(results);
62-
}
63-
})
64-
65-
.catch((err: unknown) => {
66-
void extLogger.log(
67-
`${this.name} failed. Reason: ${getErrorMessage(err)}`,
68-
);
69-
})
66+
private async launchDiscovery(): Promise<void> {
67+
let results: T | undefined;
68+
try {
69+
results = await this.discover();
70+
} catch (err) {
71+
void this.logger.log(
72+
`${this.name} failed. Reason: ${getErrorMessage(err)}`,
73+
);
74+
results = undefined;
75+
}
7076

71-
.finally(() => {
72-
if (this.retry) {
73-
// Another refresh request came in while we were still running a previous discovery
74-
// operation. Since the discovery results we just computed are now stale, we'll launch
75-
// another discovery operation instead of updating.
76-
// Note that by doing this inside of `finally`, we will relaunch discovery even if the
77-
// initial discovery operation failed.
78-
this.retry = false;
79-
this.launchDiscovery();
80-
}
81-
});
77+
if (this.restartWhenFinished) {
78+
// Another refresh request came in while we were still running a previous discovery
79+
// operation. Since the discovery results we just computed are now stale, we'll launch
80+
// another discovery operation instead of updating.
81+
// We want to relaunch discovery regardless of if the initial discovery operation
82+
// succeeded or failed.
83+
this.restartWhenFinished = false;
84+
await this.launchDiscovery();
85+
} else {
86+
// If the discovery was successful, then update any listeners with the results.
87+
if (results !== undefined) {
88+
this.update(results);
89+
}
90+
}
8291
}
8392

8493
/**

0 commit comments

Comments
 (0)