Compare commits

...

37 Commits

Author SHA1 Message Date
shati-patel
692e1235e8 v1.4.8
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2021-05-05 17:41:02 +01:00
Andrew Eisenberg
b69bbf5c5d Update integration test cli versions 2021-04-30 10:11:03 -07:00
Shati Patel
b64284c43e Apply suggestions from code review
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2021-04-29 10:31:51 -07:00
Shati Patel
67eaaadfce Update changelog 2021-04-29 10:31:51 -07:00
Shati Patel
a9545458b9 minor unrelated typo fixes 2021-04-29 10:31:51 -07:00
Shati Patel
3e1b121471 Prompt users to choose a DB language 2021-04-29 10:31:51 -07:00
Shati Patel
28d7a26b5f Fix syntax in CodeQL code scanning workflow 2021-04-28 16:19:24 +01:00
Andrew Eisenberg
1d49ae5b99 Actions: Add permissions block to code scanning workflow (#850) 2021-04-26 17:57:13 +00:00
Andrew Eisenberg
b00826d76a Use the main branch of the codeql action
This commit switches to the bleeding edge, main branch of the
codeql action. This helps us test the action before merging all
of the new changes into main, which occurs roughly once a week.

If there are commits that introduce bugs in codeql-action, then
we will be more likely to catch it before releasing to the world
if we are using it in this extension.
2021-04-26 08:50:42 -07:00
Shati Patel
eab5865a5c Fix conflict in changelog 2021-04-26 07:53:03 -07:00
Shati Patel
0e8cd0d2b1 Update changelog 2021-04-26 07:53:03 -07:00
Shati Patel
8281f408dc Add command to copy version information 2021-04-26 07:53:03 -07:00
Andrew Eisenberg
fce9bbce20 Update changelog 2021-04-23 14:57:28 -07:00
Andrew Eisenberg
dc5efcedba Watch for changes in directory structure
This ensures that directories renamed, added or deleted are
properly checked to see if they contain tests. The test tree
will be correctly updated when any directory changes.s
2021-04-23 14:57:28 -07:00
aeisenberg
f6c67bf696 Bump version to v1.4.8 2021-04-23 17:29:40 +01:00
Andrew Eisenberg
3fce04a24b v1.4.7
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2021-04-23 08:11:50 -07:00
Henry Mercer
fba8f51d1b Add polyfill for path to fix a bug that prevented the results view from being loaded (#842)
* Add a polyfill for the Node.js path module

Webpack >v5 doesn't include polyfills for core modules from Node.js by
default. Since we use `path` in the results table UI, we need to include
our own polyfill. This commit adds `path-browserify` to the
distributed extension.

As future work, we could move SARIF location rendering into the core
extension so we don't need to use `path.basename` in the UI. This would
allow us to remove the polyfill.

* Add changelog note
2021-04-23 12:53:48 +01:00
aeisenberg
31ee3cb978 Bump version to v1.4.7 2021-04-23 03:57:48 -07:00
Andrew Eisenberg
4d99126994 v1.4.6
Some checks failed
Release / Release (push) Has been cancelled
Release / Publish to VS Code Marketplace (push) Has been cancelled
Release / Publish to Open VSX Registry (push) Has been cancelled
2021-04-21 11:33:47 -07:00
Henry Mercer
ced34ad704 Add changelog note 2021-04-21 15:43:57 +01:00
Henry Mercer
f5e0011aa1 Forward all query metadata to the queryserver 2021-04-21 15:43:57 +01:00
Andrew Eisenberg
a0b759ecd8 Avoid printing a stack trace when there is no resultsPath
I don't know exactly when this can happen, but a customer has just
shown me a stack trace like this:

```
TypeError: Cannot destructure property 'resultsPath' of 'resultsPaths' as it is undefined.
    at Object.interpretResults (/xxx/.vscode/extensions/github.vscode-codeql-1.4.5/out/query-results.js:120:13)
    at InterfaceManager._getInterpretedResults (/xxx/.vscode/extensions/github.vscode-codeql-1.4.5/out/interface.js:377:45)
    at InterfaceManager.showResultsAsDiagnostics (/xxx/.vscode/extensions/github.vscode-codeql-1.4.5/out/interface.js:447:43)
    at runMicrotasks (<anonymous>)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at async InterfaceManager.handleMsgFromView (/xxx/.vscode/extensions/github.vscode-codeql-1.4.5/out/interface.js:151:29)
```

This commit will avoid printing this stack trace and instead print
a more descriptive message to the logs.
2021-04-20 12:55:13 -07:00
Andrew Eisenberg
58cf4db9ee Add v2.5.1 to cli versions in integration test 2021-04-19 13:53:21 -07:00
Henry Mercer
e0c5ae815c Remove commented out code 2021-04-19 08:44:57 -07:00
Andrew Eisenberg
bf5ed193be Avoid opening the results panel on db deletion
Fixes https://github.com/github/vscode-codeql/issues/823
2021-04-19 08:05:27 -07:00
Aditya Sharad
aa60fbc213 Actions: Simplify code scanning workflow
Run only on pushes and PRs against `main`.
2021-04-14 11:58:46 -07:00
Andrew Eisenberg
bdb2feb559 Refactor version constraints
A simple refactoring that simplifies and unifies how we check if a
feature is supported by a specific cli version.
2021-04-13 10:36:54 -07:00
Andrew Eisenberg
5b08fd0df1 Fix CHANGELOG 2021-04-10 11:19:32 -07:00
Andrew Eisenberg
c83dbde20f Add cli version for message 2021-04-09 15:19:47 -07:00
Edoardo Pirovano
e033578cd2 Add feature to jump to the .ql file referenced by a .qlref 2021-04-09 15:19:47 -07:00
Andrew Eisenberg
c082a38b6b Add a canary setting to avoid caching AST viewer queries (#818)
When codeql library developers are working on PrintAST queries, it is
not easy to use the AST Viewer. The AST Viewer caches results so that
multiple calls to view the AST of the same file are nearly
instantaneous.

However, this breaks down if you are changing the actual queries that
perform AST viewing. In this case, you do not want the cache to be
active.

This commit adds an undocumented setting that prevents caching. To
enable, set:

```
"codeQL.isCanary": true,
"codeQL.astViewer.disableCache": true
```

Note that *both* settings must be true for this to work.

This behaviour and all canary behaviour should be documented somewhere.
I will add that later.
2021-04-01 14:12:13 -07:00
Andrew Eisenberg
bdda27703a Ensure snippets.json is copied when packaging the extension 2021-03-31 10:47:48 -07:00
Andrew Eisenberg
36bfb3987e Fix dependabot warnings (#816)
This commit updates to webpack 5 in order to fix some dependabot errors.
Because webpack 5 introduces some breaking changes, this commit also
makes some minor changes to the build code.
2021-03-29 19:46:20 +00:00
Andrew Eisenberg
6d26491243 Avoid displaying error message for @kind table queries
Also, add a unit test for this area.
2021-03-29 08:16:51 -07:00
Edoardo Pirovano
98a2bbbb47 Limit error messages shown in popups to 2 lines 2021-03-28 16:14:55 -07:00
Aditya Sharad
fb6bed6042 Actions: Test against CodeQL CLI 2.5.0 (#812) 2021-03-26 11:31:31 -07:00
github-actions[bot]
df0cc921fd Bump version to v1.4.6 (#805)
* Bump version to v1.4.6

* Update CHANGELOG.md

Co-authored-by: adityasharad <adityasharad@users.noreply.github.com>
Co-authored-by: Aditya Sharad <6874315+adityasharad@users.noreply.github.com>
2021-03-23 00:40:39 +00:00
25 changed files with 1773 additions and 3445 deletions

View File

@@ -10,7 +10,12 @@ assignees: ''
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
**Version**
The CodeQL and VS Code version in which the bug occurs.
<!-- To copy version information for the CodeQL extension, click "CodeQL CLI vX.X.X" in the status bar at the bottom of the screen.
To copy detailed version information for VS Code itself, see https://code.visualstudio.com/docs/supporting/FAQ#_how-do-i-find-the-version. -->
**To reproduce**
Steps to reproduce the behavior.
**Expected behavior**

View File

@@ -2,24 +2,30 @@ name: "Code Scanning - CodeQL"
on:
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '0 0 * * 0'
- cron: '21 17 * * 0'
jobs:
codeql:
strategy:
fail-fast: false
runs-on: ubuntu-latest
permissions:
contents: read
security-events: write
pull-requests: read
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@main
with:
languages: javascript
config-file: ./.github/codeql/codeql-config.yml
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@main

View File

@@ -126,7 +126,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
version: ['v2.2.6', 'v2.3.3', 'v2.4.5', 'v2.4.6']
version: ['v2.2.6', 'v2.3.3', 'v2.4.5', 'v2.4.6', 'v2.5.3']
env:
CLI_VERSION: ${{ matrix.version }}
TEST_CODEQL_PATH: '${{ github.workspace }}/codeql'

View File

@@ -1,6 +1,21 @@
# CodeQL for Visual Studio Code: Changelog
## [UNRELEASED]
## 1.4.8 - 05 May 2021
- Copy version information to the clipboard when a user clicks the CodeQL section of the status bar. [#845](https://github.com/github/vscode-codeql/pull/845)
- Ensure changes in directories that contain tests will be properly updated in the test explorer. [#846](https://github.com/github/vscode-codeql/pull/846)
- Remind users to choose a language when downloading a database from LGTM. [#852](https://github.com/github/vscode-codeql/pull/852)
## 1.4.7 - 23 April 2021
- Fix a bug that prevented the results view from being loaded. [#842](https://github.com/github/vscode-codeql/pull/842)
## 1.4.6 - 21 April 2021
- Avoid showing an error popup when running a query with `@kind table` metadata. [#814](https://github.com/github/vscode-codeql/pull/814)
- Add an option to jump from a .qlref file to the .ql file it references. [#815](https://github.com/github/vscode-codeql/pull/815)
- Avoid opening the results panel when a database is deleted. [#831](https://github.com/github/vscode-codeql/pull/831)
- Forward all query metadata to the CLI when interpreting results. [#838](https://github.com/github/vscode-codeql/pull/838)
## 1.4.5 - 22 March 2021

View File

@@ -13,6 +13,7 @@ const packageFiles = [
'CHANGELOG.md',
'README.md',
'language-configuration.json',
'snippets.json',
'media',
'node_modules',
'out'

View File

@@ -13,7 +13,10 @@ export const config: webpack.Configuration = {
},
devtool: 'inline-source-map',
resolve: {
extensions: ['.js', '.ts', '.tsx', '.json']
extensions: ['.js', '.ts', '.tsx', '.json'],
fallback: {
path: require.resolve('path-browserify')
}
},
module: {
rules: [

View File

@@ -6,21 +6,23 @@ export function compileView(cb: (err?: Error) => void) {
if (error) {
cb(error);
}
console.log(stats.toString({
errorDetails: true,
colors: true,
assets: false,
builtAt: false,
version: false,
hash: false,
entrypoints: false,
timings: false,
modules: false,
errors: true
}));
if (stats.hasErrors()) {
cb(new Error('Compilation errors detected.'));
return;
if (stats) {
console.log(stats.toString({
errorDetails: true,
colors: true,
assets: false,
builtAt: false,
version: false,
hash: false,
entrypoints: false,
timings: false,
modules: false,
errors: true
}));
if (stats.hasErrors()) {
cb(new Error('Compilation errors detected.'));
return;
}
}
cb();

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.4.5",
"version": "1.4.8",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -34,6 +34,7 @@
"onCommand:codeQLDatabases.chooseDatabaseLgtm",
"onCommand:codeQL.setCurrentDatabase",
"onCommand:codeQL.viewAst",
"onCommand:codeQL.openReferencedFile",
"onCommand:codeQL.chooseDatabaseFolder",
"onCommand:codeQL.chooseDatabaseArchive",
"onCommand:codeQL.chooseDatabaseInternet",
@@ -229,6 +230,10 @@
"command": "codeQL.quickEval",
"title": "CodeQL: Quick Evaluation"
},
{
"command": "codeQL.openReferencedFile",
"title": "CodeQL: Open Referenced File"
},
{
"command": "codeQL.quickQuery",
"title": "CodeQL: Quick Query"
@@ -237,6 +242,10 @@
"command": "codeQL.openDocumentation",
"title": "CodeQL: Open Documentation"
},
{
"command": "codeQL.copyVersion",
"title": "CodeQL: Copy Version Information"
},
{
"command": "codeQLDatabases.chooseDatabaseFolder",
"title": "Choose Database from Folder",
@@ -619,6 +628,11 @@
"command": "codeQL.runQueries",
"group": "9_qlCommands",
"when": "resourceScheme != codeql-zip-archive"
},
{
"command": "codeQL.openReferencedFile",
"group": "9_qlCommands",
"when": "resourceExtname == .qlref"
}
],
"commandPalette": [
@@ -634,6 +648,10 @@
"command": "codeQL.quickEval",
"when": "editorLangId == ql"
},
{
"command": "codeQL.openReferencedFile",
"when": "resourceExtname == .qlref"
},
{
"command": "codeQL.setCurrentDatabase",
"when": "false"
@@ -771,6 +789,10 @@
{
"command": "codeQL.quickEval",
"when": "editorLangId == ql"
},
{
"command": "codeQL.openReferencedFile",
"when": "resourceExtname == .qlref"
}
]
},
@@ -835,6 +857,7 @@
"js-yaml": "^3.14.0",
"minimist": "~1.2.5",
"node-fetch": "~2.6.0",
"path-browserify": "^1.0.1",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"semver": "~7.3.2",
@@ -904,15 +927,15 @@
"sinon-chai": "~3.5.0",
"style-loader": "~0.23.1",
"through2": "^3.0.1",
"ts-loader": "^5.4.5",
"ts-loader": "^8.1.0",
"ts-node": "^8.3.0",
"ts-protoc-gen": "^0.9.0",
"typescript": "~3.8.3",
"typescript-formatter": "^7.2.2",
"vsce": "^1.65.0",
"vscode-test": "^1.4.0",
"webpack": "^4.38.0",
"webpack-cli": "^3.3.2"
"webpack": "^5.28.0",
"webpack-cli": "^4.6.0"
},
"husky": {
"hooks": {

View File

@@ -12,7 +12,6 @@ import { promisify } from 'util';
import { CancellationToken, Disposable } from 'vscode';
import { BQRSInfo, DecodedBqrsChunk } from './pure/bqrs-cli-types';
import * as config from './config';
import { CliConfig } from './config';
import { DistributionProvider, FindDistributionResultKind } from './distribution';
import { assertNever } from './pure/helpers-pure';
@@ -73,6 +72,11 @@ export interface UpgradesInfo {
*/
export type QlpacksInfo = { [name: string]: string[] };
/**
* The expected output of `codeql resolve qlref`.
*/
export type QlrefInfo = { resolvedPath: string };
// `codeql bqrs interpret` requires both of these to be present or
// both absent.
export interface SourceInfo {
@@ -127,21 +131,6 @@ interface BqrsDecodeOptions {
*/
export class CodeQLCliServer implements Disposable {
/**
* CLI version where --kind=DIL was introduced
*/
private static CLI_VERSION_WITH_DECOMPILE_KIND_DIL = new SemVer('2.3.0');
/**
* CLI version where languages are exposed during a `codeql resolve database` command.
*/
private static CLI_VERSION_WITH_LANGUAGE = new SemVer('2.4.1');
/**
* CLI version where `codeql resolve upgrades` supports
* the `--allow-downgrades` flag
*/
private static CLI_VERSION_WITH_DOWNGRADES = new SemVer('2.4.4');
/** The process for the cli server, or undefined if one doesn't exist yet */
process?: child_process.ChildProcessWithoutNullStreams;
@@ -158,6 +147,8 @@ export class CodeQLCliServer implements Disposable {
/** Path to current codeQL executable, or undefined if not running yet. */
codeQlPath: string | undefined;
cliConstraints = new CliVersionConstraint(this);
/**
* When set to true, ignore some modal popups and assume user has clicked "yes".
*/
@@ -461,12 +452,15 @@ export class CodeQLCliServer implements Disposable {
* @param command The `codeql` command to be run, provided as an array of command/subcommand names.
* @param commandArgs The arguments to pass to the `codeql` command.
* @param description Description of the action being run, to be shown in log and error messages.
* @param addFormat Whether or not to add commandline arguments to specify the format as JSON.
* @param progressReporter Used to output progress messages, e.g. to the status bar.
* @returns The contents of the command's stdout, if the command succeeded.
*/
async runJsonCodeQlCliCommand<OutputType>(command: string[], commandArgs: string[], description: string, progressReporter?: ProgressReporter): Promise<OutputType> {
// Add format argument first, in case commandArgs contains positional parameters.
const args = ['--format', 'json'].concat(commandArgs);
async runJsonCodeQlCliCommand<OutputType>(command: string[], commandArgs: string[], description: string, addFormat = true, progressReporter?: ProgressReporter): Promise<OutputType> {
let args: string[] = [];
if (addFormat) // Add format argument first, in case commandArgs contains positional parameters.
args = args.concat(['--format', 'json']);
args = args.concat(commandArgs);
const result = await this.runCodeQlCliCommand(command, args, description, progressReporter);
try {
return JSON.parse(result) as OutputType;
@@ -505,6 +499,18 @@ export class CodeQLCliServer implements Disposable {
);
}
public async resolveQlref(qlref: string): Promise<QlrefInfo> {
const subcommandArgs = [
qlref
];
return await this.runJsonCodeQlCliCommand<QlrefInfo>(
['resolve', 'qlref'],
subcommandArgs,
'Resolving qlref',
false
);
}
/**
* Runs QL tests.
* @param testPaths Full paths of the tests to run.
@@ -549,7 +555,7 @@ export class CodeQLCliServer implements Disposable {
if (queryMemoryMb !== undefined) {
args.push('--ram', queryMemoryMb.toString());
}
return await this.runJsonCodeQlCliCommand<string[]>(['resolve', 'ram'], args, 'Resolving RAM settings', progressReporter);
return await this.runJsonCodeQlCliCommand<string[]>(['resolve', 'ram'], args, 'Resolving RAM settings', true, progressReporter);
}
/**
* Gets the headers (and optionally pagination info) of a bqrs.
@@ -590,10 +596,10 @@ export class CodeQLCliServer implements Disposable {
async runInterpretCommand(format: string, metadata: QueryMetadata, resultsPath: string, interpretedResultsPath: string, sourceInfo?: SourceInfo) {
const args = [
`-t=kind=${metadata.kind}`,
`-t=id=${metadata.id}`,
'--output', interpretedResultsPath,
'--format', format,
// Forward all of the query metadata.
...Object.entries(metadata).map(([key, value]) => `-t=${key}=${value}`)
];
if (format == SARIF_FORMAT) {
// TODO: This flag means that we don't group interpreted results
@@ -602,9 +608,6 @@ export class CodeQLCliServer implements Disposable {
// grouping client-side.
args.push('--no-group-results');
}
if (config.isCanary() && metadata.scored !== undefined) {
args.push(`-t=scored=${metadata.scored}`);
}
if (sourceInfo !== undefined) {
args.push(
'--source-archive', sourceInfo.sourceArchive,
@@ -691,7 +694,7 @@ export class CodeQLCliServer implements Disposable {
const args = ['--additional-packs', searchPath.join(path.delimiter), '--dbscheme', dbScheme];
if (targetDbScheme) {
args.push('--target-dbscheme', targetDbScheme);
if (allowDowngradesIfPossible && await this.supportsDowngrades()) {
if (allowDowngradesIfPossible && await this.cliConstraints.supportsDowngrades()) {
args.push('--allow-downgrades');
}
}
@@ -744,7 +747,7 @@ export class CodeQLCliServer implements Disposable {
}
async generateDil(qloFile: string, outFile: string): Promise<void> {
const extraArgs = await this.supportsDecompileDil()
const extraArgs = await this.cliConstraints.supportsDecompileDil()
? ['--kind', 'dil', '-o', outFile, qloFile]
: ['-o', outFile, qloFile];
await this.runCodeQlCliCommand(
@@ -761,18 +764,6 @@ export class CodeQLCliServer implements Disposable {
return this._version;
}
private async supportsDecompileDil() {
return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_DECOMPILE_KIND_DIL) >= 0;
}
public async supportsLanguageName() {
return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_LANGUAGE) >= 0;
}
public async supportsDowngrades() {
return (await this.getVersion()).compare(CodeQLCliServer.CLI_VERSION_WITH_DOWNGRADES) >= 0;
}
private async refreshVersion() {
const distribution = await this.distributionProvider.getDistribution();
switch (distribution.kind) {
@@ -908,11 +899,11 @@ class SplitBuffer {
/**
* A version of startsWith that isn't overriden by a broken version of ms-python.
*
*
* The definition comes from
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
* which is CC0/public domain
*
*
* See https://github.com/github/vscode-codeql/issues/802 for more context as to why we need it.
*/
private static startsWith(s: string, searchString: string, position: number): boolean {
@@ -1001,3 +992,60 @@ export function shouldDebugQueryServer() {
&& process.env.QUERY_SERVER_JAVA_DEBUG !== '0'
&& process.env.QUERY_SERVER_JAVA_DEBUG?.toLocaleLowerCase() !== 'false';
}
export class CliVersionConstraint {
/**
* CLI version where --kind=DIL was introduced
*/
public static CLI_VERSION_WITH_DECOMPILE_KIND_DIL = new SemVer('2.3.0');
/**
* CLI version where languages are exposed during a `codeql resolve database` command.
*/
public static CLI_VERSION_WITH_LANGUAGE = new SemVer('2.4.1');
/**
* CLI version where `codeql resolve upgrades` supports
* the `--allow-downgrades` flag
*/
public static CLI_VERSION_WITH_DOWNGRADES = new SemVer('2.4.4');
/**
* CLI version where the `codeql resolve qlref` command is available.
*/
public static CLI_VERSION_WITH_RESOLVE_QLREF = new SemVer('2.5.1');
/**
* CLI version where database registration was introduced
*/
public static CLI_VERSION_WITH_DB_REGISTRATION = new SemVer('2.4.1');
constructor(private readonly cli: CodeQLCliServer) {
/**/
}
private async isVersionAtLeast(v: SemVer) {
return (await this.cli.getVersion()).compare(v) >= 0;
}
public async supportsDecompileDil() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_DECOMPILE_KIND_DIL);
}
public async supportsLanguageName() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_LANGUAGE);
}
public async supportsDowngrades() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_DOWNGRADES);
}
public async supportsResolveQlref() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF);
}
async supportsDatabaseRegistration() {
return this.isVersionAtLeast(CliVersionConstraint.CLI_VERSION_WITH_DB_REGISTRATION);
}
}

View File

@@ -41,6 +41,7 @@ const ROOT_SETTING = new Setting('codeQL');
// Global configuration
const TELEMETRY_SETTING = new Setting('telemetry', ROOT_SETTING);
const AST_VIEWER_SETTING = new Setting('astViewer', ROOT_SETTING);
const GLOBAL_TELEMETRY_SETTING = new Setting('telemetry');
export const LOG_TELEMETRY = new Setting('logTelemetry', TELEMETRY_SETTING);
@@ -279,3 +280,8 @@ export const CANARY_FEATURES = new Setting('canary', ROOT_SETTING);
export function isCanary() {
return !!CANARY_FEATURES.getValue<boolean>();
}
/**
* Avoids caching in the AST viewer if the user is also a canary user.
*/
export const NO_CACHE_AST_VIEWER = new Setting('disableCache', AST_VIEWER_SETTING);

View File

@@ -25,6 +25,7 @@ import {
} from './keyType';
import { FullLocationLink, getLocationsForUriString, TEMPLATE_NAME } from './locationFinder';
import { qlpackOfDatabase, resolveQueries } from './queryResolver';
import { isCanary, NO_CACHE_AST_VIEWER } from '../config';
/**
* Run templated CodeQL queries to find definitions and references in
@@ -141,7 +142,9 @@ export class TemplatePrintAstProvider {
if (!document) {
throw new Error('Cannot view the AST. Please select a valid source file inside a CodeQL database.');
}
const queryResults = await this.cache.get(document.uri.toString(), progress, token);
const queryResults = this.shouldCache()
? await this.cache.get(document.uri.toString(), progress, token)
: await this.getAst(document.uri.toString(), progress, token);
return new AstBuilder(
queryResults, this.cli,
@@ -150,6 +153,10 @@ export class TemplatePrintAstProvider {
);
}
private shouldCache() {
return !(isCanary() && NO_CACHE_AST_VIEWER.getValue<boolean>());
}
private async getAst(
uriString: string,
progress: ProgressCallback,

View File

@@ -108,7 +108,7 @@ class DatabaseTreeDataProvider extends DisposableObject
}
private handleDidChangeDatabaseItem = (event: DatabaseChangedEvent): void => {
// Note that events from the databse manager are instances of DatabaseChangedEvent
// Note that events from the database manager are instances of DatabaseChangedEvent
// and events fired by the UI are instances of DatabaseItem
// When event.item is undefined, then the entire tree is refreshed.
@@ -295,7 +295,7 @@ export class DatabaseUI extends DisposableObject {
'codeQLDatabases.chooseDatabaseLgtm',
this.handleChooseDatabaseLgtm,
{
title: 'Adding database from LGTM',
title: 'Adding database from LGTM. Choose a language from the dropdown, if requested.',
})
);
this.push(
@@ -423,9 +423,9 @@ export class DatabaseUI extends DisposableObject {
if (failures.length) {
const dirname = path.dirname(failures[0]);
showAndLogErrorMessage(
`Failed to delete unused databases:\n ${
failures.join('\n ')
}\n. To delete unused databases, please remove them manually from the storage folder ${dirname}.`
`Failed to delete unused databases (${
failures.join(', ')
}).\nTo delete unused databases, please remove them manually from the storage folder ${dirname}.`
);
}
};
@@ -703,7 +703,7 @@ export class DatabaseUI extends DisposableObject {
* 2. If the selected URI is a directory matching db-*, choose the containing directory
* 3. choose the current directory
*
* @param uri a URI that is a datbase folder or inside it
* @param uri a URI that is a database folder or inside it
*
* @return the actual database folder found by using the heuristics above.
*/

View File

@@ -808,7 +808,7 @@ export class DatabaseManager extends DisposableObject {
token: vscode.CancellationToken,
dbItem: DatabaseItem,
) {
if (dbItem.contents && (await this.qs.supportsDatabaseRegistration())) {
if (dbItem.contents && (await this.cli.cliConstraints.supportsDatabaseRegistration())) {
const databases: Dataset[] = [{
dbDir: dbItem.contents.datasetUri.fsPath,
workingSet: 'default'
@@ -822,7 +822,7 @@ export class DatabaseManager extends DisposableObject {
token: vscode.CancellationToken,
dbItem: DatabaseItem,
) {
if (dbItem.contents && (await this.qs.supportsDatabaseRegistration())) {
if (dbItem.contents && (await this.cli.cliConstraints.supportsDatabaseRegistration())) {
const databases: Dataset[] = [{
dbDir: dbItem.contents.datasetUri.fsPath,
workingSet: 'default'
@@ -852,7 +852,7 @@ export class DatabaseManager extends DisposableObject {
}
private async getPrimaryLanguage(dbPath: string) {
if (!(await this.cli.supportsLanguageName())) {
if (!(await this.cli.cliConstraints.supportsLanguageName())) {
// return undefined so that we recalculate on restart until the cli is at a version that
// supports this feature. This recalculation is cheap since we avoid calling into the cli
// unless we know it can return the langauges property.

View File

@@ -13,12 +13,13 @@ import {
window
} from 'vscode';
import { LanguageClient } from 'vscode-languageclient';
import * as os from 'os';
import * as path from 'path';
import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api';
import { AstViewer } from './astViewer';
import * as archiveFilesystemProvider from './archive-filesystem-provider';
import { CodeQLCliServer } from './cli';
import { CodeQLCliServer, CliVersionConstraint } from './cli';
import {
CliConfigListener,
DistributionConfigListener,
@@ -139,7 +140,7 @@ export interface CodeQLExtensionInterface {
/**
* Returns the CodeQLExtensionInterface, or an empty object if the interface is not
* available afer activation is complete. This will happen if there is no cli
* available after activation is complete. This will happen if there is no cli
* installed when the extension starts. Downloading and installing the cli
* will happen at a later time.
*
@@ -480,6 +481,25 @@ async function activateWithInstalledDistribution(
}
}
async function openReferencedFile(
selectedQuery: Uri
): Promise<void> {
if (qs !== undefined) {
if (await cliServer.cliConstraints.supportsResolveQlref()) {
const resolved = await cliServer.resolveQlref(selectedQuery.path);
const uri = Uri.file(resolved.resolvedPath);
await window.showTextDocument(uri, { preview: false });
} else {
helpers.showAndLogErrorMessage(
'Jumping from a .qlref file to the .ql file it references is not '
+ 'supported with the CLI version you are running.\n'
+ `Please upgrade your CLI to version ${
CliVersionConstraint.CLI_VERSION_WITH_RESOLVE_QLREF
} or later to use this feature.`);
}
}
}
ctx.subscriptions.push(tmpDirDisposal);
logger.log('Initializing CodeQL language server.');
@@ -616,6 +636,12 @@ async function activateWithInstalledDistribution(
}
)
);
ctx.subscriptions.push(
commandRunner(
'codeQL.openReferencedFile',
openReferencedFile
)
);
ctx.subscriptions.push(
commandRunnerWithProgress('codeQL.restartQueryServer', async (
@@ -656,7 +682,7 @@ async function activateWithInstalledDistribution(
) =>
databaseUI.handleChooseDatabaseLgtm(progress, token),
{
title: 'Adding database from LGTM',
title: 'Adding database from LGTM. Choose a language from the dropdown, if requested.',
})
);
ctx.subscriptions.push(
@@ -675,6 +701,14 @@ async function activateWithInstalledDistribution(
commandRunner('codeQL.openDocumentation', async () =>
env.openExternal(Uri.parse('https://codeql.github.com/docs/'))));
ctx.subscriptions.push(
commandRunner('codeQL.copyVersion', async () => {
const text = `CodeQL extension version: ${extension?.packageJSON.version} \nCodeQL CLI version: ${await cliServer.getVersion()} \nPlatform: ${os.platform()} ${os.arch()}`;
env.clipboard.writeText(text);
helpers.showAndLogInformationMessage(text);
}));
logger.log('Starting language server.');
ctx.subscriptions.push(client.start());

View File

@@ -29,8 +29,13 @@ export async function showAndLogErrorMessage(message: string, {
items = [] as string[],
fullMessage = undefined as (string | undefined)
} = {}): Promise<string | undefined> {
return internalShowAndLog(message, items, outputLogger, Window.showErrorMessage, fullMessage);
return internalShowAndLog(dropLinesExceptInitial(message), items, outputLogger, Window.showErrorMessage, fullMessage);
}
function dropLinesExceptInitial(message: string, n = 2) {
return message.toString().split(/\r?\n/).slice(0, n).join('\n');
}
/**
* Show a warning message and log it to the console
*
@@ -362,7 +367,7 @@ export class CachedOperation<U> {
* `cli.CodeQLCliServer.resolveDatabase` and use the first entry in the
* `languages` property.
*
* @see cli.CodeQLCliServer.supportsLanguageName
* @see cli.CliVersionConstraint.supportsLanguageName
* @see cli.CodeQLCliServer.resolveDatabase
*/
const dbSchemeToLanguage = {

View File

@@ -137,9 +137,11 @@ export class InterfaceManager extends DisposableObject {
this.databaseManager.onDidChangeDatabaseItem(({ kind }) => {
if (kind === DatabaseEventKind.Remove) {
this._diagnosticCollection.clear();
this.postMessage({
t: 'untoggleShowProblems'
});
if (this.isShowingPanel()) {
this.postMessage({
t: 'untoggleShowProblems'
});
}
}
})
);
@@ -149,6 +151,10 @@ export class InterfaceManager extends DisposableObject {
this.postMessage({ t: 'navigatePath', direction });
}
private isShowingPanel() {
return !!this._panel;
}
// Returns the webview panel, creating it if it doesn't already
// exist.
getPanel(): vscode.WebviewPanel {
@@ -168,6 +174,7 @@ export class InterfaceManager extends DisposableObject {
]
}
));
this._panel.onDidDispose(
() => {
this._panel = undefined;
@@ -547,24 +554,26 @@ export class InterfaceManager extends DisposableObject {
sourceInfo: cli.SourceInfo | undefined,
sourceLocationPrefix: string,
sortState: InterpretedResultsSortState | undefined
): Promise<Interpretation> {
): Promise<Interpretation | undefined> {
if (!resultsPaths) {
this.logger.log('No results path. Cannot display interpreted results.');
return undefined;
}
const sarif = await interpretResults(
this.cliServer,
metadata,
resultsPaths,
sourceInfo
);
sarif.runs.forEach(run => {
if (run.results !== undefined) {
sortInterpretedResults(run.results, sortState);
}
});
const numTotalResults = (() => {
if (sarif.runs.length === 0) return 0;
if (sarif.runs[0].results === undefined) return 0;
return sarif.runs[0].results.length;
})();
const numTotalResults = sarif.runs[0]?.results?.length || 0;
const interpretation: Interpretation = {
sarif,
@@ -634,7 +643,7 @@ export class InterfaceManager extends DisposableObject {
// If interpretation fails, accept the error and continue
// trying to render uninterpreted results anyway.
showAndLogErrorMessage(
`Exception during results interpretation: ${e.message}. Will show raw results instead.`
`Showing raw results instead of interpreted ones due to an error. ${e.message}`
);
}
}
@@ -666,6 +675,10 @@ export class InterfaceManager extends DisposableObject {
undefined
);
if (!interpretation) {
return;
}
try {
await this.showProblemResultsAsDiagnostics(interpretation, database);
} catch (e) {

View File

@@ -167,9 +167,11 @@ export class QLTestDiscovery extends Discovery<QLTestDiscoveryResults> {
protected update(results: QLTestDiscoveryResults): void {
this._testDirectory = results.testDirectory;
// Watch for changes to any `.ql` or `.qlref` file in any of the QL packs that contain tests.
this.watcher.clear();
// Watch for changes to any `.ql` or `.qlref` file in any of the QL packs that contain tests.
this.watcher.addWatch(new RelativePattern(results.watchPath, '**/*.{ql,qlref}'));
// need to explicitly watch for changes to directories themselves.
this.watcher.addWatch(new RelativePattern(results.watchPath, '**/'));
this._onDidChangeTests.fire();
}

View File

@@ -186,14 +186,13 @@ export function ensureMetadataIsComplete(metadata: QueryMetadata | undefined) {
if (metadata === undefined) {
throw new Error('Can\'t interpret results without query metadata');
}
let { kind, id, scored } = metadata;
if (kind === undefined) {
if (metadata.kind === undefined) {
throw new Error('Can\'t interpret results without query metadata including kind');
}
if (id === undefined) {
if (metadata.id === undefined) {
// Interpretation per se doesn't really require an id, but the
// SARIF format does, so in the absence of one, we use a dummy id.
id = 'dummy-id';
metadata.id = 'dummy-id';
}
return { kind, id, scored };
return metadata;
}

View File

@@ -8,7 +8,6 @@ import { QueryServerConfig } from './config';
import { Logger, ProgressReporter } from './logging';
import { completeQuery, EvaluationResult, progress, ProgressMessage, WithProgressId } from './pure/messages';
import * as messages from './pure/messages';
import { SemVer } from 'semver';
import { ProgressCallback, ProgressTask } from './commandRunner';
type ServerOpts = {
@@ -50,11 +49,6 @@ type WithProgressReporting = (task: (progress: ProgressReporter, token: Cancella
*/
export class QueryServerClient extends DisposableObject {
/**
* Query Server version where database registration was introduced
*/
private static VERSION_WITH_DB_REGISTRATION = new SemVer('2.4.1');
serverProcess?: ServerProcess;
evaluationResultCallbacks: { [key: number]: (res: EvaluationResult) => void };
progressCallbacks: { [key: number]: ((res: ProgressMessage) => void) | undefined };
@@ -145,7 +139,7 @@ export class QueryServerClient extends DisposableObject {
args.push(this.config.cacheSize.toString());
}
if (await this.supportsDatabaseRegistration()) {
if (await this.cliServer.cliConstraints.supportsDatabaseRegistration()) {
args.push('--require-db-registration');
}
@@ -202,10 +196,6 @@ export class QueryServerClient extends DisposableObject {
this.evaluationResultCallbacks = {};
}
async supportsDatabaseRegistration() {
return (await this.cliServer.getVersion()).compare(QueryServerClient.VERSION_WITH_DB_REGISTRATION) >= 0;
}
registerCallback(callback: (res: EvaluationResult) => void): number {
const id = this.nextCallback++;
this.evaluationResultCallbacks[id] = callback;

View File

@@ -174,7 +174,10 @@ export class QueryInfo {
if (!hasKind) {
logger.log('Cannot produce interpreted results since the query does not have @kind metadata.');
}
return hasMetadataFile && hasKind;
const isTable = hasKind && this.metadata?.kind === 'table';
return hasMetadataFile && hasKind && !isTable;
}
/**
@@ -640,8 +643,11 @@ export async function compileAndRunQueryAgainstDatabase(
formattedMessages.push(formatted);
qs.logger.log(formatted);
}
if (quickEval && formattedMessages.length <= 3) {
showAndLogErrorMessage('Quick evaluation compilation failed: \n' + formattedMessages.join('\n'));
if (quickEval && formattedMessages.length <= 2) {
// If there are more than 2 error messages, they will not be displayed well in a popup
// and will be trimmed by the function displaying the error popup. Accordingly, we only
// try to show the errors if there are 2 or less, otherwise we direct the user to the log.
showAndLogErrorMessage('Quick evaluation compilation failed: ' + formattedMessages.join('\n'));
} else {
showAndLogErrorMessage((quickEval ? 'Quick evaluation' : 'Query') + compilationFailedErrorTail);
}

View File

@@ -19,7 +19,7 @@ export class CodeQlStatusBarHandler extends DisposableObject {
this.push(this.item);
this.push(workspace.onDidChangeConfiguration(this.handleDidChangeConfiguration, this));
this.push(distributionConfigListener.onDidChangeConfiguration(() => this.updateStatusItem()));
this.item.command = 'codeQL.openDocumentation';
this.item.command = 'codeQL.copyVersion';
this.updateStatusItem();
}
@@ -37,7 +37,7 @@ export class CodeQlStatusBarHandler extends DisposableObject {
private async updateStatusItem() {
const canary = CANARY_FEATURES.getValue() ? ' (Canary)' : '';
// since getting the verison may take a few seconds, initialize with some
// since getting the version may take a few seconds, initialize with some
// meaningful text.
this.item.text = `CodeQL${canary}`;

View File

@@ -129,7 +129,7 @@ describe('using the query server', function() {
const parsedResults = new Checkpoint<void>();
it('should register the database if necessary', async () => {
if (await qs.supportsDatabaseRegistration()) {
if (await cliServer.cliConstraints.supportsDatabaseRegistration()) {
await qs.sendRequest(messages.registerDatabases, { databases: [db] }, token, (() => { /**/ }) as any);
}
});

View File

@@ -67,11 +67,13 @@ describe('databases', () => {
} as unknown as ExtensionContext,
{
sendRequest: sendRequestSpy,
supportsDatabaseRegistration: supportsDatabaseRegistrationSpy,
onDidStartQueryServer: () => { /**/ }
} as unknown as QueryServerClient,
{
supportsLanguageName: supportsLanguageNameSpy,
cliConstraints: {
supportsLanguageName: supportsLanguageNameSpy,
supportsDatabaseRegistration: supportsDatabaseRegistrationSpy,
},
resolveDatabase: resolveDatabaseSpy
} as unknown as CodeQLCliServer,
{} as Logger,

View File

@@ -24,6 +24,23 @@ describe('run-queries', () => {
expect(info.dataset).to.eq('file:///abc');
});
it('should check if interpreted results can be created', async () => {
const info = createMockQueryInfo();
(info.dbItem.hasMetadataFile as sinon.SinonStub).returns(true);
expect(await info.canHaveInterpretedResults()).to.eq(true);
(info.dbItem.hasMetadataFile as sinon.SinonStub).returns(false);
expect(await info.canHaveInterpretedResults()).to.eq(false);
(info.dbItem.hasMetadataFile as sinon.SinonStub).returns(true);
info.metadata!.kind = undefined;
expect(await info.canHaveInterpretedResults()).to.eq(false);
info.metadata!.kind = 'table';
expect(await info.canHaveInterpretedResults()).to.eq(false);
});
describe('compile', () => {
it('should compile', async () => {
const info = createMockQueryInfo();
@@ -73,9 +90,14 @@ describe('run-queries', () => {
{
contents: {
datasetUri: 'file:///abc'
}
},
hasMetadataFile: sinon.stub()
} as unknown as DatabaseItem,
'my-scheme' // queryDbscheme
'my-scheme', // queryDbscheme,
undefined,
{
kind: 'problem'
}
);
}