Compare commits

...

71 Commits

Author SHA1 Message Date
jcreedcmu
e0383b3f9a Merge pull request #355 from jcreedcmu/jcreed/1.1.3
Some checks failed
Build Extension / Build (ubuntu-latest) (push) Has been cancelled
Build Extension / Build (windows-latest) (push) Has been cancelled
Build Extension / Test (ubuntu-latest) (push) Has been cancelled
Build Extension / Test (windows-latest) (push) Has been cancelled
Release / Release (push) Has been cancelled
Update CHANGELOG for release.
2020-05-08 12:41:15 -04:00
Jason Reed
0d972d7916 Update CHANGELOG for release. 2020-05-08 12:34:37 -04:00
jcreedcmu
ab020f24ae Merge pull request #354 from aeisenberg/aesienberg/database-commands
Rename database and open database directory
2020-05-08 12:30:57 -04:00
jcreedcmu
81cbf26910 Merge branch 'master' into aesienberg/database-commands 2020-05-08 12:08:59 -04:00
Andrew Eisenberg
2e2f101131 Update changelog 2020-05-07 21:58:16 -07:00
Andrew Eisenberg
610d40c99c Add a command to open a database directory externally 2020-05-07 15:51:00 -07:00
Andrew Eisenberg
adf6f66517 Add ability to rename database in database tree 2020-05-07 15:50:59 -07:00
Dave Bartolomeo
8f84989d98 Merge pull request #352 from jcreedcmu/jcreed/update-lsp
Update versions of json-rpc dependencies.
2020-05-07 12:02:30 -04:00
Jason Reed
22c9386123 Use ^versions not ~versions. 2020-05-07 11:32:17 -04:00
jcreedcmu
53e1794b50 Merge pull request #351 from jcreedcmu/jcreed/no-paginate
Don't paginate at all in experimental bqrs parsing codepath
2020-05-07 09:46:31 -04:00
Jason Reed
307d6d7c7f Update versions of json-rpc dependencies. 2020-05-07 09:45:23 -04:00
Jason Reed
a0e60fb154 Don't paginate at all in experimental bqrs parsing codepath 2020-05-06 12:07:47 -04:00
jcreedcmu
8b5bdbb6ef Merge pull request #350 from jcreedcmu/jcreed/cli-bqrs-parsing
Experimental: Enable parsing bqrs with the cli instead of in the webview
2020-05-06 11:34:35 -04:00
Jason Reed
0ad9cdd5ac Address review comments and fix formatting. 2020-05-06 10:39:27 -04:00
Jason Reed
c3b2e9d478 Add experimental use of cli bqrs parsing.
When `codeQL.experimentalBqrsParsing` is set to true, parse raw
results from the bqrs file using the cli, rather than doing it in the
webview.
2020-05-05 17:00:20 -04:00
Jason Reed
c20bbd9606 Fix formatting.
This is simultaneously compatible with eslint and tsfmt.
2020-05-05 16:21:58 -04:00
jcreedcmu
6080a0d585 Merge pull request #347 from jcreedcmu/jcreed/launch-config
internal: Revert specifying workspace in launch config
2020-05-05 13:08:28 -04:00
jcreedcmu
9fda320589 Merge pull request #340 from jcreedcmu/jcreed/no-qhelp-alias
Remove 'qhelp' as global alias for 'xml' filetype
2020-05-05 11:01:12 -04:00
Jason Reed
143b51ef82 Revert specifying workspace in launch config
The behavior without this line is to use whichever workspace was
opened last when testing. I find this more convenient, since I have
several (non-vscode-codeql-starter-workspace) local workspaces I use
for manual testing, and it's nice to have them persist from one run to
the next.
2020-05-05 10:58:54 -04:00
Alexander Eyers-Taylor
51d4c87af4 Merge pull request #346 from jcreedcmu/jcreed/fix-jump-to-def-bug
Jump-to-definition: Fix mistakenly always using the references query
2020-05-01 18:41:02 +01:00
Jason Reed
be5efc01ee Jump-to-definition: Fix mistakenly always using the references query 2020-05-01 13:17:17 -04:00
jcreedcmu
08a30c454a Merge pull request #345 from jcreedcmu/jcreed/better-empty-message
Add suggestive message to alerts view when 0 alerts, >0 raw results.
2020-05-01 09:01:25 -04:00
Jason Reed
1377969213 Add suggestive message to alerts view when 0 alerts, >0 raw results.
Fixes https://github.com/github/codeql-coreql-team/issues/383.
2020-05-01 08:17:51 -04:00
jcreedcmu
41f1aae71d Merge pull request #344 from github/shati/changelog-date
Changelog: Add release date
2020-04-28 13:49:41 -04:00
Shati Patel
62cae6ead1 Changelog: Add release date 2020-04-28 18:30:45 +01:00
jcreedcmu
39e3627e06 Merge pull request #343 from github/version/bump-to-v1.1.3
Bump version to v1.1.3
2020-04-28 11:40:48 -04:00
github-actions[bot]
43586c91d9 Bump version to v1.1.3 2020-04-28 15:34:08 +00:00
jcreedcmu
8efb060031 Merge pull request #337 from jcreedcmu/jcreed/jump-to-def
Some checks failed
Build Extension / Build (ubuntu-latest) (push) Has been cancelled
Build Extension / Build (windows-latest) (push) Has been cancelled
Build Extension / Test (ubuntu-latest) (push) Has been cancelled
Build Extension / Test (windows-latest) (push) Has been cancelled
Release / Release (push) Has been cancelled
Add experimental support for Jump-to-def and Find-references
2020-04-28 11:24:55 -04:00
Jason Reed
31414b7506 Remove 'qhelp' as global alias for 'xml' filetype 2020-04-23 10:06:27 -04:00
Jason Reed
e242a8fbeb Review comments 2020-04-22 14:09:20 -04:00
Jason Reed
ee591e802f Fix lint violations 2020-04-21 13:19:07 -04:00
Jason Reed
7df8905aa0 Scope tags for ide queries 2020-04-21 13:14:19 -04:00
Shati Patel
23b1c00179 Update changelog 2020-04-21 08:27:29 -07:00
Jason Reed
701804b6a4 Guard find-references with experimental setting 2020-04-21 11:15:12 -04:00
Jason Reed
66665bf25e Refactor definitions/references finding 2020-04-21 11:15:12 -04:00
Jason Reed
1c6b4a6d1e Put CachedOperation in helpers. 2020-04-21 11:15:12 -04:00
Jason Reed
28be98411d Add constants for bqrs column kinds 2020-04-21 11:15:12 -04:00
Jason Reed
5592a77963 Can autodetect language 2020-04-21 11:15:12 -04:00
Jason Reed
a6cd08fb0b Add wrapper for 'resolve queries' cli command 2020-04-21 11:15:12 -04:00
Jason Reed
881c909540 Make rudimentary jump-to-definition work 2020-04-21 11:15:12 -04:00
Henry Mercer
f5e3af02e4 Merge pull request #336 from github/pr-template
Update link in PR template
2020-04-21 12:11:30 +01:00
Shati Patel
3eca4f6734 Update link in PR template 2020-04-20 23:22:25 +01:00
Andrew Eisenberg
596ccdb722 Update change log 2020-04-20 08:50:54 -07:00
Andrew Eisenberg
2aeda002fa Add sort by date added for databases
This uses the dateAdded field on databases. It will only work for 
databases added after that field was added. Otherwise, the dateAdded
property will be undefined.
2020-04-20 08:50:54 -07:00
Andrew Eisenberg
27623f3325 Add dateAdded field for databases
Existing databases will have this value as `undefined`. Newly added
databases will have this value as the Unix timestamp when added.

Stuffing this value into the `options` field of the database. The
name is not quite right, but it seems like this is the right place.
Should I rename?
2020-04-20 08:50:54 -07:00
Andrew Eisenberg
f3df3b9f3e Rename svg for sorting dates add svg for sorting names 2020-04-20 08:50:54 -07:00
Andrew Eisenberg
5850ed3288 refactor: Move icons into light/dark folders
This follows the vscode styles where all icons for dark mode are in
the `dark` folder and all for light mode in the `light` folder. They
always have the same name.
2020-04-20 08:50:54 -07:00
Andrew Eisenberg
a2f8c85359 Add command to sort databases by name
This assumes that we will be adding other sorting mechanisms in the
future, like sort by data added.

Not happy with the icon I chose, but I didn't want to spend too much
time creating a new one.
2020-04-20 08:50:54 -07:00
Andrew Eisenberg
62d9efc4ee Update changelog 2020-04-20 07:55:59 -07:00
Andrew Eisenberg
00026a7727 feat: User can see query text at time of execution
Add new command to view the query text in a synthetic, read-only
document.

Quick eval queries will show the text selected when initially running
the query. Quick eval queries where the user has a single caret
selection will show the entire line of text.
2020-04-20 07:55:59 -07:00
Jason Reed
c292f58e20 Factor out database qlpack inference. 2020-04-20 09:59:41 -04:00
jcreedcmu
6f935ae6e4 Merge pull request #331 from aeisenberg/aeisenberg/changelog
Update changelog
2020-04-16 12:58:57 -04:00
Andrew Eisenberg
1fb65cd7e9 Update changelog 2020-04-16 09:51:01 -07:00
jcreedcmu
21500f0a5b Merge pull request #330 from dbartol/dbartol/unique-highlighting
Highlight `unique` as a keyword
2020-04-15 09:49:24 -04:00
Dave Bartolomeo
efcf9815f0 Update CHANGELOG.md for unique highlighting 2020-04-14 12:53:33 -04:00
Dave Bartolomeo
f8635f41a5 Highlight unique as a keyword
`unique` is really a context-sensitive keyword, but that's even more of a hassle in a TextMate grammars than it is in the compiler itself. We'll just highlight it as a real keyword. The worst that will happen is that existing variables and predicates named "unique" will be highlighted like keywords, which will hopefully just encourage QL developers to rename those anyway.
2020-04-14 12:48:18 -04:00
jcreedcmu
e4df717d2b Merge pull request #324 from jcreedcmu/jcreed/always-save
Add 'Always Save' to running unsaved query dialog
2020-04-07 12:49:07 -04:00
jcreedcmu
9ea4b3936a Merge pull request #326 from p-/qhelp-syntax-highlighting
feat: XML syntax highlighting for qhelp files
2020-04-07 12:10:09 -04:00
Jason Reed
e5305ab4b5 Fix lint compatibly with tsfmt 2020-04-07 12:02:50 -04:00
Jason Reed
c2c86aed0a Add 'Always Save' to running unsaved query dialog 2020-04-07 12:02:50 -04:00
Dave Bartolomeo
2df512f018 Merge pull request #327 from jcreedcmu/jcreed/fix-windows-ci
chore: Fix codeql.cmd overwriting codeql.exe in CI
2020-04-07 11:48:04 -04:00
Jason Reed
ba3381fbf9 Fix codeql.cmd overwriting codeql.exe in CI 2020-04-07 10:06:29 -04:00
Peter Stöckli
869029b856 feat: XML syntax highlighting for .qhelp files 2020-04-04 16:59:02 +02:00
Andrew Eisenberg
b3ad1d6814 fix: Avoid warning user if no new launcher exists
Allow extension to remain on the deprecated launcher.
2020-03-27 08:46:45 -07:00
Andrew Eisenberg
130d3c09e3 build: Enable linting in the build 2020-03-25 12:02:12 -07:00
Andrew Eisenberg
bb28dafc43 lint: Add proper linting for react 2020-03-25 12:02:12 -07:00
Andrew Eisenberg
db6aadbf93 fix: Use a proper 'file' schema for uris 2020-03-25 11:16:32 -07:00
Henry Mercer
d97c8e864d Merge pull request #313 from shati-patel/error-typo
Fix typo in error message
2020-03-25 10:25:54 +00:00
Shati Patel
d8a6368e60 Fix typo in error message
Spotted a small typo ("exterally") and updated the error message.
2020-03-25 08:59:04 +00:00
jcreedcmu
76d6ab4e81 Merge pull request #305 from github/version/bump-to-v1.1.2
Bump version to v1.1.2
2020-03-23 13:01:36 -04:00
github-actions[bot]
bdcabae60e Bump version to v1.1.2 2020-03-23 16:56:34 +00:00
47 changed files with 1290 additions and 228 deletions

View File

@@ -7,6 +7,6 @@ Replace this with a description of the changes your pull request makes.
## Checklist
- [ ] [CHANGELOG.md](../extensions/ql-vscode/CHANGELOG.md) has been updated to incorporate all user visible changes made by this pull request.
- [ ] [CHANGELOG.md](https://github.com/github/vscode-codeql/blob/master/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/product-docs-dsp` has been cc'd in all issues for UI or other user-facing changes made by this pull request.

View File

@@ -62,11 +62,17 @@ jobs:
npm run build-ci
shell: bash
- name: Lint
run: |
cd extensions/ql-vscode
npm run lint
- name: Install CodeQL
run: |
mkdir codeql-home
curl -L --silent https://github.com/github/codeql-cli-binaries/releases/latest/download/codeql.zip -o codeql-home/codeql.zip
unzip -q -o codeql-home/codeql.zip -d codeql-home
unzip -q -o codeql-home/codeql.zip codeql/codeql.exe -d codeql-home
rm codeql-home/codeql.zip
shell: bash

3
.vscode/launch.json vendored
View File

@@ -8,8 +8,7 @@
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceRoot}/dist/vscode-codeql",
"${workspaceRoot}/../vscode-codeql-starter/vscode-codeql-starter.code-workspace"
"--extensionDevelopmentPath=${workspaceRoot}/dist/vscode-codeql"
],
"stopOnEntry": false,
"sourceMaps": true,

View File

@@ -44,6 +44,7 @@ dependencies:
classnames: 2.2.6
css-loader: 3.1.0_webpack@4.42.0
eslint: 6.8.0
eslint-plugin-react: 7.19.0_eslint@6.8.0
fs-extra: 8.1.0
glob: 7.1.6
glob-promise: 3.4.0_glob@7.1.6
@@ -78,8 +79,8 @@ dependencies:
unzipper: 0.10.10
vinyl: 2.2.0
vsce: 1.74.0
vscode-jsonrpc: 4.0.0
vscode-languageclient: 5.2.1
vscode-jsonrpc: 5.0.1
vscode-languageclient: 6.1.3
vscode-test: 1.3.0
vscode-test-adapter-api: 1.7.0
vscode-test-adapter-util: 0.7.0
@@ -101,6 +102,13 @@ packages:
dev: false
resolution:
integrity: sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==
/@babel/runtime-corejs3/7.9.2:
dependencies:
core-js-pure: 3.6.4
regenerator-runtime: 0.13.5
dev: false
resolution:
integrity: sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA==
/@gulp-sourcemaps/identity-map/1.0.2:
dependencies:
acorn: 5.7.4
@@ -1036,6 +1044,16 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-p5SvDAWrF1KEbudTofIRoFugxE8=
/array-includes/3.1.1:
dependencies:
define-properties: 1.1.3
es-abstract: 1.17.4
is-string: 1.0.5
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==
/array-initial/1.1.0:
dependencies:
array-slice: 1.1.0
@@ -1773,6 +1791,11 @@ packages:
dev: false
resolution:
integrity: sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A==
/core-js-pure/3.6.4:
dev: false
requiresBuild: true
resolution:
integrity: sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw==
/core-util-is/1.0.2:
dev: false
resolution:
@@ -2079,6 +2102,14 @@ packages:
dev: false
resolution:
integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
/doctrine/2.1.0:
dependencies:
esutils: 2.0.3
dev: false
engines:
node: '>=0.10.0'
resolution:
integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
/doctrine/3.0.0:
dependencies:
esutils: 2.0.3
@@ -2323,6 +2354,28 @@ packages:
node: '>=0.8.0'
resolution:
integrity: sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
/eslint-plugin-react/7.19.0_eslint@6.8.0:
dependencies:
array-includes: 3.1.1
doctrine: 2.1.0
eslint: 6.8.0
has: 1.0.3
jsx-ast-utils: 2.2.3
object.entries: 1.1.1
object.fromentries: 2.0.2
object.values: 1.1.1
prop-types: 15.7.2
resolve: 1.15.1
semver: 6.3.0
string.prototype.matchall: 4.0.2
xregexp: 4.3.0
dev: false
engines:
node: '>=4'
peerDependencies:
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0
resolution:
integrity: sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==
/eslint-scope/4.0.3:
dependencies:
esrecurse: 4.2.1
@@ -3397,6 +3450,16 @@ packages:
node: '>=6.0.0'
resolution:
integrity: sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==
/internal-slot/1.0.2:
dependencies:
es-abstract: 1.17.4
has: 1.0.3
side-channel: 1.0.2
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-2cQNfwhAfJIkU4KZPkDI+Gj5yNNnbqi40W9Gge6dfnk4TocEVm00B3bdiL+JINrbGJil2TeHvM4rETGzk/f/0g==
/interpret/1.2.0:
dev: false
engines:
@@ -3630,6 +3693,12 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
/is-string/1.0.5:
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
/is-subdir/1.1.1:
dependencies:
better-path-resolve: 1.0.0
@@ -3765,6 +3834,15 @@ packages:
graceful-fs: 4.2.3
resolution:
integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=
/jsx-ast-utils/2.2.3:
dependencies:
array-includes: 3.1.1
object.assign: 4.1.0
dev: false
engines:
node: '>=4.0'
resolution:
integrity: sha512-EdIHFMm+1BPynpKOpdPqiOsvnIrInRGJD7bzPZdPkjitQEqpdpUuFpq4T0npZFKTiB3RhWFdGN+oqOJIdhDhQA==
/just-debounce/1.0.0:
dev: false
resolution:
@@ -4607,6 +4685,28 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8=
/object.entries/1.1.1:
dependencies:
define-properties: 1.1.3
es-abstract: 1.17.4
function-bind: 1.1.1
has: 1.0.3
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==
/object.fromentries/2.0.2:
dependencies:
define-properties: 1.1.3
es-abstract: 1.17.4
function-bind: 1.1.1
has: 1.0.3
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==
/object.getownpropertydescriptors/2.1.0:
dependencies:
define-properties: 1.1.3
@@ -4642,6 +4742,17 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60=
/object.values/1.1.1:
dependencies:
define-properties: 1.1.3
es-abstract: 1.17.4
function-bind: 1.1.1
has: 1.0.3
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==
/once/1.4.0:
dependencies:
wrappy: 1.0.2
@@ -5399,6 +5510,10 @@ packages:
dev: false
resolution:
integrity: sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==
/regenerator-runtime/0.13.5:
dev: false
resolution:
integrity: sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
/regex-not/1.0.2:
dependencies:
extend-shallow: 3.0.2
@@ -5408,6 +5523,15 @@ packages:
node: '>=0.10.0'
resolution:
integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==
/regexp.prototype.flags/1.3.0:
dependencies:
define-properties: 1.1.3
es-abstract: 1.17.4
dev: false
engines:
node: '>= 0.4'
resolution:
integrity: sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
/regexpp/2.0.1:
dev: false
engines:
@@ -5715,6 +5839,13 @@ packages:
dev: false
resolution:
integrity: sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==
/side-channel/1.0.2:
dependencies:
es-abstract: 1.17.4
object-inspect: 1.7.0
dev: false
resolution:
integrity: sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA==
/sigmund/1.0.1:
dev: false
resolution:
@@ -5983,6 +6114,17 @@ packages:
node: '>=8'
resolution:
integrity: sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
/string.prototype.matchall/4.0.2:
dependencies:
define-properties: 1.1.3
es-abstract: 1.17.4
has-symbols: 1.0.1
internal-slot: 1.0.2
regexp.prototype.flags: 1.3.0
side-channel: 1.0.2
dev: false
resolution:
integrity: sha512-N/jp6O5fMf9os0JU3E72Qhf590RSRZU/ungsL/qJUYVTNv7hTG0P/dbPjxINVN9jpscu3nzYwKESU3P3RY5tOg==
/string.prototype.padend/3.1.0:
dependencies:
define-properties: 1.1.3
@@ -6759,32 +6901,32 @@ packages:
hasBin: true
resolution:
integrity: sha512-8zWM9bZBNn9my40kkxAxdY4nhb9ADfazXsyDgx1thbRaLPbmPTlmqQ55vCAyWYFEi6XbJv8w599vzVUqsU1gHg==
/vscode-jsonrpc/4.0.0:
/vscode-jsonrpc/5.0.1:
dev: false
engines:
node: '>=8.0.0 || >=10.0.0'
resolution:
integrity: sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==
/vscode-languageclient/5.2.1:
integrity: sha512-JvONPptw3GAQGXlVV2utDcHx0BiY34FupW/kI6mZ5x06ER5DdPG/tXWMVHjTNULF5uKPOUUD0SaXg5QaubJL0A==
/vscode-languageclient/6.1.3:
dependencies:
semver: 5.7.1
vscode-languageserver-protocol: 3.14.1
semver: 6.3.0
vscode-languageserver-protocol: 3.15.3
dev: false
engines:
vscode: ^1.30
vscode: ^1.41.0
resolution:
integrity: sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q==
/vscode-languageserver-protocol/3.14.1:
integrity: sha512-YciJxk08iU5LmWu7j5dUt9/1OLjokKET6rME3cI4BRpiF6HZlusm2ZwPt0MYJ0lV5y43sZsQHhyon2xBg4ZJVA==
/vscode-languageserver-protocol/3.15.3:
dependencies:
vscode-jsonrpc: 4.0.0
vscode-languageserver-types: 3.14.0
vscode-jsonrpc: 5.0.1
vscode-languageserver-types: 3.15.1
dev: false
resolution:
integrity: sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==
/vscode-languageserver-types/3.14.0:
integrity: sha512-zrMuwHOAQRhjDSnflWdJG+O2ztMWss8GqUUB8dXLR/FPenwkiBNkMIJJYfSN6sgskvsF0rHAoBowNQfbyZnnvw==
/vscode-languageserver-types/3.15.1:
dev: false
resolution:
integrity: sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==
integrity: sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==
/vscode-test-adapter-api/1.7.0:
dev: false
engines:
@@ -6992,6 +7134,12 @@ packages:
node: '>=4'
resolution:
integrity: sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==
/xregexp/4.3.0:
dependencies:
'@babel/runtime-corejs3': 7.9.2
dev: false
resolution:
integrity: sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==
/xtend/4.0.2:
dev: false
engines:
@@ -7156,7 +7304,7 @@ packages:
peerDependencies:
glob: '*'
resolution:
integrity: sha512-14DvfY6Fj3HXp2/CNJ2zNh9MA8zPw9mUcr8WqkSsYvJow7JMcIlJ//OOONwpoSWtfrk1bk6Cin7jj9H79ItHQQ==
integrity: sha512-NkoIMaJdASYX4NjcB+nsEk/8Ff/2RLvHwL0efNOny3no6aNuJ3EkpNK0ZdX7HQdmTdY3IJPmjoJ3Rn4pkbxgdA==
tarball: 'file:projects/build-tasks.tgz'
version: 0.0.0
'file:projects/semmle-bqrs.tgz_typescript@3.8.3':
@@ -7171,7 +7319,7 @@ packages:
peerDependencies:
typescript: '*'
resolution:
integrity: sha512-24GdnvMbGfQIWMfgDhift+kYJDnG7dX03NrpX4ajZ2rckteysvq2/K7XI1OXGvUuqrt3m0/+GRDHpSI9XKDJJA==
integrity: sha512-lE3FBYrOVF1JH0ZqvF4YA+bed3JPWYucsnFe+XL140a/YR19XD+TTHIfov7VpR9qdyWfARgvmR+gf2qsguXTKQ==
tarball: 'file:projects/semmle-bqrs.tgz'
version: 0.0.0
'file:projects/semmle-io-node.tgz_typescript@3.8.3':
@@ -7186,7 +7334,7 @@ packages:
peerDependencies:
typescript: '*'
resolution:
integrity: sha512-Bj0ax/bASrHV7tamOuXZZdd3UOB4NBKdjdszIRaDvDRTu8RlEst+TVoUhkfy30qb2/6ePp3/juOJyyiBJN7u8Q==
integrity: sha512-MD9edC5HjrCfPmhktw6XmWotUmperj27/hDZiuMbuSlJ4jRKyiBtJ8Vk2Y4U41TrzsBlJfAwZW8tetPw5ujiLg==
tarball: 'file:projects/semmle-io-node.tgz'
version: 0.0.0
'file:projects/semmle-io.tgz_typescript@3.8.3':
@@ -7200,7 +7348,7 @@ packages:
peerDependencies:
typescript: '*'
resolution:
integrity: sha512-NtyviDSevxbd+hj4J66LucOzo8LU2hJ1Jh0eHw0Qu3tRZPUT8HcQlseyy29AvZR8n8eppfEZiAm/JdiHfmRPMA==
integrity: sha512-ta1lLi1COIeFwpwH523cWheWx6OE8GTqguQmOA7G6CwRF41RYbbREf/4KlOLKO/uG2akhhl+3gcWY2c5/VDC/A==
tarball: 'file:projects/semmle-io.tgz'
version: 0.0.0
'file:projects/semmle-vscode-utils.tgz':
@@ -7212,14 +7360,14 @@ packages:
dev: false
name: '@rush-temp/semmle-vscode-utils'
resolution:
integrity: sha512-5y5r8SDoN9Fp44naC9gUe8rOexeckXg2T0h9QCJAIcEgnFqOxzRc6Rv9gbMUStFKNh+rFlvmYmgPAdg5QkfgUg==
integrity: sha512-Dbwt0/Wd0VNKkRZRjFQv3hmGy/UDt36HDtEDsNgZIcQACoY1j2+mJavpQ+ZzCg4Ftj06eHDVk+ptzUEd+8Ybzw==
tarball: 'file:projects/semmle-vscode-utils.tgz'
version: 0.0.0
'file:projects/typescript-config.tgz':
dev: false
name: '@rush-temp/typescript-config'
resolution:
integrity: sha512-XuUIySaNoooIduvehnlKYaHqZJmmQoCqB1RtKhNszjCYZaSSJAnKVucViWBf5oNLKSNP7NchrD7gcoBlQ3xYvw==
integrity: sha512-qJbtY2jvt6LKkmUt/seiYyXSEB6Oip/rW+SxofQEnpyplgIQv7whTZb6g5pwlSLGl8goTaQFm4NfazKhFmxXvQ==
tarball: 'file:projects/typescript-config.tgz'
version: 0.0.0
'file:projects/vscode-codeql.tgz':
@@ -7256,6 +7404,7 @@ packages:
classnames: 2.2.6
css-loader: 3.1.0_webpack@4.42.0
eslint: 6.8.0
eslint-plugin-react: 7.19.0_eslint@6.8.0
fs-extra: 8.1.0
glob: 7.1.6
glob-promise: 3.4.0_glob@7.1.6
@@ -7284,8 +7433,8 @@ packages:
typescript-formatter: 7.2.2_typescript@3.8.3
unzipper: 0.10.10
vsce: 1.74.0
vscode-jsonrpc: 4.0.0
vscode-languageclient: 5.2.1
vscode-jsonrpc: 5.0.1
vscode-languageclient: 6.1.3
vscode-test: 1.3.0
vscode-test-adapter-api: 1.7.0
vscode-test-adapter-util: 0.7.0
@@ -7294,7 +7443,7 @@ packages:
dev: false
name: '@rush-temp/vscode-codeql'
resolution:
integrity: sha512-PvC3L2Tp+VYm+hMzTgXfdBJPLJopSQpVsT8Ym7kdIxZj/cyTWzO3A+n7HnrH5q/B4DJSRfiDwjp73GwjGhbteQ==
integrity: sha512-NvThExJ3C239HdTehHzyqzEK+QWsk5QSq//jQ6/UPcxLZuH39nxQAbrSAozv2f19URIZCOIomf8T9vOi5mfY0w==
tarball: 'file:projects/vscode-codeql.tgz'
version: 0.0.0
registry: ''
@@ -7344,6 +7493,7 @@ specifiers:
classnames: ~2.2.6
css-loader: ~3.1.0
eslint: ~6.8.0
eslint-plugin-react: ~7.19.0
fs-extra: ^8.1.0
glob: ^7.1.4
glob-promise: ^3.4.0
@@ -7378,8 +7528,8 @@ specifiers:
unzipper: ~0.10.5
vinyl: ^2.2.0
vsce: ^1.65.0
vscode-jsonrpc: ^4.0.0
vscode-languageclient: ^5.2.1
vscode-jsonrpc: 5.0.1
vscode-languageclient: 6.1.3
vscode-test: ^1.0.0
vscode-test-adapter-api: ~1.7.0
vscode-test-adapter-util: ~0.7.0

View File

@@ -27,4 +27,4 @@
"../../test",
"../../**/view"
]
}
}

View File

@@ -3,9 +3,6 @@ module.exports = {
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
modules: true,
},
project: ['tsconfig.json', './src/**/tsconfig.json'],
},
plugins: ['@typescript-eslint'],
@@ -33,6 +30,7 @@ module.exports = {
"SwitchCase": 1,
"FunctionDeclaration": { "body": 1, "parameters": 1 }
}],
"@typescript-eslint/no-throw-literal": "error"
"@typescript-eslint/no-throw-literal": "error",
"no-useless-escape": 0
},
};

View File

@@ -1,5 +1,23 @@
# CodeQL for Visual Studio Code: Changelog
## 1.1.3 - 8 May 2020
- Add a suggestion in alerts view to view raw results, when there are
raw results but no alerts.
- Add the ability to rename databases in the database view.
- Add the ability to open the directory in the filesystem
of a database.
## 1.1.2 - 28 April 2020
- Implement syntax highlighting for the new `unique` aggregate.
- Implement XML syntax highlighting for `.qhelp` files.
- Add option to auto save queries before running them.
- Add new command in query history to view the query text of the
selected query (note that this may be different from the current
contents of the query file if the file has been edited).
- Add ability to sort CodeQL databases by name or by date added.
## 1.1.1 - 23 March 2020
- Fix quick evaluation in `.qll` files.
@@ -39,7 +57,7 @@
## 1.0.3 - 13 January 2020
- Reduce the frequency of CodeQL CLI update checks to help avoid hitting GitHub API limits of 60 requests per
hour for unauthenticated IPs.
hour for unauthenticated IPs.
- Fix sorting of result sets with names containing special characters.
## 1.0.2 - 13 December 2019
@@ -48,8 +66,7 @@ hour for unauthenticated IPs.
- Allow customization of query history labels from settings and from
query history view context menu.
- Show number of results in results view.
- Add commands `CodeQL: Show Next Step on Path` and `CodeQL: Show
Previous Step on Path` for navigating the steps on the currently
- Add commands `CodeQL: Show Next Step on Path` and `CodeQL: Show Previous Step on Path` for navigating the steps on the currently
shown path result.
## 1.0.1 - 21 November 2019

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,17 @@
<?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"/>
<polygon points="301.44,304.32 427.947,120.853 427.947,93.973 250.88,93.973 250.88,128.107 376.32,128.107 250.027,310.72
250.027,338.24 432,338.24 432,304.32" fill="#C5C5C5"/>
<polygon points="234.24,422.933 283.947,373.227 184.533,373.227" fill="#C5C5C5"/>
<path d="M226.773,338.24L130.987,93.76H96L0,338.24h39.253l19.627-52.267h109.013l19.627,52.267H226.773z M71.893,250.987
L113.28,140.48l41.387,110.507H71.893z" fill="#C5C5C5"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 953 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2L6 3V6H7V3H14V5.45306L14.2071 5.29286L15 6.08576V3L14 2H7ZM8 4H10V6H8V4ZM5 9H3V11H5V9ZM2 7L1 8V13L2 14H9L10 13V8L9 7H2ZM2 13V8H9V13H2ZM8 10H6V12H8V10ZM13 4H12V7.86388L10.818 6.68192L10.1109 7.38903L12.1465 9.42454L12.8536 9.42454L14.889 7.38908L14.1819 6.68197L13 7.86388V4Z" fill="#C5C5C5"/>
</svg>

After

Width:  |  Height:  |  Size: 449 B

View File

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@@ -0,0 +1,17 @@
<?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 "/>
<polygon points="301.44,304.32 427.947,120.853 427.947,93.973 250.88,93.973 250.88,128.107 376.32,128.107 250.027,310.72
250.027,338.24 432,338.24 432,304.32 "/>
<polygon points="234.24,422.933 283.947,373.227 184.533,373.227 "/>
<path d="M226.773,338.24L130.987,93.76H96L0,338.24h39.253l19.627-52.267h109.013l19.627,52.267H226.773z M71.893,250.987
L113.28,140.48l41.387,110.507H71.893z"/>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 894 B

View File

@@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 2L6 3V6H7V3H14V5.45306L14.2071 5.29286L15 6.08576V3L14 2H7ZM8 4H10V6H8V4ZM5 9H3V11H5V9ZM2 7L1 8V13L2 14H9L10 13V8L9 7H2ZM2 13V8H9V13H2ZM8 10H6V12H8V10ZM13 4H12V7.86388L10.818 6.68192L10.1109 7.38903L12.1465 9.42454L12.8536 9.42454L14.889 7.38908L14.1819 6.68197L13 7.86388V4Z" fill="#424242"/>
</svg>

After

Width:  |  Height:  |  Size: 449 B

View File

@@ -4,7 +4,7 @@
"description": "CodeQL for Visual Studio Code",
"author": "GitHub",
"private": true,
"version": "1.1.1",
"version": "1.1.3",
"publisher": "GitHub",
"license": "MIT",
"icon": "media/VS-marketplace-CodeQL-icon.png",
@@ -77,6 +77,12 @@
".dbscheme"
],
"configuration": "./language-configuration.json"
},
{
"id": "xml",
"extensions": [
".qhelp"
]
}
],
"grammars": [
@@ -132,6 +138,11 @@
"default": false,
"description": "Enable debug logging and tuple counting when running CodeQL queries. This information is useful for debugging query performance."
},
"codeQL.runningQueries.autoSave": {
"type": "boolean",
"default": false,
"description": "Enable automatically saving a modified query file when running a query."
},
"codeQL.queryHistory.format": {
"type": "string",
"default": "[%t] %q on %d - %s",
@@ -164,8 +175,8 @@
"command": "codeQL.chooseDatabase",
"title": "CodeQL: Choose Database",
"icon": {
"light": "media/black-plus.svg",
"dark": "media/white-plus.svg"
"light": "media/light/plus.svg",
"dark": "media/dark/plus.svg"
}
},
{
@@ -192,6 +203,30 @@
"command": "codeQLDatabases.upgradeDatabase",
"title": "Upgrade Database"
},
{
"command": "codeQLDatabases.renameDatabase",
"title": "Rename Database"
},
{
"command": "codeQLDatabases.openDatabaseFolder",
"title": "Show Database Directory"
},
{
"command": "codeQLDatabases.sortByName",
"title": "Sort by Name",
"icon": {
"light": "media/light/sort-alpha.svg",
"dark": "media/dark/sort-alpha.svg"
}
},
{
"command": "codeQLDatabases.sortByDateAdded",
"title": "Sort by Date Added",
"icon": {
"light": "media/light/sort-date.svg",
"dark": "media/dark/sort-date.svg"
}
},
{
"command": "codeQL.checkForUpdatesToCLI",
"title": "CodeQL: Check for CLI Updates"
@@ -212,6 +247,10 @@
"command": "codeQLQueryHistory.showQueryLog",
"title": "Show Query Log"
},
{
"command": "codeQLQueryHistory.showQueryText",
"title": "Show Query Text"
},
{
"command": "codeQLQueryResults.nextPathStep",
"title": "CodeQL: Show Next Step on Path"
@@ -239,6 +278,16 @@
],
"menus": {
"view/title": [
{
"command": "codeQLDatabases.sortByName",
"when": "view == codeQLDatabases",
"group": "navigation"
},
{
"command": "codeQLDatabases.sortByDateAdded",
"when": "view == codeQLDatabases",
"group": "navigation"
},
{
"command": "codeQL.chooseDatabase",
"when": "view == codeQLDatabases",
@@ -261,6 +310,16 @@
"group": "9_qlCommands",
"when": "view == codeQLDatabases"
},
{
"command": "codeQLDatabases.renameDatabase",
"group": "9_qlCommands",
"when": "view == codeQLDatabases"
},
{
"command": "codeQLDatabases.openDatabaseFolder",
"group": "9_qlCommands",
"when": "view == codeQLDatabases"
},
{
"command": "codeQLQueryHistory.openQuery",
"group": "9_qlCommands",
@@ -281,6 +340,11 @@
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
},
{
"command": "codeQLQueryHistory.showQueryText",
"group": "9_qlCommands",
"when": "view == codeQLQueryHistory"
},
{
"command": "codeQLTests.showOutputDifferences",
"group": "qltest@1",
@@ -321,6 +385,14 @@
"command": "codeQLDatabases.setCurrentDatabase",
"when": "false"
},
{
"command": "codeQLDatabases.sortByName",
"when": "false"
},
{
"command": "codeQLDatabases.sortByDateAdded",
"when": "false"
},
{
"command": "codeQLDatabases.removeDatabase",
"when": "false"
@@ -341,6 +413,10 @@
"command": "codeQLQueryHistory.showQueryLog",
"when": "false"
},
{
"command": "codeQLQueryHistory.showQueryText",
"when": "false"
},
{
"command": "codeQLQueryHistory.setLabel",
"when": "false"
@@ -389,7 +465,7 @@
"update-vscode": "node ./node_modules/vscode/bin/install",
"postinstall": "node ./node_modules/vscode/bin/install",
"format": "tsfmt -r",
"lint": "eslint . --ext .ts,.tsx"
"lint": "eslint src test --ext .ts,.tsx"
},
"dependencies": {
"child-process-promise": "^2.2.1",
@@ -406,8 +482,8 @@
"tmp": "^0.1.0",
"tree-kill": "~1.2.2",
"unzipper": "~0.10.5",
"vscode-jsonrpc": "^4.0.0",
"vscode-languageclient": "^5.2.1",
"vscode-jsonrpc": "^5.0.1",
"vscode-languageclient": "^6.1.3",
"vscode-test-adapter-api": "~1.7.0",
"vscode-test-adapter-util": "~0.7.0",
"minimist": "~1.2.5"
@@ -465,6 +541,7 @@
"sinon-chai": "~3.5.0",
"@types/sinon-chai": "~3.2.3",
"proxyquire": "~2.1.3",
"@types/proxyquire": "~1.3.28"
"@types/proxyquire": "~1.3.28",
"eslint-plugin-react": "~7.19.0"
}
}

View File

@@ -0,0 +1,103 @@
import { DecodedBqrsChunk, ResultSetSchema, ColumnKind, Column, ColumnValue } from "./bqrs-cli-types";
import { LocationValue, ResultSetSchema as AdaptedSchema, ColumnSchema, ColumnType, LocationStyle } from 'semmle-bqrs';
// FIXME: This is a temporary bit of impedance matching to convert
// from the types provided by ./bqrs-cli-types, to the types used by
// the view layer.
//
// The reason that it is benign for now is that it is only used by
// feature-flag-guarded codepaths that won't be encountered by normal
// users. It is not yet guaranteed to produce correct output for raw
// results.
//
// Eventually, the view layer should be refactored to directly accept data
// of types coming from bqrs-cli-types, and this file can be deleted.
export type ResultRow = ResultValue[];
export interface ResultElement {
label: string;
location?: LocationValue;
}
export interface ResultUri {
uri: string;
}
export type ResultValue = ResultElement | ResultUri | string;
export interface RawResultSet {
readonly schema: AdaptedSchema;
readonly rows: readonly ResultRow[];
}
function adaptKind(kind: ColumnKind): ColumnType {
// XXX what about 'u'?
if (kind === 'e') {
return { type: 'e', primitiveType: 's', locationStyle: LocationStyle.FivePart, hasLabel: true }
}
else {
return { type: kind };
}
}
function adaptColumn(col: Column): ColumnSchema {
return { name: col.name!, type: adaptKind(col.kind) };
}
export function adaptSchema(schema: ResultSetSchema): AdaptedSchema {
return {
columns: schema.columns.map(adaptColumn),
name: schema.name,
tupleCount: schema.rows,
version: 0,
}
}
export function adaptValue(val: ColumnValue): ResultValue {
// XXX taking a lot of incorrect shortcuts here
if (typeof val === 'string') {
return val;
}
if (typeof val === 'number' || typeof val === 'boolean') {
return val + '';
}
const url = val.url;
if (typeof url === 'string') {
return url;
}
if (url === undefined) {
return 'none';
}
return {
label: val.label || '',
location: {
t: LocationStyle.FivePart,
lineStart: url.startLine,
lineEnd: url.endLine,
colStart: url.startColumn,
colEnd: url.endColumn,
// FIXME: This seems definitely wrong. Should we be using
// something like the code in sarif-utils.ts?
file: url.uri.replace(/file:/, ''),
}
}
}
export function adaptRow(row: ColumnValue[]): ResultRow {
return row.map(adaptValue);
}
export function adaptBqrs(schema: AdaptedSchema, page: DecodedBqrsChunk): RawResultSet {
return {
schema,
rows: page.tuples.map(adaptRow),
}
}

View File

@@ -1,14 +1,34 @@
export const PAGE_SIZE = 1000;
export type ColumnKind = "f" | "i" | "s" | "b" | "d" | "e";
/**
* The single-character codes used in the bqrs format for the the kind
* of a result column. This namespace is intentionally not an enum, see
* the "for the sake of extensibility" comment in messages.ts.
*/
// eslint-disable-next-line @typescript-eslint/no-namespace
export namespace ColumnKindCode {
export const FLOAT = "f";
export const INTEGER = "i";
export const STRING = "s";
export const BOOLEAN = "b";
export const DATE = "d";
export const ENTITY = "e";
}
export type ColumnKind =
| typeof ColumnKindCode.FLOAT
| typeof ColumnKindCode.INTEGER
| typeof ColumnKindCode.STRING
| typeof ColumnKindCode.BOOLEAN
| typeof ColumnKindCode.DATE
| typeof ColumnKindCode.ENTITY;
export interface Column {
name?: string;
kind: ColumnKind;
}
export interface ResultSetSchema {
name: string;
rows: number;

View File

@@ -10,10 +10,10 @@ import * as tk from 'tree-kill';
import * as util from 'util';
import { CancellationToken, Disposable } from 'vscode';
import { BQRSInfo, DecodedBqrsChunk } from "./bqrs-cli-types";
import { DistributionProvider } from './distribution';
import { assertNever } from './helpers-pure';
import { QueryMetadata, SortDirection } from './interface-types';
import { Logger, ProgressReporter } from './logging';
import { DistributionProvider } from "./distribution";
import { assertNever } from "./helpers-pure";
import { QueryMetadata, SortDirection } from "./interface-types";
import { Logger, ProgressReporter } from "./logging";
/**
* The version of the SARIF format that we are using.
@@ -614,6 +614,27 @@ export class CodeQLCliServer implements Disposable {
"Resolving qlpack information",
);
}
/**
* Gets information about queries in a query suite.
* @param suite The suite to resolve.
* @param additionalPacks A list of directories to search for qlpacks before searching in `searchPath`.
* @param searchPath A list of directories to search for packs not found in `additionalPacks`. If undefined,
* the default CLI search path is used.
* @returns A list of query files found.
*/
resolveQueriesInSuite(suite: string, additionalPacks: string[], searchPath?: string[]): Promise<string[]> {
const args = ['--additional-packs', additionalPacks.join(path.delimiter)];
if (searchPath !== undefined) {
args.push('--search-path', path.join(...searchPath));
}
args.push(suite);
return this.runJsonCodeQlCliCommand<string[]>(
['resolve', 'queries'],
args,
"Resolving queries",
);
}
}
/**

View File

@@ -1,5 +1,5 @@
import { DisposableObject } from 'semmle-vscode-utils';
import { workspace, Event, EventEmitter, ConfigurationChangeEvent } from 'vscode';
import { workspace, Event, EventEmitter, ConfigurationChangeEvent, ConfigurationTarget } from 'vscode';
import { DistributionManager } from './distribution';
import { logger } from './logging';
@@ -27,10 +27,32 @@ class Setting {
}
return workspace.getConfiguration(this.parent.qualifiedName).get<T>(this.name)!;
}
updateValue<T>(value: T, target: ConfigurationTarget): Thenable<void> {
if (this.parent === undefined) {
throw new Error('Cannot update the value of a root setting.');
}
return workspace.getConfiguration(this.parent.qualifiedName).update(this.name, value, target);
}
}
const ROOT_SETTING = new Setting('codeQL');
// Enable experimental features
/**
* This setting is deliberately not in package.json so that it does
* not appear in the settings ui in vscode itself. If users want to
* enable experimental features, they can add
* "codeQl.experimentalFeatures" directly in their vscode settings
* json file.
*/
export const EXPERIMENTAL_FEATURES_SETTING = new Setting('experimentalFeatures', ROOT_SETTING);
/* Advanced setting: used to enable bqrs parsing in the cli instead of in the webview. */
export const EXPERIMENTAL_BQRS_SETTING = new Setting('experimentalBqrsParsing', ROOT_SETTING);
// Distribution configuration
const DISTRIBUTION_SETTING = new Setting('cli', ROOT_SETTING);
@@ -59,6 +81,7 @@ const NUMBER_OF_THREADS_SETTING = new Setting('numberOfThreads', RUNNING_QUERIES
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);
export const AUTOSAVE_SETTING = new Setting('autoSave', RUNNING_QUERIES_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];

View File

@@ -1,9 +1,9 @@
import * as path from 'path';
import { DisposableObject } from 'semmle-vscode-utils';
import { commands, Event, EventEmitter, ExtensionContext, ProviderResult, TreeDataProvider, TreeItem, Uri, window } from 'vscode';
import { commands, Event, EventEmitter, ExtensionContext, ProviderResult, TreeDataProvider, TreeItem, Uri, window, env } from 'vscode';
import * as cli from './cli';
import { DatabaseItem, DatabaseManager, getUpgradesDirectories } from './databases';
import { getOnDiskWorkspaceFolders } from './helpers';
import { getOnDiskWorkspaceFolders, showAndLogErrorMessage } from './helpers';
import { logger } from './logging';
import { clearCacheInDatabase, UserCancellationException } from './run-queries';
import * as qsClient from './queryserver-client';
@@ -15,8 +15,8 @@ type ThemableIconPath = { light: string; dark: string } | string;
* Path to icons to display next to currently selected database.
*/
const SELECTED_DATABASE_ICON: ThemableIconPath = {
light: 'media/check-light-mode.svg',
dark: 'media/check-dark-mode.svg',
light: 'media/light/check.svg',
dark: 'media/dark/check.svg',
};
/**
@@ -34,12 +34,21 @@ function joinThemableIconPath(base: string, iconPath: ThemableIconPath): Themabl
return path.join(base, iconPath);
}
enum SortOrder {
NameAsc = 'NameAsc',
NameDesc = 'NameDesc',
DateAddedAsc = 'DateAddedAsc',
DateAddedDesc = 'DateAddedDesc'
}
/**
* Tree data provider for the databases view.
*/
class DatabaseTreeDataProvider extends DisposableObject
implements TreeDataProvider<DatabaseItem> {
private _sortOrder = SortOrder.NameAsc;
private readonly _onDidChangeTreeData = new EventEmitter<DatabaseItem | undefined>();
private currentDatabaseItem: DatabaseItem | undefined;
@@ -84,7 +93,18 @@ class DatabaseTreeDataProvider extends DisposableObject
public getChildren(element?: DatabaseItem): ProviderResult<DatabaseItem[]> {
if (element === undefined) {
return this.databaseManager.databaseItems.slice(0);
return this.databaseManager.databaseItems.slice(0).sort((db1, db2) => {
switch(this.sortOrder) {
case SortOrder.NameAsc:
return db1.name.localeCompare(db2.name);
case SortOrder.NameDesc:
return db2.name.localeCompare(db1.name);
case SortOrder.DateAddedAsc:
return (db1.dateAdded || 0) - (db2.dateAdded || 0);
case SortOrder.DateAddedDesc:
return (db2.dateAdded || 0) - (db1.dateAdded || 0);
}
});
}
else {
return [];
@@ -98,6 +118,15 @@ class DatabaseTreeDataProvider extends DisposableObject
public getCurrent(): DatabaseItem | undefined {
return this.currentDatabaseItem;
}
public get sortOrder() {
return this._sortOrder;
}
public set sortOrder(newSortOrder: SortOrder) {
this._sortOrder = newSortOrder;
this._onDidChangeTreeData.fire();
}
}
/** Gets the first element in the given list, if any, or undefined if the list is empty or undefined. */
@@ -129,21 +158,30 @@ async function chooseDatabaseDir(): Promise<Uri | undefined> {
}
export class DatabaseUI extends DisposableObject {
public constructor(ctx: ExtensionContext, private cliserver: cli.CodeQLCliServer, private databaseManager: DatabaseManager,
private readonly queryServer: qsClient.QueryServerClient | undefined) {
private treeDataProvider: DatabaseTreeDataProvider;
public constructor(
ctx: ExtensionContext,
private cliserver: cli.CodeQLCliServer,
private databaseManager: DatabaseManager,
private readonly queryServer: qsClient.QueryServerClient | undefined
) {
super();
const treeDataProvider = this.push(new DatabaseTreeDataProvider(ctx, databaseManager));
this.push(window.createTreeView('codeQLDatabases', { treeDataProvider }));
this.treeDataProvider = this.push(new DatabaseTreeDataProvider(ctx, databaseManager));
this.push(window.createTreeView('codeQLDatabases', { treeDataProvider: this.treeDataProvider }));
ctx.subscriptions.push(commands.registerCommand('codeQL.chooseDatabase', this.handleChooseDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQL.setCurrentDatabase', this.handleSetCurrentDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQL.upgradeCurrentDatabase', this.handleUpgradeCurrentDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQL.clearCache', this.handleClearCache));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.setCurrentDatabase', this.handleMakeCurrentDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.sortByName', this.handleSortByName));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.sortByDateAdded', this.handleSortByDateAdded));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.removeDatabase', this.handleRemoveDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.upgradeDatabase', this.handleUpgradeDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.renameDatabase', this.handleRenameDatabase));
ctx.subscriptions.push(commands.registerCommand('codeQLDatabases.openDatabaseFolder', this.handleOpenFolder));
}
private handleMakeCurrentDatabase = async (databaseItem: DatabaseItem): Promise<void> => {
@@ -154,6 +192,22 @@ export class DatabaseUI extends DisposableObject {
return await this.chooseAndSetDatabase();
}
private handleSortByName = async () => {
if (this.treeDataProvider.sortOrder === SortOrder.NameAsc) {
this.treeDataProvider.sortOrder = SortOrder.NameDesc;
} else {
this.treeDataProvider.sortOrder = SortOrder.NameAsc;
}
}
private handleSortByDateAdded = async () => {
if (this.treeDataProvider.sortOrder === SortOrder.DateAddedAsc) {
this.treeDataProvider.sortOrder = SortOrder.DateAddedDesc;
} else {
this.treeDataProvider.sortOrder = SortOrder.DateAddedAsc;
}
}
private handleUpgradeCurrentDatabase = async (): Promise<void> => {
await this.handleUpgradeDatabase(this.databaseManager.currentDatabaseItem);
}
@@ -220,6 +274,29 @@ export class DatabaseUI extends DisposableObject {
this.databaseManager.removeDatabaseItem(databaseItem);
}
private handleRenameDatabase = async (databaseItem: DatabaseItem): Promise<void> => {
try {
const newName = await window.showInputBox({
prompt: 'Choose new database name',
value: databaseItem.name
});
if (newName) {
this.databaseManager.renameDatabaseItem(databaseItem, newName);
}
} catch (e) {
showAndLogErrorMessage(e.message);
}
}
private handleOpenFolder = async (databaseItem: DatabaseItem): Promise<void> => {
try {
await env.openExternal(databaseItem.databaseUri);
} catch (e) {
showAndLogErrorMessage(e.message);
}
}
/**
* Return the current database directory. If we don't already have a
* current database, ask the user for one, and return that, or

View File

@@ -35,10 +35,12 @@ const DB_LIST = 'databaseList';
export interface DatabaseOptions {
displayName?: string;
ignoreSourceArchive?: boolean;
dateAdded?: number | undefined;
}
interface FullDatabaseOptions extends DatabaseOptions {
ignoreSourceArchive: boolean;
dateAdded: number | undefined;
}
interface PersistedDatabaseItem {
@@ -201,7 +203,7 @@ export interface DatabaseItem {
/** The URI of the database */
readonly databaseUri: vscode.Uri;
/** The name of the database to be displayed in the UI */
readonly name: string;
name: string;
/** The URI of the database's source archive, or `undefined` if no source archive is to be used. */
readonly sourceArchive: vscode.Uri | undefined;
/**
@@ -209,6 +211,12 @@ export interface DatabaseItem {
* Will be `undefined` if the database is invalid. Can be updated by calling `refresh()`.
*/
readonly contents: DatabaseContents | undefined;
/**
* The date this database was added as a unix timestamp. Or undefined if we don't know.
*/
readonly dateAdded: number | undefined;
/** If the database is invalid, describes why. */
readonly error: Error | undefined;
/**
@@ -279,6 +287,10 @@ class DatabaseItemImpl implements DatabaseItem {
}
}
public set name(newName: string) {
this.options.displayName = newName;
}
public get sourceArchive(): vscode.Uri | undefined {
if (this.options.ignoreSourceArchive || (this._contents === undefined)) {
return undefined;
@@ -292,6 +304,10 @@ class DatabaseItemImpl implements DatabaseItem {
return this._contents;
}
public get dateAdded(): number | undefined {
return this.options.dateAdded;
}
public get error(): Error | undefined {
return this._error;
}
@@ -430,27 +446,26 @@ class DatabaseItemImpl implements DatabaseItem {
*/
function eventFired<T>(event: vscode.Event<T>, timeoutMs = 1000): Promise<T | undefined> {
return new Promise((res, _rej) => {
let timeout: NodeJS.Timeout | undefined;
let disposable: vscode.Disposable | undefined;
function dispose() {
if (timeout !== undefined) clearTimeout(timeout);
if (disposable !== undefined) disposable.dispose();
}
disposable = event(e => {
res(e);
dispose();
});
timeout = setTimeout(() => {
const timeout = setTimeout(() => {
logger.log(`Waiting for event ${event} timed out after ${timeoutMs}ms`);
res(undefined);
dispose();
}, timeoutMs);
const disposable = event(e => {
res(e);
dispose();
});
function dispose() {
clearTimeout(timeout);
disposable.dispose();
}
});
}
export class DatabaseManager extends DisposableObject {
private readonly _onDidChangeDatabaseItem =
this.push(new vscode.EventEmitter<DatabaseItem | undefined>());
readonly onDidChangeDatabaseItem = this._onDidChangeDatabaseItem.event;
private readonly _onDidChangeCurrentDatabaseItem =
@@ -468,8 +483,9 @@ export class DatabaseManager extends DisposableObject {
this.loadPersistedState(); // Let this run async.
}
public async openDatabase(uri: vscode.Uri, options?: DatabaseOptions):
Promise<DatabaseItem> {
public async openDatabase(
uri: vscode.Uri, options?: DatabaseOptions
): Promise<DatabaseItem> {
const contents = await resolveDatabaseContents(uri);
const realOptions = options || {};
@@ -478,7 +494,8 @@ export class DatabaseManager extends DisposableObject {
const fullOptions: FullDatabaseOptions = {
ignoreSourceArchive: (realOptions.ignoreSourceArchive !== undefined) ?
realOptions.ignoreSourceArchive : isQLTestDatabase,
displayName: realOptions.displayName
displayName: realOptions.displayName,
dateAdded: realOptions.dateAdded || Date.now()
};
const databaseItem = new DatabaseItemImpl(uri, contents, fullOptions, (item) => {
this._onDidChangeDatabaseItem.fire(item);
@@ -528,11 +545,13 @@ export class DatabaseManager extends DisposableObject {
}
}
private async createDatabaseItemFromPersistedState(state: PersistedDatabaseItem):
Promise<DatabaseItem> {
private async createDatabaseItemFromPersistedState(
state: PersistedDatabaseItem
): Promise<DatabaseItem> {
let displayName: string | undefined = undefined;
let ignoreSourceArchive = false;
let dateAdded = undefined;
if (state.options) {
if (typeof state.options.displayName === 'string') {
displayName = state.options.displayName;
@@ -540,10 +559,14 @@ export class DatabaseManager extends DisposableObject {
if (typeof state.options.ignoreSourceArchive === 'boolean') {
ignoreSourceArchive = state.options.ignoreSourceArchive;
}
if (typeof state.options.dateAdded === 'number') {
dateAdded = state.options.dateAdded;
}
}
const fullOptions: FullDatabaseOptions = {
ignoreSourceArchive: ignoreSourceArchive,
displayName: displayName
ignoreSourceArchive,
displayName,
dateAdded
};
const item = new DatabaseItemImpl(vscode.Uri.parse(state.uri), undefined, fullOptions,
(item) => {
@@ -613,12 +636,23 @@ export class DatabaseManager extends DisposableObject {
return this._databaseItems.find(item => item.databaseUri.toString(true) === uriString);
}
public findDatabaseItemBySourceArchive(uri: vscode.Uri): DatabaseItem | undefined {
const uriString = uri.toString(true);
return this._databaseItems.find(item => item.sourceArchive && item.sourceArchive.toString(true) === uriString);
}
private async addDatabaseItem(item: DatabaseItemImpl) {
this._databaseItems.push(item);
this.updatePersistedDatabaseList();
this._onDidChangeDatabaseItem.fire(undefined);
}
public async renameDatabaseItem(item: DatabaseItem, newName: string) {
item.name = newName;
this.updatePersistedDatabaseList();
this._onDidChangeDatabaseItem.fire(item);
}
public removeDatabaseItem(item: DatabaseItem) {
if (this._currentDatabaseItem == item)
this._currentDatabaseItem = undefined;

View File

@@ -0,0 +1,204 @@
import * as fs from 'fs-extra';
import * as yaml from 'js-yaml';
import * as tmp from 'tmp';
import * as vscode from "vscode";
import { decodeSourceArchiveUri, zipArchiveScheme } from "./archive-filesystem-provider";
import { ColumnKindCode, EntityValue, getResultSetSchema, LineColumnLocation, UrlValue } from "./bqrs-cli-types";
import { CodeQLCliServer } from "./cli";
import { DatabaseItem, DatabaseManager } from "./databases";
import * as helpers from './helpers';
import { CachedOperation } from './helpers';
import * as messages from "./messages";
import { QueryServerClient } from "./queryserver-client";
import { compileAndRunQueryAgainstDatabase, QueryWithResults } from "./run-queries";
/**
* Run templated CodeQL queries to find definitions and references in
* source-language files. We may eventually want to find a way to
* generalize this to other custom queries, e.g. showing dataflow to
* or from a selected identifier.
*/
const TEMPLATE_NAME = "selectedSourceFile";
const SELECT_QUERY_NAME = "#select";
enum KeyType {
DefinitionQuery = 'DefinitionQuery',
ReferenceQuery = 'ReferenceQuery',
}
function tagOfKeyType(keyType: KeyType): string {
switch (keyType) {
case KeyType.DefinitionQuery: return "ide-contextual-queries/local-definitions";
case KeyType.ReferenceQuery: return "ide-contextual-queries/local-references";
}
}
async function resolveQueries(cli: CodeQLCliServer, qlpack: string, keyType: KeyType): Promise<string[]> {
const suiteFile = tmp.fileSync({ postfix: '.qls' }).name;
const suiteYaml = { qlpack, include: { kind: 'definitions', 'tags contain': tagOfKeyType(keyType) } };
await fs.writeFile(suiteFile, yaml.safeDump(suiteYaml), 'utf8');
const queries = await cli.resolveQueriesInSuite(suiteFile, helpers.getOnDiskWorkspaceFolders());
if (queries.length === 0) {
throw new Error("Couldn't find any queries for qlpack");
}
return queries;
}
async function qlpackOfDatabase(cli: CodeQLCliServer, db: DatabaseItem): Promise<string | undefined> {
if (db.contents === undefined)
return undefined;
const datasetPath = db.contents.datasetUri.fsPath;
const { qlpack } = await helpers.resolveDatasetFolder(cli, datasetPath);
return qlpack;
}
interface FullLocationLink extends vscode.LocationLink {
originUri: vscode.Uri;
}
export class TemplateQueryDefinitionProvider implements vscode.DefinitionProvider {
private cache: CachedOperation<vscode.LocationLink[]>;
constructor(
private cli: CodeQLCliServer,
private qs: QueryServerClient,
private dbm: DatabaseManager,
) {
this.cache = new CachedOperation<vscode.LocationLink[]>(this.getDefinitions.bind(this));
}
async getDefinitions(uriString: string): Promise<vscode.LocationLink[]> {
return getLinksForUriString(this.cli, this.qs, this.dbm, uriString, KeyType.DefinitionQuery, (src, _dest) => src === uriString);
}
async provideDefinition(document: vscode.TextDocument, position: vscode.Position, _token: vscode.CancellationToken): Promise<vscode.LocationLink[]> {
const fileLinks = await this.cache.get(document.uri.toString());
const locLinks: vscode.LocationLink[] = [];
for (const link of fileLinks) {
if (link.originSelectionRange!.contains(position)) {
locLinks.push(link);
}
}
return locLinks;
}
}
export class TemplateQueryReferenceProvider implements vscode.ReferenceProvider {
private cache: CachedOperation<FullLocationLink[]>;
constructor(
private cli: CodeQLCliServer,
private qs: QueryServerClient,
private dbm: DatabaseManager,
) {
this.cache = new CachedOperation<FullLocationLink[]>(this.getReferences.bind(this));
}
async getReferences(uriString: string): Promise<FullLocationLink[]> {
return getLinksForUriString(this.cli, this.qs, this.dbm, uriString, KeyType.ReferenceQuery, (_src, dest) => dest === uriString);
}
async provideReferences(document: vscode.TextDocument, position: vscode.Position, _context: vscode.ReferenceContext, _token: vscode.CancellationToken): Promise<vscode.Location[]> {
const fileLinks = await this.cache.get(document.uri.toString());
const locLinks: vscode.Location[] = [];
for (const link of fileLinks) {
if (link.targetRange!.contains(position)) {
locLinks.push({ range: link.originSelectionRange!, uri: link.originUri });
}
}
return locLinks;
}
}
interface FileRange {
file: vscode.Uri;
range: vscode.Range;
}
async function getLinksFromResults(results: QueryWithResults, cli: CodeQLCliServer, db: DatabaseItem, filter: (srcFile: string, destFile: string) => boolean): Promise<FullLocationLink[]> {
const localLinks: FullLocationLink[] = [];
const bqrsPath = results.query.resultsPaths.resultsPath;
const info = await cli.bqrsInfo(bqrsPath);
const selectInfo = getResultSetSchema(SELECT_QUERY_NAME, info);
if (selectInfo && selectInfo.columns.length == 3
&& selectInfo.columns[0].kind == ColumnKindCode.ENTITY
&& selectInfo.columns[1].kind == ColumnKindCode.ENTITY
&& selectInfo.columns[2].kind == ColumnKindCode.STRING) {
// TODO: Page this
const allTuples = await cli.bqrsDecode(bqrsPath, SELECT_QUERY_NAME);
for (const tuple of allTuples.tuples) {
const src = tuple[0] as EntityValue;
const dest = tuple[1] as EntityValue;
const srcFile = src.url && fileRangeFromURI(src.url, db);
const destFile = dest.url && fileRangeFromURI(dest.url, db);
if (srcFile && destFile && filter(srcFile.file.toString(), destFile.file.toString())) {
localLinks.push({ targetRange: destFile.range, targetUri: destFile.file, originSelectionRange: srcFile.range, originUri: srcFile.file });
}
}
}
return localLinks;
}
async function getLinksForUriString(
cli: CodeQLCliServer,
qs: QueryServerClient,
dbm: DatabaseManager,
uriString: string,
keyType: KeyType,
filter: (src: string, dest: string) => boolean
) {
const uri = decodeSourceArchiveUri(vscode.Uri.parse(uriString));
const sourceArchiveUri = vscode.Uri.file(uri.sourceArchiveZipPath).with({ scheme: zipArchiveScheme });
const db = dbm.findDatabaseItemBySourceArchive(sourceArchiveUri);
if (db) {
const qlpack = await qlpackOfDatabase(cli, db);
if (qlpack === undefined) {
throw new Error("Can't infer qlpack from database source archive");
}
const links: FullLocationLink[] = []
for (const query of await resolveQueries(cli, qlpack, keyType)) {
const templates: messages.TemplateDefinitions = {
[TEMPLATE_NAME]: {
values: {
tuples: [[{
stringValue: uri.pathWithinSourceArchive
}]]
}
}
};
const results = await compileAndRunQueryAgainstDatabase(cli, qs, db, false, vscode.Uri.file(query), templates);
if (results.result.resultType == messages.QueryResultType.SUCCESS) {
links.push(...await getLinksFromResults(results, cli, db, filter));
}
}
return links;
} else {
return [];
}
}
function fileRangeFromURI(uri: UrlValue, db: DatabaseItem): FileRange | undefined {
if (typeof uri === "string") {
return undefined;
} else if ('startOffset' in uri) {
return undefined;
} else {
const loc = uri as LineColumnLocation;
const range = new vscode.Range(Math.max(0, loc.startLine - 1),
Math.max(0, loc.startColumn - 1),
Math.max(0, loc.endLine - 1),
Math.max(0, loc.endColumn));
try {
const parsed = vscode.Uri.parse(uri.uri, true);
if (parsed.scheme === "file") {
return { file: db.resolveSourceFile(parsed.fsPath), range };
}
return undefined;
} catch (e) {
return undefined;
}
}
}

View File

@@ -84,4 +84,4 @@ export abstract class Discovery<T> extends DisposableObject {
* @param results The discovery results returned by the `discover` function.
*/
protected abstract update(results: T): void;
}
}

View File

@@ -112,7 +112,13 @@ export class DistributionManager implements DistributionProvider {
"that a CodeQL executable exists at the specified path or remove the setting.");
return undefined;
}
if (deprecatedCodeQlLauncherName() && this._config.customCodeQlPath.endsWith(deprecatedCodeQlLauncherName()!)) {
// emit a warning if using a deprecated launcher and a non-deprecated launcher exists
if (
deprecatedCodeQlLauncherName() &&
this._config.customCodeQlPath.endsWith(deprecatedCodeQlLauncherName()!) &&
await this.hasNewLauncherName()
) {
warnDeprecatedLauncher();
}
return this._config.customCodeQlPath;
@@ -173,6 +179,21 @@ export class DistributionManager implements DistributionProvider {
return this._onDidChangeDistribution;
}
/**
* @return true if the non-deprecated launcher name exists on the file system
* in the same directory as the specified launcher only if using an external
* installation. False otherwise.
*/
private async hasNewLauncherName(): Promise<boolean> {
if (!this._config.customCodeQlPath) {
// not managed externally
return false;
}
const dir = path.dirname(this._config.customCodeQlPath);
const newLaunderPath = path.join(dir, codeQlLauncherName());
return await fs.pathExists(newLaunderPath);
}
private readonly _config: DistributionConfig;
private readonly _extensionSpecificDistributionManager: ExtensionSpecificDistributionManager;
private readonly _updateCheckRateLimiter: InvocationRateLimiter<DistributionUpdateCheckResult>;

View File

@@ -1,25 +1,23 @@
import { commands, Disposable, ExtensionContext, extensions, ProgressLocation, ProgressOptions, window as Window, Uri } from 'vscode';
import { commands, Disposable, ExtensionContext, extensions, languages, ProgressLocation, ProgressOptions, Uri, window as Window } from 'vscode';
import { LanguageClient } from 'vscode-languageclient';
import { testExplorerExtensionId, TestHub } from 'vscode-test-adapter-api';
import * as archiveFilesystemProvider from './archive-filesystem-provider';
import { DistributionConfigListener, QueryServerConfigListener, QueryHistoryConfigListener } from './config';
import { CodeQLCliServer } from './cli';
import { DistributionConfigListener, QueryHistoryConfigListener, QueryServerConfigListener, EXPERIMENTAL_FEATURES_SETTING } from './config';
import { DatabaseManager } from './databases';
import { DatabaseUI } from './databases-ui';
import {
DistributionUpdateCheckResultKind, DistributionManager, FindDistributionResult, FindDistributionResultKind, GithubApiError,
DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT, GithubRateLimitedError
} from './distribution';
import { TemplateQueryDefinitionProvider, TemplateQueryReferenceProvider } from './definitions';
import { DEFAULT_DISTRIBUTION_VERSION_CONSTRAINT, DistributionManager, DistributionUpdateCheckResultKind, FindDistributionResult, FindDistributionResultKind, GithubApiError, GithubRateLimitedError } from './distribution';
import * as helpers from './helpers';
import { assertNever } from './helpers-pure';
import { spawnIdeServer } from './ide-server';
import { InterfaceManager, WebviewReveal } from './interface';
import { ideServerLogger, logger, queryServerLogger } from './logging';
import { compileAndRunQueryAgainstDatabase, tmpDirDisposal, UserCancellationException } from './run-queries';
import { CompletedQuery } from './query-results';
import { QueryHistoryManager } from './query-history';
import { CompletedQuery } from './query-results';
import * as qsClient from './queryserver-client';
import { CodeQLCliServer } from './cli';
import { assertNever } from './helpers-pure';
import { displayQuickQuery } from './quick-query';
import { TestHub, testExplorerExtensionId } from 'vscode-test-adapter-api';
import { compileAndRunQueryAgainstDatabase, tmpDirDisposal, UserCancellationException } from './run-queries';
import { QLTestAdapterFactory } from './test-adapter';
import { TestUIService } from './test-ui';
@@ -337,6 +335,17 @@ async function activateWithInstalledDistribution(ctx: ExtensionContext, distribu
}));
ctx.subscriptions.push(client.start());
if (EXPERIMENTAL_FEATURES_SETTING.getValue()) {
languages.registerDefinitionProvider(
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
new TemplateQueryDefinitionProvider(cliServer, qs, dbm)
);
languages.registerReferenceProvider(
{ scheme: archiveFilesystemProvider.zipArchiveScheme },
new TemplateQueryReferenceProvider(cliServer, qs, dbm)
);
}
}
function initializeLogging(ctx: ExtensionContext): void {

View File

@@ -1,5 +1,9 @@
import * as fs from 'fs-extra';
import * as glob from 'glob-promise';
import * as yaml from 'js-yaml';
import * as path from 'path';
import { CancellationToken, ExtensionContext, ProgressOptions, window as Window, workspace } from 'vscode';
import { CodeQLCliServer } from './cli';
import { logger } from './logging';
import { QueryInfo } from './run-queries';
@@ -244,3 +248,110 @@ function createRateLimitedResult(): RateLimitedResult {
kind: InvocationRateLimiterResultKind.RateLimited
};
}
export type DatasetFolderInfo = {
dbscheme: string;
qlpack: string;
}
export async function getQlPackForDbscheme(cliServer: CodeQLCliServer, dbschemePath: string): Promise<string> {
const qlpacks = await cliServer.resolveQlpacks(getOnDiskWorkspaceFolders());
const packs: { packDir: string | undefined; packName: string }[] =
Object.entries(qlpacks).map(([packName, dirs]) => {
if (dirs.length < 1) {
logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`);
return { packName, packDir: undefined };
}
if (dirs.length > 1) {
logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`);
}
return {
packName,
packDir: dirs[0]
}
});
for (const { packDir, packName } of packs) {
if (packDir !== undefined) {
const qlpack = yaml.safeLoad(await fs.readFile(path.join(packDir, 'qlpack.yml'), 'utf8'));
if (qlpack.dbscheme !== undefined && path.basename(qlpack.dbscheme) === path.basename(dbschemePath)) {
return packName;
}
}
}
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
}
export async function resolveDatasetFolder(cliServer: CodeQLCliServer, datasetFolder: string): Promise<DatasetFolderInfo> {
const dbschemes = await glob(path.join(datasetFolder, '*.dbscheme'))
if (dbschemes.length < 1) {
throw new Error(`Can't find dbscheme for current database in ${datasetFolder}`);
}
dbschemes.sort();
const dbscheme = dbschemes[0];
if (dbschemes.length > 1) {
Window.showErrorMessage(`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`);
}
const qlpack = await getQlPackForDbscheme(cliServer, dbscheme);
return { dbscheme, qlpack };
}
/**
* A cached mapping from strings to value of type U.
*/
export class CachedOperation<U> {
private readonly operation: (t: string) => Promise<U>;
private readonly cached: Map<string, U>;
private readonly lru: string[];
private readonly inProgressCallbacks: Map<string, [(u: U) => void, (reason?: any) => void][]>;
constructor(operation: (t: string) => Promise<U>, private cacheSize = 100) {
this.operation = operation;
this.lru = [];
this.inProgressCallbacks = new Map<string, [(u: U) => void, (reason?: any) => void][]>();
this.cached = new Map<string, U>();
}
async get(t: string): Promise<U> {
// Try and retrieve from the cache
const fromCache = this.cached.get(t);
if (fromCache !== undefined) {
// Move to end of lru list
this.lru.push(this.lru.splice(this.lru.findIndex(v => v === t), 1)[0])
return fromCache;
}
// Otherwise check if in progress
const inProgressCallback = this.inProgressCallbacks.get(t);
if (inProgressCallback !== undefined) {
// If so wait for it to resolve
return await new Promise((resolve, reject) => {
inProgressCallback.push([resolve, reject]);
});
}
// Otherwise compute the new value, but leave a callback to allow sharing work
const callbacks: [(u: U) => void, (reason?: any) => void][] = [];
this.inProgressCallbacks.set(t, callbacks);
try {
const result = await this.operation(t);
callbacks.forEach(f => f[0](result));
this.inProgressCallbacks.delete(t);
if (this.lru.length > this.cacheSize) {
const toRemove = this.lru.shift()!;
this.cached.delete(toRemove);
}
this.lru.push(t);
this.cached.set(t, result);
return result;
} catch (e) {
// Rethrow error on all callbacks
callbacks.forEach(f => f[1](e));
throw e;
} finally {
this.inProgressCallbacks.delete(t);
}
}
}

View File

@@ -1,5 +1,6 @@
import * as sarif from 'sarif';
import { ResolvableLocationValue } from 'semmle-bqrs';
import { RawResultSet } from './adapt';
/**
* Only ever show this many results per run in interpreted results.
@@ -77,6 +78,12 @@ export interface SetStateMsg {
* This is useful to prevent properties like scroll state being lost when rendering the sorted results after sorting a column.
*/
shouldKeepOldResultsWhileRendering: boolean;
/**
* An experimental way of providing results from the extension.
* Should be undefined unless config.EXPERIMENTAL_BQRS_SETTING is set to true.
*/
resultSets?: RawResultSet[];
}
/** Advance to the next or previous path no in the path viewer */

View File

@@ -16,6 +16,8 @@ import * as messages from './messages';
import { CompletedQuery, interpretResults } from './query-results';
import { QueryInfo, tmpDir } from './run-queries';
import { parseSarifLocation, parseSarifPlainTextMessage } from './sarif-utils';
import { adaptSchema, adaptBqrs, RawResultSet } from './adapt';
import { EXPERIMENTAL_BQRS_SETTING } from './config';
/**
* interface.ts
@@ -349,9 +351,7 @@ export class InterfaceManager extends DisposableObject {
const showButton = "View Results";
const queryName = results.queryName;
const resultPromise = vscode.window.showInformationMessage(
`Finished running query ${
queryName.length > 0 ? `${queryName}` : ""
}.`,
`Finished running query ${queryName.length > 0 ? ` "${queryName}"` : ""}.`,
showButton
);
// Address this click asynchronously so we still update the
@@ -363,6 +363,19 @@ export class InterfaceManager extends DisposableObject {
});
}
let resultSets: RawResultSet[] | undefined;
if (EXPERIMENTAL_BQRS_SETTING.getValue()) {
resultSets = [];
const schemas = await this.cliServer.bqrsInfo(results.query.resultsPaths.resultsPath);
for (const schema of schemas["result-sets"]) {
const chunk = await this.cliServer.bqrsDecode(results.query.resultsPaths.resultsPath, schema.name);
const adaptedSchema = adaptSchema(schema);
const resultSet = adaptBqrs(adaptedSchema, chunk);
resultSets.push(resultSet);
}
}
await this.postMessage({
t: "setState",
interpretation,
@@ -370,6 +383,7 @@ export class InterfaceManager extends DisposableObject {
resultsPath: this.convertPathToWebviewUri(
results.query.resultsPaths.resultsPath
),
resultSets,
sortedResultsMap,
database: results.database,
shouldKeepOldResultsWhileRendering,

View File

@@ -60,7 +60,7 @@ export class OutputChannelLogger extends DisposableObject implements Logger {
* function if you don't need to guarantee that the log writing is complete before
* continuing.
*/
async log(message: string, options = { } as LogOptions): Promise<void> {
async log(message: string, options = {} as LogOptions): Promise<void> {
if (options.trailingNewline === undefined) {
options.trailingNewline = true;
}
@@ -116,7 +116,7 @@ class AdditionalLogLocation extends Disposable {
super(() => { /**/ });
}
async log(message: string, options = { } as LogOptions): Promise<void> {
async log(message: string, options = {} as LogOptions): Promise<void> {
if (options.trailingNewline === undefined) {
options.trailingNewline = true;
}

View File

@@ -64,7 +64,7 @@ export class QLTestDirectory extends QLTestNode {
private createChildDirectory(name: string): QLTestDirectory {
const existingChild = this._children.find((child) => child.name === name);
if (existingChild !== undefined) {
return <QLTestDirectory>existingChild;
return existingChild as QLTestDirectory;
}
else {
const newChild = new QLTestDirectory(path.join(this.path, name), name);
@@ -87,6 +87,7 @@ export class QLTestFile extends QLTestNode {
}
public finish(): void {
/**/
}
}

View File

@@ -6,6 +6,7 @@ import { QueryHistoryConfig } from './config';
import { QueryWithResults } from './run-queries';
import * as helpers from './helpers';
import { logger } from './logging';
import { URLSearchParams } from 'url';
/**
* query-history.ts
@@ -18,9 +19,32 @@ import { logger } from './logging';
export type QueryHistoryItemOptions = {
label?: string; // user-settable label
queryText?: string; // stored query for quick query
queryText?: string; // text of the selected file
isQuickQuery?: boolean;
}
const SHOW_QUERY_TEXT_MSG = `\
////////////////////////////////////////////////////////////////////////////////////
// This is the text of the entire query file when it was executed for this query //
// run. The text or dependent libraries may have changed since then. //
// //
// This buffer is readonly. To re-execute this query, you must open the original //
// query file. //
////////////////////////////////////////////////////////////////////////////////////
`;
const SHOW_QUERY_TEXT_QUICK_EVAL_MSG = `\
////////////////////////////////////////////////////////////////////////////////////
// This is the Quick Eval selection of the query file when it was executed for //
// this query run. The text or dependent libraries may have changed since then. //
// //
// This buffer is readonly. To re-execute this query, you must open the original //
// query file. //
////////////////////////////////////////////////////////////////////////////////////
`;
/**
* Path to icon to display next to a failed query history item.
*/
@@ -137,7 +161,7 @@ export class QueryHistoryManager {
const textDocument = await vscode.workspace.openTextDocument(vscode.Uri.file(queryHistoryItem.query.program.queryPath));
const editor = await vscode.window.showTextDocument(textDocument, vscode.ViewColumn.One);
const queryText = queryHistoryItem.options.queryText;
if (queryText !== undefined) {
if (queryText !== undefined && queryHistoryItem.options.isQuickQuery) {
await editor.edit(edit => edit.replace(textDocument.validateRange(
new vscode.Range(0, 0, textDocument.lineCount, 0)), queryText)
);
@@ -192,13 +216,13 @@ export class QueryHistoryManager {
async handleShowQueryLog(queryHistoryItem: CompletedQuery) {
if (queryHistoryItem.logFileLocation) {
const uri = vscode.Uri.parse(queryHistoryItem.logFileLocation);
const uri = vscode.Uri.file(queryHistoryItem.logFileLocation);
try {
await vscode.window.showTextDocument(uri, {
});
} catch (e) {
if (e.message.includes('Files above 50MB cannot be synchronized with extensions')) {
const res = await helpers.showBinaryChoiceDialog('File is too large to open in the editor, do you want to open exterally?');
const res = await helpers.showBinaryChoiceDialog('File is too large to open in the editor, do you want to open it externally?');
if (res) {
try {
await vscode.commands.executeCommand('revealFileInOS', uri);
@@ -218,6 +242,36 @@ export class QueryHistoryManager {
}
}
async handleShowQueryText(queryHistoryItem: CompletedQuery) {
try {
const queryName = queryHistoryItem.queryName.endsWith('.ql') ? queryHistoryItem.queryName : queryHistoryItem.queryName + '.ql';
const params = new URLSearchParams({
isQuickEval: String(!!queryHistoryItem.query.quickEvalPosition),
queryText: await this.getQueryText(queryHistoryItem)
});
const uri = vscode.Uri.parse(`codeql:${queryHistoryItem.query.queryID}-${queryName}?${params.toString()}`);
const doc = await vscode.workspace.openTextDocument(uri);
await vscode.window.showTextDocument(doc, { preview: false });
} catch (e) {
helpers.showAndLogErrorMessage(e.message);
}
}
async getQueryText(queryHistoryItem: CompletedQuery): Promise<string> {
if (queryHistoryItem.options.queryText) {
return queryHistoryItem.options.queryText;
} else if (queryHistoryItem.query.quickEvalPosition) {
// capture all selected lines
const startLine = queryHistoryItem.query.quickEvalPosition.line;
const endLine = queryHistoryItem.query.quickEvalPosition.endLine;
const textDocument =
await vscode.workspace.openTextDocument(queryHistoryItem.query.quickEvalPosition.fileName);
return textDocument.getText(new vscode.Range(startLine - 1, 0, endLine, 0));
} else {
return '';
}
}
constructor(
ctx: ExtensionContext,
private queryHistoryConfigListener: QueryHistoryConfig,
@@ -240,12 +294,24 @@ export class QueryHistoryManager {
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.removeHistoryItem', this.handleRemoveHistoryItem.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.setLabel', this.handleSetLabel.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryLog', this.handleShowQueryLog.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.showQueryText', this.handleShowQueryText.bind(this)));
ctx.subscriptions.push(vscode.commands.registerCommand('codeQLQueryHistory.itemClicked', async (item) => {
return this.handleItemClicked(item);
}));
queryHistoryConfigListener.onDidChangeQueryHistoryConfiguration(() => {
this.treeDataProvider.refresh();
});
// displays query text in a read-only document
vscode.workspace.registerTextDocumentContentProvider('codeql', {
provideTextDocumentContent(uri: vscode.Uri): vscode.ProviderResult<string> {
const params = new URLSearchParams(uri.query)
return (
JSON.parse(params.get('isQuickEval') || '') ? SHOW_QUERY_TEXT_QUICK_EVAL_MSG : SHOW_QUERY_TEXT_MSG
) + params.get('queryText');
}
});
}
addQuery(info: QueryWithResults): CompletedQuery {

View File

@@ -14,7 +14,7 @@ export class CompletedQuery implements QueryWithResults {
readonly query: QueryInfo;
readonly result: messages.EvaluationResult;
readonly database: DatabaseInfo;
readonly logFileLocation?: string
readonly logFileLocation?: string;
options: QueryHistoryItemOptions;
dispose: () => void;

View File

@@ -1,5 +1,4 @@
import * as fs from 'fs-extra';
import * as glob from 'glob-promise';
import * as yaml from 'js-yaml';
import * as path from 'path';
import { ExtensionContext, window as Window, workspace, Uri } from 'vscode';
@@ -18,33 +17,6 @@ export function isQuickQueryPath(queryPath: string): boolean {
return path.basename(queryPath) === QUICK_QUERY_QUERY_NAME;
}
async function getQlPackFor(cliServer: CodeQLCliServer, dbschemePath: string): Promise<string> {
const qlpacks = await cliServer.resolveQlpacks(helpers.getOnDiskWorkspaceFolders());
const packs: { packDir: string | undefined; packName: string }[] =
Object.entries(qlpacks).map(([packName, dirs]) => {
if (dirs.length < 1) {
logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has no directories`);
return { packName, packDir: undefined };
}
if (dirs.length > 1) {
logger.log(`In getQlPackFor ${dbschemePath}, qlpack ${packName} has more than one directory; arbitrarily choosing the first`);
}
return {
packName,
packDir: dirs[0]
}
});
for (const { packDir, packName } of packs) {
if (packDir !== undefined) {
const qlpack = yaml.safeLoad(await fs.readFile(path.join(packDir, 'qlpack.yml'), 'utf8'));
if (qlpack.dbscheme !== undefined && path.basename(qlpack.dbscheme) === path.basename(dbschemePath)) {
return packName;
}
}
}
throw new Error(`Could not find qlpack file for dbscheme ${dbschemePath}`);
}
/**
* `getBaseText` heuristically returns an appropriate import statement
* prelude based on the filename of the dbscheme file given. TODO: add
@@ -70,23 +42,26 @@ function getQuickQueriesDir(ctx: ExtensionContext): string {
return queriesPath;
}
/**
* Show a buffer the user can enter a simple query into.
*/
export async function displayQuickQuery(ctx: ExtensionContext, cliServer: CodeQLCliServer, databaseUI: DatabaseUI) {
try {
function updateQuickQueryDir(queriesDir: string, index: number, len: number) {
workspace.updateWorkspaceFolders(
index,
len,
{ uri: Uri.file(queriesDir), name: QUICK_QUERY_WORKSPACE_FOLDER_NAME }
);
}
try {
const workspaceFolders = workspace.workspaceFolders || [];
const queriesDir = await getQuickQueriesDir(ctx);
function updateQuickQueryDir(index: number, len: number) {
workspace.updateWorkspaceFolders(
index,
len,
{ uri: Uri.file(queriesDir), name: QUICK_QUERY_WORKSPACE_FOLDER_NAME }
);
}
// If there is already a quick query open, don't clobber it, just
// show it.
const existing = workspace.textDocuments.find(doc => path.basename(doc.uri.fsPath) === QUICK_QUERY_QUERY_NAME);
@@ -107,16 +82,16 @@ export async function displayQuickQuery(ctx: ExtensionContext, cliServer: CodeQL
if (workspace.workspaceFile === undefined) {
const makeMultiRoot = await helpers.showBinaryChoiceDialog('Quick query requires multiple folders in the workspace. Reload workspace as multi-folder workspace?');
if (makeMultiRoot) {
updateQuickQueryDir(workspaceFolders.length, 0);
updateQuickQueryDir(queriesDir, workspaceFolders.length, 0);
}
return;
}
const index = workspaceFolders.findIndex(folder => folder.name === QUICK_QUERY_WORKSPACE_FOLDER_NAME)
if (index === -1)
updateQuickQueryDir(workspaceFolders.length, 0);
updateQuickQueryDir(queriesDir, workspaceFolders.length, 0);
else
updateQuickQueryDir(index, 1);
updateQuickQueryDir(queriesDir, index, 1);
// We're going to infer which qlpack to use from the current database
const dbItem = await databaseUI.getDatabaseItem();
@@ -125,19 +100,7 @@ export async function displayQuickQuery(ctx: ExtensionContext, cliServer: CodeQL
}
const datasetFolder = await dbItem.getDatasetFolder(cliServer);
const dbschemes = await glob(path.join(datasetFolder, '*.dbscheme'))
if (dbschemes.length < 1) {
throw new Error(`Can't find dbscheme for current database in ${datasetFolder}`);
}
dbschemes.sort();
const dbscheme = dbschemes[0];
if (dbschemes.length > 1) {
Window.showErrorMessage(`Found multiple dbschemes in ${datasetFolder} during quick query; arbitrarily choosing the first, ${dbscheme}, to decide what library to use.`);
}
const qlpack = await getQlPackFor(cliServer, dbscheme);
const { qlpack, dbscheme } = await helpers.resolveDatasetFolder(cliServer, datasetFolder);
const quickQueryQlpackYaml: any = {
name: "quick-query",
version: "1.0.0",

View File

@@ -7,6 +7,7 @@ import * as vscode from 'vscode';
import { ErrorCodes, ResponseError } from 'vscode-languageclient';
import * as cli from './cli';
import * as config from './config';
import { DatabaseItem, getUpgradesDirectories } from './databases';
import * as helpers from './helpers';
import { DatabaseInfo, QueryMetadata, ResultsPaths } from './interface-types';
@@ -121,6 +122,9 @@ export class QueryInfo {
): Promise<messages.CompilationMessage[]> {
let compiled: messages.CheckQueryResult | undefined;
try {
const target = this.quickEvalPosition ? {
quickEval: { quickEvalPos: this.quickEvalPosition }
} : { query: {} };
const params: messages.CompileQueryParams = {
compilationOptions: {
computeNoLocationUrls: true,
@@ -136,11 +140,7 @@ export class QueryInfo {
},
queryToCheck: this.program,
resultPath: this.compiledQueryPath,
target: this.quickEvalPosition ? {
quickEval: { quickEvalPos: this.quickEvalPosition }
} : {
query: {}
}
target,
};
compiled = await helpers.withProgress({
@@ -239,8 +239,10 @@ async function getSelectedPosition(editor: vscode.TextEditor): Promise<messages.
// Convert from 0-based to 1-based line and column numbers.
return {
fileName: await convertToQlPath(editor.document.fileName),
line: pos.line + 1, column: pos.character + 1,
endLine: posEnd.line + 1, endColumn: posEnd.character + 1
line: pos.line + 1,
column: pos.character + 1,
endLine: posEnd.line + 1,
endColumn: posEnd.character + 1
};
}
@@ -259,7 +261,7 @@ async function checkDbschemeCompatibility(
if (query.dbItem.contents !== undefined && query.dbItem.contents.dbSchemeUri !== undefined) {
const { scripts, finalDbscheme } = await cliServer.resolveUpgrades(query.dbItem.contents.dbSchemeUri.fsPath, searchPath);
const hash = async function (filename: string): Promise<string> {
const hash = async function(filename: string): Promise<string> {
return crypto.createHash('sha256').update(await fs.readFile(filename)).digest('hex');
}
@@ -294,19 +296,39 @@ async function checkDbschemeCompatibility(
}
}
/** Prompts the user to save `document` if it has unsaved changes. */
async function promptUserToSaveChanges(document: vscode.TextDocument): Promise<void> {
/**
* Prompts the user to save `document` if it has unsaved changes.
* Returns true if we should save changes.
*/
async function promptUserToSaveChanges(document: vscode.TextDocument): Promise<boolean> {
if (document.isDirty) {
// TODO: add 'always save' button which records preference in configuration
if (await helpers.showBinaryChoiceDialog('Query file has unsaved changes. Save now?')) {
await document.save();
if (config.AUTOSAVE_SETTING.getValue()) {
return true;
}
else {
const yesItem = { title: 'Yes', isCloseAffordance: false };
const alwaysItem = { title: 'Always Save', isCloseAffordance: false };
const noItem = { title: 'No', isCloseAffordance: true }
const message = 'Query file has unsaved changes. Save now?';
const chosenItem = await vscode.window.showInformationMessage(message, { modal: true }, yesItem, alwaysItem, noItem);
if (chosenItem === alwaysItem) {
await config.AUTOSAVE_SETTING.updateValue(true, vscode.ConfigurationTarget.Workspace);
return true;
}
if (chosenItem === yesItem) {
return true;
}
}
}
return false;
}
type SelectedQuery = {
queryPath: string;
quickEvalPosition?: messages.Position;
quickEvalText?: string;
};
/**
@@ -357,10 +379,13 @@ export async function determineSelectedQuery(selectedResourceUri: vscode.Uri | u
// if the same file is open with unsaved changes in the active editor,
// then prompt the user to save it first.
if (editor !== undefined && editor.document.uri.fsPath === queryPath) {
await promptUserToSaveChanges(editor.document);
if (await promptUserToSaveChanges(editor.document)) {
editor.document.save();
}
}
let quickEvalPosition: messages.Position | undefined = undefined;
let quickEvalText: string | undefined = undefined;
if (quickEval) {
if (editor == undefined) {
throw new Error('Can\'t run quick evaluation without an active editor.');
@@ -371,9 +396,10 @@ export async function determineSelectedQuery(selectedResourceUri: vscode.Uri | u
throw new Error('The selected resource for quick evaluation should match the active editor.');
}
quickEvalPosition = await getSelectedPosition(editor);
quickEvalText = editor.document.getText(editor.selection);
}
return { queryPath, quickEvalPosition };
return { queryPath, quickEvalPosition, quickEvalText };
}
export async function compileAndRunQueryAgainstDatabase(
@@ -390,12 +416,14 @@ export async function compileAndRunQueryAgainstDatabase(
}
// Determine which query to run, based on the selection and the active editor.
const { queryPath, quickEvalPosition } = await determineSelectedQuery(selectedQueryUri, quickEval);
const { queryPath, quickEvalPosition, quickEvalText } = await determineSelectedQuery(selectedQueryUri, quickEval);
// If this is quick query, store the query text
const historyItemOptions: QueryHistoryItemOptions = {};
if (isQuickQueryPath(queryPath)) {
historyItemOptions.queryText = await fs.readFile(queryPath, 'utf8');
historyItemOptions.queryText = await fs.readFile(queryPath, 'utf8');
historyItemOptions.isQuickQuery === isQuickQueryPath(queryPath);
if (quickEval) {
historyItemOptions.queryText = quickEvalText;
}
// Get the workspace folder paths.

View File

@@ -165,7 +165,7 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
}
private discoverTests(): void {
this._tests.fire(<TestLoadStartedEvent>{ type: 'started' });
this._tests.fire({ type: 'started' } as TestLoadStartedEvent);
const testDirectories = this.qlTestDiscovery.testDirectories;
const children = testDirectories.map(
@@ -178,10 +178,10 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
children
};
this._tests.fire(<TestLoadFinishedEvent>{
this._tests.fire({
type: 'finished',
suite: children.length > 0 ? testSuite : undefined
});
} as TestLoadFinishedEvent);
}
public async run(tests: string[]): Promise<void> {
@@ -194,17 +194,16 @@ export class QLTestAdapter extends DisposableObject implements TestAdapter {
this.runningTask = this.track(new CancellationTokenSource());
this._testStates.fire(<TestRunStartedEvent>{ type: 'started', tests: tests });
const testAdapter = this;
this._testStates.fire({ type: 'started', tests: tests } as TestRunStartedEvent);
try {
await this.runTests(tests, this.runningTask.token);
}
catch (e) {
/**/
}
testAdapter._testStates.fire(<TestRunFinishedEvent>{ type: 'finished' });
testAdapter.clearTask();
this._testStates.fire({ type: 'finished' } as TestRunFinishedEvent);
this.clearTask();
}
private clearTask(): void {

View File

@@ -6,6 +6,8 @@ import { DisposableObject, UIService } from 'semmle-vscode-utils';
import { TestHub, TestController, TestAdapter, TestRunStartedEvent, TestRunFinishedEvent, TestEvent, TestSuiteEvent } from 'vscode-test-adapter-api';
import { QLTestAdapter, getExpectedFile, getActualFile } from './test-adapter';
type VSCodeTestEvent = TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent;
/**
* Test event listener. Currently unused, but left in to keep the plumbing hooked up for future use.
*/
@@ -16,7 +18,8 @@ class QLTestListener extends DisposableObject {
this.push(adapter.testStates(this.onTestStatesEvent, this));
}
private onTestStatesEvent(_e: TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent): void {
private onTestStatesEvent(_e: VSCodeTestEvent): void {
/**/
}
}

View File

@@ -1,8 +1,13 @@
module.exports = {
ecmaFeatures: {
jsx: true,
},
env: {
browser: true
},
extends: [
"plugin:react/recommended"
],
settings: {
react: {
version: 'detect'
}
}
}

View File

@@ -69,6 +69,14 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
});
}
renderNoResults(): JSX.Element {
if (this.props.nonemptyRawResults) {
return <span>No Alerts. See <a href='#' onClick={this.props.showRawResults}>raw results</a>.</span>;
} else {
return <span>No Alerts</span>;
}
}
render(): JSX.Element {
const { databaseUri, resultSet } = this.props;
@@ -156,18 +164,19 @@ export class PathTable extends React.Component<PathTableProps, PathTableState> {
return (e) => this.toggle(e, indices);
};
const noResults = <span>No Results</span>; // TODO: Maybe make this look nicer
if (resultSet.sarif.runs.length === 0 ||
resultSet.sarif.runs[0].results === undefined ||
resultSet.sarif.runs[0].results.length === 0) {
return this.renderNoResults();
}
let expansionIndex = 0;
if (resultSet.sarif.runs.length === 0) return noResults;
if (resultSet.sarif.runs[0].results === undefined) return noResults;
resultSet.sarif.runs[0].results.forEach((result, resultIndex) => {
const text = result.message.text || '[no text]';
const msg: JSX.Element[] =
result.relatedLocations === undefined ?
[<span>{text}</span>] :
[<span key="0">{text}</span>] :
renderRelatedLocations(text, result.relatedLocations);
const currentResultExpanded = this.state.expanded[expansionIndex];

View File

@@ -1,6 +1,7 @@
import * as React from "react";
import { renderLocation, ResultTableProps, zebraStripe, className, nextSortDirection } from "./result-table-utils";
import { RawTableResultSet, ResultValue, vscode } from "./results";
import { RawTableResultSet, vscode } from "./results";
import { ResultValue } from "../adapt";
import { SortDirection, RAW_RESULTS_LIMIT, RawResultsSortState } from "../interface-types";
export type RawTableProps = ResultTableProps & {

View File

@@ -10,6 +10,18 @@ export interface ResultTableProps {
metadata?: QueryMetadata;
resultsPath: string | undefined;
sortState?: RawResultsSortState;
/**
* Holds if there are any raw results. When that is the case, we
* want to direct users to pay attention to raw results if
* interpreted results are empty.
*/
nonemptyRawResults: boolean;
/**
* Callback to show raw results.
*/
showRawResults: () => void;
}
export const className = 'vscode-codeql__result-table';

View File

@@ -123,6 +123,7 @@ export class ResultTables
const resultSets = this.getResultSets();
const resultSet = resultSets.find(resultSet => resultSet.schema.name == selectedTable);
const nonemptyRawResults = resultSets.some(resultSet => resultSet.t == 'RawResultSet' && resultSet.rows.length > 0);
const numberOfResults = resultSet && renderResultCountString(resultSet);
return <div>
@@ -149,7 +150,9 @@ export class ResultTables
<ResultTable key={resultSet.schema.name} resultSet={resultSet}
databaseUri={this.props.database.databaseUri}
resultsPath={this.props.resultsPath}
sortState={this.props.sortStates.get(resultSet.schema.name)} />
sortState={this.props.sortStates.get(resultSet.schema.name)}
nonemptyRawResults={nonemptyRawResults}
showRawResults={() => { this.setState({ selectedTable: SELECT_TABLE_NAME }) }} />
}
</div>;
}

View File

@@ -1,11 +1,12 @@
import * as React from 'react';
import * as Rdom from 'react-dom';
import * as bqrs from 'semmle-bqrs';
import { ElementBase, LocationValue, PrimitiveColumnValue, PrimitiveTypeKind, ResultSetSchema, tryGetResolvableLocation } from 'semmle-bqrs';
import { ElementBase, PrimitiveColumnValue, PrimitiveTypeKind, ResultSetSchema, tryGetResolvableLocation } from 'semmle-bqrs';
import { assertNever } from '../helpers-pure';
import { DatabaseInfo, FromResultsViewMsg, Interpretation, IntoResultsViewMsg, SortedResultSetInfo, RawResultsSortState, NavigatePathMsg, QueryMetadata, ResultsPaths } from '../interface-types';
import { EventHandlers as EventHandlerList } from './event-handler-list';
import { ResultTables } from './result-tables';
import { RawResultSet, ResultValue, ResultRow } from '../adapt';
/**
* results.tsx
@@ -23,19 +24,6 @@ interface VsCodeApi {
declare const acquireVsCodeApi: () => VsCodeApi;
export const vscode = acquireVsCodeApi();
export interface ResultElement {
label: string;
location?: LocationValue;
}
export interface ResultUri {
uri: string;
}
export type ResultValue = ResultElement | ResultUri | string;
export type ResultRow = ResultValue[];
export type RawTableResultSet = { t: 'RawResultSet' } & RawResultSet;
export type PathTableResultSet = { t: 'SarifResultSet'; readonly schema: ResultSetSchema; name: string } & Interpretation;
@@ -43,11 +31,6 @@ export type ResultSet =
| RawTableResultSet
| PathTableResultSet;
export interface RawResultSet {
readonly schema: ResultSetSchema;
readonly rows: readonly ResultRow[];
}
async function* getChunkIterator(response: Response): AsyncIterableIterator<Uint8Array> {
if (!response.ok) {
throw new Error(`Failed to load results: (${response.status}) ${response.statusText}`);
@@ -62,9 +45,7 @@ async function* getChunkIterator(response: Response): AsyncIterableIterator<Uint
}
}
function translatePrimitiveValue(value: PrimitiveColumnValue, type: PrimitiveTypeKind):
ResultValue {
function translatePrimitiveValue(value: PrimitiveColumnValue, type: PrimitiveTypeKind): ResultValue {
switch (type) {
case 'i':
case 'f':
@@ -127,6 +108,7 @@ async function parseResultSets(response: Response): Promise<readonly ResultSet[]
interface ResultsInfo {
resultsPath: string;
resultSets: ResultSet[] | undefined;
origResultsPaths: ResultsPaths;
database: DatabaseInfo;
interpretation: Interpretation | undefined;
@@ -187,6 +169,7 @@ class App extends React.Component<{}, ResultsViewState> {
case 'setState':
this.updateStateWithNewResultsInfo({
resultsPath: msg.resultsPath,
resultSets: msg.resultSets?.map(x => ({ t: 'RawResultSet', ...x })),
origResultsPaths: msg.origResultsPaths,
sortedResultsMap: new Map(Object.entries(msg.sortedResultsMap)),
database: msg.database,
@@ -247,8 +230,9 @@ class App extends React.Component<{}, ResultsViewState> {
let results: Results | null = null;
let statusText = '';
try {
const resultSets = resultsInfo.resultSets || await this.getResultSets(resultsInfo);
results = {
resultSets: await this.getResultSets(resultsInfo),
resultSets,
database: resultsInfo.database,
sortStates: this.getSortStates(resultsInfo)
};

View File

@@ -188,8 +188,12 @@ describe("Release version ordering", () => {
});
describe('Launcher path', () => {
const pathToCmd = `abc${path.sep}codeql.cmd`;
const pathToExe = `abc${path.sep}codeql.exe`;
let sandbox: sinon.SinonSandbox;
let warnSpy: sinon.SinonSpy;
let errorSpy: sinon.SinonSpy;
let logSpy: sinon.SinonSpy;
let fsSpy: sinon.SinonSpy;
let platformSpy: sinon.SinonSpy;
@@ -209,37 +213,37 @@ describe('Launcher path', () => {
it('should not warn with proper launcher name', async () => {
launcherThatExists = 'codeql.exe';
const result = await getExecutableFromDirectory('abc');
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
expect(fsSpy).to.have.been.calledWith(pathToExe);
// correct launcher has been found, so alternate one not looked for
expect(fsSpy).not.to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
expect(fsSpy).not.to.have.been.calledWith(pathToCmd);
// no warning message
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
// No log message
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
expect(result).to.equal(`abc${path.sep}codeql.exe`);
expect(result).to.equal(pathToExe);
});
it('should warn when using a hard-coded deprecated launcher name', async () => {
launcherThatExists = 'codeql.cmd';
path.sep;
const result = await getExecutableFromDirectory('abc');
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
expect(fsSpy).to.have.been.calledWith(pathToExe);
expect(fsSpy).to.have.been.calledWith(pathToCmd);
// Should have opened a warning message
expect(warnSpy).to.have.been.calledWith(sinon.match.string);
// No log message
expect(logSpy).not.to.have.been.calledWith(sinon.match.string);
expect(result).to.equal(`abc${path.sep}codeql.cmd`);
expect(result).to.equal(pathToCmd);
});
it('should avoid warn when no launcher is found', async () => {
launcherThatExists = 'xxx';
const result = await getExecutableFromDirectory('abc', false);
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
expect(fsSpy).to.have.been.calledWith(pathToExe);
expect(fsSpy).to.have.been.calledWith(pathToCmd);
// no warning message
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
@@ -251,8 +255,8 @@ describe('Launcher path', () => {
it('should warn when no launcher is found', async () => {
launcherThatExists = 'xxx';
const result = await getExecutableFromDirectory('abc', true);
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.exe`);
expect(fsSpy).to.have.been.calledWith(`abc${path.sep}codeql.cmd`);
expect(fsSpy).to.have.been.calledWith(pathToExe);
expect(fsSpy).to.have.been.calledWith(pathToCmd);
// no warning message
expect(warnSpy).not.to.have.been.calledWith(sinon.match.string);
@@ -261,9 +265,46 @@ describe('Launcher path', () => {
expect(result).to.equal(undefined);
});
it('should not warn when deprecated launcher is used, but no new launcher is available', async () => {
const manager = new (createModule().DistributionManager)(undefined as any, { customCodeQlPath: pathToCmd } as any, undefined as any);
launcherThatExists = 'codeql.cmd';
const result = await manager.getCodeQlPathWithoutVersionCheck();
expect(result).to.equal(pathToCmd);
// no warning or error message
expect(warnSpy).to.have.callCount(0);
expect(errorSpy).to.have.callCount(0);
});
it('should warn when deprecated launcher is used, and new launcher is available', async () => {
const manager = new (createModule().DistributionManager)(undefined as any, { customCodeQlPath: pathToCmd } as any, undefined as any);
launcherThatExists = ''; // pretend both launchers exist
const result = await manager.getCodeQlPathWithoutVersionCheck();
expect(result).to.equal(pathToCmd);
// has warning message
expect(warnSpy).to.have.callCount(1);
expect(errorSpy).to.have.callCount(0);
});
it('should warn when launcher path is incorrect', async () => {
const manager = new (createModule().DistributionManager)(undefined as any, { customCodeQlPath: pathToCmd } as any, undefined as any);
launcherThatExists = 'xxx'; // pretend neither launcher exists
const result = await manager.getCodeQlPathWithoutVersionCheck();
expect(result).to.equal(undefined);
// no error message
expect(warnSpy).to.have.callCount(0);
expect(errorSpy).to.have.callCount(1);
});
function createModule() {
sandbox = sinon.createSandbox();
warnSpy = sandbox.spy();
errorSpy = sandbox.spy();
logSpy = sandbox.spy();
// pretend that only the .cmd file exists
fsSpy = sandbox.stub().callsFake(arg => arg.endsWith(launcherThatExists) ? true : false);
@@ -271,7 +312,8 @@ describe('Launcher path', () => {
return proxyquire('../../distribution', {
'./helpers': {
showAndLogWarningMessage: warnSpy
showAndLogWarningMessage: warnSpy,
showAndLogErrorMessage: errorSpy
},
'./logging': {
'logger': {

View File

@@ -422,6 +422,11 @@ repository:
keyword: 'true'
name: constant.language.boolean.true.ql
unique:
match:
keyword: 'unique'
name: keyword.aggregate.unique.ql
where:
match:
keyword: 'where'
@@ -478,6 +483,8 @@ repository:
- include: '#then'
- include: '#this'
- include: '#true'
# `unique` is not really a keyword, but we'll highlight it as if it is.
- include: '#unique'
- include: '#where'
# A keyword that can be the first token of a predicate declaration.

View File

@@ -3,6 +3,6 @@ module.exports = {
mocha: true
},
parserOptions: {
project: 'tsconfig.json',
project: './test/tsconfig.json',
},
}

View File

@@ -0,0 +1,8 @@
{
"include": [
"**/*.ts"
],
"compilerOptions": {
"noEmit": true
}
}