Compare commits

...

64 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
Aditya Sharad
cd7354446b v1.4.5 (#804)
Some checks failed
Code Scanning - CodeQL / codeql (push) Has been cancelled
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-03-22 17:18:32 -07:00
Alexander Eyers-Taylor
d909f98fcb Fix running tests when ms-python is installed. (#803)
Co-authored-by: Aditya Sharad <6874315+adityasharad@users.noreply.github.com>
2021-03-22 16:54:02 -07:00
Andrew Eisenberg
8c2db75886 Avoid showing an error when query has not @kind metadata (#801)
Fixes #800
2021-03-22 08:03:13 -07:00
Aditya Sharad
73e560e6da Actions: Test against CodeQL 2.4.6
Deliberately keeping 2.4.5 as well, to keep testing enterprise compatibility.
2021-03-19 17:01:58 -07:00
aeisenberg
ada1180468 Bump version to v1.4.5 2021-03-19 15:39:32 -07:00
Shati Patel
d1e70816aa Update pull_request_template.md (#791) 2021-03-19 17:38:56 +00:00
Andrew Eisenberg
df936167d5 v1.4.4
Some checks failed
Code Scanning - CodeQL / codeql (push) Has been cancelled
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-03-19 10:24:35 -07:00
Andrew Eisenberg
0327ec358c Update Changelog 2021-03-19 08:58:10 -07:00
Edoardo Pirovano
7a78fca252 Report description for test failure when possible 2021-03-19 08:58:10 -07:00
Edoardo Pirovano
10e86f1835 Add some commonly used QL snippets 2021-03-17 08:43:00 -07:00
Edoardo Pirovano
dbaed3acd5 Implement viewing of query results as a CSV 2021-03-17 08:04:46 -07:00
Edoardo Pirovano
6830bdd28d Add option to pass additional arguments when running tests 2021-03-16 13:45:00 -07:00
Edoardo Pirovano
e316decae1 Implement sorting of query history by name, date, and result count 2021-03-15 11:18:47 -07:00
Marcono1234
a86c1ce69b Use HTTPS for links 2021-03-14 22:58:50 -07:00
Marcono1234
01418cba26 Update Semmle links in extension README 2021-03-14 22:58:50 -07:00
Edoardo Pirovano
35d98f62e8 Limit scope of save cache option. 2021-03-12 08:46:45 -08:00
Edoardo Pirovano
b30121b84c Apply suggestions from code review
Co-authored-by: Andrew Eisenberg <aeisenberg@github.com>
2021-03-12 08:46:45 -08:00
Edoardo Pirovano
fd15217a20 Expand disk cache evaluator options 2021-03-12 08:46:45 -08:00
Shati Patel
1d03702334 Docs: Update Telemetry links 2021-03-09 08:41:52 -08:00
Andrew Eisenberg
c47029e9eb Update cli version used in integration tests 2021-03-08 13:25:24 -08:00
Alexander Eyers-Taylor
5fdfb44c2e Use downgrades when fixing dbscheme mismatches where possible. (#765) 2021-03-04 10:48:12 +00:00
Andrew Eisenberg
6e40478440 Add error message when interpretation fails
One way it can fail is if the SARIF is too large. We explicitly call
out that error because the raw message received from the node runtime
is not very understandable.
2021-03-02 14:03:19 -08:00
Andrew Eisenberg
9e68b4f061 Use codeQL.runningQueries.numberOfThreads to run interpretation
When running `codeql bqrs interpret`, ensure the
`codeQL.runningQueries.numberOfThreads` setting is respected.
2021-03-02 13:47:12 -08:00
Andrew Eisenberg
0f82875b9d Allow raw project slugs for fetching lgtm dbs
The following is now acceptable for fetching the codeql lgtm database:

```
g/github/codeql
```
2021-03-02 11:40:51 -08:00
aeisenberg
fd52f66f6d Bump version to v1.4.4 2021-03-02 10:23:52 -08:00
Henry Mercer
42cfa45d7e Update page size setting description 2021-02-26 15:22:00 +00:00
Andrew Eisenberg
5023f91475 Bump test timeouts
Necessary because we just added some extra waiting
in order to ensure that config listeners have all
fired.
2021-02-22 12:50:39 -08:00
42 changed files with 2450 additions and 3596 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

@@ -9,4 +9,4 @@ Replace this with a description of the changes your pull request makes.
- [ ] [CHANGELOG.md](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md) has been updated to incorporate all user visible changes made by this pull request.
- [ ] Issues have been created for any UI or other user-facing changes made by this pull request.
- [ ] `@github/docs-content-dsp` has been cc'd in all issues for UI or other user-facing changes made by this pull request.
- [ ] `@github/docs-content-codeql` has been cc'd in all issues for UI or other user-facing changes made by this pull request.

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.4']
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,5 +1,5 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [

View File

@@ -25,7 +25,7 @@ Here are a few things you can do that will increase the likelihood of your pull
* Follow the [style guide][style].
* Write tests. Tests that don't require the VS Code API are located [here](extensions/ql-vscode/test). Integration tests that do require the VS Code API are located [here](extensions/ql-vscode/src/vscode-tests).
* Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests.
* Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
* Write a [good commit message](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html).
## Setting up a local build

View File

@@ -1,5 +1,39 @@
# CodeQL for Visual Studio Code: Changelog
## 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
- Avoid showing an error popup when user runs a query without `@kind` metadata. [#801](https://github.com/github/vscode-codeql/pull/801)
- Fix running of tests when the `ms-python` extension is installed. [#803](https://github.com/github/vscode-codeql/pull/803)
## 1.4.4 - 19 March 2021
- Introduce evaluator options for saving intermediate results to the disk cache (`codeQL.runningQueries.saveCache`) and for limiting the size of this cache (`codeQL.runningQueries.cacheSize`). [#778](https://github.com/github/vscode-codeql/pull/778)
- Respect the `codeQL.runningQueries.numberOfThreads` setting when creating SARIF files during result interpretation. [#771](https://github.com/github/vscode-codeql/pull/771)
- Allow using raw LGTM project slugs for fetching LGTM databases. [#769](https://github.com/github/vscode-codeql/pull/769)
- Better error messages when BQRS interpretation fails to produce SARIF. [#770](https://github.com/github/vscode-codeql/pull/770)
- Implement sorting of the query history view by name, date, and results count. [#777](https://github.com/github/vscode-codeql/pull/777)
- Add a configuration option to pass additional arguments to the CLI when running tests. [#785](https://github.com/github/vscode-codeql/pull/785)
- Introduce option to view query results as CSV. [#784](https://github.com/github/vscode-codeql/pull/784)
- Add some snippets for commonly used QL statements. [#782](https://github.com/github/vscode-codeql/pull/782)
- More descriptive error messages on QL test failures. [#788](https://github.com/github/vscode-codeql/pull/788)
## 1.4.3 - 22 February 2021
- Avoid displaying an error when removing orphaned databases and the storage folder does not exist. [#748](https://github.com/github/vscode-codeql/pull/748)
@@ -12,7 +46,7 @@
- Add a status bar item for the CodeQL CLI to show the current version. [#741](https://github.com/github/vscode-codeql/pull/741)
- Fix version constraint for flagging CLI support of non-destructive updates. [#744](https://github.com/github/vscode-codeql/pull/744)
- Add a _More Information_ button in the telemetry popup that opens [TELEMETRY.md](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/TELEMETRY.md) in a browser tab. [#742](https://github.com/github/vscode-codeql/pull/742)
- Add a _More Information_ button in the telemetry popup that opens the [telemetry documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code) in a browser tab. [#742](https://github.com/github/vscode-codeql/pull/742)
## 1.4.1 - 29 January 2021
@@ -22,7 +56,7 @@
- Fix bug where databases are not reregistered when the query server restarts. [#734](https://github.com/github/vscode-codeql/pull/734)
- Fix bug where upgrade requests were erroneously being marked as failed. [#734](https://github.com/github/vscode-codeql/pull/734)
- On a strictly opt-in basis, collect anonymized usage data from the VS Code extension, helping improve CodeQL's usability and performance. See [TELEMETRY.md](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/TELEMETRY.md) for more information on exactly what data is collected and what it is used for. [#611](https://github.com/github/vscode-codeql/pull/611)
- On a strictly opt-in basis, collect anonymized usage data from the VS Code extension, helping improve CodeQL's usability and performance. See the [telemetry documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code) for more information on exactly what data is collected and what it is used for. [#611](https://github.com/github/vscode-codeql/pull/611)
## 1.3.10 - 20 January 2021

View File

@@ -1,6 +1,6 @@
# CodeQL extension for Visual Studio Code
This project is an extension for Visual Studio Code that adds rich language support for [CodeQL](https://help.semmle.com/codeql) and allows you to easily find problems in codebases. In particular, the extension:
This project is an extension for Visual Studio Code that adds rich language support for [CodeQL](https://codeql.github.com/docs/) and allows you to easily find problems in codebases. In particular, the extension:
- Enables you to use CodeQL to query databases generated from source code.
- Shows the flow of data through the results of path queries, which is essential for triaging security results.
@@ -12,7 +12,7 @@ To see what has changed in the last few versions of the extension, see the [Chan
## Quick start overview
The information in this `README` file describes the quickest way to start using CodeQL.
For information about other configurations, see the separate [CodeQL help](https://help.semmle.com/codeql/codeql-for-vscode.html).
For information about other configurations, see the separate [CodeQL help](https://codeql.github.com/docs/codeql-for-visual-studio-code/).
### Quick start: Installing and configuring the extension
@@ -40,9 +40,9 @@ The CodeQL extension requires a minimum of Visual Studio Code 1.39. Older versio
### Checking access to the CodeQL CLI
The extension uses the [CodeQL CLI](https://help.semmle.com/codeql/codeql-cli.html) to compile and run queries. The extension automatically manages access to the CLI for you by default (recommended). To check for updates to the CodeQL CLI, you can use the **CodeQL: Check for CLI Updates** command.
The extension uses the [CodeQL CLI](https://codeql.github.com/docs/codeql-cli/) to compile and run queries. The extension automatically manages access to the CLI for you by default (recommended). To check for updates to the CodeQL CLI, you can use the **CodeQL: Check for CLI Updates** command.
If you want to override the default behavior and use a CodeQL CLI that's already on your machine, see [Configuring access to the CodeQL CLI](https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html#configuring-access-to-the-codeql-cli).
If you want to override the default behavior and use a CodeQL CLI that's already on your machine, see [Configuring access to the CodeQL CLI](https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/#configuring-access-to-the-codeql-cli).
If you have any difficulty with CodeQL CLI access, see the **CodeQL Extension Log** in the **Output** view for any error messages.
@@ -52,7 +52,7 @@ When you're working with CodeQL, you need access to the standard CodeQL librarie
Initially, we recommend that you clone and use the ready-to-use [starter workspace](https://github.com/github/vscode-codeql-starter/).
This includes libraries and queries for the main supported languages, with folders set up ready for your custom queries. After cloning the workspace (use `git clone --recursive`), you can use it in the same way as any other VS Code workspace—with the added advantage that you can easily update the CodeQL libraries.
For information about configuring an existing workspace for CodeQL, [see the documentation](https://help.semmle.com/codeql/codeql-for-vscode/procedures/setting-up.html#updating-an-existing-workspace-for-codeql).
For information about configuring an existing workspace for CodeQL, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/setting-up-codeql-in-visual-studio-code/#updating-an-existing-workspace-for-codeql).
## Upgrading CodeQL standard libraries
@@ -75,7 +75,7 @@ You can find all the commands contributed by the extension in the Command Palett
### Importing a database from LGTM
While you can use the [CodeQL CLI to create your own databases](https://help.semmle.com/codeql/codeql-cli/procedures/create-codeql-database.html), the simplest way to start is by downloading a database from LGTM.com.
While you can use the [CodeQL CLI to create your own databases](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/), the simplest way to start is by downloading a database from LGTM.com.
1. Open [LGTM.com](https://lgtm.com/#explore) in your browser.
1. Search for a project you're interested in, for example [Apache Kafka](https://lgtm.com/projects/g/apache/kafka).
@@ -100,11 +100,11 @@ If there are any problems running a query, a notification is displayed in the bo
## What next?
For more information about the CodeQL extension, [see the documentation](https://help.semmle.com/codeql/codeql-for-vscode.html). Otherwise, you could:
For more information about the CodeQL extension, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/). Otherwise, you could:
- [Create a database for a different codebase](https://help.semmle.com/codeql/codeql-cli/procedures/create-codeql-database.html).
- [Create a database for a different codebase](https://codeql.github.com/docs/codeql-cli/creating-codeql-databases/).
- [Try out variant analysis](https://help.semmle.com/QL/learn-ql/ql-training.html).
- [Learn more about CodeQL](https://help.semmle.com/QL/learn-ql/).
- [Learn more about CodeQL](https://codeql.github.com/docs/).
- [Read how security researchers use CodeQL to find CVEs](https://securitylab.github.com/research).
## License
@@ -113,4 +113,4 @@ The CodeQL extension for Visual Studio Code is [licensed](LICENSE.md) under the
## Data and Telemetry
If you specifically opt-in to permit GitHub to do so, GitHub will collect usage data and metrics for the purposes of helping the core developers to improve the CodeQL extension for VS Code. This data will not be shared with any parties outside of GitHub. IP addresses and installation IDs will be retained for a maximum of 30 days. Anonymous data will be retained for a maximum of 180 days. Please see [telemetry](TELEMETRY.md) for more information.
If you specifically opt-in to permit GitHub to do so, GitHub will collect usage data and metrics for the purposes of helping the core developers to improve the CodeQL extension for VS Code. This data will not be shared with any parties outside of GitHub. IP addresses and installation IDs will be retained for a maximum of 30 days. Anonymous data will be retained for a maximum of 180 days. For more information about telemetry, [see the documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code).

View File

@@ -1,47 +0,0 @@
# Telemetry in the CodeQL extension for VS Code
If you specifically opt-in to permit GitHub to do so, GitHub will collect usage data and metrics for the purposes of helping the core developers to improve the CodeQL extension for VS Code. This data will not be shared with any parties outside of GitHub. IP addresses and installation IDs will be retained for a maximum of 30 days. Anonymous data will be retained for a maximum of 180 days.
## Why do you collect data?
GitHub collects aggregated, anonymous usage data and metrics to help us improve CodeQL for VS Code. IP addresses and installation IDs are collected only to ensure that anonymous data is not duplicated during aggregation.
## What data is collected
If you opt in, GitHub collects the following information related to the usage of the extension. The data collected are:
- The identifiers of any CodeQL-related [VS Code commands](https://code.visualstudio.com/docs/getstarted/tips-and-tricks#_command-palette) that are run
- For each command: the timestamp, time taken, and whether or not the command completed successfully
- VS Code and extension version
- Randomly generated GUID that uniquely identifies a CodeQL extension installation. (Discarded before aggregation.)
- IP address of the client sending the telemetry data. (Discarded before aggregation.)
- Whether or not the `codeQL.canary` setting is enabled and set to `true`
## How long will data be retained?
IP address and GUIDs will be retained for a maximum of 30 days. Anonymous, aggregated data that includes command identifiers, run times, and timestamps will be retained for a maximum of 180 days.
## Who will have access to this data?
IP address and GUIDs will only be available to the core developers of CodeQL. Aggregated data will be available to GitHub employees.
## What data is **NOT** collected?
We only collect the minimal amount of data we need to answer the questions about how our users are experiencing this product. To that end, we do not collect the following information:
- No GitHub user ID
- No CodeQL database names or contents
- No contents of CodeQL queries
- No filesystem paths.
## How do I disable telemetry reporting?
When telemetry collection is disabled, no data will be sent to GitHub servers.
You can disable telemetry collection by setting `codeQL.telemetry.enableTelemetry` to `false` in [your settings](https://code.visualstudio.com/docs/getstarted/settings#_settings-editor). Telemetry collection is _disabled_ by default.
Additionally, telemetry collection will be disabled if the global `telemetry.enableTelemetry` setting is set to `false`. For more information on global telemetry collection, see [Microsofts documentation](https://code.visualstudio.com/docs/supporting/faq#_how-to-disable-telemetry-reporting).
## More information
See GitHub's [Privacy Statement](https://docs.github.com/en/free-pro-team@latest/github/site-policy/github-privacy-statement) and [Terms of Service](https://docs.github.com/en/free-pro-team@latest/github/site-policy/github-terms-of-service) for more information.

View File

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

View File

@@ -1,5 +1,5 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"declaration": true,
"strict": true,

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();

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" fill="none"
viewBox="0 0 432 432" style="enable-background:new 0 0 432 432;" xml:space="preserve">
<g>
<g>
<g>
<polygon points="234.24,9.067 183.893,59.413 284.587,59.413" fill="#C5C5C5"/>
<path d="m 259.24622,341.40906 v -32.34375 q 13.35937,6.32812 27.07031,9.66797 13.71094,3.33984 26.89453,3.33984 35.15625,0 53.61328,-23.55469 18.63282,-23.73047 21.26953,-71.89453 -10.19531,15.11719 -25.83984,23.20313 -15.64453,8.08593 -34.62891,8.08593 -39.375,0 -62.40234,-23.73046 -22.85156,-23.90625 -22.85156,-65.21485 0,-40.42969 23.90625,-64.86328 23.90625,-24.433594 63.63281,-24.433594 45.52734,0 69.43359,34.980474 24.08204,34.80468 24.08204,101.25 0,62.05078 -29.53125,99.14062 -29.35547,36.91406 -79.10157,36.91406 -13.35937,0 -27.07031,-2.63672 -13.71094,-2.63671 -28.47656,-7.91015 z m 70.66406,-111.26953 q 23.90625,0 37.79297,-16.34766 14.0625,-16.34766 14.0625,-44.82422 0,-28.30078 -14.0625,-44.64844 -13.88672,-16.52343 -37.79297,-16.52343 -23.90625,0 -37.96875,16.52343 -13.88672,16.34766 -13.88672,44.64844 0,28.47656 13.88672,44.82422 14.0625,16.34766 37.96875,16.34766 z" fill="#C5C5C5" />
<polygon points="234.24,422.933 283.947,373.227 184.533,373.227" fill="#C5C5C5"/>
<path d="M 35.300905,316.97546 H 93.308718 V 116.76062 L 30.203249,129.41687 V 97.07312 L 92.957155,84.41687 h 35.507815 v 232.55859 h 58.00781 v 29.88282 H 35.300905 Z" fill="#C5C5C5"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 432 432" style="enable-background:new 0 0 432 432;" xml:space="preserve">
<g>
<g>
<g>
<polygon points="234.24,9.067 183.893,59.413 284.587,59.413" />
<path d="m 259.24622,341.40906 v -32.34375 q 13.35937,6.32812 27.07031,9.66797 13.71094,3.33984 26.89453,3.33984 35.15625,0 53.61328,-23.55469 18.63282,-23.73047 21.26953,-71.89453 -10.19531,15.11719 -25.83984,23.20313 -15.64453,8.08593 -34.62891,8.08593 -39.375,0 -62.40234,-23.73046 -22.85156,-23.90625 -22.85156,-65.21485 0,-40.42969 23.90625,-64.86328 23.90625,-24.433594 63.63281,-24.433594 45.52734,0 69.43359,34.980474 24.08204,34.80468 24.08204,101.25 0,62.05078 -29.53125,99.14062 -29.35547,36.91406 -79.10157,36.91406 -13.35937,0 -27.07031,-2.63672 -13.71094,-2.63671 -28.47656,-7.91015 z m 70.66406,-111.26953 q 23.90625,0 37.79297,-16.34766 14.0625,-16.34766 14.0625,-44.82422 0,-28.30078 -14.0625,-44.64844 -13.88672,-16.52343 -37.79297,-16.52343 -23.90625,0 -37.96875,16.52343 -13.88672,16.34766 -13.88672,44.64844 0,28.47656 13.88672,44.82422 14.0625,16.34766 37.96875,16.34766 z" />
<polygon points="234.24,422.933 283.947,373.227 184.533,373.227" />
<path d="M 35.300905,316.97546 H 93.308718 V 116.76062 L 30.203249,129.41687 V 97.07312 L 92.957155,84.41687 h 35.507815 v 232.55859 h 58.00781 v 29.88282 H 35.300905 Z" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

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.3",
"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",
@@ -106,6 +107,12 @@
"path": "./out/syntaxes/dbscheme.tmLanguage.json"
}
],
"snippets": [
{
"language": "ql",
"path": "./snippets.json"
}
],
"configuration": {
"type": "object",
"title": "CodeQL",
@@ -123,6 +130,21 @@
"maximum": 1024,
"description": "Number of threads for running queries."
},
"codeQL.runningQueries.saveCache": {
"type": "boolean",
"default": false,
"scope": "window",
"description": "Aggressively save intermediate results to the disk cache. This may speed up subsequent queries if they are similar. Be aware that using this option will greatly increase disk usage and initial evaluation time."
},
"codeQL.runningQueries.cacheSize": {
"type": [
"integer",
"null"
],
"default": null,
"minimum": 1024,
"description": "Maximum size of the disk cache (in MB). Leave blank to allow the evaluator to automatically adjust the size of the disk cache based on the size of the codebase and the complexity of the queries being executed."
},
"codeQL.runningQueries.timeout": {
"type": [
"integer",
@@ -160,12 +182,18 @@
"codeQL.resultsDisplay.pageSize": {
"type": "integer",
"default": 200,
"description": "Number of queries displayed per page of the results view."
"description": "Max number of query results to display per page in the results view."
},
"codeQL.queryHistory.format": {
"type": "string",
"default": "[%t] %q on %d - %s",
"description": "Default string for how to label query history items. %t is the time of the query, %q is the query name, %d is the database name, and %s is a status string."
"default": "%q on %d - %s, %r result count [%t]",
"description": "Default string for how to label query history items. %t is the time of the query, %q is the query name, %d is the database name, %r is the number of results, and %s is a status string."
},
"codeQL.runningTests.additionalTestArguments": {
"scope": "machine",
"type": "array",
"default": [],
"markdownDescription": "Additional command line arguments to pass to the CLI when [running tests](https://codeql.github.com/docs/codeql-cli/manual/test-run/). This setting should be an array of strings, each containing an argument to be passed."
},
"codeQL.runningTests.numberOfThreads": {
"scope": "window",
@@ -179,7 +207,7 @@
"type": "boolean",
"default": false,
"scope": "application",
"markdownDescription": "Specifies whether to send CodeQL usage telemetry. This setting AND the global `#telemetry.enableTelemetry#` setting must be checked for telemetry to be sent to GitHub. For more information, see [TELEMETRY.md](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/TELEMETRY.md)"
"markdownDescription": "Specifies whether to send CodeQL usage telemetry. This setting AND the global `#telemetry.enableTelemetry#` setting must be checked for telemetry to be sent to GitHub. For more information, see the [telemetry documentation](https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code)"
},
"codeQL.telemetry.logTelemetry": {
"type": "boolean",
@@ -202,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"
@@ -210,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",
@@ -342,6 +378,30 @@
"dark": "media/dark/trash.svg"
}
},
{
"command": "codeQLQueryHistory.sortByName",
"title": "Sort by Name",
"icon": {
"light": "media/light/sort-alpha.svg",
"dark": "media/dark/sort-alpha.svg"
}
},
{
"command": "codeQLQueryHistory.sortByDate",
"title": "Sort by Query Date",
"icon": {
"light": "media/light/sort-date.svg",
"dark": "media/dark/sort-date.svg"
}
},
{
"command": "codeQLQueryHistory.sortByCount",
"title": "Sort by Results Count",
"icon": {
"light": "media/light/sort-num.svg",
"dark": "media/dark/sort-num.svg"
}
},
{
"command": "codeQLQueryHistory.showQueryLog",
"title": "Show Query Log"
@@ -351,8 +411,12 @@
"title": "Show Query Text"
},
{
"command": "codeQLQueryHistory.viewSarif",
"title": "View SARIF"
"command": "codeQLQueryHistory.viewCsvResults",
"title": "View Results (CSV)"
},
{
"command": "codeQLQueryHistory.viewSarifResults",
"title": "View Results (SARIF)"
},
{
"command": "codeQLQueryHistory.viewDil",
@@ -446,6 +510,21 @@
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.sortByName",
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.sortByDate",
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLQueryHistory.sortByCount",
"when": "view == codeQLQueryHistory",
"group": "navigation"
},
{
"command": "codeQLAstViewer.clear",
"when": "view == codeQLAstViewer",
@@ -509,7 +588,12 @@
"when": "view == codeQLQueryHistory"
},
{
"command": "codeQLQueryHistory.viewSarif",
"command": "codeQLQueryHistory.viewCsvResults",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
},
{
"command": "codeQLQueryHistory.viewSarifResults",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory && viewItem == interpretedResultsItem"
},
@@ -544,6 +628,11 @@
"command": "codeQL.runQueries",
"group": "9_qlCommands",
"when": "resourceScheme != codeql-zip-archive"
},
{
"command": "codeQL.openReferencedFile",
"group": "9_qlCommands",
"when": "resourceExtname == .qlref"
}
],
"commandPalette": [
@@ -559,6 +648,10 @@
"command": "codeQL.quickEval",
"when": "editorLangId == ql"
},
{
"command": "codeQL.openReferencedFile",
"when": "resourceExtname == .qlref"
},
{
"command": "codeQL.setCurrentDatabase",
"when": "false"
@@ -636,7 +729,11 @@
"when": "false"
},
{
"command": "codeQLQueryHistory.viewSarif",
"command": "codeQLQueryHistory.viewCsvResults",
"when": "false"
},
{
"command": "codeQLQueryHistory.viewSarifResults",
"when": "false"
},
{
@@ -651,6 +748,18 @@
"command": "codeQLQueryHistory.compareWith",
"when": "false"
},
{
"command": "codeQLQueryHistory.sortByName",
"when": "false"
},
{
"command": "codeQLQueryHistory.sortByDate",
"when": "false"
},
{
"command": "codeQLQueryHistory.sortByCount",
"when": "false"
},
{
"command": "codeQLAstViewer.gotoCode",
"when": "false"
@@ -680,6 +789,10 @@
{
"command": "codeQL.quickEval",
"when": "editorLangId == ql"
},
{
"command": "codeQL.openReferencedFile",
"when": "resourceExtname == .qlref"
}
]
},
@@ -744,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",
@@ -813,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

@@ -0,0 +1,134 @@
{
"Query Metadata": {
"prefix": "querymetadata",
"body": [
"/**",
" * @name $1",
" * @description $2",
" * @kind $3",
" * @id $4",
" * @tags $5",
" */"
],
"description": "Metadata for a query"
},
"Class": {
"prefix": "class",
"body": ["class $1 extends $2 {", "\t$0", "}"],
"description": "A class"
},
"From/Where/Select": {
"prefix": "from",
"body": ["from $1", "where $2", "select $3"],
"description": "A from/where/select statement"
},
"Predicate": {
"prefix": "predicate",
"body": ["predicate $1($2) {", "\t$0", "}"],
"description": "A predicate"
},
"Dataflow Tracking Class": {
"prefix": "dataflowtracking",
"body": [
"class $1 extends DataFlow::Configuration {",
"\t$1() { this = \"$1\" }",
"\t",
"\toverride predicate isSource(DataFlow::Node node) {",
"\t\t${2:none()}",
"\t}",
"\t",
"\toverride predicate isSink(DataFlow::Node node) {",
"\t\t${3:none()}",
"\t}",
"}"
],
"description": "Boilerplate for a dataflow tracking class"
},
"Taint Tracking Class": {
"prefix": "tainttracking",
"body": [
"class $1 extends TaintTracking::Configuration {",
"\t$1() { this = \"$1\" }",
"\t",
"\toverride predicate isSource(DataFlow::Node node) {",
"\t\t${2:none()}",
"\t}",
"\t",
"\toverride predicate isSink(DataFlow::Node node) {",
"\t\t${3:none()}",
"\t}",
"}"
],
"description": "Boilerplate for a taint tracking class"
},
"Count": {
"prefix": "count",
"body": ["count($1 | $2 | $3)"],
"description": "A count aggregate"
},
"Max": {
"prefix": "max",
"body": ["max($1 | $2 | $3)"],
"description": "A max aggregate"
},
"Min": {
"prefix": "min",
"body": ["min($1 | $2 | $3)"],
"description": "A min aggregate"
},
"Average": {
"prefix": "avg",
"body": ["avg($1 | $2 | $3)"],
"description": "An average aggregate"
},
"Sum": {
"prefix": "sum",
"body": ["sum($1 | $2 | $3)"],
"description": "A sum aggregate"
},
"Concatenation": {
"prefix": "concat",
"body": ["concat($1 | $2 | $3)"],
"description": "A concatenation aggregate"
},
"Rank": {
"prefix": "rank",
"body": ["rank[$1]($2 | $3 | $4)"],
"description": "A rank aggregate"
},
"Strict Sum": {
"prefix": "strictsum",
"body": ["strictsum($1 | $2 | $3)"],
"description": "A strict sum aggregate"
},
"Strict Concatenation": {
"prefix": "strictconcat",
"body": ["strictconcat($1 | $2 | $3)"],
"description": "A strict concatenation aggregate"
},
"Strict Count": {
"prefix": "strictcount",
"body": ["strictcount($1 | $2 | $3)"],
"description": "A strict count aggregate"
},
"Unique": {
"prefix": "unique",
"body": ["unique($1 | $2 | $3)"],
"description": "A unique aggregate"
},
"Exists": {
"prefix": "exists",
"body": ["exists($1 | $2 | $3)"],
"description": "An exists quantifier"
},
"For All": {
"prefix": "forall",
"body": ["forall($1 | $2 | $3)"],
"description": "A for all quantifier"
},
"For All and Exists": {
"prefix": "forex",
"body": ["forex($1 | $2 | $3)"],
"description": "A for all and exists quantifier"
}
}

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';
@@ -25,6 +24,11 @@ import { CompilationMessage } from './pure/messages';
*/
const SARIF_FORMAT = 'sarifv2.1.0';
/**
* The string used to specify CSV format.
*/
const CSV_FORMAT = 'csv';
/**
* Flags to pass to all cli commands.
*/
@@ -68,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 {
@@ -99,6 +108,7 @@ export interface TestCompleted {
evaluationMs: number;
expected: string;
diff: string[] | undefined;
failureDescription?: string;
}
/**
@@ -121,15 +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');
/** The process for the cli server, or undefined if one doesn't exist yet */
process?: child_process.ChildProcessWithoutNullStreams;
@@ -146,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".
*/
@@ -449,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;
@@ -493,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.
@@ -503,12 +521,12 @@ export class CodeQLCliServer implements Disposable {
testPaths: string[], workspaces: string[], options: TestRunOptions
): AsyncGenerator<TestCompleted, void, unknown> {
const subcommandArgs = [
const subcommandArgs = this.cliConfig.additionalTestArguments.concat([
'--additional-packs', workspaces.join(path.delimiter),
'--threads',
this.cliConfig.numberTestThreads.toString(),
...testPaths
];
]);
for await (const event of await this.runAsyncCodeQlCliCommand<TestCompleted>(['test', 'run'],
subcommandArgs, 'Run CodeQL Tests', options.cancellationToken, options.logger)) {
@@ -537,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.
@@ -576,20 +594,19 @@ export class CodeQLCliServer implements Disposable {
return await this.runJsonCodeQlCliCommand<DecodedBqrsChunk>(['bqrs', 'decode'], subcommandArgs, 'Reading bqrs data');
}
async interpretBqrs(metadata: { kind: string; id: string; scored?: string }, resultsPath: string, interpretedResultsPath: string, sourceInfo?: SourceInfo): Promise<sarif.Log> {
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', SARIF_FORMAT,
'--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
// by primary location. We may want to revisit whether we call
// interpretation with and without this flag, or do some
// grouping client-side.
'--no-group-results',
];
if (config.isCanary() && metadata.scored !== undefined) {
args.push(`-t=scored=${metadata.scored}`);
args.push('--no-group-results');
}
if (sourceInfo !== undefined) {
args.push(
@@ -597,14 +614,28 @@ export class CodeQLCliServer implements Disposable {
'--source-location-prefix', sourceInfo.sourceLocationPrefix
);
}
args.push(
'--threads',
this.cliConfig.numberThreads.toString(),
);
args.push(resultsPath);
await this.runCodeQlCliCommand(['bqrs', 'interpret'], args, 'Interpreting query results');
}
async interpretBqrs(metadata: QueryMetadata, resultsPath: string, interpretedResultsPath: string, sourceInfo?: SourceInfo): Promise<sarif.Log> {
await this.runInterpretCommand(SARIF_FORMAT, metadata, resultsPath, interpretedResultsPath, sourceInfo);
let output: string;
try {
output = await fs.readFile(interpretedResultsPath, 'utf8');
} catch (err) {
throw new Error(`Reading output of interpretation failed: ${err.stderr || err}`);
} catch (e) {
const rawMessage = e.stderr || e.message;
const errorMessage = rawMessage.startsWith('Cannot create a string')
? `SARIF too large. ${rawMessage}`
: rawMessage;
throw new Error(`Reading output of interpretation failed: ${errorMessage}`);
}
try {
return JSON.parse(output) as sarif.Log;
@@ -613,6 +644,9 @@ export class CodeQLCliServer implements Disposable {
}
}
async generateResultsCsv(metadata: QueryMetadata, resultsPath: string, csvPath: string, sourceInfo?: SourceInfo): Promise<void> {
await this.runInterpretCommand(CSV_FORMAT, metadata, resultsPath, csvPath, sourceInfo);
}
async sortBqrs(resultsPath: string, sortedResultsPath: string, resultSet: string, sortKeys: number[], sortDirections: SortDirection[]): Promise<void> {
const sortDirectionStrings = sortDirections.map(direction => {
@@ -652,15 +686,19 @@ export class CodeQLCliServer implements Disposable {
* Gets information necessary for upgrading a database.
* @param dbScheme the path to the dbscheme of the database to be upgraded.
* @param searchPath A list of directories to search for upgrade scripts.
* @param allowDowngradesIfPossible Whether we should try and include downgrades of we can.
* @param targetDbScheme The dbscheme to try to upgrade to.
* @returns A list of database upgrade script directories
*/
resolveUpgrades(dbScheme: string, searchPath: string[], targetDbScheme?: string): Promise<UpgradesInfo> {
async resolveUpgrades(dbScheme: string, searchPath: string[], allowDowngradesIfPossible: boolean, targetDbScheme?: string): Promise<UpgradesInfo> {
const args = ['--additional-packs', searchPath.join(path.delimiter), '--dbscheme', dbScheme];
if (targetDbScheme) {
args.push('--target-dbscheme', targetDbScheme);
if (allowDowngradesIfPossible && await this.cliConstraints.supportsDowngrades()) {
args.push('--allow-downgrades');
}
}
return this.runJsonCodeQlCliCommand<UpgradesInfo>(
return await this.runJsonCodeQlCliCommand<UpgradesInfo>(
['resolve', 'upgrades'],
args,
'Resolving database upgrade scripts',
@@ -709,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(
@@ -726,14 +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;
}
private async refreshVersion() {
const distribution = await this.distributionProvider.getDistribution();
switch (distribution.kind) {
@@ -867,6 +897,20 @@ class SplitBuffer {
this.buffer += this.separators[0]; // Append a separator to the end to ensure the last line is returned.
}
/**
* 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 {
const pos = position > 0 ? position | 0 : 0;
return s.substring(pos, pos + searchString.length) === searchString;
}
/**
* Extract the next full line from the buffer, if one is available.
* @returns The text of the next available full line (without the separator), or `undefined` if no
@@ -875,7 +919,7 @@ class SplitBuffer {
public getNextLine(): string | undefined {
while (this.searchIndex <= (this.buffer.length - this.maxSeparatorLength)) {
for (const separator of this.separators) {
if (this.buffer.startsWith(separator, this.searchIndex)) {
if (SplitBuffer.startsWith(this.buffer, separator, this.searchIndex)) {
const line = this.buffer.substr(0, this.searchIndex);
this.buffer = this.buffer.substr(this.searchIndex + separator.length);
this.searchIndex = 0;
@@ -948,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);
@@ -73,24 +74,29 @@ export interface DistributionConfig {
const RUNNING_QUERIES_SETTING = new Setting('runningQueries', ROOT_SETTING);
const NUMBER_OF_THREADS_SETTING = new Setting('numberOfThreads', RUNNING_QUERIES_SETTING);
const SAVE_CACHE_SETTING = new Setting('saveCache', RUNNING_QUERIES_SETTING);
const CACHE_SIZE_SETTING = new Setting('cacheSize', RUNNING_QUERIES_SETTING);
const TIMEOUT_SETTING = new Setting('timeout', RUNNING_QUERIES_SETTING);
const MEMORY_SETTING = new Setting('memory', RUNNING_QUERIES_SETTING);
const DEBUG_SETTING = new Setting('debug', RUNNING_QUERIES_SETTING);
const RUNNING_TESTS_SETTING = new Setting('runningTests', ROOT_SETTING);
const RESULTS_DISPLAY_SETTING = new Setting('resultsDisplay', ROOT_SETTING);
export const ADDITIONAL_TEST_ARGUMENTS_SETTING = new Setting('additionalTestArguments', RUNNING_TESTS_SETTING);
export const NUMBER_OF_TEST_THREADS_SETTING = new Setting('numberOfThreads', RUNNING_TESTS_SETTING);
export const MAX_QUERIES = new Setting('maxQueries', RUNNING_QUERIES_SETTING);
export const AUTOSAVE_SETTING = new Setting('autoSave', RUNNING_QUERIES_SETTING);
export const PAGE_SIZE = new Setting('pageSize', RESULTS_DISPLAY_SETTING);
/** When these settings change, the running query server should be restarted. */
const QUERY_SERVER_RESTARTING_SETTINGS = [NUMBER_OF_THREADS_SETTING, MEMORY_SETTING, DEBUG_SETTING];
const QUERY_SERVER_RESTARTING_SETTINGS = [NUMBER_OF_THREADS_SETTING, SAVE_CACHE_SETTING, CACHE_SIZE_SETTING, MEMORY_SETTING, DEBUG_SETTING];
export interface QueryServerConfig {
codeQlPath: string;
debug: boolean;
numThreads: number;
saveCache: boolean;
cacheSize: number;
queryMemoryMb?: number;
timeoutSecs: number;
onDidChangeConfiguration?: Event<void>;
@@ -104,10 +110,12 @@ export interface QueryHistoryConfig {
onDidChangeConfiguration: Event<void>;
}
const CLI_SETTINGS = [NUMBER_OF_TEST_THREADS_SETTING];
const CLI_SETTINGS = [ADDITIONAL_TEST_ARGUMENTS_SETTING, NUMBER_OF_TEST_THREADS_SETTING, NUMBER_OF_THREADS_SETTING];
export interface CliConfig {
additionalTestArguments: string[];
numberTestThreads: number;
numberThreads: number;
onDidChangeConfiguration?: Event<void>;
}
@@ -193,6 +201,14 @@ export class QueryServerConfigListener extends ConfigListener implements QuerySe
return NUMBER_OF_THREADS_SETTING.getValue<number>();
}
public get saveCache(): boolean {
return SAVE_CACHE_SETTING.getValue<boolean>();
}
public get cacheSize(): number {
return CACHE_SIZE_SETTING.getValue<number | null>() || 0;
}
/** Gets the configured query timeout, in seconds. This looks up the setting at the time of access. */
public get timeoutSecs(): number {
return TIMEOUT_SETTING.getValue<number | null>() || 0;
@@ -230,11 +246,18 @@ export class QueryHistoryConfigListener extends ConfigListener implements QueryH
}
export class CliConfigListener extends ConfigListener implements CliConfig {
public get additionalTestArguments(): string[] {
return ADDITIONAL_TEST_ARGUMENTS_SETTING.getValue();
}
public get numberTestThreads(): number {
return NUMBER_OF_TEST_THREADS_SETTING.getValue();
}
public get numberThreads(): number {
return NUMBER_OF_THREADS_SETTING.getValue<number>();
}
protected handleDidChangeConfiguration(e: ConfigurationChangeEvent): void {
this.handleDidChangeConfigurationForRelevantSettings(CLI_SETTINGS, e);
}
@@ -257,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

@@ -74,7 +74,7 @@ export async function promptImportLgtmDatabase(
): Promise<DatabaseItem | undefined> {
const lgtmUrl = await window.showInputBox({
prompt:
'Enter the project URL on LGTM (e.g., https://lgtm.com/projects/g/github/codeql)',
'Enter the project slug or URL on LGTM (e.g., g/github/codeql or https://lgtm.com/projects/g/github/codeql)',
});
if (!lgtmUrl) {
return;
@@ -352,13 +352,14 @@ export async function findDirWithFile(
/**
* The URL pattern is https://lgtm.com/projects/{provider}/{org}/{name}/{irrelevant-subpages}.
* There are several possibilities for the provider: in addition to GitHub.com(g),
* There are several possibilities for the provider: in addition to GitHub.com (g),
* LGTM currently hosts projects from Bitbucket (b), GitLab (gl) and plain git (git).
*
* After the {provider}/{org}/{name} path components, there may be the components
* related to sub pages.
* This function accepts any url that matches the pattern above. It also accepts the
* raw project slug, e.g., `g/myorg/myproject`
*
* This function accepts any url that matches the patter above
* After the `{provider}/{org}/{name}` path components, there may be the components
* related to sub pages.
*
* @param lgtmUrl The URL to the lgtm project
*
@@ -370,6 +371,10 @@ export function looksLikeLgtmUrl(lgtmUrl: string | undefined): lgtmUrl is string
return false;
}
if (convertRawLgtmSlug(lgtmUrl)) {
return true;
}
try {
const uri = Uri.parse(lgtmUrl, true);
if (uri.scheme !== 'https') {
@@ -387,9 +392,23 @@ export function looksLikeLgtmUrl(lgtmUrl: string | undefined): lgtmUrl is string
}
}
function convertRawLgtmSlug(maybeSlug: string): string | undefined {
if (!maybeSlug) {
return;
}
const segments = maybeSlug.split('/');
const providers = ['g', 'gl', 'b', 'git'];
if (segments.length === 3 && providers.includes(segments[0])) {
return `https://lgtm.com/projects/${maybeSlug}`;
}
return;
}
// exported for testing
export async function convertToDatabaseUrl(lgtmUrl: string) {
try {
lgtmUrl = convertRawLgtmSlug(lgtmUrl) || lgtmUrl;
const uri = Uri.parse(lgtmUrl, true);
const paths = ['api', 'v1.0'].concat(
uri.path.split('/').filter((segment) => segment)

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.
*
@@ -472,12 +473,30 @@ async function activateWithInstalledDistribution(
progress,
token
);
const item = qhm.addQuery(info);
const item = qhm.buildCompletedQuery(info);
await showResultsForCompletedQuery(item, WebviewReveal.NotForced);
// The call to showResults potentially creates SARIF file;
// Update the tree item context value to allow viewing that
// SARIF file from context menu.
await qhm.refreshTreeView(item);
// Note we must update the query history view after showing results as the
// display and sorting might depend on the number of results
await qhm.addCompletedQuery(item);
}
}
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.`);
}
}
}
@@ -617,6 +636,12 @@ async function activateWithInstalledDistribution(
}
)
);
ctx.subscriptions.push(
commandRunner(
'codeQL.openReferencedFile',
openReferencedFile
)
);
ctx.subscriptions.push(
commandRunnerWithProgress('codeQL.restartQueryServer', async (
@@ -657,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(
@@ -676,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;
@@ -236,61 +243,67 @@ export class InterfaceManager extends DisposableObject {
}
private async handleMsgFromView(msg: FromResultsViewMsg): Promise<void> {
switch (msg.t) {
case 'viewSourceFile': {
await jumpToLocation(msg, this.databaseManager, this.logger);
break;
}
case 'toggleDiagnostics': {
if (msg.visible) {
const databaseItem = this.databaseManager.findDatabaseItem(
Uri.parse(msg.databaseUri)
);
if (databaseItem !== undefined) {
await this.showResultsAsDiagnostics(
msg.origResultsPaths,
msg.metadata,
databaseItem
try {
switch (msg.t) {
case 'viewSourceFile': {
await jumpToLocation(msg, this.databaseManager, this.logger);
break;
}
case 'toggleDiagnostics': {
if (msg.visible) {
const databaseItem = this.databaseManager.findDatabaseItem(
Uri.parse(msg.databaseUri)
);
if (databaseItem !== undefined) {
await this.showResultsAsDiagnostics(
msg.origResultsPaths,
msg.metadata,
databaseItem
);
}
} else {
// TODO: Only clear diagnostics on the same database.
this._diagnosticCollection.clear();
}
break;
}
case 'resultViewLoaded':
this._panelLoaded = true;
this._panelLoadedCallBacks.forEach((cb) => cb());
this._panelLoadedCallBacks = [];
break;
case 'changeSort':
await this.changeRawSortState(msg.resultSetName, msg.sortState);
break;
case 'changeInterpretedSort':
await this.changeInterpretedSortState(msg.sortState);
break;
case 'changePage':
if (msg.selectedTable === ALERTS_TABLE_NAME) {
await this.showPageOfInterpretedResults(msg.pageNumber);
}
else {
await this.showPageOfRawResults(
msg.selectedTable,
msg.pageNumber,
// When we are in an unsorted state, we guarantee that
// sortedResultsInfo doesn't have an entry for the current
// result set. Use this to determine whether or not we use
// the sorted bqrs file.
this._displayedQuery?.sortedResultsInfo.has(msg.selectedTable) || false
);
}
} else {
// TODO: Only clear diagnostics on the same database.
this._diagnosticCollection.clear();
}
break;
break;
case 'openFile':
await this.openFile(msg.filePath);
break;
default:
assertNever(msg);
}
case 'resultViewLoaded':
this._panelLoaded = true;
this._panelLoadedCallBacks.forEach((cb) => cb());
this._panelLoadedCallBacks = [];
break;
case 'changeSort':
await this.changeRawSortState(msg.resultSetName, msg.sortState);
break;
case 'changeInterpretedSort':
await this.changeInterpretedSortState(msg.sortState);
break;
case 'changePage':
if (msg.selectedTable === ALERTS_TABLE_NAME) {
await this.showPageOfInterpretedResults(msg.pageNumber);
}
else {
await this.showPageOfRawResults(
msg.selectedTable,
msg.pageNumber,
// When we are in an unsorted state, we guarantee that
// sortedResultsInfo doesn't have an entry for the current
// result set. Use this to determine whether or not we use
// the sorted bqrs file.
this._displayedQuery?.sortedResultsInfo.has(msg.selectedTable) || false
);
}
break;
case 'openFile':
await this.openFile(msg.filePath);
break;
default:
assertNever(msg);
} catch (e) {
showAndLogErrorMessage(e.message, {
fullMessage: e.stack
});
}
}
@@ -394,6 +407,7 @@ export class InterfaceManager extends DisposableObject {
}
);
const resultSet = transformBqrsResultSet(schema, chunk);
results.setResultCount(interpretationPage?.numTotalResults || resultSet.schema.rows);
const parsedResultSets: ParsedResultSets = {
pageNumber: 0,
pageSize,
@@ -540,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,
@@ -626,8 +642,8 @@ export class InterfaceManager extends DisposableObject {
} catch (e) {
// If interpretation fails, accept the error and continue
// trying to render uninterpreted results anyway.
this.logger.log(
`Exception during results interpretation: ${e.message}. Will show raw results instead.`
showAndLogErrorMessage(
`Showing raw results instead of interpreted ones due to an error. ${e.message}`
);
}
}
@@ -659,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

@@ -1,6 +1,6 @@
import * as path from 'path';
import * as vscode from 'vscode';
import { window as Window } from 'vscode';
import { window as Window, env } from 'vscode';
import { CompletedQuery } from './query-results';
import { QueryHistoryConfig } from './config';
import { QueryWithResults } from './run-queries';
@@ -15,6 +15,7 @@ import { URLSearchParams } from 'url';
import { QueryServerClient } from './queryserver-client';
import { DisposableObject } from './pure/disposable-object';
import { commandRunner } from './commandRunner';
import { assertNever } from './pure/helpers-pure';
/**
* query-history.ts
@@ -58,10 +59,21 @@ const SHOW_QUERY_TEXT_QUICK_EVAL_MSG = `\
*/
const FAILED_QUERY_HISTORY_ITEM_ICON = 'media/red-x.svg';
enum SortOrder {
NameAsc = 'NameAsc',
NameDesc = 'NameDesc',
DateAsc = 'DateAsc',
DateDesc = 'DateDesc',
CountAsc = 'CountAsc',
CountDesc = 'CountDesc',
}
/**
* Tree data provider for the query history view.
*/
export class HistoryTreeDataProvider extends DisposableObject {
private _sortOrder = SortOrder.DateAsc;
private _onDidChangeTreeData = super.push(new vscode.EventEmitter<CompletedQuery | undefined>());
readonly onDidChangeTreeData: vscode.Event<CompletedQuery | undefined> = this
@@ -111,7 +123,24 @@ export class HistoryTreeDataProvider extends DisposableObject {
getChildren(
element?: CompletedQuery
): vscode.ProviderResult<CompletedQuery[]> {
return element ? [] : this.history;
return element ? [] : this.history.sort((q1, q2) => {
switch (this.sortOrder) {
case SortOrder.NameAsc:
return q1.toString().localeCompare(q2.toString(), env.language);
case SortOrder.NameDesc:
return q2.toString().localeCompare(q1.toString(), env.language);
case SortOrder.DateAsc:
return q1.date.getTime() - q2.date.getTime();
case SortOrder.DateDesc:
return q2.date.getTime() - q1.date.getTime();
case SortOrder.CountAsc:
return q1.resultCount - q2.resultCount;
case SortOrder.CountDesc:
return q2.resultCount - q1.resultCount;
default:
assertNever(this.sortOrder);
}
});
}
getParent(_element: CompletedQuery): vscode.ProviderResult<CompletedQuery> {
@@ -157,6 +186,15 @@ export class HistoryTreeDataProvider extends DisposableObject {
find(queryId: number): CompletedQuery | undefined {
return this.allHistory.find((query) => query.query.queryID === queryId);
}
public get sortOrder() {
return this._sortOrder;
}
public set sortOrder(newSortOrder: SortOrder) {
this._sortOrder = newSortOrder;
this._onDidChangeTreeData.fire();
}
}
/**
@@ -224,6 +262,24 @@ export class QueryHistoryManager extends DisposableObject {
this.handleRemoveHistoryItem.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.sortByName',
this.handleSortByName.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.sortByDate',
this.handleSortByDate.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.sortByCount',
this.handleSortByCount.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.setLabel',
@@ -250,8 +306,14 @@ export class QueryHistoryManager extends DisposableObject {
);
this.push(
commandRunner(
'codeQLQueryHistory.viewSarif',
this.handleViewSarif.bind(this)
'codeQLQueryHistory.viewCsvResults',
this.handleViewCsvResults.bind(this)
)
);
this.push(
commandRunner(
'codeQLQueryHistory.viewSarifResults',
this.handleViewSarifResults.bind(this)
)
);
this.push(
@@ -345,6 +407,30 @@ export class QueryHistoryManager extends DisposableObject {
}
}
async handleSortByName() {
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
} else {
this.treeDataProvider.sortOrder = SortOrder.NameAsc;
}
}
async handleSortByDate() {
if (this.treeDataProvider.sortOrder === SortOrder.DateAsc) {
this.treeDataProvider.sortOrder = SortOrder.DateDesc;
} else {
this.treeDataProvider.sortOrder = SortOrder.DateAsc;
}
}
async handleSortByCount() {
if (this.treeDataProvider.sortOrder === SortOrder.CountAsc) {
this.treeDataProvider.sortOrder = SortOrder.CountDesc;
} else {
this.treeDataProvider.sortOrder = SortOrder.CountAsc;
}
}
async handleSetLabel(
singleItem: CompletedQuery,
multiSelect: CompletedQuery[]
@@ -362,7 +448,12 @@ export class QueryHistoryManager extends DisposableObject {
if (response !== undefined) {
// Interpret empty string response as 'go back to using default'
singleItem.options.label = response === '' ? undefined : response;
this.treeDataProvider.refresh(singleItem);
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc ||
this.treeDataProvider.sortOrder === SortOrder.NameDesc) {
this.treeDataProvider.refresh();
} else {
this.treeDataProvider.refresh(singleItem);
}
}
}
@@ -459,7 +550,7 @@ export class QueryHistoryManager extends DisposableObject {
await vscode.window.showTextDocument(doc, { preview: false });
}
async handleViewSarif(
async handleViewSarifResults(
singleItem: CompletedQuery,
multiSelect: CompletedQuery[]
) {
@@ -480,6 +571,19 @@ export class QueryHistoryManager extends DisposableObject {
}
}
async handleViewCsvResults(
singleItem: CompletedQuery,
multiSelect: CompletedQuery[]
) {
if (!this.assertSingleQuery(multiSelect)) {
return;
}
await this.tryOpenExternalFile(
await singleItem.query.ensureCsvProduced(this.qs)
);
}
async handleViewDil(
singleItem: CompletedQuery,
multiSelect: CompletedQuery[],
@@ -511,11 +615,14 @@ export class QueryHistoryManager extends DisposableObject {
}
}
addQuery(info: QueryWithResults): CompletedQuery {
buildCompletedQuery(info: QueryWithResults): CompletedQuery {
const item = new CompletedQuery(info, this.queryHistoryConfigListener);
return item;
}
addCompletedQuery(item: CompletedQuery) {
this.treeDataProvider.pushQuery(item);
this.updateTreeViewSelectionIfVisible();
return item;
}
find(queryId: number): CompletedQuery | undefined {
@@ -690,3 +797,4 @@ the file in the file explorer and dragging it into the workspace.`
this.treeDataProvider.refresh(completedQuery);
}
}

View File

@@ -11,12 +11,14 @@ import { QueryHistoryConfig } from './config';
import { QueryHistoryItemOptions } from './query-history';
export class CompletedQuery implements QueryWithResults {
readonly date: Date;
readonly time: string;
readonly query: QueryInfo;
readonly result: messages.EvaluationResult;
readonly database: DatabaseInfo;
readonly logFileLocation?: string;
options: QueryHistoryItemOptions;
resultCount: number;
dispose: () => void;
/**
@@ -44,8 +46,14 @@ export class CompletedQuery implements QueryWithResults {
this.options = evaluation.options;
this.dispose = evaluation.dispose;
this.time = new Date().toLocaleString(env.language);
this.date = new Date();
this.time = this.date.toLocaleString(env.language);
this.sortedResultsInfo = new Map();
this.resultCount = 0;
}
setResultCount(value: number) {
this.resultCount = value;
}
get databaseName(): string {
@@ -80,11 +88,12 @@ export class CompletedQuery implements QueryWithResults {
}
interpolate(template: string): string {
const { databaseName, queryName, time, statusString } = this;
const { databaseName, queryName, time, resultCount, statusString } = this;
const replacements: { [k: string]: string } = {
t: time,
q: queryName,
d: databaseName,
r: resultCount.toString(),
s: statusString,
'%': '%',
};
@@ -170,17 +179,20 @@ export async function interpretResults(
if (await fs.pathExists(interpretedResultsPath)) {
return JSON.parse(await fs.readFile(interpretedResultsPath, 'utf8'));
}
return await server.interpretBqrs(ensureMetadataIsComplete(metadata), resultsPath, interpretedResultsPath, sourceInfo);
}
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 await server.interpretBqrs({ kind, id, scored }, resultsPath, interpretedResultsPath, sourceInfo);
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 };
@@ -136,7 +130,16 @@ export class QueryServerClient extends DisposableObject {
const ramArgs = await this.cliServer.resolveRam(this.config.queryMemoryMb, progressReporter);
const args = ['--threads', this.config.numThreads.toString()].concat(ramArgs);
if (await this.supportsDatabaseRegistration()) {
if (this.config.saveCache) {
args.push('--save-cache');
}
if (this.config.cacheSize > 0) {
args.push('--max-disk-cache');
args.push(this.config.cacheSize.toString());
}
if (await this.cliServer.cliConstraints.supportsDatabaseRegistration()) {
args.push('--require-db-registration');
}
@@ -193,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

@@ -24,6 +24,7 @@ import { QueryHistoryItemOptions } from './query-history';
import * as qsClient from './queryserver-client';
import { isQuickQueryPath } from './quick-query';
import { compileDatabaseUpgradeSequence, hasNondestructiveUpgradeCapabilities, upgradeDatabaseExplicit } from './upgrades';
import { ensureMetadataIsComplete } from './query-results';
/**
* run-queries.ts
@@ -53,6 +54,7 @@ export class QueryInfo {
readonly compiledQueryPath: string;
readonly dilPath: string;
readonly csvPath: string;
readonly resultsPaths: ResultsPaths;
readonly dataset: Uri; // guarantee the existence of a well-defined dataset dir at this point
readonly queryID: number;
@@ -68,6 +70,7 @@ export class QueryInfo {
this.queryID = QueryInfo.nextQueryId++;
this.compiledQueryPath = path.join(tmpDir.name, `compiledQuery${this.queryID}.qlo`);
this.dilPath = path.join(tmpDir.name, `results${this.queryID}.dil`);
this.csvPath = path.join(tmpDir.name, `results${this.queryID}.csv`);
this.resultsPaths = {
resultsPath: path.join(tmpDir.name, `results${this.queryID}.bqrs`),
interpretedResultsPath: path.join(tmpDir.name, `interpretedResults${this.queryID}.sarif`)
@@ -166,7 +169,15 @@ export class QueryInfo {
if (!hasMetadataFile) {
logger.log('Cannot produce interpreted results since the database does not have a .dbinfo or codeql-database.yml file.');
}
return hasMetadataFile;
const hasKind = !!this.metadata?.kind;
if (!hasKind) {
logger.log('Cannot produce interpreted results since the query does not have @kind metadata.');
}
const isTable = hasKind && this.metadata?.kind === 'table';
return hasMetadataFile && hasKind && !isTable;
}
/**
@@ -183,6 +194,13 @@ export class QueryInfo {
return fs.pathExists(this.dilPath);
}
/**
* Holds if this query already has CSV results produced
*/
async hasCsv(): Promise<boolean> {
return fs.pathExists(this.csvPath);
}
async ensureDilPath(qs: qsClient.QueryServerClient): Promise<string> {
if (await this.hasDil()) {
return this.dilPath;
@@ -198,8 +216,27 @@ export class QueryInfo {
return this.dilPath;
}
async ensureCsvProduced(qs: qsClient.QueryServerClient): Promise<string> {
if (await this.hasCsv()) {
return this.csvPath;
}
let sourceInfo;
if (this.dbItem.sourceArchive !== undefined) {
sourceInfo = {
sourceArchive: this.dbItem.sourceArchive.fsPath,
sourceLocationPrefix: await this.dbItem.getSourceLocationPrefix(
qs.cliServer
),
};
}
await qs.cliServer.generateResultsCsv(ensureMetadataIsComplete(this.metadata), this.resultsPaths.resultsPath, this.csvPath, sourceInfo);
return this.csvPath;
}
}
export interface QueryWithResults {
readonly query: QueryInfo;
readonly result: messages.EvaluationResult;
@@ -294,7 +331,7 @@ async function checkDbschemeCompatibility(
const searchPath = getOnDiskWorkspaceFolders();
if (query.dbItem.contents !== undefined && query.dbItem.contents.dbSchemeUri !== undefined) {
const { finalDbscheme } = await cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath);
const { finalDbscheme } = await cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath, false);
const hash = async function(filename: string): Promise<string> {
return crypto.createHash('sha256').update(await fs.readFile(filename)).digest('hex');
};
@@ -348,7 +385,7 @@ async function compileNonDestructiveUpgrade(
if (!query.dbItem?.contents?.dbSchemeUri) {
throw new Error('Database is invalid, and cannot be upgraded.');
}
const { scripts, matchesTarget } = await qs.cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath, query.queryDbscheme);
const { scripts, matchesTarget } = await qs.cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath, true, query.queryDbscheme);
if (!matchesTarget) {
reportNoUpgradePath(query);
@@ -606,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

@@ -166,7 +166,7 @@ export class TelemetryListener extends ConfigListener {
// Extension won't start until this completes.
result = await showBinaryChoiceWithUrlDialog(
'Does the CodeQL Extension by GitHub have your permission to collect usage data and metrics to help us improve CodeQL for VSCode?',
'https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/TELEMETRY.md'
'https://codeql.github.com/docs/codeql-for-visual-studio-code/about-telemetry-in-codeql-for-visual-studio-code'
);
}
if (result !== undefined) {

View File

@@ -223,8 +223,8 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
? 'errored'
: 'failed';
let message: string | undefined;
if (event.diff?.length) {
message = ['', `${state}: ${event.test}`, ...event.diff, ''].join('\n');
if (event.failureDescription || event.diff?.length) {
message = ['', `${state}: ${event.test}`, event.failureDescription || event.diff?.join('\n'), ''].join('\n');
testLogger.log(message);
}
this._testStates.fire({

View File

@@ -171,7 +171,8 @@ export async function upgradeDatabaseExplicit(
}
const upgradeInfo = await qs.cliServer.resolveUpgrades(
db.contents.dbSchemeUri.fsPath,
searchPath
searchPath,
false
);
const { scripts, finalDbscheme } = upgradeInfo;

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

@@ -15,7 +15,10 @@ import {
chai.use(chaiAsPromised);
const expect = chai.expect;
describe('config listeners', () => {
describe('config listeners', function() {
// Because we are adding some extra waiting, need to bump the test timeouts.
this.timeout(5000);
let sandbox: Sinon.SinonSandbox;
beforeEach(() => {
sandbox = Sinon.createSandbox();
@@ -38,6 +41,10 @@ describe('config listeners', () => {
{
clazz: CliConfigListener,
settings: [{
name: 'codeQL.runningQueries.numberOfThreads',
property: 'numberThreads',
values: [0, 1]
}, {
name: 'codeQL.runningTests.numberOfThreads',
property: 'numberTestThreads',
values: [1, 0]
@@ -57,6 +64,14 @@ describe('config listeners', () => {
name: 'codeQL.runningQueries.numberOfThreads',
property: 'numThreads',
values: [0, 1]
}, {
name: 'codeQL.runningQueries.saveCache',
property: 'saveCache',
values: [false, true]
}, {
name: 'codeQL.runningQueries.cacheSize',
property: 'cacheSize',
values: [0, 1]
}, {
name: 'codeQL.runningQueries.memory',
property: 'queryMemoryMb',

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

@@ -55,7 +55,18 @@ describe('databaseFetcher', function() {
);
});
it('should fail on a nonexistant prohect', async () => {
it('should convert a raw slug to a database url with extra path segments', async () => {
quickPickSpy.resolves('python');
const lgtmUrl =
'g/github/codeql';
const dbUrl = await convertToDatabaseUrl(lgtmUrl);
expect(dbUrl).to.equal(
'https://lgtm.com/api/v1.0/snapshots/1506465042581/python'
);
});
it('should fail on a nonexistent project', async () => {
quickPickSpy.resolves('javascript');
const lgtmUrl = 'https://lgtm.com/projects/g/github/hucairz';
expect(convertToDatabaseUrl(lgtmUrl)).to.rejectedWith(/Invalid LGTM URL/);
@@ -71,6 +82,10 @@ describe('databaseFetcher', function() {
.to.be.false;
expect(looksLikeLgtmUrl('https://ww.lgtm.com/projects/g/github')).to.be
.false;
expect(looksLikeLgtmUrl('g/github')).to.be
.false;
expect(looksLikeLgtmUrl('ggg/github/myproj')).to.be
.false;
});
it('should handle valid urls', () => {
@@ -86,6 +101,10 @@ describe('databaseFetcher', function() {
'https://lgtm.com/projects/g/github/codeql/sub/pages?query=string'
)
).to.be.true;
expect(looksLikeLgtmUrl('g/github/myproj')).to.be
.true;
expect(looksLikeLgtmUrl('git/github/myproj')).to.be
.true;
});
});

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'
}
);
}

View File

@@ -1,5 +1,5 @@
{
"$schema": "http://json.schemastore.org/tsconfig",
"$schema": "https://json.schemastore.org/tsconfig",
"compilerOptions": {
"declaration": true,
"strict": true,