Compare commits
64 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
692e1235e8 | ||
|
|
b69bbf5c5d | ||
|
|
b64284c43e | ||
|
|
67eaaadfce | ||
|
|
a9545458b9 | ||
|
|
3e1b121471 | ||
|
|
28d7a26b5f | ||
|
|
1d49ae5b99 | ||
|
|
b00826d76a | ||
|
|
eab5865a5c | ||
|
|
0e8cd0d2b1 | ||
|
|
8281f408dc | ||
|
|
fce9bbce20 | ||
|
|
dc5efcedba | ||
|
|
f6c67bf696 | ||
|
|
3fce04a24b | ||
|
|
fba8f51d1b | ||
|
|
31ee3cb978 | ||
|
|
4d99126994 | ||
|
|
ced34ad704 | ||
|
|
f5e0011aa1 | ||
|
|
a0b759ecd8 | ||
|
|
58cf4db9ee | ||
|
|
e0c5ae815c | ||
|
|
bf5ed193be | ||
|
|
aa60fbc213 | ||
|
|
bdb2feb559 | ||
|
|
5b08fd0df1 | ||
|
|
c83dbde20f | ||
|
|
e033578cd2 | ||
|
|
c082a38b6b | ||
|
|
bdda27703a | ||
|
|
36bfb3987e | ||
|
|
6d26491243 | ||
|
|
98a2bbbb47 | ||
|
|
fb6bed6042 | ||
|
|
df0cc921fd | ||
|
|
cd7354446b | ||
|
|
d909f98fcb | ||
|
|
8c2db75886 | ||
|
|
73e560e6da | ||
|
|
ada1180468 | ||
|
|
d1e70816aa | ||
|
|
df936167d5 | ||
|
|
0327ec358c | ||
|
|
7a78fca252 | ||
|
|
10e86f1835 | ||
|
|
dbaed3acd5 | ||
|
|
6830bdd28d | ||
|
|
e316decae1 | ||
|
|
a86c1ce69b | ||
|
|
01418cba26 | ||
|
|
35d98f62e8 | ||
|
|
b30121b84c | ||
|
|
fd15217a20 | ||
|
|
1d03702334 | ||
|
|
c47029e9eb | ||
|
|
5fdfb44c2e | ||
|
|
6e40478440 | ||
|
|
9e68b4f061 | ||
|
|
0f82875b9d | ||
|
|
fd52f66f6d | ||
|
|
42cfa45d7e | ||
|
|
5023f91475 |
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
7
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -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**
|
||||
|
||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@@ -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.
|
||||
|
||||
16
.github/workflows/codeql.yml
vendored
16
.github/workflows/codeql.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -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'
|
||||
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -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": [
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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).
|
||||
|
||||
@@ -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 [Microsoft’s 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.
|
||||
@@ -13,6 +13,7 @@ const packageFiles = [
|
||||
'CHANGELOG.md',
|
||||
'README.md',
|
||||
'language-configuration.json',
|
||||
'snippets.json',
|
||||
'media',
|
||||
'node_modules',
|
||||
'out'
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/tsconfig",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
|
||||
@@ -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: [
|
||||
|
||||
@@ -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();
|
||||
|
||||
15
extensions/ql-vscode/media/dark/sort-num.svg
Normal file
15
extensions/ql-vscode/media/dark/sort-num.svg
Normal 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 |
15
extensions/ql-vscode/media/light/sort-num.svg
Normal file
15
extensions/ql-vscode/media/light/sort-num.svg
Normal 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 |
4787
extensions/ql-vscode/package-lock.json
generated
4787
extensions/ql-vscode/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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": {
|
||||
|
||||
134
extensions/ql-vscode/snippets.json
Normal file
134
extensions/ql-vscode/snippets.json
Normal 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"
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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}`;
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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'
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/tsconfig",
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"compilerOptions": {
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
|
||||
Reference in New Issue
Block a user