From 26e619ba71fb77295d5c6d1a4db2a943c6cd6bb7 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 8 Jan 2024 16:40:45 +0100 Subject: [PATCH 01/77] Enable no-fallthrough ESLint rule --- extensions/ql-vscode/.eslintrc.js | 1 - extensions/ql-vscode/src/codeql-cli/cli.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js index 29eae845e..801df9054 100644 --- a/extensions/ql-vscode/.eslintrc.js +++ b/extensions/ql-vscode/.eslintrc.js @@ -57,7 +57,6 @@ const baseConfig = { "func-style": "off", "i18n-text/no-en": "off", "no-invalid-this": "off", - "no-fallthrough": "off", "no-console": "off", "no-shadow": "off", "github/array-foreach": "off", diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 30e306152..9c493587d 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -1532,7 +1532,7 @@ export class CodeQLCliServer implements Disposable { const distribution = await this.distributionProvider.getDistribution(); switch (distribution.kind) { case FindDistributionResultKind.CompatibleDistribution: - + // eslint-disable-next-line no-fallthrough -- Intentional fallthrough case FindDistributionResultKind.IncompatibleDistribution: return distribution.version; From 188bc53761731e5de9820587920fd6d75864b2ac Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 8 Jan 2024 16:44:41 +0100 Subject: [PATCH 02/77] Enable no-useless-escape ESLint rule These fixes maintain the current functionality and do not make any changes to the matching of the regular expressions. --- extensions/ql-vscode/.eslintrc.js | 1 - extensions/ql-vscode/src/common/helpers-pure.ts | 4 ++-- extensions/ql-vscode/src/common/sarif-utils.ts | 2 +- extensions/ql-vscode/src/extension.ts | 4 ++-- .../ql-vscode/src/language-support/language-support.ts | 8 ++++---- .../ql-vscode/src/variant-analysis/export-results.ts | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js index 801df9054..eb420e3a8 100644 --- a/extensions/ql-vscode/.eslintrc.js +++ b/extensions/ql-vscode/.eslintrc.js @@ -47,7 +47,6 @@ const baseConfig = { "@typescript-eslint/no-throw-literal": "error", "@typescript-eslint/consistent-type-imports": "error", "import/consistent-type-specifier-style": ["error", "prefer-top-level"], - "no-useless-escape": 0, camelcase: "off", curly: ["error", "all"], "escompat/no-regexp-lookbehind": "off", diff --git a/extensions/ql-vscode/src/common/helpers-pure.ts b/extensions/ql-vscode/src/common/helpers-pure.ts index 319faef36..1c204eb3e 100644 --- a/extensions/ql-vscode/src/common/helpers-pure.ts +++ b/extensions/ql-vscode/src/common/helpers-pure.ts @@ -40,13 +40,13 @@ export const asyncFilter = async function ( * - `owner` is made up of alphanumeric characters, hyphens, underscores, or periods * - `repo` is made up of alphanumeric characters, hyphens, underscores, or periods */ -export const REPO_REGEX = /^[a-zA-Z0-9-_\.]+\/[a-zA-Z0-9-_\.]+$/; +export const REPO_REGEX = /^[a-zA-Z0-9-_.]+\/[a-zA-Z0-9-_.]+$/; /** * This regex matches GiHub organization and user strings. These are made up for alphanumeric * characters, hyphens, underscores or periods. */ -export const OWNER_REGEX = /^[a-zA-Z0-9-_\.]+$/; +export const OWNER_REGEX = /^[a-zA-Z0-9-_.]+$/; export function getErrorMessage(e: unknown): string { if (e instanceof RedactableError) { diff --git a/extensions/ql-vscode/src/common/sarif-utils.ts b/extensions/ql-vscode/src/common/sarif-utils.ts index d69df80ba..61e3d3a38 100644 --- a/extensions/ql-vscode/src/common/sarif-utils.ts +++ b/extensions/ql-vscode/src/common/sarif-utils.ts @@ -47,7 +47,7 @@ export function parseSarifPlainTextMessage( // Technically we could have any uri in the target but we don't output that yet. // The possibility of escaping outside the link is not mentioned in the sarif spec but we always output sartif this way. const linkRegex = - /(?<=(?([^\\\]\[]|\\\\|\\\]|\\\[)*)\]\((?[0-9]+)\)/g; + /(?<=(?([^\\\][]|\\\\|\\\]|\\\[)*)\]\((?[0-9]+)\)/g; let result: RegExpExecArray | null; let curIndex = 0; while ((result = linkRegex.exec(message)) !== null) { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index e646f6fa6..8a2ce6a6c 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -607,11 +607,11 @@ async function getDistributionDisplayingDistributionWarnings( case DistributionKind.ExtensionManaged: return 'Please update the CodeQL CLI by running the "CodeQL: Check for CLI Updates" command.'; case DistributionKind.CustomPathConfig: - return `Please update the \"CodeQL CLI Executable Path\" setting to point to a CLI in the version range ${codeQlVersionRange}.`; + return `Please update the "CodeQL CLI Executable Path" setting to point to a CLI in the version range ${codeQlVersionRange}.`; case DistributionKind.PathEnvironmentVariable: return ( `Please update the CodeQL CLI on your PATH to a version compatible with ${codeQlVersionRange}, or ` + - `set the \"CodeQL CLI Executable Path\" setting to the path of a CLI version compatible with ${codeQlVersionRange}.` + `set the "CodeQL CLI Executable Path" setting to the path of a CLI version compatible with ${codeQlVersionRange}.` ); } })(); diff --git a/extensions/ql-vscode/src/language-support/language-support.ts b/extensions/ql-vscode/src/language-support/language-support.ts index 6d2a9a973..e73bab07f 100644 --- a/extensions/ql-vscode/src/language-support/language-support.ts +++ b/extensions/ql-vscode/src/language-support/language-support.ts @@ -18,7 +18,7 @@ export function install() { langConfig.wordPattern = new RegExp(langConfig.wordPattern); langConfig.onEnterRules = onEnterRules; langConfig.indentationRules = { - decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[\}\]].*$/, + decreaseIndentPattern: /^((?!.*?\/\*).*\*\/)?\s*[}\]].*$/, increaseIndentPattern: /^((?!\/\/).)*(\{[^}"'`]*|\([^)"'`]*|\[[^\]"'`]*)$/, }; delete langConfig.autoClosingPairs; @@ -31,18 +31,18 @@ export function install() { const onEnterRules: OnEnterRule[] = [ { // e.g. /** | */ - beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + beforeText: /^\s*\/\*\*(?!\/)([^*]|\*(?!\/))*$/, afterText: /^\s*\*\/$/, action: { indentAction: IndentAction.IndentOutdent, appendText: " * " }, }, { // e.g. /** ...| - beforeText: /^\s*\/\*\*(?!\/)([^\*]|\*(?!\/))*$/, + beforeText: /^\s*\/\*\*(?!\/)([^*]|\*(?!\/))*$/, action: { indentAction: IndentAction.None, appendText: " * " }, }, { // e.g. * ...| - beforeText: /^(\t|[ ])*[ ]\*([ ]([^\*]|\*(?!\/))*)?$/, + beforeText: /^(\t|[ ])*[ ]\*([ ]([^*]|\*(?!\/))*)?$/, // oneLineAboveText: /^(\s*(\/\*\*|\*)).*/, action: { indentAction: IndentAction.None, appendText: "* " }, }, diff --git a/extensions/ql-vscode/src/variant-analysis/export-results.ts b/extensions/ql-vscode/src/variant-analysis/export-results.ts index c1da3d75b..c36c0fb33 100644 --- a/extensions/ql-vscode/src/variant-analysis/export-results.ts +++ b/extensions/ql-vscode/src/variant-analysis/export-results.ts @@ -356,7 +356,7 @@ async function exportToLocalMarkdown( // This needs to use .then to ensure we aren't keeping the progress notification open. We shouldn't await the // "Open exported results" button click. void showInformationMessageWithAction( - `Variant analysis results exported to \"${exportedResultsPath}\".`, + `Variant analysis results exported to "${exportedResultsPath}".`, "Open exported results", ).then(async (shouldOpenExportedResults) => { if (!shouldOpenExportedResults) { From 58ea742e850572e34c46e7d41fd8d4dd996e52e9 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 8 Jan 2024 16:47:28 +0100 Subject: [PATCH 03/77] Enable func-style ESLint rule --- extensions/ql-vscode/.eslintrc.js | 1 - extensions/ql-vscode/src/common/helpers-pure.ts | 4 ++-- extensions/ql-vscode/test/matchers/toEqualPath.ts | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js index eb420e3a8..9feabd103 100644 --- a/extensions/ql-vscode/.eslintrc.js +++ b/extensions/ql-vscode/.eslintrc.js @@ -53,7 +53,6 @@ const baseConfig = { "etc/no-implicit-any-catch": "error", "filenames/match-regex": "off", "filenames/match-regexp": "off", - "func-style": "off", "i18n-text/no-en": "off", "no-invalid-this": "off", "no-console": "off", diff --git a/extensions/ql-vscode/src/common/helpers-pure.ts b/extensions/ql-vscode/src/common/helpers-pure.ts index 1c204eb3e..40f659e9d 100644 --- a/extensions/ql-vscode/src/common/helpers-pure.ts +++ b/extensions/ql-vscode/src/common/helpers-pure.ts @@ -27,13 +27,13 @@ export function assertNever(value: never): never { /** * Use to perform array filters where the predicate is asynchronous. */ -export const asyncFilter = async function ( +export async function asyncFilter( arr: T[], predicate: (arg0: T) => Promise, ) { const results = await Promise.all(arr.map(predicate)); return arr.filter((_, index) => results[index]); -}; +} /** * This regex matches strings of the form `owner/repo` where: diff --git a/extensions/ql-vscode/test/matchers/toEqualPath.ts b/extensions/ql-vscode/test/matchers/toEqualPath.ts index de82ddbe4..da048a84c 100644 --- a/extensions/ql-vscode/test/matchers/toEqualPath.ts +++ b/extensions/ql-vscode/test/matchers/toEqualPath.ts @@ -2,6 +2,7 @@ import { expect } from "@jest/globals"; import type { MatcherFunction } from "expect"; import { pathsEqual } from "../../src/common/files"; +// eslint-disable-next-line func-style -- We need to set the type of this function const toEqualPath: MatcherFunction<[expectedPath: unknown]> = function ( actual, expectedPath, From 01e7b3fc0e7013dd1d10dc88e77f5a3784ca7d68 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 8 Jan 2024 16:52:03 +0100 Subject: [PATCH 04/77] Remove camelcase ESLint rule override --- extensions/ql-vscode/.eslintrc.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js index 9feabd103..5837d4c0a 100644 --- a/extensions/ql-vscode/.eslintrc.js +++ b/extensions/ql-vscode/.eslintrc.js @@ -47,7 +47,6 @@ const baseConfig = { "@typescript-eslint/no-throw-literal": "error", "@typescript-eslint/consistent-type-imports": "error", "import/consistent-type-specifier-style": ["error", "prefer-top-level"], - camelcase: "off", curly: ["error", "all"], "escompat/no-regexp-lookbehind": "off", "etc/no-implicit-any-catch": "error", From c7c79175d2b0f3ed5971e8d74e34992edebea133 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Mon, 8 Jan 2024 16:54:07 +0100 Subject: [PATCH 05/77] Remove non-existent filenames/match-regexp ESLint rule --- extensions/ql-vscode/.eslintrc.js | 1 - 1 file changed, 1 deletion(-) diff --git a/extensions/ql-vscode/.eslintrc.js b/extensions/ql-vscode/.eslintrc.js index 5837d4c0a..e75ac123e 100644 --- a/extensions/ql-vscode/.eslintrc.js +++ b/extensions/ql-vscode/.eslintrc.js @@ -51,7 +51,6 @@ const baseConfig = { "escompat/no-regexp-lookbehind": "off", "etc/no-implicit-any-catch": "error", "filenames/match-regex": "off", - "filenames/match-regexp": "off", "i18n-text/no-en": "off", "no-invalid-this": "off", "no-console": "off", From 1b737678ba2b30da51247036560476d82eee307a Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 9 Jan 2024 14:09:50 +0000 Subject: [PATCH 06/77] Update broken badge in README (#3213) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 94a578bd3..f5dc1b9e8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ The extension is released. You can download it from the [Visual Studio Marketpla To see what has changed in the last few versions of the extension, see the [Changelog](https://github.com/github/vscode-codeql/blob/main/extensions/ql-vscode/CHANGELOG.md). -[![CI status badge](https://github.com/github/vscode-codeql/workflows/Build%20Extension/badge.svg)](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amaster) +[![CI status badge](https://github.com/github/vscode-codeql/workflows/Build%20Extension/badge.svg)](https://github.com/github/vscode-codeql/actions?query=workflow%3A%22Build+Extension%22+branch%3Amain) [![VS Marketplace badge](https://vsmarketplacebadges.dev/version/github.vscode-codeql.svg)](https://marketplace.visualstudio.com/items?itemName=github.vscode-codeql) ## Features From ac745a69556b7cb1f85cb13bb5ae3def876385c2 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 10 Jan 2024 15:54:17 +0100 Subject: [PATCH 07/77] Move Ruby access paths functions --- .../languages/ruby/access-paths.ts | 41 ++++++++++++++++ .../src/model-editor/languages/ruby/index.ts | 49 +++---------------- 2 files changed, 48 insertions(+), 42 deletions(-) create mode 100644 extensions/ql-vscode/src/model-editor/languages/ruby/access-paths.ts diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/access-paths.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/access-paths.ts new file mode 100644 index 000000000..f8942cee1 --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/access-paths.ts @@ -0,0 +1,41 @@ +export function parseRubyMethodFromPath(path: string): string { + const match = path.match(/Method\[([^\]]+)].*/); + if (match) { + return match[1]; + } else { + return ""; + } +} + +export function parseRubyAccessPath(path: string): { + methodName: string; + path: string; +} { + const match = path.match(/Method\[([^\]]+)]\.(.*)/); + if (match) { + return { methodName: match[1], path: match[2] }; + } else { + return { methodName: "", path: "" }; + } +} + +export function rubyMethodSignature(typeName: string, methodName: string) { + return `${typeName}#${methodName}`; +} + +export function rubyMethodPath(methodName: string) { + if (methodName === "") { + return ""; + } + + return `Method[${methodName}]`; +} + +export function rubyPath(methodName: string, path: string) { + const methodPath = rubyMethodPath(methodName); + if (methodPath === "") { + return path; + } + + return `${methodPath}.${path}`; +} diff --git a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts index a4f33c13d..45c3f8a57 100644 --- a/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts +++ b/extensions/ql-vscode/src/model-editor/languages/ruby/index.ts @@ -4,48 +4,13 @@ import { Mode } from "../../shared/mode"; import { parseGenerateModelResults } from "./generate"; import type { MethodArgument } from "../../method"; import { getArgumentsList } from "../../method"; - -function parseRubyMethodFromPath(path: string): string { - const match = path.match(/Method\[([^\]]+)].*/); - if (match) { - return match[1]; - } else { - return ""; - } -} - -function parseRubyAccessPath(path: string): { - methodName: string; - path: string; -} { - const match = path.match(/Method\[([^\]]+)]\.(.*)/); - if (match) { - return { methodName: match[1], path: match[2] }; - } else { - return { methodName: "", path: "" }; - } -} - -function rubyMethodSignature(typeName: string, methodName: string) { - return `${typeName}#${methodName}`; -} - -function rubyMethodPath(methodName: string) { - if (methodName === "") { - return ""; - } - - return `Method[${methodName}]`; -} - -function rubyPath(methodName: string, path: string) { - const methodPath = rubyMethodPath(methodName); - if (methodPath === "") { - return path; - } - - return `${methodPath}.${path}`; -} +import { + parseRubyAccessPath, + parseRubyMethodFromPath, + rubyMethodPath, + rubyMethodSignature, + rubyPath, +} from "./access-paths"; export const ruby: ModelsAsDataLanguage = { availableModes: [Mode.Framework], From ea6e148df9acf50e913c165f772b9dfd42bd8b9c Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 10 Jan 2024 16:20:13 +0100 Subject: [PATCH 08/77] Add functions for parsing and validating access paths This adds functions for parsing and validating access paths to prepare for future functionality where we're going to be parsing and validating access paths. --- .../src/model-editor/shared/access-paths.ts | 128 +++++++++ .../model-editor/shared/access-paths.test.ts | 251 ++++++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 extensions/ql-vscode/src/model-editor/shared/access-paths.ts create mode 100644 extensions/ql-vscode/test/unit-tests/model-editor/shared/access-paths.test.ts diff --git a/extensions/ql-vscode/src/model-editor/shared/access-paths.ts b/extensions/ql-vscode/src/model-editor/shared/access-paths.ts new file mode 100644 index 000000000..ba0c22006 --- /dev/null +++ b/extensions/ql-vscode/src/model-editor/shared/access-paths.ts @@ -0,0 +1,128 @@ +/** + * This file contains functions for parsing and validating access paths. + * + * This intentionally does not simply split by '.' since tokens may contain dots, + * e.g. `Field[foo.Bar.x]`. Instead, it uses some simple parsing to match valid tokens. + * + * Valid syntax was determined based on this file: + * https://github.com/github/codeql/blob/a04830b8b2d3e5f7df8e1f80f06c020b987a89a3/ruby/ql/lib/codeql/ruby/dataflow/internal/AccessPathSyntax.qll + * + * In contrast to that file, we do not use a regex for parsing to allow us to be more lenient. + * For example, we can parse partial access paths such as `Field[foo.Bar.x` without error. + */ + +/** + * A range of characters in an access path. The start position is inclusive, the end position is exclusive. + */ +type AccessPathRange = { + /** + * Zero-based index of the first character of the token. + */ + start: number; + /** + * Zero-based index of the character after the last character of the token. + */ + end: number; +}; + +/** + * A token in an access path. For example, `Argument[foo]` is a token. + */ +type AccessPartToken = { + text: string; + range: AccessPathRange; +}; + +/** + * Parses an access path into tokens. + * + * @param path The access path to parse. + * @returns An array of tokens. + */ +export function parseAccessPathTokens(path: string): AccessPartToken[] { + const parts: AccessPartToken[] = []; + + let currentPart = ""; + let currentPathStart = 0; + // Keep track of the number of brackets we can parse the path correctly when it contains + // nested brackets such as `Argument[foo[bar].test].Element`. + let bracketCounter = 0; + for (let i = 0; i < path.length; i++) { + const c = path[i]; + + if (c === "[") { + bracketCounter++; + } else if (c === "]") { + bracketCounter--; + } else if (c === "." && bracketCounter === 0) { + // A part ends when we encounter a dot that is not inside brackets. + parts.push({ + text: currentPart, + range: { + start: currentPathStart, + end: i, + }, + }); + currentPart = ""; + currentPathStart = i + 1; + continue; + } + + currentPart += c; + } + + // The last part should not be followed by a dot, so we need to add it manually. + // If the path is empty, such as for `Argument[foo].`, then this is still correct + // since the `validateAccessPath` function will check that none of the tokens are + // empty. + parts.push({ + text: currentPart, + range: { + start: currentPathStart, + end: path.length, + }, + }); + + return parts; +} + +// Regex for a single part of the access path +const tokenRegex = /^(\w+)(?:\[([^\]]*)])?$/; + +type AccessPathDiagnostic = { + range: AccessPathRange; + message: string; +}; + +/** + * Validates an access path and returns any errors. This requires that the path is a valid path + * and does not allow partial access paths. + * + * @param path The access path to validate. + * @returns An array of diagnostics for any errors in the access path. + */ +export function validateAccessPath(path: string): AccessPathDiagnostic[] { + if (path === "") { + return []; + } + + const tokens = parseAccessPathTokens(path); + + return tokens + .map((token): AccessPathDiagnostic | null => { + if (tokenRegex.test(token.text)) { + return null; + } + + let message = "Invalid access path"; + if (token.range.start === token.range.end) { + message = "Unexpected empty token"; + } + + return { + range: token.range, + message, + }; + }) + .filter((token): token is AccessPathDiagnostic => token !== null); +} diff --git a/extensions/ql-vscode/test/unit-tests/model-editor/shared/access-paths.test.ts b/extensions/ql-vscode/test/unit-tests/model-editor/shared/access-paths.test.ts new file mode 100644 index 000000000..52933670b --- /dev/null +++ b/extensions/ql-vscode/test/unit-tests/model-editor/shared/access-paths.test.ts @@ -0,0 +1,251 @@ +import { + parseAccessPathTokens, + validateAccessPath, +} from "../../../../src/model-editor/shared/access-paths"; + +describe("parseAccessPathTokens", () => { + it.each([ + { + path: "Argument[foo].Element.Field[@test]", + parts: [ + { + range: { + start: 0, + end: 13, + }, + text: "Argument[foo]", + }, + { + range: { + start: 14, + end: 21, + }, + text: "Element", + }, + { + range: { + start: 22, + end: 34, + }, + text: "Field[@test]", + }, + ], + }, + { + path: "Argument[foo].Element.Field[foo.Bar.x]", + parts: [ + { + range: { + start: 0, + end: 13, + }, + text: "Argument[foo]", + }, + { + range: { + start: 14, + end: 21, + }, + text: "Element", + }, + { + range: { + start: 22, + end: 38, + }, + text: "Field[foo.Bar.x]", + }, + ], + }, + { + path: "Argument[", + parts: [ + { + range: { + start: 0, + end: 9, + }, + text: "Argument[", + }, + ], + }, + { + path: "Argument[se", + parts: [ + { + range: { + start: 0, + end: 11, + }, + text: "Argument[se", + }, + ], + }, + { + path: "Argument[foo].Field[", + parts: [ + { + range: { + start: 0, + end: 13, + }, + text: "Argument[foo]", + }, + { + range: { + start: 14, + end: 20, + }, + text: "Field[", + }, + ], + }, + { + path: "Argument[foo].", + parts: [ + { + text: "Argument[foo]", + range: { + end: 13, + start: 0, + }, + }, + { + text: "", + range: { + end: 14, + start: 14, + }, + }, + ], + }, + { + path: "Argument[foo]..", + parts: [ + { + text: "Argument[foo]", + range: { + end: 13, + start: 0, + }, + }, + { + text: "", + range: { + end: 14, + start: 14, + }, + }, + { + text: "", + range: { + end: 15, + start: 15, + }, + }, + ], + }, + { + path: "Argument[foo[bar].test].Element.", + parts: [ + { + range: { + start: 0, + end: 23, + }, + text: "Argument[foo[bar].test]", + }, + { + range: { + start: 24, + end: 31, + }, + text: "Element", + }, + { + range: { + start: 32, + end: 32, + }, + text: "", + }, + ], + }, + ])(`parses correctly for $path`, ({ path, parts }) => { + expect(parseAccessPathTokens(path)).toEqual(parts); + }); +}); + +describe("validateAccessPath", () => { + it.each([ + { + path: "Argument[foo].Element.Field[@test]", + diagnostics: [], + }, + { + path: "Argument[foo].Element.Field[foo.Bar.x]", + diagnostics: [], + }, + { + path: "Argument[", + diagnostics: [ + { + message: "Invalid access path", + range: { + start: 0, + end: 9, + }, + }, + ], + }, + { + path: "Argument[se", + diagnostics: [ + { + message: "Invalid access path", + range: { + start: 0, + end: 11, + }, + }, + ], + }, + { + path: "Argument[foo].Field[", + diagnostics: [ + { + message: "Invalid access path", + range: { + start: 14, + end: 20, + }, + }, + ], + }, + { + path: "Argument[foo].", + diagnostics: [ + { message: "Unexpected empty token", range: { start: 14, end: 14 } }, + ], + }, + { + path: "Argument[foo]..", + diagnostics: [ + { message: "Unexpected empty token", range: { start: 14, end: 14 } }, + { message: "Unexpected empty token", range: { start: 15, end: 15 } }, + ], + }, + { + path: "Argument[foo[bar].test].Element.", + diagnostics: [ + { message: "Invalid access path", range: { start: 0, end: 23 } }, + { message: "Unexpected empty token", range: { start: 32, end: 32 } }, + ], + }, + ])( + `validates $path correctly with $diagnostics.length errors`, + ({ path, diagnostics }) => { + expect(validateAccessPath(path)).toEqual(diagnostics); + }, + ); +}); From 77f84c6ca9fcfc03b7c0c0be001c46c3882a9ced Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Thu, 11 Jan 2024 09:41:44 +0000 Subject: [PATCH 09/77] Improve user experience when no database is selected (#3214) --- extensions/ql-vscode/CHANGELOG.md | 1 + .../src/databases/local-databases-ui.ts | 137 +++++++++++++++++- .../src/local-queries/local-queries.ts | 16 +- 3 files changed, 138 insertions(+), 16 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 5f3260f5c..9ff1d459d 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] +- If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) - Add a prompt for downloading a GitHub database when opening a GitHub repository. [#3138](https://github.com/github/vscode-codeql/pull/3138) - Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125) - Add comparison of alerts when comparing query results. This allows viewing path explanations for differences in alerts. [#3113](https://github.com/github/vscode-codeql/pull/3113) diff --git a/extensions/ql-vscode/src/databases/local-databases-ui.ts b/extensions/ql-vscode/src/databases/local-databases-ui.ts index 608626bd8..674c1ecd3 100644 --- a/extensions/ql-vscode/src/databases/local-databases-ui.ts +++ b/extensions/ql-vscode/src/databases/local-databases-ui.ts @@ -5,6 +5,7 @@ import type { ProviderResult, TreeDataProvider, CancellationToken, + QuickPickItem, } from "vscode"; import { EventEmitter, @@ -28,7 +29,11 @@ import type { ProgressCallback, ProgressContext, } from "../common/vscode/progress"; -import { withInheritedProgress, withProgress } from "../common/vscode/progress"; +import { + UserCancellationException, + withInheritedProgress, + withProgress, +} from "../common/vscode/progress"; import { isLikelyDatabaseRoot, isLikelyDbLanguageFolder, @@ -52,7 +57,10 @@ import { createMultiSelectionCommand, createSingleSelectionCommand, } from "../common/vscode/selection-commands"; -import { tryGetQueryLanguage } from "../common/query-language"; +import { + getLanguageDisplayName, + tryGetQueryLanguage, +} from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; enum SortOrder { @@ -227,6 +235,18 @@ async function chooseDatabaseDir(byFolder: boolean): Promise { return getFirst(chosen); } +interface DatabaseSelectionQuickPickItem extends QuickPickItem { + databaseKind: "new" | "existing"; +} + +export interface DatabaseQuickPickItem extends QuickPickItem { + databaseItem: DatabaseItem; +} + +interface DatabaseImportQuickPickItems extends QuickPickItem { + importType: "URL" | "github" | "archive" | "folder"; +} + export class DatabaseUI extends DisposableObject { private treeDataProvider: DatabaseTreeDataProvider; @@ -794,13 +814,120 @@ export class DatabaseUI extends DisposableObject { * notification if it tries to perform any long-running operations. */ private async getDatabaseItemInternal( - progress: ProgressContext | undefined, + progressContext: ProgressContext | undefined, ): Promise { if (this.databaseManager.currentDatabaseItem === undefined) { - await this.chooseAndSetDatabase(false, progress); + progressContext?.progress({ + maxStep: 2, + step: 1, + message: "Choosing database", + }); + await this.promptForDatabase(); + } + return this.databaseManager.currentDatabaseItem; + } + + private async promptForDatabase(): Promise { + const quickPickItems: DatabaseSelectionQuickPickItem[] = [ + { + label: "$(database) Existing database", + detail: "Select an existing database from your workspace", + alwaysShow: true, + databaseKind: "existing", + }, + { + label: "$(arrow-down) New database", + detail: "Import a new database from the cloud or your local machine", + alwaysShow: true, + databaseKind: "new", + }, + ]; + const selectedOption = + await window.showQuickPick( + quickPickItems, + { + placeHolder: "Select an option", + ignoreFocusOut: true, + }, + ); + + if (!selectedOption) { + throw new UserCancellationException("No database selected", true); } - return this.databaseManager.currentDatabaseItem; + if (selectedOption.databaseKind === "existing") { + await this.selectExistingDatabase(); + } else if (selectedOption.databaseKind === "new") { + await this.importNewDatabase(); + } + } + + private async selectExistingDatabase() { + const dbItems: DatabaseQuickPickItem[] = + this.databaseManager.databaseItems.map((dbItem) => ({ + label: dbItem.name, + description: getLanguageDisplayName(dbItem.language), + databaseItem: dbItem, + })); + + const selectedDatabase = await window.showQuickPick(dbItems, { + placeHolder: "Select a database", + ignoreFocusOut: true, + }); + + if (!selectedDatabase) { + throw new UserCancellationException("No database selected", true); + } + + await this.databaseManager.setCurrentDatabaseItem( + selectedDatabase.databaseItem, + ); + } + + private async importNewDatabase() { + const importOptions: DatabaseImportQuickPickItems[] = [ + { + label: "$(github) GitHub", + detail: "Import a database from a GitHub repository", + alwaysShow: true, + importType: "github", + }, + { + label: "$(link) URL", + detail: "Import a database archive or folder from a remote URL", + alwaysShow: true, + importType: "URL", + }, + { + label: "$(file-zip) Archive", + detail: "Import a database from a local ZIP archive", + alwaysShow: true, + importType: "archive", + }, + { + label: "$(folder) Folder", + detail: "Import a database from a local folder", + alwaysShow: true, + importType: "folder", + }, + ]; + const selectedImportOption = + await window.showQuickPick(importOptions, { + placeHolder: "Import a database from...", + ignoreFocusOut: true, + }); + if (!selectedImportOption) { + throw new UserCancellationException("No database selected", true); + } + if (selectedImportOption.importType === "github") { + await this.handleChooseDatabaseGithub(); + } else if (selectedImportOption.importType === "URL") { + await this.handleChooseDatabaseInternet(); + } else if (selectedImportOption.importType === "archive") { + await this.handleChooseDatabaseArchive(); + } else if (selectedImportOption.importType === "folder") { + await this.handleChooseDatabaseFolder(); + } } /** diff --git a/extensions/ql-vscode/src/local-queries/local-queries.ts b/extensions/ql-vscode/src/local-queries/local-queries.ts index 7e42e3160..5b3e44d97 100644 --- a/extensions/ql-vscode/src/local-queries/local-queries.ts +++ b/extensions/ql-vscode/src/local-queries/local-queries.ts @@ -3,12 +3,7 @@ import type { ProgressUpdate, } from "../common/vscode/progress"; import { withProgress } from "../common/vscode/progress"; -import type { - CancellationToken, - QuickPickItem, - Range, - TabInputText, -} from "vscode"; +import type { CancellationToken, Range, TabInputText } from "vscode"; import { CancellationTokenSource, Uri, window } from "vscode"; import { TeeLogger, @@ -23,7 +18,10 @@ import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; import { displayQuickQuery } from "./quick-query"; import type { CoreCompletedQuery, QueryRunner } from "../query-server"; import type { QueryHistoryManager } from "../query-history/query-history-manager"; -import type { DatabaseUI } from "../databases/local-databases-ui"; +import type { + DatabaseQuickPickItem, + DatabaseUI, +} from "../databases/local-databases-ui"; import type { ResultsView } from "./results-view"; import type { DatabaseItem, @@ -55,10 +53,6 @@ import { tryGetQueryLanguage } from "../common/query-language"; import type { LanguageContextStore } from "../language-context-store"; import type { ExtensionApp } from "../common/vscode/vscode-app"; -interface DatabaseQuickPickItem extends QuickPickItem { - databaseItem: DatabaseItem; -} - export enum QuickEvalType { None, QuickEval, From 6ab1f4c7ee60e3fe23c32dea86d09abfc0743243 Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 11 Jan 2024 09:37:30 +0000 Subject: [PATCH 10/77] v1.12.0 --- extensions/ql-vscode/CHANGELOG.md | 1 + extensions/ql-vscode/package-lock.json | 4 ++-- extensions/ql-vscode/package.json | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 5f3260f5c..504a3d23a 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -6,6 +6,7 @@ - Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125) - Add comparison of alerts when comparing query results. This allows viewing path explanations for differences in alerts. [#3113](https://github.com/github/vscode-codeql/pull/3113) - Fix a bug where the CodeQL CLI and variant analysis results were corrupted after extraction in VS Code Insiders. [#3151](https://github.com/github/vscode-codeql/pull/3151) & [#3152](https://github.com/github/vscode-codeql/pull/3152) +- Add progress reporting for unzipping files [3157](https://github.com/github/vscode-codeql/pull/3157) - Add option to cancel opening the model editor. [#3189](https://github.com/github/vscode-codeql/pull/3189) ## 1.11.0 - 13 December 2023 diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index fcc53e582..e7f26e63d 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codeql", - "version": "1.11.1", + "version": "1.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-codeql", - "version": "1.11.1", + "version": "1.12.0", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1fa606758..4abd20f9f 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.11.1", + "version": "1.12.0", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", From 3d5ef60fe0378a3eb0d6e101ae9d618ecca84484 Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 11 Jan 2024 10:50:11 +0100 Subject: [PATCH 11/77] Update extensions/ql-vscode/CHANGELOG.md Co-authored-by: Koen Vlaswinkel --- extensions/ql-vscode/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 504a3d23a..1c1a2ee38 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -6,7 +6,7 @@ - Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125) - Add comparison of alerts when comparing query results. This allows viewing path explanations for differences in alerts. [#3113](https://github.com/github/vscode-codeql/pull/3113) - Fix a bug where the CodeQL CLI and variant analysis results were corrupted after extraction in VS Code Insiders. [#3151](https://github.com/github/vscode-codeql/pull/3151) & [#3152](https://github.com/github/vscode-codeql/pull/3152) -- Add progress reporting for unzipping files [3157](https://github.com/github/vscode-codeql/pull/3157) +- Show progress when extracting the CodeQL CLI distribution during installation. [#3157](https://github.com/github/vscode-codeql/pull/3157) - Add option to cancel opening the model editor. [#3189](https://github.com/github/vscode-codeql/pull/3189) ## 1.11.0 - 13 December 2023 From 8834111f5770731b9ccdac49a8883d75b75c1670 Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 11 Jan 2024 10:08:00 +0000 Subject: [PATCH 12/77] Update changelog release date --- extensions/ql-vscode/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 1c1a2ee38..9c41cc5a9 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,6 +1,6 @@ # CodeQL for Visual Studio Code: Changelog -## [UNRELEASED] +## 1.12.0 - 11 January 2024 - Add a prompt for downloading a GitHub database when opening a GitHub repository. [#3138](https://github.com/github/vscode-codeql/pull/3138) - Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125) From 7f730d24b0020d3bd66ffa8d1f3a475c09d79bad Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 11 Jan 2024 12:09:10 +0100 Subject: [PATCH 13/77] Add helper functions for suggestion box This adds some helper functions that will be used for a suggestion box in the future. There is one helper function for highlighting part of a text case-insensitively. Another helper function will try to find followup options based on a list of tokens. The tests for these functions use access paths as input, but these functions are not intended to be specific to access paths. They can be used for any list of options/string. --- .../SuggestBox/__tests__/highlight.test.ts | 72 +++++++++ .../SuggestBox/__tests__/options.test.ts | 138 ++++++++++++++++++ .../src/view/common/SuggestBox/highlight.ts | 49 +++++++ .../src/view/common/SuggestBox/options.ts | 44 ++++++ 4 files changed, 303 insertions(+) create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/__tests__/highlight.test.ts create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/__tests__/options.test.ts create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/options.ts diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/highlight.test.ts b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/highlight.test.ts new file mode 100644 index 000000000..283dea416 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/highlight.test.ts @@ -0,0 +1,72 @@ +import { createHighlights } from "../highlight"; + +describe("createHighlights", () => { + it.each([ + { + text: "Argument[foo].Element.Field[@test]", + search: "Argument[foo]", + snippets: [ + { text: "Argument[foo]", highlight: true }, + { + text: ".Element.Field[@test]", + highlight: false, + }, + ], + }, + { + text: "Field[@test]", + search: "test", + snippets: [ + { text: "Field[@", highlight: false }, + { + text: "test", + highlight: true, + }, + { + text: "]", + highlight: false, + }, + ], + }, + { + text: "Field[@test]", + search: "TEST", + snippets: [ + { text: "Field[@", highlight: false }, + { + text: "test", + highlight: true, + }, + { + text: "]", + highlight: false, + }, + ], + }, + { + text: "Field[@test]", + search: "[@TEST", + snippets: [ + { text: "Field", highlight: false }, + { + text: "[@test", + highlight: true, + }, + { + text: "]", + highlight: false, + }, + ], + }, + { + text: "Field[@test]", + search: "", + snippets: [{ text: "Field[@test]", highlight: false }], + }, + ])( + `creates highlights for $text with $search`, + ({ text, search, snippets }) => { + expect(createHighlights(text, search)).toEqual(snippets); + }, + ); +}); diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/options.test.ts b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/options.test.ts new file mode 100644 index 000000000..e04bd29c9 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/options.test.ts @@ -0,0 +1,138 @@ +import { findMatchingOptions } from "../options"; + +type TestOption = { + label: string; + value: string; + followup?: TestOption[]; +}; + +const suggestedOptions: TestOption[] = [ + { + label: "Argument[self]", + value: "Argument[self]", + }, + { + label: "Argument[0]", + value: "Argument[0]", + followup: [ + { + label: "Element[0]", + value: "Argument[0].Element[0]", + }, + { + label: "Element[1]", + value: "Argument[0].Element[1]", + }, + ], + }, + { + label: "Argument[1]", + value: "Argument[1]", + }, + { + label: "Argument[text_rep:]", + value: "Argument[text_rep:]", + }, + { + label: "Argument[block]", + value: "Argument[block]", + followup: [ + { + label: "Parameter[0]", + value: "Argument[block].Parameter[0]", + followup: [ + { + label: "Element[:query]", + value: "Argument[block].Parameter[0].Element[:query]", + }, + { + label: "Element[:parameters]", + value: "Argument[block].Parameter[0].Element[:parameters]", + }, + ], + }, + { + label: "Parameter[1]", + value: "Argument[block].Parameter[1]", + followup: [ + { + label: "Field[@query]", + value: "Argument[block].Parameter[1].Field[@query]", + }, + ], + }, + ], + }, + { + label: "ReturnValue", + value: "ReturnValue", + }, +]; + +describe("findMatchingOptions", () => { + it.each([ + { + // Argument[block]. + tokens: ["Argument[block]", ""], + options: ["Argument[block].Parameter[0]", "Argument[block].Parameter[1]"], + }, + { + // Argument[block].Parameter[0] + tokens: ["Argument[block]", "Parameter[0]"], + options: ["Argument[block].Parameter[0]"], + }, + { + // Argument[block].Parameter[0]. + tokens: ["Argument[block]", "Parameter[0]", ""], + options: [ + "Argument[block].Parameter[0].Element[:query]", + "Argument[block].Parameter[0].Element[:parameters]", + ], + }, + { + // "" + tokens: [""], + options: [ + "Argument[self]", + "Argument[0]", + "Argument[1]", + "Argument[text_rep:]", + "Argument[block]", + "ReturnValue", + ], + }, + { + // "" + tokens: [], + options: [ + "Argument[self]", + "Argument[0]", + "Argument[1]", + "Argument[text_rep:]", + "Argument[block]", + "ReturnValue", + ], + }, + { + // block + tokens: ["block"], + options: ["Argument[block]"], + }, + { + // l + tokens: ["l"], + options: ["Argument[self]", "Argument[block]", "ReturnValue"], + }, + { + // L + tokens: ["L"], + options: ["Argument[self]", "Argument[block]", "ReturnValue"], + }, + ])(`creates options for $value`, ({ tokens, options }) => { + expect( + findMatchingOptions(suggestedOptions, tokens).map( + (option) => option.value, + ), + ).toEqual(options); + }); +}); diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts b/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts new file mode 100644 index 000000000..216740506 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts @@ -0,0 +1,49 @@ +type Snippet = { + text: string; + highlight: boolean; +}; + +/** + * Highlight creates a list of snippets that can be used to render a highlighted + * string. This highlight is case-insensitive. + * + * @param text The text in which to create highlights + * @param search The string that will be highlighted in the text. + * @returns A list of snippets that can be used to render a highlighted string. + */ +export function createHighlights(text: string, search: string): Snippet[] { + if (search === "") { + return [{ text, highlight: false }]; + } + + const searchLower = search.toLowerCase(); + const textLower = text.toLowerCase(); + + const highlights: Snippet[] = []; + + let index = 0; + for (;;) { + const searchIndex = textLower.indexOf(searchLower, index); + if (searchIndex === -1) { + break; + } + + highlights.push({ + text: text.substring(index, searchIndex), + highlight: false, + }); + highlights.push({ + text: text.substring(searchIndex, searchIndex + search.length), + highlight: true, + }); + + index = searchIndex + search.length; + } + + highlights.push({ + text: text.substring(index), + highlight: false, + }); + + return highlights.filter((highlight) => highlight.text !== ""); +} diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/options.ts b/extensions/ql-vscode/src/view/common/SuggestBox/options.ts new file mode 100644 index 000000000..2f15b45cf --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/options.ts @@ -0,0 +1,44 @@ +type Option> = { + label: string; + followup?: T[]; +}; + +function findNestedMatchingOptions>( + parts: string[], + options: T[], +): T[] { + const part = parts[0]; + const rest = parts.slice(1); + + if (!part) { + return options; + } + + const matchingOption = options.find((item) => item.label === part); + if (!matchingOption) { + return []; + } + + if (rest.length === 0) { + return matchingOption.followup ?? []; + } + + return findNestedMatchingOptions(rest, matchingOption.followup ?? []); +} + +export function findMatchingOptions>( + options: T[], + tokens: string[], +): T[] { + if (tokens.length === 0) { + return options; + } + const prefixTokens = tokens.slice(0, tokens.length - 1); + const lastToken = tokens[tokens.length - 1]; + + const matchingOptions = findNestedMatchingOptions(prefixTokens, options); + + return matchingOptions.filter((item) => + item.label.toLowerCase().includes(lastToken.toLowerCase()), + ); +} From d77261aaa59b8a8c4086fb9f2a218687a4790ba8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 11 Jan 2024 12:25:03 +0000 Subject: [PATCH 14/77] Bump version to v1.12.1 --- extensions/ql-vscode/CHANGELOG.md | 2 ++ extensions/ql-vscode/package-lock.json | 4 ++-- extensions/ql-vscode/package.json | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 6d1d81d9e..24811f21e 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -1,5 +1,7 @@ # CodeQL for Visual Studio Code: Changelog +## [UNRELEASED] + ## 1.12.0 - 11 January 2024 - If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index e7f26e63d..1d9d5490c 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-codeql", - "version": "1.12.0", + "version": "1.12.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-codeql", - "version": "1.12.0", + "version": "1.12.1", "hasInstallScript": true, "license": "MIT", "dependencies": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 4abd20f9f..c7aa76bda 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -4,7 +4,7 @@ "description": "CodeQL for Visual Studio Code", "author": "GitHub", "private": true, - "version": "1.12.0", + "version": "1.12.1", "publisher": "GitHub", "license": "MIT", "icon": "media/VS-marketplace-CodeQL-icon.png", From 0df0cca9d872cfa51f305fa76b7981b3cf20b9a4 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 11 Jan 2024 14:13:02 +0100 Subject: [PATCH 15/77] Create SuggestBox component --- extensions/ql-vscode/package-lock.json | 58 +++-- extensions/ql-vscode/package.json | 1 + .../src/stories/common/SuggestBox.stories.tsx | 146 +++++++++++ .../src/view/common/SuggestBox/SuggestBox.tsx | 227 ++++++++++++++++++ .../view/common/SuggestBox/SuggestBoxItem.tsx | 86 +++++++ .../src/view/common/SuggestBox/index.ts | 1 + .../src/view/common/SuggestBox/options.ts | 3 +- 7 files changed, 500 insertions(+), 22 deletions(-) create mode 100644 extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/SuggestBoxItem.tsx create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/index.ts diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index fcc53e582..43263180a 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -10,6 +10,7 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { + "@floating-ui/react": "^0.26.5", "@octokit/plugin-retry": "^6.0.1", "@octokit/rest": "^20.0.2", "@vscode/codicons": "^0.0.35", @@ -2967,42 +2968,57 @@ "dev": true }, "node_modules/@floating-ui/core": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz", - "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==", - "dev": true, + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz", + "integrity": "sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==", "dependencies": { - "@floating-ui/utils": "^0.1.3" + "@floating-ui/utils": "^0.2.0" } }, "node_modules/@floating-ui/dom": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", - "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", - "dev": true, + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz", + "integrity": "sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==", "dependencies": { - "@floating-ui/core": "^1.4.2", - "@floating-ui/utils": "^0.1.3" + "@floating-ui/core": "^1.5.3", + "@floating-ui/utils": "^0.2.0" } }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz", - "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==", - "dev": true, + "node_modules/@floating-ui/react": { + "version": "0.26.5", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.5.tgz", + "integrity": "sha512-LJeSQa+yOwV0Tdpc/C3Vr92QMrwRqRMTk4yOwsRJKc57x3Lcw317GE0EV+ECM7+Z89yEAPBe7nzbDEWfkWCrBA==", "dependencies": { - "@floating-ui/dom": "^1.5.1" + "@floating-ui/react-dom": "^2.0.5", + "@floating-ui/utils": "^0.2.0", + "tabbable": "^6.0.1" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.5.tgz", + "integrity": "sha512-UsBK30Bg+s6+nsgblXtZmwHhgS2vmbuQK22qgt2pTQM6M3X6H1+cQcLXqgRY3ihVLcZJE6IvqDQozhsnIVqK/Q==", + "dependencies": { + "@floating-ui/dom": "^1.5.4" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react/node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/@floating-ui/utils": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", - "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==", - "dev": true + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", + "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" }, "node_modules/@github/browserslist-config": { "version": "1.0.0", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1fa606758..c2b7dd8c6 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1908,6 +1908,7 @@ "prepare": "cd ../.. && husky install" }, "dependencies": { + "@floating-ui/react": "^0.26.5", "@octokit/plugin-retry": "^6.0.1", "@octokit/rest": "^20.0.2", "@vscode/codicons": "^0.0.35", diff --git a/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx b/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx new file mode 100644 index 000000000..ce4c2f5c3 --- /dev/null +++ b/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx @@ -0,0 +1,146 @@ +import type { Meta, StoryFn } from "@storybook/react"; + +import { styled } from "styled-components"; + +import { Codicon } from "../../view/common"; +import { SuggestBox as SuggestBoxComponent } from "../../view/common/SuggestBox/SuggestBox"; +import { useCallback, useState } from "react"; + +export default { + title: "Suggest Box", + component: SuggestBoxComponent, +} as Meta; + +type StoryOption = { + label: string; + icon: string; + details?: string; + value: string; + followup?: StoryOption[]; +}; + +const Template: StoryFn> = (args) => { + const [value, setValue] = useState(""); + + const handleChange = useCallback( + (value: string) => { + args.onChange(value); + setValue(value); + }, + [args], + ); + + return ( + + {...args} + value={value} + onChange={handleChange} + /> + ); +}; + +const Icon = styled(Codicon)` + margin-right: 4px; + color: var(--vscode-symbolIcon-fieldForeground); + font-size: 16px; +`; + +const suggestedOptions: StoryOption[] = [ + { + label: "Argument[self]", + icon: "symbol-class", + details: "sqlite3.SQLite3::Database", + value: "Argument[self]", + }, + { + label: "Argument[0]", + icon: "symbol-parameter", + details: "name", + value: "Argument[0]", + followup: [ + { + label: "Element[0]", + icon: "symbol-field", + value: "Argument[0].Element[0]", + details: "first character", + }, + { + label: "Element[1]", + icon: "symbol-field", + value: "Argument[0].Element[1]", + details: "second character", + }, + { + label: "Element[any]", + icon: "symbol-field", + value: "Argument[0].Element[any]", + details: "any character", + }, + ], + }, + { + label: "Argument[1]", + icon: "symbol-parameter", + details: "arity", + value: "Argument[1]", + }, + { + label: "Argument[text_rep:]", + icon: "symbol-parameter", + details: "text_rep:", + value: "Argument[text_rep:]", + }, + { + label: "Argument[block]", + icon: "symbol-parameter", + details: "&block", + value: "Argument[block]", + followup: [ + { + label: "Parameter[0]", + icon: "symbol-parameter", + value: "Argument[block].Parameter[0]", + details: "val", + followup: [ + { + label: "Element[:query]", + icon: "symbol-key", + value: "Argument[block].Parameter[0].Element[:query]", + }, + { + label: "Element[:parameters]", + icon: "symbol-key", + value: "Argument[block].Parameter[0].Element[:parameters]", + }, + ], + }, + { + label: "Parameter[1]", + icon: "symbol-parameter", + value: "Argument[block].Parameter[1]", + details: "context", + followup: [ + { + label: "Field[@query]", + icon: "symbol-field", + value: "Argument[block].Parameter[1].Field[@query]", + }, + ], + }, + ], + }, + { + label: "ReturnValue", + icon: "symbol-variable", + details: undefined, + value: "ReturnValue", + }, +]; + +export const AccessPath = Template.bind({}); +AccessPath.args = { + options: suggestedOptions, + parseValueToTokens: (value: string) => value.split("."), + getIcon: (option: StoryOption) => , + getDetails: (option: StoryOption) => option.details, +}; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx new file mode 100644 index 000000000..544de25c1 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx @@ -0,0 +1,227 @@ +import type { FormEvent, ReactNode } from "react"; +import { useCallback, useMemo, useRef, useState, useEffect } from "react"; +import { + autoUpdate, + flip, + FloatingFocusManager, + FloatingPortal, + size, + useDismiss, + useFloating, + useFocus, + useInteractions, + useListNavigation, + useRole, +} from "@floating-ui/react"; +import { styled } from "styled-components"; +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; +import type { Option } from "./options"; +import { findMatchingOptions } from "./options"; +import { SuggestBoxItem } from "./SuggestBoxItem"; + +const Input = styled(VSCodeTextField)` + width: 430px; + + font-family: var(--vscode-editor-font-family); +`; + +const Container = styled.div` + width: 430px; + display: flex; + flex-direction: column; + border-radius: 3px; + + background-color: var(--vscode-editorSuggestWidget-background); + border: 1px solid var(--vscode-editorSuggestWidget-border); + + user-select: none; +`; + +const ListContainer = styled(Container)` + font-size: 95%; +`; + +const NoSuggestionsContainer = styled(Container)` + padding-top: 2px; + padding-bottom: 2px; +`; + +const NoSuggestionsText = styled.div` + padding-left: 22px; +`; + +type Props> = { + value?: string; + onChange: (value: string) => void; + options: T[]; + + /** + * Parse the value into tokens that can be used to match against the options. The + * tokens will be passed to {@link findMatchingOptions}. + * @param value The user-entered value to parse. + */ + parseValueToTokens: (value: string) => string[]; + + /** + * Get the icon to display for an option. + * @param option The option to get the icon for. + */ + getIcon?: (option: T) => ReactNode | undefined; + /** + * Get the details text to display for an option. + * @param option The option to get the details for. + */ + getDetails?: (option: T) => ReactNode | undefined; + + disabled?: boolean; + + "aria-label"?: string; +}; + +export const SuggestBox = >({ + value = "", + onChange, + options, + parseValueToTokens, + getIcon, + getDetails, + disabled, + "aria-label": ariaLabel, +}: Props) => { + const [isOpen, setIsOpen] = useState(false); + const [activeIndex, setActiveIndex] = useState(null); + + const listRef = useRef>([]); + + const { refs, floatingStyles, context } = useFloating({ + whileElementsMounted: autoUpdate, + open: isOpen, + onOpenChange: setIsOpen, + placement: "bottom-start", + middleware: [ + // Flip when the popover is too close to the bottom of the screen + flip({ padding: 10 }), + // Resize the popover to be fill the available height + size({ + apply({ availableHeight, elements }) { + Object.assign(elements.floating.style, { + maxHeight: `${availableHeight}px`, + }); + }, + padding: 10, + }), + ], + }); + + const focus = useFocus(context); + const role = useRole(context, { role: "listbox" }); + const dismiss = useDismiss(context); + const listNav = useListNavigation(context, { + listRef, + activeIndex, + onNavigate: setActiveIndex, + virtual: true, + loop: true, + }); + + const { getReferenceProps, getFloatingProps, getItemProps } = useInteractions( + [focus, role, dismiss, listNav], + ); + + const handleInput = useCallback( + (event: FormEvent) => { + const value = event.currentTarget.value; + onChange(value); + setIsOpen(true); + setActiveIndex(0); + }, + [onChange], + ); + + const suggestionItems = useMemo(() => { + return findMatchingOptions(options, parseValueToTokens(value)); + }, [options, value, parseValueToTokens]); + + useEffect(() => { + if (disabled) { + setIsOpen(false); + } + }, [disabled]); + + return ( + <> + { + // When the user presses the enter key, select the active item + if ( + event.key === "Enter" && + activeIndex !== null && + suggestionItems[activeIndex] + ) { + onChange(suggestionItems[activeIndex].value); + setActiveIndex(null); + setIsOpen(false); + } + }, + disabled, + })} + /> + {isOpen && ( + + {value && suggestionItems.length === 0 && ( + + No suggestions. + + )} + {suggestionItems.length > 0 && ( + + + {suggestionItems.map((item, index) => ( + + ))} + + + )} + + )} + + ); +}; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBoxItem.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBoxItem.tsx new file mode 100644 index 000000000..1693d46f1 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBoxItem.tsx @@ -0,0 +1,86 @@ +import type { HTMLProps, ReactNode } from "react"; +import { forwardRef } from "react"; +import { useId } from "@floating-ui/react"; +import { styled } from "styled-components"; + +const Container = styled.div<{ $active: boolean }>` + display: flex; + box-sizing: border-box; + padding-right: 10px; + background-repeat: no-repeat; + background-position: 2px 2px; + white-space: nowrap; + cursor: pointer; + touch-action: none; + padding-left: 2px; + + font-family: var(--vscode-editor-font-family); + + color: ${(props) => + props.$active + ? "var(--vscode-editorSuggestWidget-selectedForeground)" + : "var(--vscode-editorSuggestWidget-foreground)"}; + background-color: ${(props) => + props.$active + ? "var(--vscode-editorSuggestWidget-selectedBackground)" + : "transparent"}; +`; + +const LabelContainer = styled.div` + flex: 1; + display: flex; + overflow: hidden; + white-space: pre; + justify-content: space-between; + align-items: center; +`; + +const Label = styled.span` + flex-shrink: 1; + flex-grow: 1; + overflow: hidden; + text-overflow: ellipsis; +`; + +const DetailsLabel = styled.span` + overflow: hidden; + flex-shrink: 4; + max-width: 70%; + + font-size: 85%; + margin-left: 1.1em; + opacity: 0.7; + text-overflow: ellipsis; + white-space: nowrap; +`; + +type Props = { + active: boolean; + icon?: ReactNode; + labelText: ReactNode; + details?: ReactNode; +}; + +export const SuggestBoxItem = forwardRef< + HTMLDivElement, + Props & HTMLProps +>(({ children, active, icon, labelText, details, ...props }, ref) => { + const id = useId(); + return ( + + {icon} + + + {details && {details}} + + + ); +}); +SuggestBoxItem.displayName = "SuggestBoxItem"; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/index.ts b/extensions/ql-vscode/src/view/common/SuggestBox/index.ts new file mode 100644 index 000000000..d5914f4d3 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/index.ts @@ -0,0 +1 @@ +export * from "./SuggestBox"; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/options.ts b/extensions/ql-vscode/src/view/common/SuggestBox/options.ts index 2f15b45cf..dfaadbad4 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/options.ts +++ b/extensions/ql-vscode/src/view/common/SuggestBox/options.ts @@ -1,5 +1,6 @@ -type Option> = { +export type Option> = { label: string; + value: string; followup?: T[]; }; From 1a135c501d173a71e7bc97639bbb87a208e1c9b4 Mon Sep 17 00:00:00 2001 From: Nora Date: Thu, 11 Jan 2024 13:23:00 +0000 Subject: [PATCH 16/77] Move changelog entry to correct section --- extensions/ql-vscode/CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 24811f21e..2631c77dd 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,9 +2,10 @@ ## [UNRELEASED] +- If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) + ## 1.12.0 - 11 January 2024 -- If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) - Add a prompt for downloading a GitHub database when opening a GitHub repository. [#3138](https://github.com/github/vscode-codeql/pull/3138) - Avoid showing a popup when hovering over source elements in database source files. [#3125](https://github.com/github/vscode-codeql/pull/3125) - Add comparison of alerts when comparing query results. This allows viewing path explanations for differences in alerts. [#3113](https://github.com/github/vscode-codeql/pull/3113) From b4da6c71f367f06b7dbbd53bd8861e78e7738e10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 13:58:28 +0000 Subject: [PATCH 17/77] Bump eslint-plugin-prettier from 5.0.1 to 5.1.3 in /extensions/ql-vscode Bumps [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) from 5.0.1 to 5.1.3. - [Release notes](https://github.com/prettier/eslint-plugin-prettier/releases) - [Changelog](https://github.com/prettier/eslint-plugin-prettier/blob/master/CHANGELOG.md) - [Commits](https://github.com/prettier/eslint-plugin-prettier/compare/v5.0.1...v5.1.3) --- updated-dependencies: - dependency-name: eslint-plugin-prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 13 +++++++------ extensions/ql-vscode/package.json | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 1d9d5490c..90ddca7ea 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -110,7 +110,7 @@ "eslint-plugin-github": "^4.4.1", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest-dom": "^5.0.1", - "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.31.8", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.4", @@ -15858,23 +15858,24 @@ } }, "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.1.3.tgz", + "integrity": "sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==", "dev": true, "dependencies": { "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" + "synckit": "^0.8.6" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "funding": { - "url": "https://opencollective.com/prettier" + "url": "https://opencollective.com/eslint-plugin-prettier" }, "peerDependencies": { "@types/eslint": ">=8.0.0", "eslint": ">=8.0.0", + "eslint-config-prettier": "*", "prettier": ">=3.0.0" }, "peerDependenciesMeta": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index c7aa76bda..0a5901658 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2008,7 +2008,7 @@ "eslint-plugin-github": "^4.4.1", "eslint-plugin-import": "^2.29.1", "eslint-plugin-jest-dom": "^5.0.1", - "eslint-plugin-prettier": "^5.0.0", + "eslint-plugin-prettier": "^5.1.3", "eslint-plugin-react": "^7.31.8", "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-storybook": "^0.6.4", From 6ec727a8a245087e272409cf11d0d271ffed78a5 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 11 Jan 2024 15:28:20 +0100 Subject: [PATCH 18/77] Add tests for SuggestBox component --- .../src/view/common/SuggestBox/SuggestBox.tsx | 23 +- .../SuggestBox/__tests__/SuggestBox.test.tsx | 240 ++++++++++++++++++ 2 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/__tests__/SuggestBox.test.tsx diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx index 544de25c1..0e338892f 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx @@ -1,5 +1,5 @@ import type { FormEvent, ReactNode } from "react"; -import { useCallback, useMemo, useRef, useState, useEffect } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { autoUpdate, flip, @@ -50,7 +50,7 @@ const NoSuggestionsText = styled.div` padding-left: 22px; `; -type Props> = { +export type SuggestBoxProps> = { value?: string; onChange: (value: string) => void; options: T[]; @@ -76,6 +76,14 @@ type Props> = { disabled?: boolean; "aria-label"?: string; + + /** + * Can be used to render a different component for the input. This is used + * in testing to use default HTML components rather than the VSCodeTextField + * for easier testing. + * @param props The props returned by `getReferenceProps` of {@link useInteractions} + */ + renderInputComponent?: (props: Record) => ReactNode; }; export const SuggestBox = >({ @@ -87,7 +95,8 @@ export const SuggestBox = >({ getDetails, disabled, "aria-label": ariaLabel, -}: Props) => { + renderInputComponent = (props) => , +}: SuggestBoxProps) => { const [isOpen, setIsOpen] = useState(false); const [activeIndex, setActiveIndex] = useState(null); @@ -150,8 +159,8 @@ export const SuggestBox = >({ return ( <> - >({ } }, disabled, - })} - /> + }), + )} {isOpen && ( {value && suggestionItems.length === 0 && ( diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/SuggestBox.test.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/SuggestBox.test.tsx new file mode 100644 index 000000000..21225f898 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/__tests__/SuggestBox.test.tsx @@ -0,0 +1,240 @@ +import { render as reactRender, screen } from "@testing-library/react"; +import type { SuggestBoxProps } from "../SuggestBox"; +import { SuggestBox } from "../SuggestBox"; +import { userEvent } from "@testing-library/user-event"; + +type TestOption = { + label: string; + value: string; + followup?: TestOption[]; +}; + +const options: TestOption[] = [ + { + label: "Argument[self]", + value: "Argument[self]", + }, + { + label: "Argument[0]", + value: "Argument[0]", + followup: [ + { + label: "Element[0]", + value: "Argument[0].Element[0]", + }, + { + label: "Element[1]", + value: "Argument[0].Element[1]", + }, + { + label: "Element[any]", + value: "Argument[0].Element[any]", + }, + ], + }, + { + label: "Argument[1]", + value: "Argument[1]", + }, + { + label: "Argument[text_rep:]", + value: "Argument[text_rep:]", + }, + { + label: "Argument[block]", + value: "Argument[block]", + followup: [ + { + label: "Parameter[0]", + value: "Argument[block].Parameter[0]", + followup: [ + { + label: "Element[:query]", + value: "Argument[block].Parameter[0].Element[:query]", + }, + { + label: "Element[:parameters]", + value: "Argument[block].Parameter[0].Element[:parameters]", + }, + ], + }, + { + label: "Parameter[1]", + value: "Argument[block].Parameter[1]", + followup: [ + { + label: "Field[@query]", + value: "Argument[block].Parameter[1].Field[@query]", + }, + ], + }, + ], + }, + { + label: "ReturnValue", + value: "ReturnValue", + }, +]; + +describe("SuggestBox", () => { + const onChange = jest.fn(); + const parseValueToTokens = jest.fn(); + const render = (props?: Partial>) => + reactRender( + } + {...props} + />, + ); + + beforeEach(() => { + onChange.mockReset(); + parseValueToTokens + .mockReset() + .mockImplementation((value: string) => value.split(".")); + }); + + it("does not render the options by default", () => { + render(); + + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + }); + + it("renders the options after clicking on the text field", async () => { + render(); + + await userEvent.click(screen.getByRole("combobox")); + + expect(screen.getAllByRole("option")).toHaveLength(options.length); + }); + + it("calls onChange after entering text", async () => { + render({ + value: "Argument[block]", + }); + + await userEvent.type(screen.getByRole("combobox"), "."); + + expect(onChange).toHaveBeenCalledWith("Argument[block]."); + }); + + it("calls onChange after clearing text", async () => { + render({ + value: "Argument[block].", + }); + + await userEvent.clear(screen.getByRole("combobox")); + + expect(onChange).toHaveBeenCalledWith(""); + }); + + it("renders matching options with a single token", async () => { + render({ + value: "block", + }); + + await userEvent.click(screen.getByRole("combobox")); + + expect(screen.getByRole("option")).toHaveTextContent("Argument[block]"); + }); + + it("renders followup options with a token and an empty token", async () => { + render({ + value: "Argument[block].", + }); + + await userEvent.click(screen.getByRole("combobox")); + + expect(screen.getAllByRole("option")).toHaveLength(2); + }); + + it("renders matching followup options with two tokens", async () => { + render({ + value: "Argument[block].1", + }); + + await userEvent.click(screen.getByRole("combobox")); + + expect(screen.getByRole("option")).toHaveTextContent("Parameter[1]"); + }); + + it("closes the options when selecting an option", async () => { + render({ + value: "Argument[block].1", + }); + + await userEvent.click(screen.getByRole("combobox")); + await userEvent.keyboard("{Enter}"); + + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + }); + + it("shows no suggestions with no matching followup options", async () => { + render({ + value: "Argument[block].block", + }); + + await userEvent.click(screen.getByRole("combobox")); + + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + expect(screen.getByText("No suggestions.")).toBeInTheDocument(); + }); + + it("can navigate the options using the keyboard", async () => { + render({ + value: "", + }); + + await userEvent.click(screen.getByRole("combobox")); + await userEvent.keyboard( + "{ArrowDown}{ArrowDown}{ArrowUp}{ArrowDown}{ArrowDown}{Enter}", + ); + + expect(onChange).toHaveBeenCalledWith("Argument[text_rep:]"); + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + }); + + it("can use loop navigation when using the keyboard", async () => { + render({ + value: "", + }); + + await userEvent.click(screen.getByRole("combobox")); + await userEvent.keyboard("{ArrowUp}{ArrowUp}{Enter}"); + + expect(onChange).toHaveBeenCalledWith("Argument[block]"); + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + }); + + it("can close the options using escape", async () => { + render({ + value: "", + }); + + await userEvent.click(screen.getByRole("combobox")); + + expect(screen.getAllByRole("option")).toHaveLength(options.length); + + await userEvent.keyboard("{Escape}"); + + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + }); + + it("opens the options when using backspace on a selected option", async () => { + render({ + value: "Argument[block].1", + }); + + await userEvent.click(screen.getByRole("combobox")); + await userEvent.keyboard("{Enter}"); + + expect(screen.queryByRole("option")).not.toBeInTheDocument(); + + await userEvent.keyboard("{Backspace}"); + + expect(screen.getAllByRole("option")).toHaveLength(1); + }); +}); From 1dc6111fbad17e7a5724a0231ce608cb07080bce Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 10:47:48 -0500 Subject: [PATCH 19/77] Switch to built-in VS Code test UI unconditionally --- README.md | 7 - extensions/ql-vscode/README.md | 2 - extensions/ql-vscode/package-lock.json | 27 -- extensions/ql-vscode/package.json | 3 - extensions/ql-vscode/src/common/commands.ts | 11 +- extensions/ql-vscode/src/extension.ts | 29 +-- .../src/query-testing/test-adapter.ts | 238 ------------------ .../src/query-testing/test-manager-base.ts | 3 +- .../src/query-testing/test-tree-node.ts | 9 - .../ql-vscode/src/query-testing/test-ui.ts | 71 ------ .../jest-runner-installed-extensions.ts | 18 +- 11 files changed, 11 insertions(+), 407 deletions(-) delete mode 100644 extensions/ql-vscode/src/query-testing/test-tree-node.ts delete mode 100644 extensions/ql-vscode/src/query-testing/test-ui.ts diff --git a/README.md b/README.md index f5dc1b9e8..03b564f5a 100644 --- a/README.md +++ b/README.md @@ -21,13 +21,6 @@ To see what has changed in the last few versions of the extension, see the [Chan This project will track new feature development in CodeQL and, whenever appropriate, bring that functionality to the Visual Studio Code experience. -## Dependencies - -This extension depends on the following two extensions for required functionality. They will be installed automatically when you install VS Code CodeQL. - -- [Test Adapter Converter](https://marketplace.visualstudio.com/items?itemName=ms-vscode.test-adapter-converter) -- [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer) - ## Contributing This project welcomes contributions. See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to build, install, and contribute. diff --git a/extensions/ql-vscode/README.md b/extensions/ql-vscode/README.md index 7558d27cf..91c0016fc 100644 --- a/extensions/ql-vscode/README.md +++ b/extensions/ql-vscode/README.md @@ -17,8 +17,6 @@ For information about other configurations, see the separate [CodeQL help](https ### Quick start: Installing and configuring the extension 1. [Install the extension](#installing-the-extension). - *Note: vscode-codeql installs the following dependencies for required functionality: [Test Adapter Converter](https://marketplace.visualstudio.com/items?itemName=ms-vscode.test-adapter-converter), [Test Explorer UI](https://marketplace.visualstudio.com/items?itemName=hbenl.vscode-test-explorer).* - 1. [Check access to the CodeQL CLI](#checking-access-to-the-codeql-cli). 1. [Clone the CodeQL starter workspace](#cloning-the-codeql-starter-workspace). diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index fcc53e582..e79cf467c 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -40,8 +40,6 @@ "vscode-extension-telemetry": "^0.1.6", "vscode-jsonrpc": "^8.0.2", "vscode-languageclient": "^8.0.2", - "vscode-test-adapter-api": "^1.7.0", - "vscode-test-adapter-util": "^0.7.0", "yauzl": "^2.10.0", "zip-a-folder": "^3.1.3" }, @@ -31984,31 +31982,6 @@ "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.3.tgz", "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, - "node_modules/vscode-test-adapter-api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/vscode-test-adapter-api/-/vscode-test-adapter-api-1.9.0.tgz", - "integrity": "sha512-lltjehUP0J9H3R/HBctjlqeUCwn2t9Lbhj2Y500ib+j5Y4H3hw+hVTzuSsfw16LtxY37knlU39QIlasa7svzOQ==", - "engines": { - "vscode": "^1.23.0" - } - }, - "node_modules/vscode-test-adapter-util": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/vscode-test-adapter-util/-/vscode-test-adapter-util-0.7.1.tgz", - "integrity": "sha512-OZZvLDDNhayVVISyTmgUntOhMzl6j9/wVGfNqI2zuR5bQIziTQlDs9W29dFXDTGXZOxazS6uiHkrr86BKDzYUA==", - "dependencies": { - "tslib": "^1.11.1", - "vscode-test-adapter-api": "^1.8.0" - }, - "engines": { - "vscode": "^1.24.0" - } - }, - "node_modules/vscode-test-adapter-util/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" - }, "node_modules/w3c-xmlserializer": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 1fa606758..b905707fe 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -21,7 +21,6 @@ "Programming Languages" ], "extensionDependencies": [ - "hbenl.vscode-test-explorer", "vscode.git" ], "capabilities": { @@ -1938,8 +1937,6 @@ "vscode-extension-telemetry": "^0.1.6", "vscode-jsonrpc": "^8.0.2", "vscode-languageclient": "^8.0.2", - "vscode-test-adapter-api": "^1.7.0", - "vscode-test-adapter-util": "^0.7.0", "yauzl": "^2.10.0", "zip-a-folder": "^3.1.3" }, diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index dbd640b3b..57c471ec7 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -1,10 +1,9 @@ import type { CommandManager } from "../packages/commands"; -import type { Uri, Range, TextDocumentShowOptions } from "vscode"; +import type { Uri, Range, TextDocumentShowOptions, TestItem } from "vscode"; import type { AstItem } from "../language-support"; import type { DbTreeViewItem } from "../databases/ui/db-tree-view-item"; import type { DatabaseItem } from "../databases/local-databases"; import type { QueryHistoryInfo } from "../query-history/query-history-info"; -import type { TestTreeNode } from "../query-testing/test-tree-node"; import type { VariantAnalysis, VariantAnalysisScannedRepository, @@ -334,11 +333,9 @@ export type SummaryLanguageSupportCommands = { }; export type TestUICommands = { - "codeQLTests.showOutputDifferences": (node: TestTreeNode) => Promise; - "codeQLTests.acceptOutput": (node: TestTreeNode) => Promise; - "codeQLTests.acceptOutputContextTestItem": ( - node: TestTreeNode, - ) => Promise; + "codeQLTests.showOutputDifferences": (node: TestItem) => Promise; + "codeQLTests.acceptOutput": (node: TestItem) => Promise; + "codeQLTests.acceptOutputContextTestItem": (node: TestItem) => Promise; }; export type MockGitHubApiServerCommands = { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 8a2ce6a6c..58be99fad 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -15,8 +15,6 @@ import { arch, homedir, platform } from "os"; import { ensureDir } from "fs-extra"; import { join } from "path"; import { dirSync } from "tmp-promise"; -import type { TestHub } from "vscode-test-adapter-api"; -import { testExplorerExtensionId } from "vscode-test-adapter-api"; import { lt, parse } from "semver"; import { watch } from "chokidar"; import { @@ -28,7 +26,6 @@ import { CliConfigListener, DistributionConfigListener, GitHubDatabaseConfigListener, - isCanary, joinOrderWarningThreshold, QueryHistoryConfigListener, QueryServerConfigListener, @@ -90,8 +87,6 @@ import { } from "./common/logging/vscode"; import { QueryHistoryManager } from "./query-history/query-history-manager"; import type { CompletedLocalQueryInfo } from "./query-results"; -import { QLTestAdapterFactory } from "./query-testing/test-adapter"; -import { TestUIService } from "./query-testing/test-ui"; import { CompareView } from "./compare/compare-view"; import { initializeTelemetry, @@ -130,7 +125,6 @@ import { DebuggerUI } from "./debugger/debugger-ui"; import { ModelEditorModule } from "./model-editor/model-editor-module"; import { TestManager } from "./query-testing/test-manager"; import { TestRunner } from "./query-testing/test-runner"; -import type { TestManagerBase } from "./query-testing/test-manager-base"; import { QueryRunner, QueryServerClient } from "./query-server"; import { QueriesModule } from "./queries-panel/queries-module"; import { OpenReferencedFileCodeLensProvider } from "./local-queries/open-referenced-file-code-lens-provider"; @@ -977,27 +971,8 @@ async function activateWithInstalledDistribution( const testRunner = new TestRunner(dbm, cliServer); ctx.subscriptions.push(testRunner); - let testManager: TestManagerBase | undefined = undefined; - if (isCanary()) { - testManager = new TestManager(app, testRunner, cliServer); - ctx.subscriptions.push(testManager); - } else { - const testExplorerExtension = extensions.getExtension( - testExplorerExtensionId, - ); - if (testExplorerExtension) { - const testHub = testExplorerExtension.exports; - const testAdapterFactory = new QLTestAdapterFactory( - testHub, - testRunner, - cliServer, - ); - ctx.subscriptions.push(testAdapterFactory); - - testManager = new TestUIService(app, testHub); - ctx.subscriptions.push(testManager); - } - } + const testManager = new TestManager(app, testRunner, cliServer); + ctx.subscriptions.push(testManager); const testUiCommands = testManager?.getCommands() ?? {}; diff --git a/extensions/ql-vscode/src/query-testing/test-adapter.ts b/extensions/ql-vscode/src/query-testing/test-adapter.ts index eef9a2672..afb9b47b7 100644 --- a/extensions/ql-vscode/src/query-testing/test-adapter.ts +++ b/extensions/ql-vscode/src/query-testing/test-adapter.ts @@ -1,26 +1,4 @@ import { extname } from "path"; -import type { Event, WorkspaceFolder } from "vscode"; -import { CancellationTokenSource, EventEmitter } from "vscode"; -import type { - TestAdapter, - TestEvent, - TestHub, - TestInfo, - TestLoadFinishedEvent, - TestLoadStartedEvent, - TestRunFinishedEvent, - TestRunStartedEvent, - TestSuiteEvent, - TestSuiteInfo, -} from "vscode-test-adapter-api"; -import { TestAdapterRegistrar } from "vscode-test-adapter-util"; -import { QLTestDiscovery } from "./qltest-discovery"; -import { DisposableObject } from "../common/disposable-object"; -import type { CodeQLCliServer, TestCompleted } from "../codeql-cli/cli"; -import { testLogger } from "../common/logging/vscode"; -import type { TestRunner } from "./test-runner"; -import type { FileTreeNode } from "../common/file-tree-nodes"; -import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes"; /** * Get the full path of the `.expected` file for the specified QL test. @@ -47,28 +25,6 @@ function getTestOutputFile(testPath: string, extension: string): string { return changeExtension(testPath, extension); } -/** - * A factory service that creates `QLTestAdapter` objects for workspace folders on demand. - */ -export class QLTestAdapterFactory extends DisposableObject { - constructor( - testHub: TestHub, - testRunner: TestRunner, - cliServer: CodeQLCliServer, - ) { - super(); - - // this will register a QLTestAdapter for each WorkspaceFolder - this.push( - new TestAdapterRegistrar( - testHub, - (workspaceFolder) => - new QLTestAdapter(workspaceFolder, testRunner, cliServer), - ), - ); - } -} - /** * Change the file extension of the specified path. * @param p The original file path. @@ -77,197 +33,3 @@ export class QLTestAdapterFactory extends DisposableObject { function changeExtension(p: string, ext: string): string { return p.slice(0, -extname(p).length) + ext; } - -/** - * Test adapter for QL tests. - */ -export class QLTestAdapter extends DisposableObject implements TestAdapter { - private readonly qlTestDiscovery: QLTestDiscovery; - private readonly _tests = this.push( - new EventEmitter(), - ); - private readonly _testStates = this.push( - new EventEmitter< - TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent - >(), - ); - private readonly _autorun = this.push(new EventEmitter()); - private runningTask?: CancellationTokenSource = undefined; - - constructor( - public readonly workspaceFolder: WorkspaceFolder, - private readonly testRunner: TestRunner, - cliServer: CodeQLCliServer, - ) { - super(); - - this.qlTestDiscovery = this.push( - new QLTestDiscovery(workspaceFolder, cliServer), - ); - void this.qlTestDiscovery.refresh(); - - this.push(this.qlTestDiscovery.onDidChangeTests(this.discoverTests, this)); - } - - public get tests(): Event { - return this._tests.event; - } - - public get testStates(): Event< - TestRunStartedEvent | TestRunFinishedEvent | TestSuiteEvent | TestEvent - > { - return this._testStates.event; - } - - public get autorun(): Event | undefined { - return this._autorun.event; - } - - private static createTestOrSuiteInfos( - testNodes: readonly FileTreeNode[], - ): Array { - return testNodes.map((childNode) => { - return QLTestAdapter.createTestOrSuiteInfo(childNode); - }); - } - - private static createTestOrSuiteInfo( - testNode: FileTreeNode, - ): TestSuiteInfo | TestInfo { - if (testNode instanceof FileTreeLeaf) { - return QLTestAdapter.createTestInfo(testNode); - } else if (testNode instanceof FileTreeDirectory) { - return QLTestAdapter.createTestSuiteInfo(testNode, testNode.name); - } else { - throw new Error("Unexpected test type."); - } - } - - private static createTestInfo(testFile: FileTreeLeaf): TestInfo { - return { - type: "test", - id: testFile.path, - label: testFile.name, - tooltip: testFile.path, - file: testFile.path, - }; - } - - private static createTestSuiteInfo( - testDirectory: FileTreeDirectory, - label: string, - ): TestSuiteInfo { - return { - type: "suite", - id: testDirectory.path, - label, - children: QLTestAdapter.createTestOrSuiteInfos(testDirectory.children), - tooltip: testDirectory.path, - }; - } - - public async load(): Promise { - this.discoverTests(); - } - - private discoverTests(): void { - this._tests.fire({ type: "started" } as TestLoadStartedEvent); - - const testDirectory = this.qlTestDiscovery.testDirectory; - let testSuite: TestSuiteInfo | undefined; - if (testDirectory?.children.length) { - const children = QLTestAdapter.createTestOrSuiteInfos( - testDirectory.children, - ); - testSuite = { - type: "suite", - label: "CodeQL", - id: testDirectory.path, - children, - }; - } - this._tests.fire({ - type: "finished", - suite: testSuite, - } as TestLoadFinishedEvent); - } - - public async run(tests: string[]): Promise { - if (this.runningTask !== undefined) { - throw new Error("Tests already running."); - } - - testLogger.outputChannel.clear(); - testLogger.outputChannel.show(true); - - this.runningTask = this.track(new CancellationTokenSource()); - const token = this.runningTask.token; - - this._testStates.fire({ - type: "started", - tests, - } as TestRunStartedEvent); - - await this.testRunner.run(tests, testLogger, token, (event) => - this.processTestEvent(event), - ); - - this._testStates.fire({ type: "finished" } as TestRunFinishedEvent); - this.clearTask(); - } - - private clearTask(): void { - if (this.runningTask !== undefined) { - const runningTask = this.runningTask; - this.runningTask = undefined; - this.disposeAndStopTracking(runningTask); - } - } - - public cancel(): void { - if (this.runningTask !== undefined) { - void testLogger.log("Cancelling test run..."); - this.runningTask.cancel(); - this.clearTask(); - } - } - - private async processTestEvent(event: TestCompleted): Promise { - const state = event.pass - ? "passed" - : event.messages?.length - ? "errored" - : "failed"; - let message: string | undefined; - if (event.failureDescription || event.diff?.length) { - message = - event.failureStage === "RESULT" - ? [ - "", - `${state}: ${event.test}`, - event.failureDescription || event.diff?.join("\n"), - "", - ].join("\n") - : [ - "", - `${event.failureStage?.toLowerCase() ?? "unknown stage"} error: ${ - event.test - }`, - event.failureDescription || - `${event.messages[0].severity}: ${event.messages[0].message}`, - "", - ].join("\n"); - void testLogger.log(message); - } - this._testStates.fire({ - type: "test", - state, - test: event.test, - message, - decorations: event.messages?.map((msg) => ({ - line: msg.position.line, - message: msg.message, - })), - }); - } -} diff --git a/extensions/ql-vscode/src/query-testing/test-manager-base.ts b/extensions/ql-vscode/src/query-testing/test-manager-base.ts index 75d92d4f4..e7f7e18a3 100644 --- a/extensions/ql-vscode/src/query-testing/test-manager-base.ts +++ b/extensions/ql-vscode/src/query-testing/test-manager-base.ts @@ -6,9 +6,8 @@ import type { TestItem, TextDocumentShowOptions } from "vscode"; import { Uri, window } from "vscode"; import { basename } from "path"; import type { App } from "../common/app"; -import type { TestTreeNode } from "./test-tree-node"; -type TestNode = TestTreeNode | TestItem; +type TestNode = TestItem; /** * Base class for both the legacy and new test services. Implements commands that are common to diff --git a/extensions/ql-vscode/src/query-testing/test-tree-node.ts b/extensions/ql-vscode/src/query-testing/test-tree-node.ts deleted file mode 100644 index 0cd6a6d54..000000000 --- a/extensions/ql-vscode/src/query-testing/test-tree-node.ts +++ /dev/null @@ -1,9 +0,0 @@ -import type { TestSuiteInfo, TestInfo } from "vscode-test-adapter-api"; - -/** - * Tree view node for a test, suite, or collection. This object is passed as the argument to the - * command handler of a context menu item for a tree view item. - */ -export interface TestTreeNode { - readonly info: TestSuiteInfo | TestInfo; -} diff --git a/extensions/ql-vscode/src/query-testing/test-ui.ts b/extensions/ql-vscode/src/query-testing/test-ui.ts deleted file mode 100644 index c45fc01a9..000000000 --- a/extensions/ql-vscode/src/query-testing/test-ui.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { - TestHub, - TestController, - TestAdapter, - TestRunStartedEvent, - TestRunFinishedEvent, - TestEvent, - TestSuiteEvent, -} from "vscode-test-adapter-api"; -import type { TestTreeNode } from "./test-tree-node"; -import { DisposableObject } from "../common/disposable-object"; -import { QLTestAdapter } from "./test-adapter"; -import type { App } from "../common/app"; -import { TestManagerBase } from "./test-manager-base"; - -type VSCodeTestEvent = - | TestRunStartedEvent - | TestRunFinishedEvent - | TestSuiteEvent - | TestEvent; - -/** - * Test event listener. Currently unused, but left in to keep the plumbing hooked up for future use. - */ -class QLTestListener extends DisposableObject { - constructor(adapter: TestAdapter) { - super(); - - this.push(adapter.testStates(this.onTestStatesEvent, this)); - } - - private onTestStatesEvent(_e: VSCodeTestEvent): void { - /**/ - } -} - -/** - * Service that implements all UI and commands for QL tests. - */ -export class TestUIService extends TestManagerBase implements TestController { - private readonly listeners: Map = new Map(); - - public constructor( - app: App, - private readonly testHub: TestHub, - ) { - super(app); - - testHub.registerTestController(this); - } - - public dispose(): void { - this.testHub.unregisterTestController(this); - - super.dispose(); - } - - public registerTestAdapter(adapter: TestAdapter): void { - this.listeners.set(adapter, new QLTestListener(adapter)); - } - - public unregisterTestAdapter(adapter: TestAdapter): void { - if (adapter instanceof QLTestAdapter) { - this.listeners.delete(adapter); - } - } - - protected getTestPath(node: TestTreeNode): string { - return node.info.id; - } -} diff --git a/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts b/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts index 40bca3e6f..869ce06ba 100644 --- a/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts +++ b/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts @@ -50,20 +50,10 @@ export default class JestRunnerInstalledExtensions extends VSCodeTestRunner { const [cli, ...args] = resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); - spawnSync( - cli, - [ - ...args, - "--install-extension", - "hbenl.vscode-test-explorer", - "--install-extension", - "ms-vscode.test-adapter-converter", - ], - { - encoding: "utf-8", - stdio: "inherit", - }, - ); + spawnSync(cli, args, { + encoding: "utf-8", + stdio: "inherit", + }); installedOnVsCodeVersions.add(versionKey); } From 40ba2c03d37029dde1071df80e6d42f78bf003cb Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 11 Jan 2024 16:11:01 +0000 Subject: [PATCH 20/77] Rename variant analysis mapping functions (#3231) --- .../variant-analysis-manager.ts | 10 ++-- ...rocessor.ts => variant-analysis-mapper.ts} | 52 ++++++++----------- .../variant-analysis-monitor.ts | 4 +- ...est.ts => variant-analysis-mapper.test.ts} | 22 ++++---- .../variant-analysis-monitor.test.ts | 14 ++--- 5 files changed, 48 insertions(+), 54 deletions(-) rename extensions/ql-vscode/src/variant-analysis/{variant-analysis-processor.ts => variant-analysis-mapper.ts} (82%) rename extensions/ql-vscode/test/unit-tests/variant-analysis/{variant-analysis-processor.test.ts => variant-analysis-mapper.test.ts} (92%) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index c05a70e2b..654e4c0ad 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -43,9 +43,9 @@ import type { } from "./variant-analysis-results-manager"; import { getQueryName, prepareRemoteQueryRun } from "./run-remote-query"; import { - processVariantAnalysis, - processVariantAnalysisRepositoryTask, -} from "./variant-analysis-processor"; + mapVariantAnalysis, + mapVariantAnalysisRepositoryTask, +} from "./variant-analysis-mapper"; import PQueue from "p-queue"; import { createTimestampFile, saveBeforeStart } from "../run-queries-shared"; import { readFile, remove, pathExists } from "fs-extra"; @@ -279,7 +279,7 @@ export class VariantAnalysisManager throw e; } - const processedVariantAnalysis = processVariantAnalysis( + const processedVariantAnalysis = mapVariantAnalysis( variantAnalysisSubmission, variantAnalysisResponse, ); @@ -619,7 +619,7 @@ export class VariantAnalysisManager scannedRepo.repository.id, ); - repoTask = processVariantAnalysisRepositoryTask(repoTaskResponse); + repoTask = mapVariantAnalysisRepositoryTask(repoTaskResponse); } catch (e) { repoState.downloadStatus = VariantAnalysisScannedRepositoryDownloadStatus.Failed; diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-processor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts similarity index 82% rename from extensions/ql-vscode/src/variant-analysis/variant-analysis-processor.ts rename to extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts index f6b83136c..3a3db0f87 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-processor.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts @@ -23,11 +23,11 @@ import { VariantAnalysisRepoStatus, } from "./shared/variant-analysis"; -export function processVariantAnalysis( +export function mapVariantAnalysis( submission: VariantAnalysisSubmission, response: ApiVariantAnalysis, ): VariantAnalysis { - return processUpdatedVariantAnalysis( + return mapUpdatedVariantAnalysis( { query: { name: submission.query.name, @@ -43,7 +43,7 @@ export function processVariantAnalysis( ); } -export function processUpdatedVariantAnalysis( +export function mapUpdatedVariantAnalysis( previousVariantAnalysis: Pick< VariantAnalysis, "query" | "databases" | "executionStartTime" @@ -54,13 +54,13 @@ export function processUpdatedVariantAnalysis( let skippedRepos: VariantAnalysisSkippedRepositories = {}; if (response.scanned_repositories) { - scannedRepos = processScannedRepositories( + scannedRepos = mapScannedRepositories( response.scanned_repositories as ApiVariantAnalysisScannedRepository[], ); } if (response.skipped_repositories) { - skippedRepos = processSkippedRepositories( + skippedRepos = mapSkippedRepositories( response.skipped_repositories as ApiVariantAnalysisSkippedRepositories, ); } @@ -77,7 +77,7 @@ export function processUpdatedVariantAnalysis( executionStartTime: previousVariantAnalysis.executionStartTime, createdAt: response.created_at, updatedAt: response.updated_at, - status: processApiStatus(response.status), + status: mapApiStatus(response.status), completedAt: response.completed_at, actionsWorkflowRunId: response.actions_workflow_run_id, scannedRepos, @@ -85,15 +85,13 @@ export function processUpdatedVariantAnalysis( }; if (response.failure_reason) { - variantAnalysis.failureReason = processFailureReason( - response.failure_reason, - ); + variantAnalysis.failureReason = mapFailureReason(response.failure_reason); } return variantAnalysis; } -export function processVariantAnalysisRepositoryTask( +export function mapVariantAnalysisRepositoryTask( response: ApiVariantAnalysisRepoTask, ): VariantAnalysisRepositoryTask { return { @@ -102,7 +100,7 @@ export function processVariantAnalysisRepositoryTask( fullName: response.repository.full_name, private: response.repository.private, }, - analysisStatus: processApiRepoStatus(response.analysis_status), + analysisStatus: mapApiRepoStatus(response.analysis_status), resultCount: response.result_count, artifactSizeInBytes: response.artifact_size_in_bytes, failureMessage: response.failure_message, @@ -112,7 +110,7 @@ export function processVariantAnalysisRepositoryTask( }; } -export function processScannedRepository( +export function mapScannedRepository( scannedRepo: ApiVariantAnalysisScannedRepository, ): VariantAnalysisScannedRepository { return { @@ -123,33 +121,31 @@ export function processScannedRepository( stargazersCount: scannedRepo.repository.stargazers_count, updatedAt: scannedRepo.repository.updated_at, }, - analysisStatus: processApiRepoStatus(scannedRepo.analysis_status), + analysisStatus: mapApiRepoStatus(scannedRepo.analysis_status), resultCount: scannedRepo.result_count, artifactSizeInBytes: scannedRepo.artifact_size_in_bytes, failureMessage: scannedRepo.failure_message, }; } -function processScannedRepositories( +function mapScannedRepositories( scannedRepos: ApiVariantAnalysisScannedRepository[], ): VariantAnalysisScannedRepository[] { - return scannedRepos.map((scannedRepo) => - processScannedRepository(scannedRepo), - ); + return scannedRepos.map((scannedRepo) => mapScannedRepository(scannedRepo)); } -function processSkippedRepositories( +function mapSkippedRepositories( skippedRepos: ApiVariantAnalysisSkippedRepositories, ): VariantAnalysisSkippedRepositories { return { - accessMismatchRepos: processRepoGroup(skippedRepos.access_mismatch_repos), - notFoundRepos: processNotFoundRepoGroup(skippedRepos.not_found_repos), - noCodeqlDbRepos: processRepoGroup(skippedRepos.no_codeql_db_repos), - overLimitRepos: processRepoGroup(skippedRepos.over_limit_repos), + accessMismatchRepos: mapRepoGroup(skippedRepos.access_mismatch_repos), + notFoundRepos: mapNotFoundRepoGroup(skippedRepos.not_found_repos), + noCodeqlDbRepos: mapRepoGroup(skippedRepos.no_codeql_db_repos), + overLimitRepos: mapRepoGroup(skippedRepos.over_limit_repos), }; } -function processRepoGroup( +function mapRepoGroup( repoGroup: ApiVariantAnalysisSkippedRepositoryGroup | undefined, ): VariantAnalysisSkippedRepositoryGroup | undefined { if (!repoGroup) { @@ -172,7 +168,7 @@ function processRepoGroup( }; } -function processNotFoundRepoGroup( +function mapNotFoundRepoGroup( repoGroup: ApiVariantAnalysisNotFoundRepositoryGroup | undefined, ): VariantAnalysisSkippedRepositoryGroup | undefined { if (!repoGroup) { @@ -191,7 +187,7 @@ function processNotFoundRepoGroup( }; } -function processApiRepoStatus( +function mapApiRepoStatus( analysisStatus: ApiVariantAnalysisRepoStatus, ): VariantAnalysisRepoStatus { switch (analysisStatus) { @@ -210,9 +206,7 @@ function processApiRepoStatus( } } -function processApiStatus( - status: ApiVariantAnalysisStatus, -): VariantAnalysisStatus { +function mapApiStatus(status: ApiVariantAnalysisStatus): VariantAnalysisStatus { if (status === "succeeded") { return VariantAnalysisStatus.Succeeded; } else if (status === "in_progress") { @@ -226,7 +220,7 @@ function processApiStatus( } } -export function processFailureReason( +export function mapFailureReason( failureReason: ApiVariantAnalysisFailureReason, ): VariantAnalysisFailureReason { switch (failureReason) { diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts index f2ce075b5..490cf883d 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-monitor.ts @@ -11,7 +11,7 @@ import { repoHasDownloadableArtifact, } from "./shared/variant-analysis"; import type { VariantAnalysis as ApiVariantAnalysis } from "./gh-api/variant-analysis"; -import { processUpdatedVariantAnalysis } from "./variant-analysis-processor"; +import { mapUpdatedVariantAnalysis } from "./variant-analysis-mapper"; import { DisposableObject } from "../common/disposable-object"; import { sleep } from "../common/time"; import { getErrorMessage } from "../common/helpers-pure"; @@ -119,7 +119,7 @@ export class VariantAnalysisMonitor extends DisposableObject { continue; } - variantAnalysis = processUpdatedVariantAnalysis( + variantAnalysis = mapUpdatedVariantAnalysis( variantAnalysis, variantAnalysisSummary, ); diff --git a/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-processor.test.ts b/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts similarity index 92% rename from extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-processor.test.ts rename to extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts index caa742ceb..e443d7166 100644 --- a/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-processor.test.ts +++ b/extensions/ql-vscode/test/unit-tests/variant-analysis/variant-analysis-mapper.test.ts @@ -3,10 +3,10 @@ import type { VariantAnalysisScannedRepository as ApiVariantAnalysisScannedRepos import type { VariantAnalysisScannedRepository } from "../../../src/variant-analysis/shared/variant-analysis"; import { VariantAnalysisRepoStatus } from "../../../src/variant-analysis/shared/variant-analysis"; import { - processScannedRepository, - processVariantAnalysis, - processVariantAnalysisRepositoryTask, -} from "../../../src/variant-analysis/variant-analysis-processor"; + mapScannedRepository, + mapVariantAnalysis, + mapVariantAnalysisRepositoryTask, +} from "../../../src/variant-analysis/variant-analysis-mapper"; import { createMockScannedRepo, createMockScannedRepos, @@ -17,7 +17,7 @@ import { createMockSubmission } from "../../factories/variant-analysis/shared/va import { createMockVariantAnalysisRepoTask } from "../../factories/variant-analysis/gh-api/variant-analysis-repo-task"; import { QueryLanguage } from "../../../src/common/query-language"; -describe(processVariantAnalysis.name, () => { +describe(mapVariantAnalysis.name, () => { const scannedRepos = createMockScannedRepos(); const skippedRepos = createMockSkippedRepos(); const mockApiResponse = createMockApiResponse( @@ -27,8 +27,8 @@ describe(processVariantAnalysis.name, () => { ); const mockSubmission = createMockSubmission(); - it("should process an API response and return a variant analysis", () => { - const result = processVariantAnalysis(mockSubmission, mockApiResponse); + it("should map an API response and return a variant analysis", () => { + const result = mapVariantAnalysis(mockSubmission, mockApiResponse); const { access_mismatch_repos, @@ -173,11 +173,11 @@ describe(processVariantAnalysis.name, () => { } }); -describe(processVariantAnalysisRepositoryTask.name, () => { +describe(mapVariantAnalysisRepositoryTask.name, () => { const mockApiResponse = createMockVariantAnalysisRepoTask(); it("should return the correct result", () => { - expect(processVariantAnalysisRepositoryTask(mockApiResponse)).toEqual({ + expect(mapVariantAnalysisRepositoryTask(mockApiResponse)).toEqual({ repository: { id: mockApiResponse.repository.id, fullName: mockApiResponse.repository.full_name, @@ -194,7 +194,7 @@ describe(processVariantAnalysisRepositoryTask.name, () => { }); }); -describe(processScannedRepository.name, () => { +describe(mapScannedRepository.name, () => { const mockApiResponse = createMockScannedRepo( faker.word.sample(), faker.datatype.boolean(), @@ -202,7 +202,7 @@ describe(processScannedRepository.name, () => { ); it("should return the correct result", () => { - expect(processScannedRepository(mockApiResponse)).toEqual({ + expect(mapScannedRepository(mockApiResponse)).toEqual({ repository: { id: mockApiResponse.repository.id, fullName: mockApiResponse.repository.full_name, diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index 52b58f9ca..3042d225d 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -14,10 +14,10 @@ import type { VariantAnalysis } from "../../../../src/variant-analysis/shared/va import { VariantAnalysisStatus } from "../../../../src/variant-analysis/shared/variant-analysis"; import { createMockScannedRepos } from "../../../factories/variant-analysis/gh-api/scanned-repositories"; import { - processFailureReason, - processScannedRepository, - processUpdatedVariantAnalysis, -} from "../../../../src/variant-analysis/variant-analysis-processor"; + mapFailureReason, + mapScannedRepository, + mapUpdatedVariantAnalysis, +} from "../../../../src/variant-analysis/variant-analysis-mapper"; import { createMockVariantAnalysis } from "../../../factories/variant-analysis/shared/variant-analysis"; import { createMockApp } from "../../../__mocks__/appMock"; import { createMockCommandManager } from "../../../__mocks__/commandsMock"; @@ -88,7 +88,7 @@ describe("Variant Analysis Monitor", () => { expect(onVariantAnalysisChangeSpy).toHaveBeenCalledWith( expect.objectContaining({ status: VariantAnalysisStatus.Failed, - failureReason: processFailureReason( + failureReason: mapFailureReason( mockFailedApiResponse.failure_reason as VariantAnalysisFailureReason, ), }), @@ -127,8 +127,8 @@ describe("Variant Analysis Monitor", () => { expect(mockEecuteCommand).toHaveBeenNthCalledWith( index + 1, "codeQL.autoDownloadVariantAnalysisResult", - processScannedRepository(succeededRepo), - processUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), + mapScannedRepository(succeededRepo), + mapUpdatedVariantAnalysis(variantAnalysis, mockApiResponse), ); }); }); From d8968db1b9464d8e275a8426ba357e121221d7d0 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 11:34:35 -0500 Subject: [PATCH 21/77] Changelog entry --- extensions/ql-vscode/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 2631c77dd..1ce3a9f6d 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -3,6 +3,9 @@ ## [UNRELEASED] - If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) +- The UI for browsing and running CodeQL tests has moved to use VS Code's built-in test UI. This makes the CodeQL test UI more consistent with the test UIs for other languages. + This change means that this extension no longer depends on the "Test Explorer UI" and "Test Adapter Converter" extensions. You can uninstall those two extensions if they are + not being used by any other extensions you may have installed. ## 1.12.0 - 11 January 2024 From c485e39b07534c3047c439a56f12e9463c9dcb14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 08:42:57 -0800 Subject: [PATCH 22/77] Bump @testing-library/dom from 9.3.3 to 9.3.4 in /extensions/ql-vscode (#3225) Bumps [@testing-library/dom](https://github.com/testing-library/dom-testing-library) from 9.3.3 to 9.3.4. - [Release notes](https://github.com/testing-library/dom-testing-library/releases) - [Changelog](https://github.com/testing-library/dom-testing-library/blob/main/CHANGELOG.md) - [Commits](https://github.com/testing-library/dom-testing-library/compare/v9.3.3...v9.3.4) --- updated-dependencies: - dependency-name: "@testing-library/dom" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 90ddca7ea..560f42c85 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -65,7 +65,7 @@ "@storybook/react": "^7.1.0", "@storybook/react-webpack5": "^7.6.7", "@storybook/theming": "^7.6.7", - "@testing-library/dom": "^9.3.0", + "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.2", @@ -8594,9 +8594,9 @@ "dev": true }, "node_modules/@testing-library/dom": { - "version": "9.3.3", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz", - "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", + "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", "dev": true, "dependencies": { "@babel/code-frame": "^7.10.4", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 0a5901658..4e530de10 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1963,7 +1963,7 @@ "@storybook/react": "^7.1.0", "@storybook/react-webpack5": "^7.6.7", "@storybook/theming": "^7.6.7", - "@testing-library/dom": "^9.3.0", + "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.0.0", "@testing-library/user-event": "^14.5.2", From 766f54b76e22a0ab840d5678360746b13a122ae2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 08:43:22 -0800 Subject: [PATCH 23/77] Bump @storybook/manager-api from 7.6.6 to 7.6.7 in /extensions/ql-vscode (#3226) Bumps [@storybook/manager-api](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/manager-api) from 7.6.6 to 7.6.7. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v7.6.7/code/lib/manager-api) --- updated-dependencies: - dependency-name: "@storybook/manager-api" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 85 ++++++++++---------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 33 insertions(+), 54 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 560f42c85..9c659eb4f 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -61,7 +61,7 @@ "@storybook/addon-links": "^7.1.0", "@storybook/components": "^7.6.7", "@storybook/csf": "^0.1.1", - "@storybook/manager-api": "^7.6.6", + "@storybook/manager-api": "^7.6.7", "@storybook/react": "^7.1.0", "@storybook/react-webpack5": "^7.6.7", "@storybook/theming": "^7.6.7", @@ -7365,23 +7365,22 @@ } }, "node_modules/@storybook/manager-api": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.6.tgz", - "integrity": "sha512-euRAbSZAUzHDt6z1Pq/g45N/RNqta9RaQAym18zt/oLWiYOIrkLmdf7kCuFYsmuA5XQBytiJqwkAD7uF1aLe0g==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-7.6.7.tgz", + "integrity": "sha512-3Wk/BvuGUlw/X05s57zZO7gJbzfUeE9Xe+CSIvuH7RY5jx9PYnNwqNlTXPXhJ5LPvwMthae7WJVn3SuBpbptoQ==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.6", - "@storybook/client-logger": "7.6.6", - "@storybook/core-events": "7.6.6", + "@storybook/channels": "7.6.7", + "@storybook/client-logger": "7.6.7", + "@storybook/core-events": "7.6.7", "@storybook/csf": "^0.1.2", "@storybook/global": "^5.0.0", - "@storybook/router": "7.6.6", - "@storybook/theming": "7.6.6", - "@storybook/types": "7.6.6", + "@storybook/router": "7.6.7", + "@storybook/theming": "7.6.7", + "@storybook/types": "7.6.7", "dequal": "^2.0.2", "lodash": "^4.17.21", "memoizerific": "^1.11.3", - "semver": "^7.3.7", "store2": "^2.14.2", "telejson": "^7.2.0", "ts-dedent": "^2.0.0" @@ -7392,13 +7391,13 @@ } }, "node_modules/@storybook/manager-api/node_modules/@storybook/channels": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.6.tgz", - "integrity": "sha512-vvo7fBe2WffPonNNOA7Xx7jcHAto8qJYlq+VMysfheXrsRRbhHl3WQOA18Vm8hV9txtqdqk0hwQiXOWvhYVpeQ==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/channels/-/channels-7.6.7.tgz", + "integrity": "sha512-u1hURhfQHHtZyRIDUENRCp+CRRm7IQfcjQaoWI06XCevQPuhVEtFUfXHjG+J74aA/JuuTLFUtqwNm1zGqbXTAQ==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.6", - "@storybook/core-events": "7.6.6", + "@storybook/client-logger": "7.6.7", + "@storybook/core-events": "7.6.7", "@storybook/global": "^5.0.0", "qs": "^6.10.0", "telejson": "^7.2.0", @@ -7410,9 +7409,9 @@ } }, "node_modules/@storybook/manager-api/node_modules/@storybook/client-logger": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.6.tgz", - "integrity": "sha512-WEvVyuQR5oNF8jcMmGA13zDjxP/l46kOBBvB6JSc8toUdtLZ/kZWSnU0ioNM8+ECpFqXHjBcF2K6uSJOEb6YEg==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.7.tgz", + "integrity": "sha512-A16zpWgsa0gSdXMR9P3bWVdC9u/1B1oG4H7Z1+JhNzgnL3CdyOYO0qFSiAtNBso4nOjIAJVb6/AoBzdRhmSVQg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" @@ -7423,9 +7422,9 @@ } }, "node_modules/@storybook/manager-api/node_modules/@storybook/core-events": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.6.tgz", - "integrity": "sha512-7+q9HiZiLxaQcwpaSLQrLdjHNHBoOoUY9ZcZXI9iNFSopOgb/ItDnzzlpv08NC7CbKae1hVKJM/t5aSTl7tCMw==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/core-events/-/core-events-7.6.7.tgz", + "integrity": "sha512-KZ5d03c47pnr5/kY26pJtWq7WpmCPXLbgyjJZDSc+TTY153BdZksvlBXRHtqM1yj2UM6QsSyIuiJaADJNAbP2w==", "dev": true, "dependencies": { "ts-dedent": "^2.0.0" @@ -7435,33 +7434,13 @@ "url": "https://opencollective.com/storybook" } }, - "node_modules/@storybook/manager-api/node_modules/@storybook/theming": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.6.tgz", - "integrity": "sha512-hNZOOxaF55iAGUEM0dvAIP6LfGMgPKCJQIk/qyotFk+SKkg3PBqzph89XfFl9yCD3KiX5cryqarULgVuNawLJg==", - "dev": true, - "dependencies": { - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.6", - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/@storybook/manager-api/node_modules/@storybook/types": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.6.tgz", - "integrity": "sha512-77vbQp3GX93OD8UzFkY4a0fAmkZrqLe61XVo6yABrwbVDY0EcAwaCF5gcXRhOHldlH7KYbLfEQkDkkKTBjX7ow==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.7.tgz", + "integrity": "sha512-VcGwrI4AkBENxkoAUJ+Z7SyMK73hpoY0TTtw2J7tc05/xdiXhkQTX15Qa12IBWIkoXCyNrtaU+q7KR8Tjzi+uw==", "dev": true, "dependencies": { - "@storybook/channels": "7.6.6", + "@storybook/channels": "7.6.7", "@types/babel__core": "^7.0.0", "@types/express": "^4.7.0", "file-system-cache": "2.3.0" @@ -8109,12 +8088,12 @@ } }, "node_modules/@storybook/router": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.6.tgz", - "integrity": "sha512-dkn81MtxrG7JMDbOHEcVZkTDVKsneg72CyqJ8ELZfC81iKQcDMQkV9mdmnMl45aKn6UrscudI4K23OxQmsevkw==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/router/-/router-7.6.7.tgz", + "integrity": "sha512-kkhNSdC3fXaQxILg8a26RKk4/ZbF/AUVrepUEyO8lwvbJ6LItTyWSE/4I9Ih4qV2Mjx33ncc8vLqM9p8r5qnMA==", "dev": true, "dependencies": { - "@storybook/client-logger": "7.6.6", + "@storybook/client-logger": "7.6.7", "memoizerific": "^1.11.3", "qs": "^6.10.0" }, @@ -8124,9 +8103,9 @@ } }, "node_modules/@storybook/router/node_modules/@storybook/client-logger": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.6.tgz", - "integrity": "sha512-WEvVyuQR5oNF8jcMmGA13zDjxP/l46kOBBvB6JSc8toUdtLZ/kZWSnU0ioNM8+ECpFqXHjBcF2K6uSJOEb6YEg==", + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.7.tgz", + "integrity": "sha512-A16zpWgsa0gSdXMR9P3bWVdC9u/1B1oG4H7Z1+JhNzgnL3CdyOYO0qFSiAtNBso4nOjIAJVb6/AoBzdRhmSVQg==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 4e530de10..cf2cf1a42 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1959,7 +1959,7 @@ "@storybook/addon-links": "^7.1.0", "@storybook/components": "^7.6.7", "@storybook/csf": "^0.1.1", - "@storybook/manager-api": "^7.6.6", + "@storybook/manager-api": "^7.6.7", "@storybook/react": "^7.1.0", "@storybook/react-webpack5": "^7.6.7", "@storybook/theming": "^7.6.7", From 0d2b7916eeb504e25886f51cc5125b4c80a88c97 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 08:43:49 -0800 Subject: [PATCH 24/77] Bump msw from 2.0.11 to 2.0.13 in /extensions/ql-vscode (#3227) Bumps [msw](https://github.com/mswjs/msw) from 2.0.11 to 2.0.13. - [Release notes](https://github.com/mswjs/msw/releases) - [Changelog](https://github.com/mswjs/msw/blob/main/CHANGELOG.md) - [Commits](https://github.com/mswjs/msw/compare/v2.0.11...v2.0.13) --- updated-dependencies: - dependency-name: msw dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 24 ++++++++++++------------ extensions/ql-vscode/package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 9c659eb4f..d616c0b50 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -23,7 +23,7 @@ "d3-graphviz": "^5.0.2", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", - "msw": "^2.0.11", + "msw": "^2.0.13", "nanoid": "^5.0.1", "node-fetch": "^2.6.7", "p-queue": "^8.0.1", @@ -3988,9 +3988,9 @@ } }, "node_modules/@mswjs/interceptors": { - "version": "0.25.13", - "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.13.tgz", - "integrity": "sha512-xfjR81WwXPHwhDbqJRHlxYmboJuiSaIKpP4I5TJVFl/EmByOU13jOBT9hmEnxcjR3jvFYoqoNKt7MM9uqerj9A==", + "version": "0.25.14", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.25.14.tgz", + "integrity": "sha512-2dnIxl+obqIqjoPXTFldhe6pcdOrqiz+GcLaQQ6hmL02OldAF7nIC+rUgTWm+iF6lvmyCVhFFqbgbapNhR8eag==", "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", @@ -24997,16 +24997,16 @@ "dev": true }, "node_modules/msw": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/msw/-/msw-2.0.11.tgz", - "integrity": "sha512-dAXFS2DxZX0uFqMPhS3oUAu8S/5IQ5qKKSwtXl3/dMTeML0C8JfSvbeWtowYg6pu4Iehgp5L/pHLrlIcG++y/A==", + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.0.13.tgz", + "integrity": "sha512-FN4GUOTxm+cucXsFFNIZooHWNGGGRZCa5HxcrbdPxSIZMmGkPW2XewidZPcQn6AXO5SisZtfijXFGDlme/BbUw==", "hasInstallScript": true, "dependencies": { "@bundled-es-modules/cookie": "^2.0.0", "@bundled-es-modules/js-levenshtein": "^2.0.1", "@bundled-es-modules/statuses": "^1.0.1", "@mswjs/cookies": "^1.1.0", - "@mswjs/interceptors": "^0.25.13", + "@mswjs/interceptors": "^0.25.14", "@open-draft/until": "^2.1.0", "@types/cookie": "^0.4.1", "@types/js-levenshtein": "^1.1.1", @@ -25035,7 +25035,7 @@ "url": "https://opencollective.com/mswjs" }, "peerDependencies": { - "typescript": ">= 4.7.x <= 5.2.x" + "typescript": ">= 4.7.x <= 5.3.x" }, "peerDependenciesMeta": { "typescript": { @@ -26130,9 +26130,9 @@ } }, "node_modules/outvariant": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.0.tgz", - "integrity": "sha512-AlWY719RF02ujitly7Kk/0QlV+pXGFDHrHf9O2OKqyqgBieaPOIeuSkL8sRK6j2WK+/ZAURq2kZsY0d8JapUiw==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.2.tgz", + "integrity": "sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ==" }, "node_modules/p-limit": { "version": "3.1.0", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index cf2cf1a42..8aea5a20f 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1921,7 +1921,7 @@ "d3-graphviz": "^5.0.2", "fs-extra": "^11.1.1", "js-yaml": "^4.1.0", - "msw": "^2.0.11", + "msw": "^2.0.13", "nanoid": "^5.0.1", "node-fetch": "^2.6.7", "p-queue": "^8.0.1", From c065c44ff35209456b12315a8f8f1160bc5c342a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 Jan 2024 08:44:11 -0800 Subject: [PATCH 25/77] Bump applicationinsights from 2.9.1 to 2.9.2 in /extensions/ql-vscode (#3228) Bumps [applicationinsights](https://github.com/microsoft/ApplicationInsights-node.js) from 2.9.1 to 2.9.2. - [Release notes](https://github.com/microsoft/ApplicationInsights-node.js/releases) - [Commits](https://github.com/microsoft/ApplicationInsights-node.js/compare/2.9.1...2.9.2) --- updated-dependencies: - dependency-name: applicationinsights dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 60 +++++++++++++------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index d616c0b50..6c0ff7077 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -98,7 +98,7 @@ "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", - "applicationinsights": "^2.3.5", + "applicationinsights": "^2.9.2", "cosmiconfig": "^9.0.0", "cross-env": "^7.0.3", "css-loader": "^6.8.1", @@ -4265,12 +4265,12 @@ } }, "node_modules/@opentelemetry/core": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.18.1.tgz", - "integrity": "sha512-kvnUqezHMhsQvdsnhnqTNfAJs3ox/isB0SVrM1dhVFw7SsB7TstuVa6fgWnN2GdPyilIFLUvvbTZoVRmx6eiRg==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.19.0.tgz", + "integrity": "sha512-w42AukJh3TP8R0IZZOVJVM/kMWu8g+lm4LzT70WtuKqhwq7KVhcDzZZuZinWZa6TtQCl7Smt2wolEYzpHabOgw==", "dev": true, "dependencies": { - "@opentelemetry/semantic-conventions": "1.18.1" + "@opentelemetry/semantic-conventions": "1.19.0" }, "engines": { "node": ">=14" @@ -4299,13 +4299,13 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.18.1.tgz", - "integrity": "sha512-JjbcQLYMttXcIabflLRuaw5oof5gToYV9fuXbcsoOeQ0BlbwUn6DAZi++PNsSz2jjPeASfDls10iaO/8BRIPRA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.19.0.tgz", + "integrity": "sha512-RgxvKuuMOf7nctOeOvpDjt2BpZvZGr9Y0vf7eGtY5XYZPkh2p7e2qub1S2IArdBMf9kEbz0SfycqCviOu9isqg==", "dev": true, "dependencies": { - "@opentelemetry/core": "1.18.1", - "@opentelemetry/semantic-conventions": "1.18.1" + "@opentelemetry/core": "1.19.0", + "@opentelemetry/semantic-conventions": "1.19.0" }, "engines": { "node": ">=14" @@ -4315,14 +4315,14 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.18.1.tgz", - "integrity": "sha512-tRHfDxN5dO+nop78EWJpzZwHsN1ewrZRVVwo03VJa3JQZxToRDH29/+MB24+yoa+IArerdr7INFJiX/iN4gjqg==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.19.0.tgz", + "integrity": "sha512-+IRvUm+huJn2KqfFW3yW/cjvRwJ8Q7FzYHoUNx5Fr0Lws0LxjMJG1uVB8HDpLwm7mg5XXH2M5MF+0jj5cM8BpQ==", "dev": true, "dependencies": { - "@opentelemetry/core": "1.18.1", - "@opentelemetry/resources": "1.18.1", - "@opentelemetry/semantic-conventions": "1.18.1" + "@opentelemetry/core": "1.19.0", + "@opentelemetry/resources": "1.19.0", + "@opentelemetry/semantic-conventions": "1.19.0" }, "engines": { "node": ">=14" @@ -4332,9 +4332,9 @@ } }, "node_modules/@opentelemetry/semantic-conventions": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.18.1.tgz", - "integrity": "sha512-+NLGHr6VZwcgE/2lw8zDIufOCGnzsA5CbQIMleXZTrgkBd0TanCX+MiDYJ1TOS4KL/Tqk0nFRxawnaYr6pkZkA==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.19.0.tgz", + "integrity": "sha512-14jRpC8f5c0gPSwoZ7SbEJni1PqI+AhAE8m1bMz6v+RPM4OlP1PT2UHBJj5Qh/ALLPjhVU/aZUK3YyjTUqqQVg==", "dev": true, "engines": { "node": ">=14" @@ -11035,9 +11035,9 @@ } }, "node_modules/applicationinsights": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.9.1.tgz", - "integrity": "sha512-hrpe/OvHFZlq+SQERD1fxaYICyunxzEBh9SolJebzYnIXkyA9zxIR87dZAh+F3+weltbqdIP8W038cvtpMNhQg==", + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-2.9.2.tgz", + "integrity": "sha512-wlDiD7v0BQNM8oNzsf9C836R5ze25u+CuCEZsbA5xMIXYYBxkqkWE/mo9GFJM7rsKaiGqpxEwWmePHKD2Lwy2w==", "dev": true, "dependencies": { "@azure/core-auth": "^1.5.0", @@ -11045,14 +11045,14 @@ "@azure/core-util": "1.2.0", "@azure/opentelemetry-instrumentation-azure-sdk": "^1.0.0-beta.5", "@microsoft/applicationinsights-web-snippet": "^1.0.1", - "@opentelemetry/api": "^1.4.1", - "@opentelemetry/core": "^1.15.2", - "@opentelemetry/sdk-trace-base": "^1.15.2", - "@opentelemetry/semantic-conventions": "^1.15.2", + "@opentelemetry/api": "^1.7.0", + "@opentelemetry/core": "^1.19.0", + "@opentelemetry/sdk-trace-base": "^1.19.0", + "@opentelemetry/semantic-conventions": "^1.19.0", "cls-hooked": "^4.2.2", "continuation-local-storage": "^3.2.1", "diagnostic-channel": "1.1.1", - "diagnostic-channel-publishers": "1.0.7" + "diagnostic-channel-publishers": "1.0.8" }, "engines": { "node": ">=8.0.0" @@ -14765,9 +14765,9 @@ } }, "node_modules/diagnostic-channel-publishers": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.7.tgz", - "integrity": "sha512-SEECbY5AiVt6DfLkhkaHNeshg1CogdLLANA8xlG/TKvS+XUgvIKl7VspJGYiEdL5OUyzMVnr7o0AwB7f+/Mjtg==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-1.0.8.tgz", + "integrity": "sha512-HmSm9hXxSPxA9BaLGY98QU1zsdjeCk113KjAYGPCen1ZP6mhVaTPzHd6UYv5r21DnWANi+f+NyPOHruGT9jpqQ==", "dev": true, "peerDependencies": { "diagnostic-channel": "*" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 8aea5a20f..64fb11354 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1996,7 +1996,7 @@ "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", "ansi-colors": "^4.1.1", - "applicationinsights": "^2.3.5", + "applicationinsights": "^2.9.2", "cosmiconfig": "^9.0.0", "cross-env": "^7.0.3", "css-loader": "^6.8.1", From 2ba23ceead55208e616587be12696bcc9a28be66 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 11:48:54 -0500 Subject: [PATCH 26/77] Fold `TestManagerBase` and related functions into `TestManager` --- .../src/query-testing/test-adapter.ts | 35 ------- .../src/query-testing/test-manager-base.ts | 74 -------------- .../src/query-testing/test-manager.ts | 98 ++++++++++++++++++- 3 files changed, 93 insertions(+), 114 deletions(-) delete mode 100644 extensions/ql-vscode/src/query-testing/test-adapter.ts delete mode 100644 extensions/ql-vscode/src/query-testing/test-manager-base.ts diff --git a/extensions/ql-vscode/src/query-testing/test-adapter.ts b/extensions/ql-vscode/src/query-testing/test-adapter.ts deleted file mode 100644 index afb9b47b7..000000000 --- a/extensions/ql-vscode/src/query-testing/test-adapter.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { extname } from "path"; - -/** - * Get the full path of the `.expected` file for the specified QL test. - * @param testPath The full path to the test file. - */ -export function getExpectedFile(testPath: string): string { - return getTestOutputFile(testPath, ".expected"); -} - -/** - * Get the full path of the `.actual` file for the specified QL test. - * @param testPath The full path to the test file. - */ -export function getActualFile(testPath: string): string { - return getTestOutputFile(testPath, ".actual"); -} - -/** - * Gets the the full path to a particular output file of the specified QL test. - * @param testPath The full path to the QL test. - * @param extension The file extension of the output file. - */ -function getTestOutputFile(testPath: string, extension: string): string { - return changeExtension(testPath, extension); -} - -/** - * Change the file extension of the specified path. - * @param p The original file path. - * @param ext The new extension, including the `.`. - */ -function changeExtension(p: string, ext: string): string { - return p.slice(0, -extname(p).length) + ext; -} diff --git a/extensions/ql-vscode/src/query-testing/test-manager-base.ts b/extensions/ql-vscode/src/query-testing/test-manager-base.ts deleted file mode 100644 index e7f7e18a3..000000000 --- a/extensions/ql-vscode/src/query-testing/test-manager-base.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { copy, createFile, lstat, pathExists } from "fs-extra"; -import type { TestUICommands } from "../common/commands"; -import { DisposableObject } from "../common/disposable-object"; -import { getActualFile, getExpectedFile } from "./test-adapter"; -import type { TestItem, TextDocumentShowOptions } from "vscode"; -import { Uri, window } from "vscode"; -import { basename } from "path"; -import type { App } from "../common/app"; - -type TestNode = TestItem; - -/** - * Base class for both the legacy and new test services. Implements commands that are common to - * both. - */ -export abstract class TestManagerBase extends DisposableObject { - protected constructor(private readonly app: App) { - super(); - } - - public getCommands(): TestUICommands { - return { - "codeQLTests.showOutputDifferences": - this.showOutputDifferences.bind(this), - "codeQLTests.acceptOutput": this.acceptOutput.bind(this), - "codeQLTests.acceptOutputContextTestItem": this.acceptOutput.bind(this), - }; - } - - /** Override to compute the path of the test file from the selected node. */ - protected abstract getTestPath(node: TestNode): string; - - private async acceptOutput(node: TestNode): Promise { - const testPath = this.getTestPath(node); - const stat = await lstat(testPath); - if (stat.isFile()) { - const expectedPath = getExpectedFile(testPath); - const actualPath = getActualFile(testPath); - await copy(actualPath, expectedPath, { overwrite: true }); - } - } - - private async showOutputDifferences(node: TestNode): Promise { - const testId = this.getTestPath(node); - const stat = await lstat(testId); - if (stat.isFile()) { - const expectedPath = getExpectedFile(testId); - const expectedUri = Uri.file(expectedPath); - const actualPath = getActualFile(testId); - const options: TextDocumentShowOptions = { - preserveFocus: true, - preview: true, - }; - - if (!(await pathExists(expectedPath))) { - // Just create a new file. - await createFile(expectedPath); - } - - if (await pathExists(actualPath)) { - const actualUri = Uri.file(actualPath); - await this.app.commands.execute( - "vscode.diff", - expectedUri, - actualUri, - `Expected vs. Actual for ${basename(testId)}`, - options, - ); - } else { - await window.showTextDocument(expectedUri, options); - } - } - } -} diff --git a/extensions/ql-vscode/src/query-testing/test-manager.ts b/extensions/ql-vscode/src/query-testing/test-manager.ts index 12381c6d9..ff1a91d88 100644 --- a/extensions/ql-vscode/src/query-testing/test-manager.ts +++ b/extensions/ql-vscode/src/query-testing/test-manager.ts @@ -1,10 +1,11 @@ -import { readFile } from "fs-extra"; +import { copy, createFile, lstat, pathExists, readFile } from "fs-extra"; import type { CancellationToken, TestController, TestItem, TestRun, TestRunRequest, + TextDocumentShowOptions, WorkspaceFolder, WorkspaceFoldersChangeEvent, } from "vscode"; @@ -15,6 +16,7 @@ import { TestRunProfileKind, Uri, tests, + window, workspace, } from "vscode"; import { DisposableObject } from "../common/disposable-object"; @@ -23,11 +25,46 @@ import type { CodeQLCliServer } from "../codeql-cli/cli"; import { getErrorMessage } from "../common/helpers-pure"; import type { BaseLogger, LogOptions } from "../common/logging"; import type { TestRunner } from "./test-runner"; -import { TestManagerBase } from "./test-manager-base"; import type { App } from "../common/app"; import { isWorkspaceFolderOnDisk } from "../common/vscode/workspace-folders"; import type { FileTreeNode } from "../common/file-tree-nodes"; import { FileTreeDirectory, FileTreeLeaf } from "../common/file-tree-nodes"; +import type { TestUICommands } from "../common/commands"; +import { basename, extname } from "path"; + +/** + * Get the full path of the `.expected` file for the specified QL test. + * @param testPath The full path to the test file. + */ +function getExpectedFile(testPath: string): string { + return getTestOutputFile(testPath, ".expected"); +} + +/** + * Get the full path of the `.actual` file for the specified QL test. + * @param testPath The full path to the test file. + */ +function getActualFile(testPath: string): string { + return getTestOutputFile(testPath, ".actual"); +} + +/** + * Gets the the full path to a particular output file of the specified QL test. + * @param testPath The full path to the QL test. + * @param extension The file extension of the output file. + */ +function getTestOutputFile(testPath: string, extension: string): string { + return changeExtension(testPath, extension); +} + +/** + * Change the file extension of the specified path. + * @param p The original file path. + * @param ext The new extension, including the `.`. + */ +function changeExtension(p: string, ext: string): string { + return p.slice(0, -extname(p).length) + ext; +} /** * Returns the complete text content of the specified file. If there is an error reading the file, @@ -108,7 +145,7 @@ class WorkspaceFolderHandler extends DisposableObject { * Service that populates the VS Code "Test Explorer" panel for CodeQL, and handles running and * debugging of tests. */ -export class TestManager extends TestManagerBase { +export class TestManager extends DisposableObject { /** * Maps from each workspace folder being tracked to the `WorkspaceFolderHandler` responsible for * tracking it. @@ -119,7 +156,7 @@ export class TestManager extends TestManagerBase { >(); public constructor( - app: App, + private readonly app: App, private readonly testRunner: TestRunner, private readonly cliServer: CodeQLCliServer, // Having this as a parameter with a default value makes passing in a mock easier. @@ -128,7 +165,7 @@ export class TestManager extends TestManagerBase { "CodeQL Tests", ), ) { - super(app); + super(); this.testController.createRunProfile( "Run", @@ -151,6 +188,15 @@ export class TestManager extends TestManagerBase { super.dispose(); } + public getCommands(): TestUICommands { + return { + "codeQLTests.showOutputDifferences": + this.showOutputDifferences.bind(this), + "codeQLTests.acceptOutput": this.acceptOutput.bind(this), + "codeQLTests.acceptOutputContextTestItem": this.acceptOutput.bind(this), + }; + } + protected getTestPath(node: TestItem): string { if (node.uri === undefined || node.uri.scheme !== "file") { throw new Error("Selected test is not a CodeQL test."); @@ -158,6 +204,48 @@ export class TestManager extends TestManagerBase { return node.uri.fsPath; } + private async acceptOutput(node: TestItem): Promise { + const testPath = this.getTestPath(node); + const stat = await lstat(testPath); + if (stat.isFile()) { + const expectedPath = getExpectedFile(testPath); + const actualPath = getActualFile(testPath); + await copy(actualPath, expectedPath, { overwrite: true }); + } + } + + private async showOutputDifferences(node: TestItem): Promise { + const testId = this.getTestPath(node); + const stat = await lstat(testId); + if (stat.isFile()) { + const expectedPath = getExpectedFile(testId); + const expectedUri = Uri.file(expectedPath); + const actualPath = getActualFile(testId); + const options: TextDocumentShowOptions = { + preserveFocus: true, + preview: true, + }; + + if (!(await pathExists(expectedPath))) { + // Just create a new file. + await createFile(expectedPath); + } + + if (await pathExists(actualPath)) { + const actualUri = Uri.file(actualPath); + await this.app.commands.execute( + "vscode.diff", + expectedUri, + actualUri, + `Expected vs. Actual for ${basename(testId)}`, + options, + ); + } else { + await window.showTextDocument(expectedUri, options); + } + } + } + /** Start tracking tests in the specified workspace folders. */ private startTrackingWorkspaceFolders( workspaceFolders: readonly WorkspaceFolder[], From 1186026315dbfb37b0f57e9bb9fe8af00d9c6d1f Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 11:56:51 -0500 Subject: [PATCH 27/77] Remove old unit tests --- .../query-testing/test-adapter.test.ts | 52 +------------------ 1 file changed, 1 insertion(+), 51 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts index 038f06353..e293bbf1e 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts @@ -1,9 +1,4 @@ -import type { - TestItem, - TestItemCollection, - TestRun, - WorkspaceFolder, -} from "vscode"; +import type { TestItem, TestItemCollection, TestRun } from "vscode"; import { CancellationTokenSource, Range, @@ -12,7 +7,6 @@ import { tests, } from "vscode"; -import { QLTestAdapter } from "../../../../src/query-testing/test-adapter"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import type { DatabaseManager } from "../../../../src/databases/local-databases"; import { mockedObject } from "../../utils/mocking.helpers"; @@ -41,50 +35,6 @@ describe("test-adapter", () => { testRunner = new TestRunner(fakeDatabaseManager, fakeCliServer); }); - it("legacy test adapter should run some tests", async () => { - const adapter = new QLTestAdapter( - mockedObject({ - name: "ABC", - uri: Uri.parse("file:/ab/c"), - }), - testRunner, - fakeCliServer, - ); - - const listenerSpy = jest.fn(); - adapter.testStates(listenerSpy); - await adapter.run([mockTestsInfo.testsPath]); - - expect(listenerSpy).toBeCalledTimes(5); - - expect(listenerSpy).toHaveBeenNthCalledWith(1, { - type: "started", - tests: [mockTestsInfo.testsPath], - }); - expect(listenerSpy).toHaveBeenNthCalledWith(2, { - type: "test", - state: "passed", - test: mockTestsInfo.dPath, - message: undefined, - decorations: [], - }); - expect(listenerSpy).toHaveBeenNthCalledWith(3, { - type: "test", - state: "errored", - test: mockTestsInfo.gPath, - message: `\ncompilation error: ${mockTestsInfo.gPath}\nERROR: abc\n`, - decorations: [{ line: 1, message: "abc" }], - }); - expect(listenerSpy).toHaveBeenNthCalledWith(4, { - type: "test", - state: "failed", - test: mockTestsInfo.hPath, - message: `\nfailed: ${mockTestsInfo.hPath}\njkh\ntuv\n`, - decorations: [], - }); - expect(listenerSpy).toHaveBeenNthCalledWith(5, { type: "finished" }); - }); - it("native test manager should run some tests", async () => { const enqueuedSpy = jest.fn(); const passedSpy = jest.fn(); From fb63ec7db0a2e3354a6bcb72ebab7abb2ca145a0 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 16:57:42 -0500 Subject: [PATCH 28/77] Consume `codeql version` JSON output for feature capabilities --- .../ql-vscode/src/codeql-cli/cli-command.ts | 24 +++++-- .../ql-vscode/src/codeql-cli/cli-version.ts | 34 ++++++++-- extensions/ql-vscode/src/codeql-cli/cli.ts | 68 ++++++++++++++----- .../ql-vscode/src/codeql-cli/distribution.ts | 20 +++--- extensions/ql-vscode/src/extension.ts | 13 ++-- 5 files changed, 119 insertions(+), 40 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli-command.ts b/extensions/ql-vscode/src/codeql-cli/cli-command.ts index 1b4ab75f9..b211793e1 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli-command.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli-command.ts @@ -3,7 +3,10 @@ import { promisify } from "util"; import type { BaseLogger } from "../common/logging"; import type { ProgressReporter } from "../common/logging/vscode"; -import { getChildProcessErrorMessage } from "../common/helpers-pure"; +import { + getChildProcessErrorMessage, + getErrorMessage, +} from "../common/helpers-pure"; /** * Flags to pass to all cli commands. @@ -11,26 +14,27 @@ import { getChildProcessErrorMessage } from "../common/helpers-pure"; export const LOGGING_FLAGS = ["-v", "--log-to-stderr"]; /** - * Runs a CodeQL CLI command without invoking the CLI server, returning the output as a string. + * Runs a CodeQL CLI command without invoking the CLI server, deserializing the output as JSON. * @param codeQlPath The path to the CLI. * @param command The `codeql` command to be run, provided as an array of command/subcommand names. * @param commandArgs The arguments to pass to the `codeql` command. * @param description Description of the action being run, to be shown in log and error messages. * @param logger Logger to write command log messages, e.g. to an output channel. * @param progressReporter Used to output progress messages, e.g. to the status bar. - * @returns The contents of the command's stdout, if the command succeeded. + * @returns A JSON object parsed from the contents of the command's stdout, if the command succeeded. */ -export async function runCodeQlCliCommand( +export async function runJsonCodeQlCliCommand( codeQlPath: string, command: string[], commandArgs: string[], description: string, logger: BaseLogger, progressReporter?: ProgressReporter, -): Promise { +): Promise { // Add logging arguments first, in case commandArgs contains positional parameters. const args = command.concat(LOGGING_FLAGS).concat(commandArgs); const argsString = args.join(" "); + let stdout: string; try { if (progressReporter !== undefined) { progressReporter.report({ message: description }); @@ -41,10 +45,18 @@ export async function runCodeQlCliCommand( const result = await promisify(execFile)(codeQlPath, args); void logger.log(result.stderr); void logger.log("CLI command succeeded."); - return result.stdout; + stdout = result.stdout; } catch (err) { throw new Error( `${description} failed: ${getChildProcessErrorMessage(err)}`, ); } + + try { + return JSON.parse(stdout) as OutputType; + } catch (err) { + throw new Error( + `Parsing output of ${description} failed: ${getErrorMessage(err)}`, + ); + } } diff --git a/extensions/ql-vscode/src/codeql-cli/cli-version.ts b/extensions/ql-vscode/src/codeql-cli/cli-version.ts index 17e995d02..975ed7d79 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli-version.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli-version.ts @@ -1,25 +1,49 @@ import type { SemVer } from "semver"; import { parse } from "semver"; -import { runCodeQlCliCommand } from "./cli-command"; +import { runJsonCodeQlCliCommand } from "./cli-command"; import type { Logger } from "../common/logging"; import { getErrorMessage } from "../common/helpers-pure"; +interface VersionResult { + version: string; + features: CliFeatures | undefined; +} + +export interface CliFeatures { + featuresInVersionResult?: boolean; + mrvaPackCreate?: boolean; +} + +export interface VersionAndFeatures { + version: SemVer; + features: CliFeatures; +} + /** * Get the version of a CodeQL CLI. */ export async function getCodeQlCliVersion( codeQlPath: string, logger: Logger, -): Promise { +): Promise { try { - const output: string = await runCodeQlCliCommand( + const output: VersionResult = await runJsonCodeQlCliCommand( codeQlPath, ["version"], - ["--format=terse"], + ["--format=json"], "Checking CodeQL version", logger, ); - return parse(output.trim()) || undefined; + + const version = parse(output.version.trim()) || undefined; + if (version === undefined) { + return undefined; + } + + return { + version, + features: output.features ?? {}, + }; } catch (e) { // Failed to run the version command. This might happen if the cli version is _really_ old, or it is corrupted. // Either way, we can't determine compatibility. diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index e04e012ea..1db2fa0ca 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -34,6 +34,7 @@ import { QueryLanguage } from "../common/query-language"; import { LINE_ENDINGS, splitStreamAtSeparators } from "../common/split-stream"; import type { Position } from "../query-server/messages"; import { LOGGING_FLAGS } from "./cli-command"; +import type { CliFeatures, VersionAndFeatures } from "./cli-version"; /** * The version of the SARIF format that we are using. @@ -193,7 +194,9 @@ type OnLineCallback = ( line: string, ) => Promise | string | undefined; -type VersionChangedListener = (newVersion: SemVer | undefined) => void; +type VersionChangedListener = ( + newVersionAndFeatures: VersionAndFeatures | undefined, +) => void; /** * This class manages a cli server started by `codeql execute cli-server` to @@ -211,8 +214,8 @@ export class CodeQLCliServer implements Disposable { /** A buffer with a single null byte. */ nullBuffer: Buffer; - /** Version of current cli, lazily computed by the `getVersion()` method */ - private _version: SemVer | undefined; + /** Version of current cli and its supported features, lazily computed by the `getVersion()` method */ + private _versionAndFeatures: VersionAndFeatures | undefined; private _versionChangedListeners: VersionChangedListener[] = []; @@ -288,7 +291,7 @@ export class CodeQLCliServer implements Disposable { const callback = (): void => { try { this.killProcessIfRunning(); - this._version = undefined; + this._versionAndFeatures = undefined; this._supportedLanguages = undefined; } finally { this.runNext(); @@ -1417,6 +1420,27 @@ export class CodeQLCliServer implements Disposable { ); } + public async packCreate( + dir: string, + workspaceFolders: string[], + outputPath: string, + moreOptions: string[], + ): Promise { + const args = [ + "--output", + outputPath, + dir, + ...moreOptions, + ...this.getAdditionalPacksArg(workspaceFolders), + ]; + + return this.runJsonCodeQlCliCommandWithAuthentication( + ["pack", "create"], + args, + "Creating pack", + ); + } + async packBundle( dir: string, workspaceFolders: string[], @@ -1481,27 +1505,35 @@ export class CodeQLCliServer implements Disposable { ); } - public async getVersion() { - if (!this._version) { + public async getVersion(): Promise { + return (await this.getVersionAndFeatures()).version; + } + + public async getFeatures(): Promise { + return (await this.getVersionAndFeatures()).features; + } + + public async getVersionAndFeatures(): Promise { + if (!this._versionAndFeatures) { try { - const newVersion = await this.refreshVersion(); - this._version = newVersion; + const newVersionAndFeatures = await this.refreshVersion(); + this._versionAndFeatures = newVersionAndFeatures; this._versionChangedListeners.forEach((listener) => - listener(newVersion), + listener(newVersionAndFeatures), ); // this._version is only undefined upon config change, so we reset CLI-based context key only when necessary. await this.app.commands.execute( "setContext", "codeql.supportsQuickEvalCount", - newVersion.compare( + newVersionAndFeatures.version.compare( CliVersionConstraint.CLI_VERSION_WITH_QUICK_EVAL_COUNT, ) >= 0, ); await this.app.commands.execute( "setContext", "codeql.supportsTrimCache", - newVersion.compare( + newVersionAndFeatures.version.compare( CliVersionConstraint.CLI_VERSION_WITH_TRIM_CACHE, ) >= 0, ); @@ -1512,23 +1544,23 @@ export class CodeQLCliServer implements Disposable { throw e; } } - return this._version; + return this._versionAndFeatures; } public addVersionChangedListener(listener: VersionChangedListener) { - if (this._version) { - listener(this._version); + if (this._versionAndFeatures) { + listener(this._versionAndFeatures); } this._versionChangedListeners.push(listener); } - private async refreshVersion() { + private async refreshVersion(): Promise { const distribution = await this.distributionProvider.getDistribution(); switch (distribution.kind) { case FindDistributionResultKind.CompatibleDistribution: // eslint-disable-next-line no-fallthrough -- Intentional fallthrough case FindDistributionResultKind.IncompatibleDistribution: - return distribution.version; + return distribution.versionAndFeatures; default: // We should not get here because if no distributions are available, then @@ -1745,4 +1777,8 @@ export class CliVersionConstraint { CliVersionConstraint.CLI_VERSION_WITH_EXTENSIBLE_PREDICATE_METADATA, ); } + + async supportsMrvaPackCreate(): Promise { + return (await this.cli.getFeatures()).mrvaPackCreate === true; + } } diff --git a/extensions/ql-vscode/src/codeql-cli/distribution.ts b/extensions/ql-vscode/src/codeql-cli/distribution.ts index 108fb4e01..e69fca721 100644 --- a/extensions/ql-vscode/src/codeql-cli/distribution.ts +++ b/extensions/ql-vscode/src/codeql-cli/distribution.ts @@ -1,11 +1,11 @@ import { createWriteStream, mkdtemp, pathExists, remove } from "fs-extra"; import { tmpdir } from "os"; import { delimiter, dirname, join } from "path"; -import type { SemVer } from "semver"; import { Range, satisfies } from "semver"; import type { Event, ExtensionContext } from "vscode"; import type { DistributionConfig } from "../config"; import { extLogger } from "../common/logging/vscode"; +import type { VersionAndFeatures } from "./cli-version"; import { getCodeQlCliVersion } from "./cli-version"; import type { ProgressCallback } from "../common/vscode/progress"; import { reportStreamProgress } from "../common/vscode/progress"; @@ -88,11 +88,11 @@ export class DistributionManager implements DistributionProvider { kind: FindDistributionResultKind.NoDistribution, }; } - const version = await getCodeQlCliVersion( + const versionAndFeatures = await getCodeQlCliVersion( distribution.codeQlPath, extLogger, ); - if (version === undefined) { + if (versionAndFeatures === undefined) { return { distribution, kind: FindDistributionResultKind.UnknownCompatibilityDistribution, @@ -119,17 +119,21 @@ export class DistributionManager implements DistributionProvider { distribution.kind !== DistributionKind.ExtensionManaged || this.config.includePrerelease; - if (!satisfies(version, this.versionRange, { includePrerelease })) { + if ( + !satisfies(versionAndFeatures.version, this.versionRange, { + includePrerelease, + }) + ) { return { distribution, kind: FindDistributionResultKind.IncompatibleDistribution, - version, + versionAndFeatures, }; } return { distribution, kind: FindDistributionResultKind.CompatibleDistribution, - version, + versionAndFeatures, }; } @@ -599,7 +603,7 @@ interface DistributionResult { interface CompatibleDistributionResult extends DistributionResult { kind: FindDistributionResultKind.CompatibleDistribution; - version: SemVer; + versionAndFeatures: VersionAndFeatures; } interface UnknownCompatibilityDistributionResult extends DistributionResult { @@ -608,7 +612,7 @@ interface UnknownCompatibilityDistributionResult extends DistributionResult { interface IncompatibleDistributionResult extends DistributionResult { kind: FindDistributionResultKind.IncompatibleDistribution; - version: SemVer; + versionAndFeatures: VersionAndFeatures; } interface NoDistributionResult { diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 58be99fad..ecc4cda54 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -418,7 +418,7 @@ export async function activate( codeQlExtension.variantAnalysisManager, ); codeQlExtension.cliServer.addVersionChangedListener((ver) => { - telemetryListener.cliVersion = ver; + telemetryListener.cliVersion = ver?.version; }); let unsupportedWarningShown = false; @@ -431,13 +431,16 @@ export async function activate( return; } - if (CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare(ver) < 0) { + if ( + CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION.compare(ver.version) < + 0 + ) { return; } void showAndLogWarningMessage( extLogger, - `You are using an unsupported version of the CodeQL CLI (${ver}). ` + + `You are using an unsupported version of the CodeQL CLI (${ver.version}). ` + `The minimum supported version is ${CliVersionConstraint.OLDEST_SUPPORTED_CLI_VERSION}. ` + `Please upgrade to a newer version of the CodeQL CLI.`, ); @@ -592,7 +595,7 @@ async function getDistributionDisplayingDistributionWarnings( switch (result.kind) { case FindDistributionResultKind.CompatibleDistribution: void extLogger.log( - `Found compatible version of CodeQL CLI (version ${result.version.raw})`, + `Found compatible version of CodeQL CLI (version ${result.versionAndFeatures.version.raw})`, ); break; case FindDistributionResultKind.IncompatibleDistribution: { @@ -612,7 +615,7 @@ async function getDistributionDisplayingDistributionWarnings( void showAndLogWarningMessage( extLogger, - `The current version of the CodeQL CLI (${result.version.raw}) ` + + `The current version of the CodeQL CLI (${result.versionAndFeatures.version.raw}) ` + `is incompatible with this extension. ${fixGuidanceMessage}`, ); break; From 29d8c65b5957c6ef1516173669a8074dfff66d0a Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 17:43:42 -0500 Subject: [PATCH 29/77] Use `codeql pack create --mrva` if available --- .../src/variant-analysis/run-remote-query.ts | 217 +++++++++++------- 1 file changed, 138 insertions(+), 79 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 421dc54e7..b38e20aa9 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -3,6 +3,7 @@ import { Uri, window } from "vscode"; import { relative, join, sep, dirname, parse, basename } from "path"; import { dump, load } from "js-yaml"; import { copy, writeFile, readFile, mkdirp } from "fs-extra"; +import type { DirectoryResult } from "tmp-promise"; import { dir, tmpName } from "tmp-promise"; import { tmpDir } from "../tmp-dir"; import { getOnDiskWorkspaceFolders } from "../common/vscode/workspace-folders"; @@ -58,21 +59,53 @@ interface GeneratedQueryPack { async function generateQueryPack( cliServer: CodeQLCliServer, queryFile: string, - queryPackDir: string, + tmpDir: RemoteQueryTempDir, ): Promise { const originalPackRoot = await findPackRoot(queryFile); const packRelativePath = relative(originalPackRoot, queryFile); - const targetQueryFileName = join(queryPackDir, packRelativePath); const workspaceFolders = getOnDiskWorkspaceFolders(); + const extensionPacks = await getExtensionPacksToInject( + cliServer, + workspaceFolders, + ); - let language: QueryLanguage | undefined; + const mustSynthesizePack = + (await getQlPackPath(originalPackRoot)) === undefined; + const cliSupportsMrvaPackCreate = + await cliServer.cliConstraints.supportsMrvaPackCreate(); - // Check if the query is already in a query pack. - // If so, copy the entire query pack to the temporary directory. - // Otherwise, copy only the query file to the temporary directory - // and generate a synthetic query pack. - if (await getQlPackPath(originalPackRoot)) { - // don't include ql files. We only want the queryFile to be copied. + const language: QueryLanguage | undefined = mustSynthesizePack + ? await askForLanguage(cliServer) // open popup to ask for language if not already hardcoded + : await findLanguage(cliServer, Uri.file(queryFile)); + if (!language) { + throw new UserCancellationException("Could not determine language."); + } + + let queryPackDir: string; + let precompilationOpts: string[]; + let needsInstall: boolean; + if (mustSynthesizePack) { + // This section applies whether or not the CLI supports MRVA pack creation directly. + + queryPackDir = tmpDir.queryPackDir; + + // Synthesize a query pack for the query. + // copy only the query file to the query pack directory + // and generate a synthetic query pack + await createNewQueryPack( + queryFile, + queryPackDir, + language, + packRelativePath, + ); + // Clear the cliServer cache so that the previous qlpack text is purged from the CLI. + await cliServer.clearCache(); + + // Install packs, since we just synthesized a dependency on the language's standard library. + needsInstall = true; + } else if (!cliSupportsMrvaPackCreate) { + // We need to copy the query pack to a temporary directory and then fix it up to work with MRVA. + queryPackDir = tmpDir.queryPackDir; await copyExistingQueryPack( cliServer, originalPackRoot, @@ -81,61 +114,64 @@ async function generateQueryPack( packRelativePath, ); - language = await findLanguage(cliServer, Uri.file(targetQueryFileName)); + // We should already have all the dependencies available, but these older versions of the CLI + // have a bug where they will not search `--additional-packs` during validation in `codeql pack bundle`. + // Installing the packs will ensure that any extension packs get put in the right place. + needsInstall = true; } else { - // open popup to ask for language if not already hardcoded - language = await askForLanguage(cliServer); - - // copy only the query file to the query pack directory - // and generate a synthetic query pack - await createNewQueryPack( - queryFile, - queryPackDir, - targetQueryFileName, - language, - packRelativePath, - ); - } - if (!language) { - throw new UserCancellationException("Could not determine language."); + // The CLI supports creating a MRVA query pack directly from the source pack. + queryPackDir = originalPackRoot; + // We expect any dependencies to be available already. + needsInstall = false; } - // Clear the cliServer cache so that the previous qlpack text is purged from the CLI. - await cliServer.clearCache(); + if (needsInstall) { + // Install the dependencies of the synthesized query pack. + await cliServer.packInstall(queryPackDir, { + workspaceFolders, + }); - let precompilationOpts: string[] = []; - if (await cliServer.cliConstraints.usesGlobalCompilationCache()) { - precompilationOpts = ["--qlx"]; - } else { - const ccache = join(originalPackRoot, ".cache"); - precompilationOpts = [ - "--qlx", - "--no-default-compilation-cache", - `--compilation-cache=${ccache}`, - ]; + // Clear the CLI cache so that the most recent qlpack lock file is used. + await cliServer.clearCache(); } - if (await cliServer.useExtensionPacks()) { - await injectExtensionPacks(cliServer, queryPackDir, workspaceFolders); - } - - await cliServer.packInstall(queryPackDir, { - workspaceFolders, - }); - // Clear the CLI cache so that the most recent qlpack lock file is used. await cliServer.clearCache(); + if (cliSupportsMrvaPackCreate) { + precompilationOpts = [ + "--mrva", + "--query", + join(queryPackDir, packRelativePath), + // We need to specify the extension packs as dependencies so that they are included in the MRVA pack. + // The version range doesn't matter, since they'll always be found by source lookup. + ...extensionPacks.map((p) => `--extension-pack=${p}@*`), + ]; + } else { + if (await cliServer.cliConstraints.usesGlobalCompilationCache()) { + precompilationOpts = ["--qlx"]; + } else { + const ccache = join(originalPackRoot, ".cache"); + precompilationOpts = [ + "--qlx", + "--no-default-compilation-cache", + `--compilation-cache=${ccache}`, + ]; + } - const bundlePath = await getPackedBundlePath(queryPackDir); + if (extensionPacks.length > 0) { + await addExtensionPacksAsDependencies(queryPackDir, extensionPacks); + } + } + + const bundlePath = tmpDir.bundleFile; void extLogger.log( `Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`, ); - await cliServer.packBundle( - queryPackDir, - workspaceFolders, - bundlePath, - precompilationOpts, - ); + await cliServer.packBundle(queryPackDir, workspaceFolders, bundlePath, [ + "--pack-path", + tmpDir.compiledPackDir, + ...precompilationOpts, + ]); const base64Pack = (await readFile(bundlePath)).toString("base64"); return { base64Pack, @@ -146,11 +182,11 @@ async function generateQueryPack( async function createNewQueryPack( queryFile: string, queryPackDir: string, - targetQueryFileName: string, language: string | undefined, packRelativePath: string, ) { void extLogger.log(`Copying ${queryFile} to ${queryPackDir}`); + const targetQueryFileName = join(queryPackDir, packRelativePath); await copy(queryFile, targetQueryFileName); void extLogger.log("Generating synthetic query pack"); const syntheticQueryPack = { @@ -242,19 +278,28 @@ function isFileSystemRoot(dir: string): boolean { return pathObj.root === dir && pathObj.base === ""; } -async function createRemoteQueriesTempDirectory() { +interface RemoteQueryTempDir { + remoteQueryDir: DirectoryResult; + queryPackDir: string; + compiledPackDir: string; + bundleFile: string; +} + +async function createRemoteQueriesTempDirectory(): Promise { const remoteQueryDir = await dir({ dir: tmpDir.name, unsafeCleanup: true, }); const queryPackDir = join(remoteQueryDir.path, "query-pack"); await mkdirp(queryPackDir); - return { remoteQueryDir, queryPackDir }; + const compiledPackDir = join(remoteQueryDir.path, "compiled-pack"); + const bundleFile = await getPackedBundlePath(tmpDir.name); + return { remoteQueryDir, queryPackDir, compiledPackDir, bundleFile }; } -async function getPackedBundlePath(queryPackDir: string) { +async function getPackedBundlePath(remoteQueryDir: string): Promise { return tmpName({ - dir: dirname(queryPackDir), + dir: remoteQueryDir, postfix: "generated.tgz", prefix: "qlpack", }); @@ -314,15 +359,14 @@ export async function prepareRemoteQueryRun( throw new UserCancellationException("Cancelled"); } - const { remoteQueryDir, queryPackDir } = - await createRemoteQueriesTempDirectory(); + const tempDir = await createRemoteQueriesTempDirectory(); let pack: GeneratedQueryPack; try { - pack = await generateQueryPack(cliServer, queryFile, queryPackDir); + pack = await generateQueryPack(cliServer, queryFile, tempDir); } finally { - await remoteQueryDir.cleanup(); + await tempDir.remoteQueryDir.cleanup(); } const { base64Pack, language } = pack; @@ -389,11 +433,38 @@ async function fixPackFile( await writeFile(packPath, dump(qlpack)); } -async function injectExtensionPacks( +async function getExtensionPacksToInject( cliServer: CodeQLCliServer, - queryPackDir: string, workspaceFolders: string[], -) { +): Promise { + const result: string[] = []; + if (await cliServer.useExtensionPacks()) { + const extensionPacks = await cliServer.resolveQlpacks( + workspaceFolders, + true, + ); + Object.entries(extensionPacks).forEach(([name, paths]) => { + // We are guaranteed that there is at least one path found for each extension pack. + // If there are multiple paths, then we have a problem. This means that there is + // ambiguity in which path to use. This is an error. + if (paths.length > 1) { + throw new Error( + `Multiple versions of extension pack '${name}' found: ${paths.join( + ", ", + )}`, + ); + } + result.push(name); + }); + } + + return result; +} + +async function addExtensionPacksAsDependencies( + queryPackDir: string, + extensionPacks: string[], +): Promise { const qlpackFile = await getQlPackPath(queryPackDir); if (!qlpackFile) { throw new Error( @@ -402,24 +473,13 @@ async function injectExtensionPacks( )} file in '${queryPackDir}'`, ); } + const syntheticQueryPack = load( await readFile(qlpackFile, "utf8"), ) as QlPackFile; const dependencies = syntheticQueryPack.dependencies ?? {}; - - const extensionPacks = await cliServer.resolveQlpacks(workspaceFolders, true); - Object.entries(extensionPacks).forEach(([name, paths]) => { - // We are guaranteed that there is at least one path found for each extension pack. - // If there are multiple paths, then we have a problem. This means that there is - // ambiguity in which path to use. This is an error. - if (paths.length > 1) { - throw new Error( - `Multiple versions of extension pack '${name}' found: ${paths.join( - ", ", - )}`, - ); - } + extensionPacks.forEach((name) => { // Add this extension pack as a dependency. It doesn't matter which // version we specify, since we are guaranteed that the extension pack // is resolved from source at the given path. @@ -429,7 +489,6 @@ async function injectExtensionPacks( syntheticQueryPack.dependencies = dependencies; await writeFile(qlpackFile, dump(syntheticQueryPack)); - await cliServer.clearCache(); } function updateDefaultSuite(qlpack: QlPackFile, packRelativePath: string) { @@ -534,7 +593,7 @@ async function getControllerRepoFromApi( } } -function removeWorkspaceRefs(qlpack: QlPackFile) { +export function removeWorkspaceRefs(qlpack: QlPackFile) { if (!qlpack.dependencies) { return; } From bdc94a3b234d84d9f3350da8cc8c4930b28bdbc5 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 18:05:00 -0500 Subject: [PATCH 30/77] Remove unused export --- extensions/ql-vscode/src/variant-analysis/run-remote-query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index b38e20aa9..0b1f4f127 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -593,7 +593,7 @@ async function getControllerRepoFromApi( } } -export function removeWorkspaceRefs(qlpack: QlPackFile) { +function removeWorkspaceRefs(qlpack: QlPackFile) { if (!qlpack.dependencies) { return; } From 2ccd99fc5bb746084bd70002ab5b373b741e0352 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 18:40:03 -0500 Subject: [PATCH 31/77] Don't use `${workspace}` in test pack --- .../vscode-tests/cli-integration/data-remote-qlpack/qlpack.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/data-remote-qlpack/qlpack.yml b/extensions/ql-vscode/test/vscode-tests/cli-integration/data-remote-qlpack/qlpack.yml index 1b3f20eee..6558fe72c 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/data-remote-qlpack/qlpack.yml +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/data-remote-qlpack/qlpack.yml @@ -1,4 +1,4 @@ name: github/remote-query-pack version: 0.0.0 dependencies: - codeql/javascript-all: ${workspace} + codeql/javascript-all: "*" From 2aacea417644bf937c7d7053399a0c10c178241c Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Thu, 11 Jan 2024 18:58:44 -0500 Subject: [PATCH 32/77] Fix test expectation --- .../variant-analysis/variant-analysis-manager.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 2c854f9ea..47603e83d 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -364,9 +364,9 @@ describe("Variant Analysis Manager", () => { // Assume the first dependency to check is the core library. if (dependenciesToCheck.length > 0) { - expect(qlpackContents.dependencies?.[dependenciesToCheck[0]]).toEqual( - "*", - ); + expect( + qlpackContents.dependencies?.[dependenciesToCheck[0]], + ).not.toEqual("${workspace}"); } const qlpackLockContents = load( packFS.fileContents("codeql-pack.lock.yml").toString("utf-8"), From 0534cb751453d1a1e72147baa77cf06ad5d35aef Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Fri, 12 Jan 2024 09:49:14 +0100 Subject: [PATCH 33/77] Simplify custom Jest test runner The custom Jest test runner was originally written to install the required extensions for the CLI integration tests. This is no longer necessary as of https://github.com/github/vscode-codeql/pull/3232, so we can remove all code that deals with downloading VS Code and installing extensions. The download of VS Code will now be handled by the base `VSCodeTestRunner`. --- extensions/ql-vscode/scripts/find-deadcode.ts | 2 +- .../activated-extension/jest.config.ts | 2 +- .../cli-integration/jest.config.ts | 2 +- .../jest-runner-installed-extensions.ts | 65 ------------------- .../jest-runner-vscode-codeql-cli.ts | 20 ++++++ 5 files changed, 23 insertions(+), 68 deletions(-) delete mode 100644 extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts create mode 100644 extensions/ql-vscode/test/vscode-tests/jest-runner-vscode-codeql-cli.ts diff --git a/extensions/ql-vscode/scripts/find-deadcode.ts b/extensions/ql-vscode/scripts/find-deadcode.ts index 2c0375022..12e3fb03e 100644 --- a/extensions/ql-vscode/scripts/find-deadcode.ts +++ b/extensions/ql-vscode/scripts/find-deadcode.ts @@ -9,7 +9,7 @@ function ignoreFile(file: string): boolean { containsPath(".storybook", file) || containsPath(join("src", "stories"), file) || pathsEqual( - join("test", "vscode-tests", "jest-runner-installed-extensions.ts"), + join("test", "vscode-tests", "jest-runner-vscode-codeql-cli.ts"), file, ) || basename(file) === "jest.config.ts" || diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/jest.config.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/jest.config.ts index 3bd6d399c..e781b2cb3 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/jest.config.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/jest.config.ts @@ -4,7 +4,7 @@ import baseConfig from "../jest.config.base"; const config: Config = { ...baseConfig, - runner: "/../jest-runner-installed-extensions.ts", + runner: "/../jest-runner-vscode-codeql-cli.ts", setupFilesAfterEnv: ["/jest.setup.ts"], }; diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts index ff7eac9da..fe634b66c 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/jest.config.ts @@ -4,7 +4,7 @@ import baseConfig from "../jest.config.base"; const config: Config = { ...baseConfig, - runner: "/../jest-runner-installed-extensions.ts", + runner: "/../jest-runner-vscode-codeql-cli.ts", setupFilesAfterEnv: ["/jest.setup.ts"], // CLI integration tests call into the CLI and execute queries, so these are expected to take a lot longer // than the default 5 seconds. diff --git a/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts b/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts deleted file mode 100644 index 869ce06ba..000000000 --- a/extensions/ql-vscode/test/vscode-tests/jest-runner-installed-extensions.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { spawnSync } from "child_process"; -import { dirname } from "path"; - -import type * as JestRunner from "jest-runner"; -import type { RunnerOptions } from "jest-runner-vscode"; -import VSCodeTestRunner from "jest-runner-vscode"; -import { cosmiconfig } from "cosmiconfig"; -import { - downloadAndUnzipVSCode, - resolveCliArgsFromVSCodeExecutablePath, -} from "@vscode/test-electron"; -import { ensureCli } from "./ensureCli"; - -export default class JestRunnerInstalledExtensions extends VSCodeTestRunner { - async runTests( - tests: JestRunner.Test[], - watcher: JestRunner.TestWatcher, - onStart: JestRunner.OnTestStart, - onResult: JestRunner.OnTestSuccess, - onFailure: JestRunner.OnTestFailure, - ): Promise { - // The CLI integration tests require certain extensions to be installed, which needs to happen before the tests are - // actually run. The below code will resolve the path to the VSCode executable, and then use that to install the - // required extensions. - - const installedOnVsCodeVersions = - new Set<`${RunnerOptions["version"]}-${RunnerOptions["platform"]}`>(); - - for (const test of tests) { - const testDir = dirname(test.path); - - const options: RunnerOptions = - ((await cosmiconfig("jest-runner-vscode").search(testDir)) - ?.config as RunnerOptions) ?? {}; - - const { version, platform } = options; - const versionKey = `${version}-${platform}` as const; - - if (installedOnVsCodeVersions.has(versionKey)) { - continue; - } - - const vscodeExecutablePath = await downloadAndUnzipVSCode( - version, - platform, - ); - - console.log(`Installing required extensions for ${vscodeExecutablePath}`); - - const [cli, ...args] = - resolveCliArgsFromVSCodeExecutablePath(vscodeExecutablePath); - - spawnSync(cli, args, { - encoding: "utf-8", - stdio: "inherit", - }); - - installedOnVsCodeVersions.add(versionKey); - } - - await ensureCli(true); - - return super.runTests(tests, watcher, onStart, onResult, onFailure); - } -} diff --git a/extensions/ql-vscode/test/vscode-tests/jest-runner-vscode-codeql-cli.ts b/extensions/ql-vscode/test/vscode-tests/jest-runner-vscode-codeql-cli.ts new file mode 100644 index 000000000..1d92e5331 --- /dev/null +++ b/extensions/ql-vscode/test/vscode-tests/jest-runner-vscode-codeql-cli.ts @@ -0,0 +1,20 @@ +import type * as JestRunner from "jest-runner"; +import VSCodeTestRunner from "jest-runner-vscode"; +import { ensureCli } from "./ensureCli"; + +export default class JestRunnerVscodeCodeqlCli extends VSCodeTestRunner { + async runTests( + tests: JestRunner.Test[], + watcher: JestRunner.TestWatcher, + onStart: JestRunner.OnTestStart, + onResult: JestRunner.OnTestSuccess, + onFailure: JestRunner.OnTestFailure, + ): Promise { + // The CLI integration tests require the CLI to be available. We do not want to install the CLI + // when VS Code is already running because this will not give any feedback to the test runner. Instead, + // we'll download the CLI now and pass the path to the CLI to VS Code. + await ensureCli(true); + + return super.runTests(tests, watcher, onStart, onResult, onFailure); + } +} From 9c42c6a851f1371136aa7c507121e274d8fae2b2 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 12 Jan 2024 09:30:13 -0500 Subject: [PATCH 34/77] Allow CodeQL checkout to be named `ql` This makes it easier for cross-repo development with `semmle-code`, where the `codeql` repo is in a submodule in a directory named `ql`. --- extensions/ql-vscode/test/vscode-tests/cli.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli.ts b/extensions/ql-vscode/test/vscode-tests/cli.ts index e875c44ea..c1110dfe8 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli.ts @@ -6,7 +6,10 @@ import { workspace } from "vscode"; */ function hasCodeQL() { const folders = workspace.workspaceFolders; - return !!folders?.some((folder) => folder.uri.path.endsWith("/codeql")); + return !!folders?.some( + (folder) => + folder.uri.path.endsWith("/codeql") || folder.uri.path.endsWith("/ql"), + ); } // describeWithCodeQL will be equal to describe if the CodeQL libraries are From e03c2b132c7f3f3e697528d8973bcae7dcaeb8c0 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 12 Jan 2024 10:42:34 -0500 Subject: [PATCH 35/77] Custom message for assertions about files existing in packs --- .../variant-analysis-manager.test.ts | 43 +++++++++++++++++-- .../utils/bundled-pack-helpers.ts | 2 +- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 47603e83d..a7536016a 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -20,10 +20,47 @@ import { ExtensionApp } from "../../../../src/common/vscode/vscode-app"; import { DbConfigStore } from "../../../../src/databases/config/db-config-store"; import { mockedQuickPickItem } from "../../utils/mocking.helpers"; import { QueryLanguage } from "../../../../src/common/query-language"; +import type { QueryPackFS } from "../../utils/bundled-pack-helpers"; import { readBundledPack } from "../../utils/bundled-pack-helpers"; import { load } from "js-yaml"; import type { ExtensionPackMetadata } from "../../../../src/model-editor/extension-pack-metadata"; import type { QlPackLockFile } from "../../../../src/packaging/qlpack-lock-file"; +import { expect } from "@jest/globals"; +import type { ExpectationResult } from "expect"; + +/** + * Custom Jest matcher to check if a file exists in a query pack. + */ +function toExistInPack( + this: jest.MatcherContext, + actual: unknown, + packFS: QueryPackFS, +): ExpectationResult { + if (typeof actual !== "string") { + throw new TypeError("Expected actual value to be a string."); + } + + const pass = packFS.fileExists(actual); + if (pass) { + return { + pass: true, + message: () => `expected ${actual} not to exist in pack`, + }; + } else { + return { + pass: false, + message: () => `expected ${actual} to exist in pack`, + }; + } +} + +expect.extend({ toExistInPack }); + +declare module "expect" { + interface Matchers { + toExistInPack(packFS: QueryPackFS): R; + } +} describe("Variant Analysis Manager", () => { let cli: CodeQLCliServer; @@ -331,14 +368,14 @@ describe("Variant Analysis Manager", () => { const packFS = await readBundledPack(request.query.pack); filesThatExist.forEach((file) => { - expect(packFS.fileExists(file)).toBe(true); + expect(file).toExistInPack(packFS); }); qlxFilesThatExist.forEach((file) => { - expect(packFS.fileExists(file)).toBe(true); + expect(file).toExistInPack(packFS); }); filesThatDoNotExist.forEach((file) => { - expect(packFS.fileExists(file)).toBe(false); + expect(file).not.toExistInPack(packFS); }); expect( diff --git a/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts b/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts index a49130c40..32c34cbec 100644 --- a/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts +++ b/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts @@ -4,7 +4,7 @@ import { extract as tar_extract } from "tar-stream"; import { pipeline } from "stream/promises"; import { createGunzip } from "zlib"; -interface QueryPackFS { +export interface QueryPackFS { fileExists: (name: string) => boolean; fileContents: (name: string) => Buffer; directoryContents: (name: string) => string[]; From 61933c34d52c9bb8ad1248d8e4a694a9961a0ecb Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 12 Jan 2024 12:41:36 -0500 Subject: [PATCH 36/77] Dump pack contents when expected file not found --- .../variant-analysis/variant-analysis-manager.test.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index a7536016a..3e8e9869f 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -41,6 +41,7 @@ function toExistInPack( } const pass = packFS.fileExists(actual); + const files = packFS.allFiles().join("\n"); if (pass) { return { pass: true, @@ -49,7 +50,8 @@ function toExistInPack( } else { return { pass: false, - message: () => `expected ${actual} to exist in pack`, + message: () => + `expected ${actual} to exist in pack. The following files were found in the pack:\n${files}`, }; } } From 6d308f8688b9e60df65f46fa810484707c444d3d Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 12 Jan 2024 12:44:16 -0500 Subject: [PATCH 37/77] Add missing change --- .../ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts b/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts index 32c34cbec..f572537bf 100644 --- a/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts +++ b/extensions/ql-vscode/test/vscode-tests/utils/bundled-pack-helpers.ts @@ -8,6 +8,7 @@ export interface QueryPackFS { fileExists: (name: string) => boolean; fileContents: (name: string) => Buffer; directoryContents: (name: string) => string[]; + allFiles: () => string[]; } export async function readBundledPack( @@ -82,5 +83,8 @@ export async function readBundledPack( ) .map((dir) => dir.substring(name.length + 1)); }, + allFiles: (): string[] => { + return Object.keys(files); + }, }; } From 9d2190a88d08fc4595d8b3c7e462178b10b23c45 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Fri, 12 Jan 2024 13:03:21 -0500 Subject: [PATCH 38/77] Add text if pack is empty --- .../variant-analysis/variant-analysis-manager.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 3e8e9869f..564b42a85 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -41,7 +41,8 @@ function toExistInPack( } const pass = packFS.fileExists(actual); - const files = packFS.allFiles().join("\n"); + const files = packFS.allFiles(); + const filesString = files.length > 0 ? files.join("\n") : ""; if (pass) { return { pass: true, @@ -51,7 +52,7 @@ function toExistInPack( return { pass: false, message: () => - `expected ${actual} to exist in pack. The following files were found in the pack:\n${files}`, + `expected ${actual} to exist in pack.\nThe following files were found in the pack:\n${filesString}`, }; } } From ee73639720235cf010f397c1eeaa32ca591a81fc Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sat, 13 Jan 2024 14:30:01 -0500 Subject: [PATCH 39/77] Expand short paths before calling `codeql pack bundle` --- .../ql-vscode/src/common/short-paths.ts | 111 ++++++++++++++++++ .../src/variant-analysis/run-remote-query.ts | 9 +- 2 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 extensions/ql-vscode/src/common/short-paths.ts diff --git a/extensions/ql-vscode/src/common/short-paths.ts b/extensions/ql-vscode/src/common/short-paths.ts new file mode 100644 index 000000000..27eac829c --- /dev/null +++ b/extensions/ql-vscode/src/common/short-paths.ts @@ -0,0 +1,111 @@ +import { platform } from "os"; +import { basename, dirname, join, normalize, resolve } from "path"; +import { lstat, readdir } from "fs/promises"; +import { extLogger } from "./logging/vscode"; + +async function log(message: string): Promise { + await extLogger.log(message); +} + +/** + * Expand a single short path component + * @param dir The absolute path of the directory containing the short path component. + * @param shortBase The shot path component to expand. + * @returns The expanded path component. + */ +async function expandShortPathComponent( + dir: string, + shortBase: string, +): Promise { + await log(`Expanding short path component: ${shortBase}`); + + const fullPath = join(dir, shortBase); + + // Use `lstat` instead of `stat` to avoid following symlinks. + const stats = await lstat(fullPath, { bigint: true }); + if (stats.dev === BigInt(0) || stats.ino === BigInt(0)) { + // No inode info, so we won't be able to find this in the directory listing. + await log(`No inode info available. Skipping.`); + return shortBase; + } + await log(`dev/inode: ${stats.dev}/${stats.ino}`); + + try { + // Enumerate the children of the parent directory, and try to find one with the same dev/inode. + const children = await readdir(dir); + for (const child of children) { + await log(`considering child: ${child}`); + try { + const childStats = await lstat(join(dir, child), { bigint: true }); + await log(`child dev/inode: ${childStats.dev}/${childStats.ino}`); + if (childStats.dev === stats.dev && childStats.ino === stats.ino) { + // Found a match. + await log(`Found a match: ${child}`); + return child; + } + } catch (e) { + // Can't read stats for the child, so skip it. + await log(`Error reading stats for child: ${e}`); + } + } + } catch (e) { + // Can't read the directory, so we won't be able to find this in the directory listing. + await log(`Error reading directory: ${e}`); + return shortBase; + } + + await log(`No match found. Returning original.`); + return shortBase; +} + +/** + * Expand the short path components in a path, including those in ancestor directories. + * @param shortPath The path to expand. + * @returns The expanded path. + */ +async function expandShortPathRecursive(shortPath: string): Promise { + const shortBase = basename(shortPath); + if (shortBase.length === 0) { + // We've reached the root. + return shortPath; + } + + const dir = await expandShortPathRecursive(dirname(shortPath)); + await log(`dir: ${dir}`); + await log(`base: ${shortBase}`); + if (shortBase.indexOf("~") < 0) { + // This component doesn't have a short name, so just append it to the (long) parent. + await log(`Component is not a short name`); + return join(dir, shortBase); + } + + // This component looks like it has a short name, so try to expand it. + const longBase = await expandShortPathComponent(dir, shortBase); + return join(dir, longBase); +} + +/** + * Expands a path that potentially contains 8.3 short names (e.g. "C:\PROGRA~1" instead of "C:\Program Files"). + * @param shortPath The path to expand. + * @returns A normalized, absolute path, with any short components expanded. + */ +export async function expandShortPaths(shortPath: string): Promise { + const absoluteShortPath = normalize(resolve(shortPath)); + if (platform() !== "win32") { + // POSIX doesn't have short paths. + return absoluteShortPath; + } + + await log(`Expanding short paths in: ${absoluteShortPath}`); + // A quick check to see if there might be any short components. + // There might be a case where a short component doesn't contain a `~`, but if there is, I haven't + // found it. + // This may find long components that happen to have a '~', but that's OK. + if (absoluteShortPath.indexOf("~") < 0) { + // No short components to expand. + await log(`Skipping due to no short components`); + return absoluteShortPath; + } + + return await expandShortPathRecursive(absoluteShortPath); +} diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 0b1f4f127..8938649a4 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -38,6 +38,7 @@ import type { QueryLanguage } from "../common/query-language"; import { tryGetQueryMetadata } from "../codeql-cli/query-metadata"; import { askForLanguage, findLanguage } from "../codeql-cli/query-language"; import type { QlPackFile } from "../packaging/qlpack-file"; +import { expandShortPaths } from "../common/short-paths"; /** * Well-known names for the query pack used by the server. @@ -286,10 +287,16 @@ interface RemoteQueryTempDir { } async function createRemoteQueriesTempDirectory(): Promise { - const remoteQueryDir = await dir({ + const shortRemoteQueryDir = await dir({ dir: tmpDir.name, unsafeCleanup: true, }); + // Expand 8.3 filenames here to work around a CLI bug where `codeql pack bundle` produces an empty + // archive if the pack path contains any 8.3 components. + const remoteQueryDir = { + ...shortRemoteQueryDir, + dir: expandShortPaths(shortRemoteQueryDir.path), + }; const queryPackDir = join(remoteQueryDir.path, "query-pack"); await mkdirp(queryPackDir); const compiledPackDir = join(remoteQueryDir.path, "compiled-pack"); From b16aeb38872f1aac586e288111f6e13a6e344ab8 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sun, 14 Jan 2024 10:17:35 -0500 Subject: [PATCH 40/77] Fix property name, and expand another path --- .../ql-vscode/src/variant-analysis/run-remote-query.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 8938649a4..5f0dea969 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -295,12 +295,14 @@ async function createRemoteQueriesTempDirectory(): Promise { // archive if the pack path contains any 8.3 components. const remoteQueryDir = { ...shortRemoteQueryDir, - dir: expandShortPaths(shortRemoteQueryDir.path), + path: await expandShortPaths(shortRemoteQueryDir.path), }; const queryPackDir = join(remoteQueryDir.path, "query-pack"); await mkdirp(queryPackDir); const compiledPackDir = join(remoteQueryDir.path, "compiled-pack"); - const bundleFile = await getPackedBundlePath(tmpDir.name); + const bundleFile = await expandShortPaths( + await getPackedBundlePath(tmpDir.name), + ); return { remoteQueryDir, queryPackDir, compiledPackDir, bundleFile }; } From f53826c09db5088aeaa25b41ad9f435e4baf84cf Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Sun, 14 Jan 2024 10:20:55 -0500 Subject: [PATCH 41/77] Better dependency version assertions --- .../variant-analysis-manager.test.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 564b42a85..ca1287ea1 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -404,9 +404,17 @@ describe("Variant Analysis Manager", () => { // Assume the first dependency to check is the core library. if (dependenciesToCheck.length > 0) { - expect( - qlpackContents.dependencies?.[dependenciesToCheck[0]], - ).not.toEqual("${workspace}"); + const dependencyVersion = + qlpackContents.dependencies?.[dependenciesToCheck[0]]; + + // There should be a version specified. + expect(dependencyVersion).toBeDefined(); + + // Any `${workspace}` placeholder should have been replaced. + // The actual version might be `*` (for the legacy code path where we replace workspace + // references with `*`) or a specific version (for the new code path where the CLI does all + // the work). + expect(dependencyVersion).not.toEqual("${workspace}"); } const qlpackLockContents = load( packFS.fileContents("codeql-pack.lock.yml").toString("utf-8"), From 281f8eeb7a737606e00a7b1f5d8bf8c724b1ed65 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Mon, 15 Jan 2024 16:51:46 +0000 Subject: [PATCH 42/77] CodeQL model editor: support saving single/selected models (#3156) --- extensions/ql-vscode/CHANGELOG.md | 1 + .../ql-vscode/src/view/common/DataGrid.tsx | 29 ++++++-- .../ql-vscode/src/view/common/Dropdown.tsx | 4 ++ .../src/view/model-editor/LibraryRow.tsx | 8 ++- .../src/view/model-editor/MethodRow.tsx | 34 +++++++-- .../src/view/model-editor/ModelEditor.tsx | 56 +++++++++++++-- .../model-editor/ModeledMethodDataGrid.tsx | 6 ++ .../view/model-editor/ModeledMethodsList.tsx | 6 ++ .../__tests__/LibraryRow.spec.tsx | 3 + .../model-editor/__tests__/MethodRow.spec.tsx | 3 + .../__tests__/ModelEditor.spec.tsx | 69 +++++++++++++++++++ .../__tests__/ModeledMethodDataGrid.spec.tsx | 3 + .../__tests__/ModeledMethodsList.spec.tsx | 3 + 13 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 extensions/ql-vscode/src/view/model-editor/__tests__/ModelEditor.spec.tsx diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 1ce3a9f6d..6d5b4ae24 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] +- In the CodeQL model editor, you can now select individual method rows and save changes to only the selected rows, instead of having to save the entire library model. [#3156](https://github.com/github/vscode-codeql/pull/3156) - If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) - The UI for browsing and running CodeQL tests has moved to use VS Code's built-in test UI. This makes the CodeQL test UI more consistent with the test UIs for other languages. This change means that this extension no longer depends on the "Test Explorer UI" and "Test Adapter Converter" extensions. You can uninstall those two extensions if they are diff --git a/extensions/ql-vscode/src/view/common/DataGrid.tsx b/extensions/ql-vscode/src/view/common/DataGrid.tsx index ee790a6ae..a8bce90b0 100644 --- a/extensions/ql-vscode/src/view/common/DataGrid.tsx +++ b/extensions/ql-vscode/src/view/common/DataGrid.tsx @@ -37,7 +37,10 @@ export function DataGrid({ gridTemplateColumns, children }: DataGridProps) { ); } -const StyledDataGridRow = styled.div<{ $focused?: boolean }>` +const StyledDataGridRow = styled.div<{ + $focused?: boolean; + $selected?: boolean; +}>` display: contents; &:hover > * { @@ -48,14 +51,18 @@ const StyledDataGridRow = styled.div<{ $focused?: boolean }>` // Use !important to override the background color set by the hover state background-color: ${(props) => props.$focused - ? "var(--vscode-editor-selectionBackground) !important" - : "inherit"}; + ? "var(--vscode-editor-findMatchHighlightBackground) !important" + : props.$selected + ? "var(--vscode-editor-selectionBackground) !important" + : "inherit"}; } `; interface DataGridRowProps { focused?: boolean; + selected?: boolean; children: ReactNode; + onClick?: () => void; "data-testid"?: string; } @@ -69,10 +76,22 @@ interface DataGridRowProps { */ export const DataGridRow = forwardRef( ( - { focused, children, "data-testid": testId }: DataGridRowProps, + { + focused, + selected, + children, + "data-testid": testId, + onClick, + }: DataGridRowProps, ref?: React.Ref, ) => ( - + {children} ), diff --git a/extensions/ql-vscode/src/view/common/Dropdown.tsx b/extensions/ql-vscode/src/view/common/Dropdown.tsx index 17c4e9048..ffd7e3582 100644 --- a/extensions/ql-vscode/src/view/common/Dropdown.tsx +++ b/extensions/ql-vscode/src/view/common/Dropdown.tsx @@ -25,6 +25,9 @@ type Props = { "aria-label"?: string; }; +const stopClickPropagation = (e: React.MouseEvent) => { + e.stopPropagation(); +}; /** * A dropdown implementation styled to look like `VSCodeDropdown`. * @@ -50,6 +53,7 @@ export function Dropdown({ value={disabled ? disabledValue : value} disabled={disabled} onChange={onChange} + onClick={stopClickPropagation} className={className} {...props} > diff --git a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx index 5f5c95faa..38f27590c 100644 --- a/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/LibraryRow.tsx @@ -71,11 +71,13 @@ export type LibraryRowProps = { methods: Method[]; modeledMethodsMap: Record; modifiedSignatures: Set; + selectedSignatures: Set; inProgressMethods: Set; viewState: ModelEditorViewState; hideModeledMethods: boolean; revealedMethodSignature: string | null; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; + onMethodClick: (methodSignature: string) => void; onSaveModelClick: (methodSignatures: string[]) => void; onGenerateFromLlmClick: ( dependencyName: string, @@ -92,11 +94,13 @@ export const LibraryRow = ({ methods, modeledMethodsMap, modifiedSignatures, + selectedSignatures, inProgressMethods, viewState, hideModeledMethods, revealedMethodSignature, onChange, + onMethodClick, onSaveModelClick, onGenerateFromLlmClick, onStopGenerateFromLlmClick, @@ -228,16 +232,18 @@ export const LibraryRow = ({ methods={methods} modeledMethodsMap={modeledMethodsMap} modifiedSignatures={modifiedSignatures} + selectedSignatures={selectedSignatures} inProgressMethods={inProgressMethods} viewState={viewState} hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} onChange={onChange} + onMethodClick={onMethodClick} /> - Save + {selectedSignatures.size === 0 ? "Save" : "Save selected"} diff --git a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx index 7323cda5a..890041560 100644 --- a/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx +++ b/extensions/ql-vscode/src/view/model-editor/MethodRow.tsx @@ -70,10 +70,12 @@ export type MethodRowProps = { methodCanBeModeled: boolean; modeledMethods: ModeledMethod[]; methodIsUnsaved: boolean; + methodIsSelected: boolean; modelingInProgress: boolean; viewState: ModelEditorViewState; revealedMethodSignature: string | null; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; + onMethodClick: (methodSignature: string) => void; }; export const MethodRow = (props: MethodRowProps) => { @@ -103,9 +105,11 @@ const ModelableMethodRow = forwardRef( method, modeledMethods: modeledMethodsProp, methodIsUnsaved, + methodIsSelected, viewState, revealedMethodSignature, onChange, + onMethodClick, } = props; const [focusedIndex, setFocusedIndex] = useState(null); @@ -186,6 +190,10 @@ const ModelableMethodRow = forwardRef( { + onMethodClick(method.signature); + }} > ( {viewState.mode === Mode.Application && ( - + { + event.stopPropagation(); + jumpToMethod(); + }} + > {method.usages.length} )} - View + { + event.stopPropagation(); + jumpToMethod(); + }} + > + View + {props.modelingInProgress && } @@ -269,7 +289,10 @@ const ModelableMethodRow = forwardRef( { + event.stopPropagation(); + handleAddModelClick(); + }} disabled={addModelButtonDisabled} > @@ -278,7 +301,10 @@ const ModelableMethodRow = forwardRef( { + event.stopPropagation(); + removeModelClickedHandlers[index](); + }} > diff --git a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx index 2cfdd8bb7..b59f5e9cc 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelEditor.tsx @@ -95,6 +95,10 @@ export function ModelEditor({ new Set(), ); + const [selectedSignatures, setSelectedSignatures] = useState>( + new Set(), + ); + const [inProgressMethods, setInProgressMethods] = useState>( new Set(), ); @@ -189,6 +193,19 @@ export function ModelEditor({ [], ); + const onMethodClick = useCallback( + (methodSignature: string) => { + const newSelectedSignatures = new Set(selectedSignatures); + if (selectedSignatures.has(methodSignature)) { + newSelectedSignatures.delete(methodSignature); + } else { + newSelectedSignatures.add(methodSignature); + } + setSelectedSignatures(newSelectedSignatures); + }, + [selectedSignatures], + ); + const onRefreshClick = useCallback(() => { vscode.postMessage({ t: "refreshMethods", @@ -198,15 +215,31 @@ export function ModelEditor({ const onSaveAllClick = useCallback(() => { vscode.postMessage({ t: "saveModeledMethods", + methodSignatures: + selectedSignatures.size === 0 + ? undefined + : Array.from(selectedSignatures), }); + }, [selectedSignatures]); + + const onDeselectAllClick = useCallback(() => { + setSelectedSignatures(new Set()); }, []); - const onSaveModelClick = useCallback((methodSignatures: string[]) => { - vscode.postMessage({ - t: "saveModeledMethods", - methodSignatures, - }); - }, []); + const onSaveModelClick = useCallback( + (methodSignatures: string[]) => { + vscode.postMessage({ + t: "saveModeledMethods", + methodSignatures: + selectedSignatures.size === 0 + ? methodSignatures + : methodSignatures.filter((signature) => + selectedSignatures.has(signature), + ), + }); + }, + [selectedSignatures], + ); const onGenerateFromSourceClick = useCallback(() => { vscode.postMessage({ @@ -309,7 +342,14 @@ export function ModelEditor({ onClick={onSaveAllClick} disabled={modifiedSignatures.size === 0} > - Save all + {selectedSignatures.size === 0 ? "Save all" : "Save selected"} + + + Deselect all Refresh @@ -339,11 +379,13 @@ export function ModelEditor({ methods={methods} modeledMethodsMap={modeledMethods} modifiedSignatures={modifiedSignatures} + selectedSignatures={selectedSignatures} inProgressMethods={inProgressMethods} viewState={viewState} hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} onChange={onChange} + onMethodClick={onMethodClick} onSaveModelClick={onSaveModelClick} onGenerateFromLlmClick={onGenerateFromLlmClick} onStopGenerateFromLlmClick={onStopGenerateFromLlmClick} diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx index c50b29127..b3e521a4b 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodDataGrid.tsx @@ -16,22 +16,26 @@ export type ModeledMethodDataGridProps = { methods: Method[]; modeledMethodsMap: Record; modifiedSignatures: Set; + selectedSignatures: Set; inProgressMethods: Set; viewState: ModelEditorViewState; hideModeledMethods: boolean; revealedMethodSignature: string | null; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; + onMethodClick: (methodSignature: string) => void; }; export const ModeledMethodDataGrid = ({ methods, modeledMethodsMap, modifiedSignatures, + selectedSignatures, inProgressMethods, viewState, hideModeledMethods, revealedMethodSignature, onChange, + onMethodClick, }: ModeledMethodDataGridProps) => { const [methodsWithModelability, numHiddenMethods]: [ Array<{ method: Method; methodCanBeModeled: boolean }>, @@ -80,10 +84,12 @@ export const ModeledMethodDataGrid = ({ methodCanBeModeled={methodCanBeModeled} modeledMethods={modeledMethods} methodIsUnsaved={modifiedSignatures.has(method.signature)} + methodIsSelected={selectedSignatures.has(method.signature)} modelingInProgress={inProgressMethods.has(method.signature)} viewState={viewState} revealedMethodSignature={revealedMethodSignature} onChange={onChange} + onMethodClick={onMethodClick} /> ); })} diff --git a/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx b/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx index 40f545121..2a1388fc7 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModeledMethodsList.tsx @@ -13,11 +13,13 @@ export type ModeledMethodsListProps = { methods: Method[]; modeledMethodsMap: Record; modifiedSignatures: Set; + selectedSignatures: Set; inProgressMethods: Set; revealedMethodSignature: string | null; viewState: ModelEditorViewState; hideModeledMethods: boolean; onChange: (methodSignature: string, modeledMethods: ModeledMethod[]) => void; + onMethodClick: (methodSignature: string) => void; onSaveModelClick: (methodSignatures: string[]) => void; onGenerateFromLlmClick: ( packageName: string, @@ -36,11 +38,13 @@ export const ModeledMethodsList = ({ methods, modeledMethodsMap, modifiedSignatures, + selectedSignatures, inProgressMethods, viewState, hideModeledMethods, revealedMethodSignature, onChange, + onMethodClick, onSaveModelClick, onGenerateFromLlmClick, onStopGenerateFromLlmClick, @@ -82,11 +86,13 @@ export const ModeledMethodsList = ({ methods={grouped[libraryName]} modeledMethodsMap={modeledMethodsMap} modifiedSignatures={modifiedSignatures} + selectedSignatures={selectedSignatures} inProgressMethods={inProgressMethods} viewState={viewState} hideModeledMethods={hideModeledMethods} revealedMethodSignature={revealedMethodSignature} onChange={onChange} + onMethodClick={onMethodClick} onSaveModelClick={onSaveModelClick} onGenerateFromLlmClick={onGenerateFromLlmClick} onStopGenerateFromLlmClick={onStopGenerateFromLlmClick} diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx index 1f16cd1b9..4b0bf01e3 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/LibraryRow.spec.tsx @@ -8,6 +8,7 @@ import { createMockModelEditorViewState } from "../../../../test/factories/model describe(LibraryRow.name, () => { const method = createMethod(); const onChange = jest.fn(); + const onMethodClick = jest.fn(); const onSaveModelClick = jest.fn(); const onGenerateFromLlmClick = jest.fn(); const onStopGenerateFromLlmClick = jest.fn(); @@ -33,11 +34,13 @@ describe(LibraryRow.name, () => { ], }} modifiedSignatures={new Set([method.signature])} + selectedSignatures={new Set()} inProgressMethods={new Set()} viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} onChange={onChange} + onMethodClick={onMethodClick} onSaveModelClick={onSaveModelClick} onGenerateFromLlmClick={onGenerateFromLlmClick} onStopGenerateFromLlmClick={onStopGenerateFromLlmClick} diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx index fd2cd07df..6782cade7 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/MethodRow.spec.tsx @@ -30,6 +30,7 @@ describe(MethodRow.name, () => { provenance: "manual", }; const onChange = jest.fn(); + const onMethodClick = jest.fn(); const viewState = createMockModelEditorViewState(); @@ -40,10 +41,12 @@ describe(MethodRow.name, () => { methodCanBeModeled={true} modeledMethods={[modeledMethod]} methodIsUnsaved={false} + methodIsSelected={false} modelingInProgress={false} revealedMethodSignature={null} viewState={viewState} onChange={onChange} + onMethodClick={onMethodClick} {...props} />, ); diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEditor.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEditor.spec.tsx new file mode 100644 index 000000000..42d3bba43 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelEditor.spec.tsx @@ -0,0 +1,69 @@ +import { act, render as reactRender, screen } from "@testing-library/react"; +import { createMethod } from "../../../../test/factories/model-editor/method-factories"; +import { ModelEditor } from "../ModelEditor"; +import { createMockModelEditorViewState } from "../../../../test/factories/model-editor/view-state"; +import { userEvent } from "@testing-library/user-event"; + +describe(ModelEditor.name, () => { + const method1 = createMethod({ + library: "sql2o", + libraryVersion: "1.6.0", + signature: "org.sql2o.Connection#createQuery(String)", + packageName: "org.sql2o", + typeName: "Connection", + methodName: "createQuery", + methodParameters: "(String)", + supported: false, + }); + const method2 = createMethod({ + library: "sql2o", + libraryVersion: "1.6.0", + signature: "org.sql2o.Query#executeScalar(Class)", + packageName: "org.sql2o", + typeName: "Query", + methodName: "executeScalar", + methodParameters: "(Class)", + supported: false, + }); + const method3 = createMethod({ + library: "sql2o", + libraryVersion: "1.6.0", + signature: "org.sql2o.Sql2o#open()", + packageName: "org.sql2o", + typeName: "Sql2o", + methodName: "open", + methodParameters: "()", + supported: true, + }); + + const viewState = createMockModelEditorViewState(); + + const render = () => + reactRender( + , + ); + + it("renders Save button when no rows are selected", () => { + render(); + + expect(screen.getByText("Save all")).toBeInTheDocument(); + }); + + it("renders Save button when rows are selected", async () => { + render(); + + await act(async () => { + await userEvent.click(screen.getAllByLabelText("Expand")[0]); + }); + + await act(async () => { + await userEvent.click(screen.getAllByTestId("modelable-method-row")[0]); + }); + + // The top-level Save button and the per-library Save button should have been updated. + expect(screen.getAllByText("Save selected")).toHaveLength(2); + }); +}); diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx index 44b44679d..c4176bf26 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodDataGrid.spec.tsx @@ -36,6 +36,7 @@ describe(ModeledMethodDataGrid.name, () => { supported: true, }); const onChange = jest.fn(); + const onMethodClick = jest.fn(); const viewState = createMockModelEditorViewState(); @@ -55,11 +56,13 @@ describe(ModeledMethodDataGrid.name, () => { ], }} modifiedSignatures={new Set([method1.signature])} + selectedSignatures={new Set()} inProgressMethods={new Set()} viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} onChange={onChange} + onMethodClick={onMethodClick} {...props} />, ); diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx index 92296b52c..6717f2faf 100644 --- a/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModeledMethodsList.spec.tsx @@ -33,6 +33,7 @@ describe(ModeledMethodsList.name, () => { methodParameters: "(String)", }); const onChange = jest.fn(); + const onMethodClick = jest.fn(); const onSaveModelClick = jest.fn(); const onGenerateFromLlmClick = jest.fn(); const onStopGenerateFromLlmClick = jest.fn(); @@ -56,11 +57,13 @@ describe(ModeledMethodsList.name, () => { ], }} modifiedSignatures={new Set([method1.signature])} + selectedSignatures={new Set()} inProgressMethods={new Set()} viewState={viewState} hideModeledMethods={false} revealedMethodSignature={null} onChange={onChange} + onMethodClick={onMethodClick} onSaveModelClick={onSaveModelClick} onGenerateFromLlmClick={onGenerateFromLlmClick} onStopGenerateFromLlmClick={onStopGenerateFromLlmClick} From 1bc560518f21874c48603a89544b0d6ff4facb40 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 15 Jan 2024 17:07:41 +0000 Subject: [PATCH 43/77] Add codeQL.runVariantAnalysisContextExplorer command --- extensions/ql-vscode/package.json | 13 +++++++++++++ extensions/ql-vscode/src/common/commands.ts | 1 + .../variant-analysis/variant-analysis-manager.ts | 12 +++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 01b09c4b5..26ad8d42e 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -545,6 +545,10 @@ "command": "codeQL.runVariantAnalysisContextEditor", "title": "CodeQL: Run Variant Analysis" }, + { + "command": "codeQL.runVariantAnalysisContextExplorer", + "title": "CodeQL: Run Variant Analysis" + }, { "command": "codeQL.exportSelectedVariantAnalysisResults", "title": "CodeQL: Export Variant Analysis Results" @@ -1321,6 +1325,11 @@ "group": "9_qlCommands", "when": "resourceScheme != codeql-zip-archive" }, + { + "command": "codeQL.runVariantAnalysisContextExplorer", + "group": "9_qlCommands", + "when": "resourceExtname == .ql && config.codeQL.canary && config.codeQL.variantAnalysis.multiQuery" + }, { "command": "codeQL.openReferencedFileContextExplorer", "group": "9_qlCommands", @@ -1397,6 +1406,10 @@ "command": "codeQL.runVariantAnalysis", "when": "editorLangId == ql && resourceExtname == .ql" }, + { + "command": "codeQL.runVariantAnalysisContextExplorer", + "when": "false" + }, { "command": "codeQL.runVariantAnalysisContextEditor", "when": "false" diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index 57c471ec7..d22768b59 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -277,6 +277,7 @@ export type VariantAnalysisCommands = { ) => Promise; "codeQL.runVariantAnalysis": (uri?: Uri) => Promise; "codeQL.runVariantAnalysisContextEditor": (uri?: Uri) => Promise; + "codeQL.runVariantAnalysisContextExplorer": ExplorerSelectionCommandFunction; "codeQLQueries.runVariantAnalysisContextMenu": TreeViewContextSingleSelectionCommandFunction; }; diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 654e4c0ad..294d1ca24 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -86,6 +86,7 @@ import { import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; import { RequestError } from "@octokit/request-error"; import { handleRequestError } from "./custom-errors"; +import { createMultiSelectionCommand } from "../common/vscode/selection-commands"; const maxRetryCount = 3; @@ -167,9 +168,11 @@ export class VariantAnalysisManager "codeQL.openVariantAnalysisView": this.showView.bind(this), "codeQL.runVariantAnalysis": this.runVariantAnalysisFromCommand.bind(this), - // Since we are tracking extension usage through commands, this command mirrors the "codeQL.runVariantAnalysis" command "codeQL.runVariantAnalysisContextEditor": this.runVariantAnalysisFromCommand.bind(this), + "codeQL.runVariantAnalysisContextExplorer": createMultiSelectionCommand( + this.runVariantAnalysisFromExplorer.bind(this), + ), "codeQLQueries.runVariantAnalysisContextMenu": this.runVariantAnalysisFromQueriesPanel.bind(this), }; @@ -194,6 +197,13 @@ export class VariantAnalysisManager ); } + private async runVariantAnalysisFromExplorer(fileURIs: Uri[]): Promise { + if (fileURIs.length !== 1) { + throw new Error("Can only run a single query at a time"); + } + return this.runVariantAnalysisFromCommand(fileURIs[0]); + } + private async runVariantAnalysisFromQueriesPanel( queryTreeViewItem: QueryTreeViewItem, ): Promise { From 1a7981a22d4cb147abdb346149ffba1b26e52443 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 15 Jan 2024 17:08:18 +0000 Subject: [PATCH 44/77] Add codeQL.runVariantAnalysisPublishedPack command --- extensions/ql-vscode/package.json | 8 ++++++++ extensions/ql-vscode/src/common/commands.ts | 1 + .../src/variant-analysis/variant-analysis-manager.ts | 6 ++++++ 3 files changed, 15 insertions(+) diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 26ad8d42e..cb43a9daa 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -549,6 +549,10 @@ "command": "codeQL.runVariantAnalysisContextExplorer", "title": "CodeQL: Run Variant Analysis" }, + { + "command": "codeQL.runVariantAnalysisPublishedPack", + "title": "CodeQL: Run Variant Analysis against published pack" + }, { "command": "codeQL.exportSelectedVariantAnalysisResults", "title": "CodeQL: Export Variant Analysis Results" @@ -1410,6 +1414,10 @@ "command": "codeQL.runVariantAnalysisContextExplorer", "when": "false" }, + { + "command": "codeQL.runVariantAnalysisPublishedPack", + "when": "config.codeQL.canary && config.codeQL.variantAnalysis.multiQuery" + }, { "command": "codeQL.runVariantAnalysisContextEditor", "when": "false" diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index d22768b59..320e6188c 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -279,6 +279,7 @@ export type VariantAnalysisCommands = { "codeQL.runVariantAnalysisContextEditor": (uri?: Uri) => Promise; "codeQL.runVariantAnalysisContextExplorer": ExplorerSelectionCommandFunction; "codeQLQueries.runVariantAnalysisContextMenu": TreeViewContextSingleSelectionCommandFunction; + "codeQL.runVariantAnalysisPublishedPack": () => Promise; }; export type DatabasePanelCommands = { diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 294d1ca24..16940d1e4 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -175,6 +175,8 @@ export class VariantAnalysisManager ), "codeQLQueries.runVariantAnalysisContextMenu": this.runVariantAnalysisFromQueriesPanel.bind(this), + "codeQL.runVariantAnalysisPublishedPack": + this.runVariantAnalysisFromPublishedPack.bind(this), }; } @@ -214,6 +216,10 @@ export class VariantAnalysisManager } } + private async runVariantAnalysisFromPublishedPack(): Promise { + throw new Error("Command not yet implemented"); + } + public async runVariantAnalysis( uri: Uri | undefined, progress: ProgressCallback, From 8c0a8e07b2c76b6458bd630a5e79a5187608fe8b Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 16 Jan 2024 12:19:37 +0000 Subject: [PATCH 45/77] Tidy up variant analysis commands (#3240) --- extensions/ql-vscode/src/common/commands.ts | 4 +- .../src/variant-analysis/run-remote-query.ts | 4 +- .../variant-analysis-manager.ts | 49 +++++++++++-------- 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/extensions/ql-vscode/src/common/commands.ts b/extensions/ql-vscode/src/common/commands.ts index 320e6188c..b1463a848 100644 --- a/extensions/ql-vscode/src/common/commands.ts +++ b/extensions/ql-vscode/src/common/commands.ts @@ -275,8 +275,8 @@ export type VariantAnalysisCommands = { "codeQL.openVariantAnalysisView": ( variantAnalysisId: number, ) => Promise; - "codeQL.runVariantAnalysis": (uri?: Uri) => Promise; - "codeQL.runVariantAnalysisContextEditor": (uri?: Uri) => Promise; + "codeQL.runVariantAnalysis": () => Promise; + "codeQL.runVariantAnalysisContextEditor": (uri: Uri) => Promise; "codeQL.runVariantAnalysisContextExplorer": ExplorerSelectionCommandFunction; "codeQLQueries.runVariantAnalysisContextMenu": TreeViewContextSingleSelectionCommandFunction; "codeQL.runVariantAnalysisPublishedPack": () => Promise; diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 421dc54e7..42779aae7 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -274,12 +274,12 @@ interface PreparedRemoteQuery { export async function prepareRemoteQueryRun( cliServer: CodeQLCliServer, credentials: Credentials, - uri: Uri | undefined, + uri: Uri, progress: ProgressCallback, token: CancellationToken, dbManager: DbManager, ): Promise { - if (!uri?.fsPath.endsWith(".ql")) { + if (!uri.fsPath.endsWith(".ql")) { throw new UserCancellationException("Not a CodeQL query file."); } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 16940d1e4..4985386a5 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -167,9 +167,9 @@ export class VariantAnalysisManager "codeQL.openVariantAnalysisLogs": this.openVariantAnalysisLogs.bind(this), "codeQL.openVariantAnalysisView": this.showView.bind(this), "codeQL.runVariantAnalysis": - this.runVariantAnalysisFromCommand.bind(this), + this.runVariantAnalysisFromCommandPalette.bind(this), "codeQL.runVariantAnalysisContextEditor": - this.runVariantAnalysisFromCommand.bind(this), + this.runVariantAnalysisFromContextEditor.bind(this), "codeQL.runVariantAnalysisContextExplorer": createMultiSelectionCommand( this.runVariantAnalysisFromExplorer.bind(this), ), @@ -184,35 +184,32 @@ export class VariantAnalysisManager return this.app.commands; } - private async runVariantAnalysisFromCommand(uri?: Uri) { - return withProgress( - async (progress, token) => - this.runVariantAnalysis( - uri || Window.activeTextEditor?.document.uri, - progress, - token, - ), - { - title: "Run Variant Analysis", - cancellable: true, - }, - ); + private async runVariantAnalysisFromCommandPalette() { + const fileUri = Window.activeTextEditor?.document.uri; + if (!fileUri) { + throw new Error("Please select a .ql file to run as a variant analysis"); + } + + await this.runVariantAnalysisCommand(fileUri); + } + + private async runVariantAnalysisFromContextEditor(uri: Uri) { + await this.runVariantAnalysisCommand(uri); } private async runVariantAnalysisFromExplorer(fileURIs: Uri[]): Promise { if (fileURIs.length !== 1) { throw new Error("Can only run a single query at a time"); } - return this.runVariantAnalysisFromCommand(fileURIs[0]); + + return this.runVariantAnalysisCommand(fileURIs[0]); } private async runVariantAnalysisFromQueriesPanel( queryTreeViewItem: QueryTreeViewItem, ): Promise { if (queryTreeViewItem.path !== undefined) { - await this.runVariantAnalysisFromCommand( - Uri.file(queryTreeViewItem.path), - ); + await this.runVariantAnalysisCommand(Uri.file(queryTreeViewItem.path)); } } @@ -220,8 +217,20 @@ export class VariantAnalysisManager throw new Error("Command not yet implemented"); } + private async runVariantAnalysisCommand(uri: Uri): Promise { + return withProgress( + async (progress, token) => { + await this.runVariantAnalysis(uri, progress, token); + }, + { + title: "Run Variant Analysis", + cancellable: true, + }, + ); + } + public async runVariantAnalysis( - uri: Uri | undefined, + uri: Uri, progress: ProgressCallback, token: CancellationToken, ): Promise { From c5517e0aea594a9269ab1bea7b57a5def215a413 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:03:53 +0000 Subject: [PATCH 46/77] CodeQL model editor: Add support for free text input in type models (#3217) --- .../src/view/common/useDebounceCallback.ts | 27 ++++++++++ .../view/model-editor/ModelInputDropdown.tsx | 11 +++- .../view/model-editor/ModelOutputDropdown.tsx | 8 +-- .../view/model-editor/ModelTypeTextbox.tsx | 52 +++++++++++++++++++ 4 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 extensions/ql-vscode/src/view/common/useDebounceCallback.ts create mode 100644 extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx diff --git a/extensions/ql-vscode/src/view/common/useDebounceCallback.ts b/extensions/ql-vscode/src/view/common/useDebounceCallback.ts new file mode 100644 index 000000000..9bde5db86 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/useDebounceCallback.ts @@ -0,0 +1,27 @@ +import { useEffect, useRef } from "react"; + +/** + * Call the callback after the value has not changed for a certain amount of time. + * @param value + * @param callback + * @param delay + */ +export function useDebounceCallback( + value: T, + callback: (value: T) => void, + delay?: number, +) { + const callbackRef = useRef<(value: T) => void>(callback); + + useEffect(() => { + callbackRef.current = callback; + }, [callback]); + + useEffect(() => { + const timer = setTimeout(() => callbackRef.current(value), delay || 500); + + return () => { + clearTimeout(timer); + }; + }, [value, delay]); +} diff --git a/extensions/ql-vscode/src/view/model-editor/ModelInputDropdown.tsx b/extensions/ql-vscode/src/view/model-editor/ModelInputDropdown.tsx index 634769627..e50b5f566 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelInputDropdown.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelInputDropdown.tsx @@ -7,11 +7,11 @@ import { modeledMethodSupportsInput, } from "../../model-editor/modeled-method"; import type { Method } from "../../model-editor/method"; -import { ReadonlyDropdown } from "../common/ReadonlyDropdown"; import type { QueryLanguage } from "../../common/query-language"; import { getModelsAsDataLanguage } from "../../model-editor/languages"; import type { ModelingStatus } from "../../model-editor/shared/modeling-status"; import { InputDropdown } from "./InputDropdown"; +import { ModelTypeTextbox } from "./ModelTypeTextbox"; type Props = { language: QueryLanguage; @@ -67,7 +67,14 @@ export const ModelInputDropdown = ({ : undefined; if (modeledMethod?.type === "type") { - return ; + return ( + + ); } const modelAccepted = isModelAccepted(modeledMethod, modelingStatus); diff --git a/extensions/ql-vscode/src/view/model-editor/ModelOutputDropdown.tsx b/extensions/ql-vscode/src/view/model-editor/ModelOutputDropdown.tsx index 39332bf93..5c5053903 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelOutputDropdown.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelOutputDropdown.tsx @@ -7,11 +7,11 @@ import { modeledMethodSupportsOutput, } from "../../model-editor/modeled-method"; import type { Method } from "../../model-editor/method"; -import { ReadonlyDropdown } from "../common/ReadonlyDropdown"; import { getModelsAsDataLanguage } from "../../model-editor/languages"; import type { QueryLanguage } from "../../common/query-language"; import type { ModelingStatus } from "../../model-editor/shared/modeling-status"; import { InputDropdown } from "./InputDropdown"; +import { ModelTypeTextbox } from "./ModelTypeTextbox"; type Props = { language: QueryLanguage; @@ -69,8 +69,10 @@ export const ModelOutputDropdown = ({ if (modeledMethod?.type === "type") { return ( - ); diff --git a/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx b/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx new file mode 100644 index 000000000..48a2de7df --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/ModelTypeTextbox.tsx @@ -0,0 +1,52 @@ +import type { ChangeEvent } from "react"; +import { useCallback, useEffect, useState } from "react"; +import type { + ModeledMethod, + TypeModeledMethod, +} from "../../model-editor/modeled-method"; +import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; +import { useDebounceCallback } from "../common/useDebounceCallback"; + +type Props = { + modeledMethod: TypeModeledMethod; + typeInfo: "path" | "relatedTypeName"; + onChange: (modeledMethod: ModeledMethod) => void; + + "aria-label"?: string; +}; + +export const ModelTypeTextbox = ({ + modeledMethod, + typeInfo, + onChange, + ...props +}: Props): JSX.Element => { + const [value, setValue] = useState( + modeledMethod[typeInfo], + ); + + useEffect(() => { + setValue(modeledMethod[typeInfo]); + }, [modeledMethod, typeInfo]); + + const handleChange = useCallback((e: ChangeEvent) => { + const target = e.target as HTMLSelectElement; + + setValue(target.value); + }, []); + + // Debounce the callback to avoid updating the model too often. + // Not doing this results in a lot of lag when typing. + useDebounceCallback( + value, + (newValue: string | undefined) => { + onChange({ + ...modeledMethod, + [typeInfo]: newValue ?? "", + }); + }, + 500, + ); + + return ; +}; From f993258a27273667fa8525c631fd9c37d48e1fc6 Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 16 Jan 2024 13:17:26 +0000 Subject: [PATCH 47/77] CodeQL model editor: Make `Type` a selectable model kind for Ruby (#3224) --- .../view/model-editor/ModelTypeDropdown.tsx | 27 ++++--- .../__tests__/ModelTypeDropdown.spec.tsx | 77 +++++++++++++++++++ 2 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx diff --git a/extensions/ql-vscode/src/view/model-editor/ModelTypeDropdown.tsx b/extensions/ql-vscode/src/view/model-editor/ModelTypeDropdown.tsx index c2dfa35d9..98b9bf7c1 100644 --- a/extensions/ql-vscode/src/view/model-editor/ModelTypeDropdown.tsx +++ b/extensions/ql-vscode/src/view/model-editor/ModelTypeDropdown.tsx @@ -1,5 +1,5 @@ import type { ChangeEvent } from "react"; -import { useCallback } from "react"; +import { useCallback, useMemo } from "react"; import type { ModeledMethod, ModeledMethodType, @@ -12,19 +12,11 @@ import type { Method } from "../../model-editor/method"; import { createEmptyModeledMethod } from "../../model-editor/modeled-method-empty"; import type { Mutable } from "../../common/mutable"; import { ReadonlyDropdown } from "../common/ReadonlyDropdown"; -import type { QueryLanguage } from "../../common/query-language"; +import { QueryLanguage } from "../../common/query-language"; import { getModelsAsDataLanguage } from "../../model-editor/languages"; import type { ModelingStatus } from "../../model-editor/shared/modeling-status"; import { InputDropdown } from "./InputDropdown"; -const options: Array<{ value: ModeledMethodType; label: string }> = [ - { value: "none", label: "Unmodeled" }, - { value: "source", label: "Source" }, - { value: "sink", label: "Sink" }, - { value: "summary", label: "Flow summary" }, - { value: "neutral", label: "Neutral" }, -]; - type Props = { language: QueryLanguage; method: Method; @@ -40,6 +32,21 @@ export const ModelTypeDropdown = ({ modelingStatus, onChange, }: Props): JSX.Element => { + const options = useMemo(() => { + const baseOptions: Array<{ value: ModeledMethodType; label: string }> = [ + { value: "none", label: "Unmodeled" }, + { value: "source", label: "Source" }, + { value: "sink", label: "Sink" }, + { value: "summary", label: "Flow summary" }, + { value: "neutral", label: "Neutral" }, + ]; + if (language === QueryLanguage.Ruby) { + baseOptions.push({ value: "type", label: "Type" }); + } + + return baseOptions; + }, [language]); + const handleChange = useCallback( (e: ChangeEvent) => { const modelsAsDataLanguage = getModelsAsDataLanguage(language); diff --git a/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx new file mode 100644 index 000000000..786907b62 --- /dev/null +++ b/extensions/ql-vscode/src/view/model-editor/__tests__/ModelTypeDropdown.spec.tsx @@ -0,0 +1,77 @@ +import { userEvent } from "@testing-library/user-event"; +import { render, screen } from "@testing-library/react"; +import { createNoneModeledMethod } from "../../../../test/factories/model-editor/modeled-method-factories"; +import { QueryLanguage } from "../../../common/query-language"; +import { ModelTypeDropdown } from "../ModelTypeDropdown"; +import { createMethod } from "../../../../test/factories/model-editor/method-factories"; + +describe(ModelTypeDropdown.name, () => { + const onChange = jest.fn(); + + beforeEach(() => { + onChange.mockReset(); + }); + + it("allows changing the type", async () => { + const method = createMethod(); + const modeledMethod = createNoneModeledMethod(); + + render( + , + ); + + await userEvent.selectOptions(screen.getByRole("combobox"), "source"); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + type: "source", + }), + ); + }); + + it("allows changing the type to 'Type' for Ruby", async () => { + const method = createMethod(); + const modeledMethod = createNoneModeledMethod(); + + render( + , + ); + + await userEvent.selectOptions(screen.getByRole("combobox"), "type"); + expect(onChange).toHaveBeenCalledWith( + expect.objectContaining({ + type: "type", + }), + ); + }); + + it("does not allow changing the type to 'Type' for Java", async () => { + const method = createMethod(); + const modeledMethod = createNoneModeledMethod(); + + render( + , + ); + + expect( + screen.queryByRole("option", { name: "Type" }), + ).not.toBeInTheDocument(); + }); +}); From 318136f5e5faa13ddf8a1b6ebb155bc69660b9cd Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Tue, 16 Jan 2024 15:05:24 +0000 Subject: [PATCH 48/77] Allow multiple file URIs to be passed into prepareRemoteQueryRun (#3241) --- .../ql-vscode/src/variant-analysis/run-remote-query.ts | 10 +++++++++- .../src/variant-analysis/variant-analysis-manager.ts | 6 +++--- .../variant-analysis/variant-analysis-manager.test.ts | 10 +++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 42779aae7..fc827797c 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -274,11 +274,19 @@ interface PreparedRemoteQuery { export async function prepareRemoteQueryRun( cliServer: CodeQLCliServer, credentials: Credentials, - uri: Uri, + uris: Uri[], progress: ProgressCallback, token: CancellationToken, dbManager: DbManager, ): Promise { + if (uris.length !== 1) { + // For now we only support a single file, but we're aiming + // to support multiple files in the near future. + throw Error("Exactly one query file must be selected."); + } + + const uri = uris[0]; + if (!uri.fsPath.endsWith(".ql")) { throw new UserCancellationException("Not a CodeQL query file."); } diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 4985386a5..38a71801a 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -220,7 +220,7 @@ export class VariantAnalysisManager private async runVariantAnalysisCommand(uri: Uri): Promise { return withProgress( async (progress, token) => { - await this.runVariantAnalysis(uri, progress, token); + await this.runVariantAnalysis([uri], progress, token); }, { title: "Run Variant Analysis", @@ -230,7 +230,7 @@ export class VariantAnalysisManager } public async runVariantAnalysis( - uri: Uri, + uris: Uri[], progress: ProgressCallback, token: CancellationToken, ): Promise { @@ -254,7 +254,7 @@ export class VariantAnalysisManager } = await prepareRemoteQueryRun( this.cliServer, this.app.credentials, - uri, + uris, progress, token, this.dbManager, diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 2c854f9ea..628a2603e 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -100,7 +100,7 @@ describe("Variant Analysis Manager", () => { const fileUri = getFile("data-remote-qlpack/in-pack.ql"); await variantAnalysisManager.runVariantAnalysis( - fileUri, + [fileUri], progress, cancellationTokenSource.token, ); @@ -121,7 +121,7 @@ describe("Variant Analysis Manager", () => { const fileUri = getFile("data-remote-no-qlpack/in-pack.ql"); await variantAnalysisManager.runVariantAnalysis( - fileUri, + [fileUri], progress, cancellationTokenSource.token, ); @@ -142,7 +142,7 @@ describe("Variant Analysis Manager", () => { const fileUri = getFile("data-remote-qlpack-nested/subfolder/in-pack.ql"); await variantAnalysisManager.runVariantAnalysis( - fileUri, + [fileUri], progress, cancellationTokenSource.token, ); @@ -163,7 +163,7 @@ describe("Variant Analysis Manager", () => { const fileUri = getFile("data-remote-no-qlpack/in-pack.ql"); const promise = variantAnalysisManager.runVariantAnalysis( - fileUri, + [fileUri], progress, cancellationTokenSource.token, ); @@ -313,7 +313,7 @@ describe("Variant Analysis Manager", () => { }) { const fileUri = getFile(queryPath); await variantAnalysisManager.runVariantAnalysis( - fileUri, + [fileUri], progress, cancellationTokenSource.token, ); From 070f0ca9b789375a1b343c26d77f766a90b69b71 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 16 Jan 2024 16:05:51 +0100 Subject: [PATCH 49/77] Add highlighting to items in suggest box This adds highlighting of the currently typed text into the shown items of the suggest box. --- .../common/SuggestBox/HighlightedText.tsx | 26 +++++++++++++++++++ .../src/view/common/SuggestBox/LabelText.tsx | 20 ++++++++++++++ .../src/view/common/SuggestBox/SuggestBox.tsx | 11 +++++--- .../src/view/common/SuggestBox/highlight.ts | 2 +- 4 files changed, 55 insertions(+), 4 deletions(-) create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/HighlightedText.tsx create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/LabelText.tsx diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/HighlightedText.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/HighlightedText.tsx new file mode 100644 index 000000000..6b1586eac --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/HighlightedText.tsx @@ -0,0 +1,26 @@ +import { styled } from "styled-components"; +import type { Snippet } from "./highlight"; + +const Normal = styled.span``; +const Highlighted = styled.span` + font-weight: 700; + color: var(--vscode-editorSuggestWidget-focusHighlightForeground); +`; + +type Props = { + snippets: Snippet[]; +}; + +export const HighlightedText = ({ snippets }: Props) => { + return ( + <> + {snippets.map((snippet, index) => + snippet.highlight ? ( + {snippet.text} + ) : ( + {snippet.text} + ), + )} + + ); +}; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/LabelText.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/LabelText.tsx new file mode 100644 index 000000000..d5c491759 --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/LabelText.tsx @@ -0,0 +1,20 @@ +import { useMemo } from "react"; +import { createHighlights } from "./highlight"; +import { HighlightedText } from "./HighlightedText"; +import type { Option } from "./options"; + +type Props> = { + item: T; + + tokens: string[]; +}; + +export const LabelText = >({ item, tokens }: Props) => { + const highlights = useMemo(() => { + const highlightedToken = tokens[tokens.length - 1] ?? ""; + + return createHighlights(item.label, highlightedToken); + }, [item, tokens]); + + return ; +}; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx index 0e338892f..1ad5a4278 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx @@ -18,6 +18,7 @@ import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; import type { Option } from "./options"; import { findMatchingOptions } from "./options"; import { SuggestBoxItem } from "./SuggestBoxItem"; +import { LabelText } from "./LabelText"; const Input = styled(VSCodeTextField)` width: 430px; @@ -147,9 +148,13 @@ export const SuggestBox = >({ [onChange], ); + const tokens = useMemo(() => { + return parseValueToTokens(value); + }, [value, parseValueToTokens]); + const suggestionItems = useMemo(() => { - return findMatchingOptions(options, parseValueToTokens(value)); - }, [options, value, parseValueToTokens]); + return findMatchingOptions(options, tokens); + }, [options, tokens]); useEffect(() => { if (disabled) { @@ -222,7 +227,7 @@ export const SuggestBox = >({ })} active={activeIndex === index} icon={getIcon?.(item)} - labelText={item.label} + labelText={} details={getDetails?.(item)} /> ))} diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts b/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts index 216740506..a1fa4ba09 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts +++ b/extensions/ql-vscode/src/view/common/SuggestBox/highlight.ts @@ -1,4 +1,4 @@ -type Snippet = { +export type Snippet = { text: string; highlight: boolean; }; From 5948008c99044259c25a20466eea134f13ee66d7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Jan 2024 15:09:30 +0000 Subject: [PATCH 50/77] Bump CLI version from v2.15.5 to v2.16.0 for integration tests (#3243) Co-authored-by: github-actions[bot] --- extensions/ql-vscode/supported_cli_versions.json | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/supported_cli_versions.json b/extensions/ql-vscode/supported_cli_versions.json index 8d0961723..d29e63e4d 100644 --- a/extensions/ql-vscode/supported_cli_versions.json +++ b/extensions/ql-vscode/supported_cli_versions.json @@ -1,4 +1,5 @@ [ + "v2.16.0", "v2.15.5", "v2.14.6", "v2.13.5", From b4250218d5decbcf34a43ab76952fc653cc59334 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 16 Jan 2024 14:32:00 +0000 Subject: [PATCH 51/77] Implement the command to download a published pack --- extensions/ql-vscode/src/codeql-cli/cli.ts | 12 ++++- .../variant-analysis-manager.ts | 47 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index e04e012ea..fc2b43fba 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -121,6 +121,16 @@ type GenerateExtensiblePredicateMetadataResult = { }>; }; +type PackDownloadResult = { + // There are other properties in this object, but they are + // not relevant for its use in the extension, so we omit them. + packs: Array<{ + name: string; + version: string; + }>; + packDir: string; +}; + /** * The expected output of `codeql resolve qlref`. */ @@ -1383,7 +1393,7 @@ export class CodeQLCliServer implements Disposable { * Downloads a specified pack. * @param packs The `` of the packs to download. */ - async packDownload(packs: string[]) { + async packDownload(packs: string[]): Promise { return this.runJsonCodeQlCliCommandWithAuthentication( ["pack", "download"], packs, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 38a71801a..011a4520a 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -87,6 +87,7 @@ import type { QueryTreeViewItem } from "../queries-panel/query-tree-view-item"; import { RequestError } from "@octokit/request-error"; import { handleRequestError } from "./custom-errors"; import { createMultiSelectionCommand } from "../common/vscode/selection-commands"; +import { askForLanguage } from "../codeql-cli/query-language"; const maxRetryCount = 3; @@ -214,7 +215,51 @@ export class VariantAnalysisManager } private async runVariantAnalysisFromPublishedPack(): Promise { - throw new Error("Command not yet implemented"); + const language = await askForLanguage(this.cliServer); + + const packName = `codeql/${language}-queries`; + const packDownloadResult = await this.cliServer.packDownload([packName]); + const downloadedPack = packDownloadResult.packs[0]; + + const packDir = `${packDownloadResult.packDir}/${downloadedPack.name}/${downloadedPack.version}`; + + const suitePath = `${packDir}/codeql-suites/${language}-code-scanning.qls`; + const resolvedQueries = await this.cliServer.resolveQueries(suitePath); + + const problemQueries = + await this.filterToOnlyProblemQueries(resolvedQueries); + + if (problemQueries.length === 0) { + void this.app.logger.showErrorMessage( + `Unable to trigger variant analysis. No problem queries found in published query pack: ${packName}.`, + ); + } + + return withProgress((progress, token) => + this.runVariantAnalysis( + problemQueries.map((q) => Uri.file(q)), + progress, + token, + ), + ); + } + + private async filterToOnlyProblemQueries( + queries: string[], + ): Promise { + const problemQueries: string[] = []; + for (const query of queries) { + const queryMetadata = await this.cliServer.resolveMetadata(query); + if ( + queryMetadata.kind === "problem" || + queryMetadata.kind === "path-problem" + ) { + problemQueries.push(query); + } else { + void this.app.logger.log(`Skipping non-problem query ${query}`); + } + } + return problemQueries; } private async runVariantAnalysisCommand(uri: Uri): Promise { From 174a38a16aea4e6e11a1af628c8412dba07389ff Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 16 Jan 2024 15:01:51 +0000 Subject: [PATCH 52/77] Make placeholder text more general --- extensions/ql-vscode/src/codeql-cli/query-language.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/codeql-cli/query-language.ts b/extensions/ql-vscode/src/codeql-cli/query-language.ts index 53d0528f1..c711f1568 100644 --- a/extensions/ql-vscode/src/codeql-cli/query-language.ts +++ b/extensions/ql-vscode/src/codeql-cli/query-language.ts @@ -63,7 +63,7 @@ export async function askForLanguage( .sort((a, b) => a.label.localeCompare(b.label)); const selectedItem = await window.showQuickPick(items, { - placeHolder: "Select target language for your query", + placeHolder: "Select target query language", ignoreFocusOut: true, }); if (!selectedItem) { From fed110f65fb5210903907a61c26ed67f803c7fcb Mon Sep 17 00:00:00 2001 From: Nora Date: Tue, 16 Jan 2024 15:26:41 +0000 Subject: [PATCH 53/77] Replace toBeCalled --- .../common/disposable-object.test.ts | 36 +++++++++---------- .../variant-analysis-manager.test.ts | 4 +-- .../variant-analysis-monitor.test.ts | 2 +- .../local-queries/local-databases.test.ts | 10 +++--- .../common/vscode/external-files.test.ts | 6 ++-- .../common/vscode/progress.test.ts | 2 +- .../ast-viewer/ast-viewer.test.ts | 2 +- .../query-history-manager.test.ts | 12 +++---- .../no-workspace/query-results.test.ts | 8 ++--- .../no-workspace/telemetry.test.ts | 28 +++++++-------- 10 files changed, 55 insertions(+), 55 deletions(-) diff --git a/extensions/ql-vscode/test/unit-tests/common/disposable-object.test.ts b/extensions/ql-vscode/test/unit-tests/common/disposable-object.test.ts index 9b1348a49..b211141e3 100644 --- a/extensions/ql-vscode/test/unit-tests/common/disposable-object.test.ts +++ b/extensions/ql-vscode/test/unit-tests/common/disposable-object.test.ts @@ -34,9 +34,9 @@ describe("DisposableObject and DisposeHandler", () => { disposableObject.dispose(); - expect(disposable1.dispose).toBeCalled(); - expect(disposable2.dispose).toBeCalled(); - expect(disposable3.dispose).toBeCalled(); + expect(disposable1.dispose).toHaveBeenCalled(); + expect(disposable2.dispose).toHaveBeenCalled(); + expect(disposable3.dispose).toHaveBeenCalled(); // pushed items must be called in reverse order expect(disposable2.dispose.mock.invocationCallOrder[0]).toBeLessThan( @@ -51,30 +51,30 @@ describe("DisposableObject and DisposeHandler", () => { disposableObject.dispose(); - expect(disposable1.dispose).not.toBeCalled(); - expect(disposable2.dispose).not.toBeCalled(); - expect(disposable3.dispose).not.toBeCalled(); + expect(disposable1.dispose).not.toHaveBeenCalled(); + expect(disposable2.dispose).not.toHaveBeenCalled(); + expect(disposable3.dispose).not.toHaveBeenCalled(); }); it("should dispose and stop tracking objects", () => { disposableObject.track(disposable1); disposableObject.disposeAndStopTracking(disposable1); - expect(disposable1.dispose).toBeCalled(); + expect(disposable1.dispose).toHaveBeenCalled(); disposable1.dispose.mockClear(); disposableObject.dispose(); - expect(disposable1.dispose).not.toBeCalled(); + expect(disposable1.dispose).not.toHaveBeenCalled(); }); it("should avoid disposing an object that is not tracked", () => { disposableObject.push(disposable1); disposableObject.disposeAndStopTracking(disposable1); - expect(disposable1.dispose).not.toBeCalled(); + expect(disposable1.dispose).not.toHaveBeenCalled(); disposableObject.dispose(); - expect(disposable1.dispose).toBeCalled(); + expect(disposable1.dispose).toHaveBeenCalled(); }); it("ahould use a dispose handler", () => { @@ -91,10 +91,10 @@ describe("DisposableObject and DisposeHandler", () => { disposableObject.dispose(handler); - expect(disposable1.dispose).toBeCalled(); - expect(disposable2.dispose).not.toBeCalled(); - expect(disposable3.dispose).toBeCalled(); - expect(disposable4.dispose).not.toBeCalled(); + expect(disposable1.dispose).toHaveBeenCalled(); + expect(disposable2.dispose).not.toHaveBeenCalled(); + expect(disposable3.dispose).toHaveBeenCalled(); + expect(disposable4.dispose).not.toHaveBeenCalled(); // now that disposableObject has been disposed, subsequent disposals are // no-ops @@ -105,10 +105,10 @@ describe("DisposableObject and DisposeHandler", () => { disposableObject.dispose(); - expect(disposable1.dispose).not.toBeCalled(); - expect(disposable2.dispose).not.toBeCalled(); - expect(disposable3.dispose).not.toBeCalled(); - expect(disposable4.dispose).not.toBeCalled(); + expect(disposable1.dispose).not.toHaveBeenCalled(); + expect(disposable2.dispose).not.toHaveBeenCalled(); + expect(disposable3.dispose).not.toHaveBeenCalled(); + expect(disposable4.dispose).not.toHaveBeenCalled(); }); class MyDisposableObject extends DisposableObject { diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index 6dc783c5a..6274483dc 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -656,7 +656,7 @@ describe("Variant Analysis Manager", () => { variantAnalysis.id, ); - expect(writeTextStub).not.toBeCalled(); + expect(writeTextStub).not.toHaveBeenCalled(); }); }); @@ -682,7 +682,7 @@ describe("Variant Analysis Manager", () => { variantAnalysis.id, ); - expect(writeTextStub).not.toBeCalled(); + expect(writeTextStub).not.toHaveBeenCalled(); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index 3042d225d..1de5d10ad 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -291,7 +291,7 @@ describe("Variant Analysis Monitor", () => { it("should not try to download any repos", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockEecuteCommand).not.toBeCalled(); + expect(mockEecuteCommand).not.toHaveBeenCalled(); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts index faf6eaa03..1b6bfe657 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts @@ -628,7 +628,7 @@ describe("local databases", () => { await (databaseManager as any).createSkeletonPacks(mockDbItem); - expect(generateSpy).not.toBeCalled(); + expect(generateSpy).not.toHaveBeenCalled(); }); it("should return early if the user escapes out of the dialog", async () => { @@ -638,7 +638,7 @@ describe("local databases", () => { await (databaseManager as any).createSkeletonPacks(mockDbItem); - expect(generateSpy).not.toBeCalled(); + expect(generateSpy).not.toHaveBeenCalled(); }); it("should return early and write choice to settings if user wants to never be asked again", async () => { @@ -652,14 +652,14 @@ describe("local databases", () => { await (databaseManager as any).createSkeletonPacks(mockDbItem); - expect(generateSpy).not.toBeCalled(); + expect(generateSpy).not.toHaveBeenCalled(); expect(setAutogenerateQlPacksSpy).toHaveBeenCalledWith("never"); }); it("should create the skeleton QL pack for the user", async () => { await (databaseManager as any).createSkeletonPacks(mockDbItem); - expect(generateSpy).toBeCalled(); + expect(generateSpy).toHaveBeenCalled(); }); }); @@ -694,7 +694,7 @@ describe("local databases", () => { await (databaseManager as any).createSkeletonPacks(mockDbItem); - expect(generateSpy).not.toBeCalled(); + expect(generateSpy).not.toHaveBeenCalled(); }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/external-files.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/external-files.test.ts index 26d3f525a..e6791a97c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/external-files.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/external-files.test.ts @@ -29,7 +29,7 @@ describe("tryOpenExternalFile", () => { Uri.file("xxx"), expect.anything(), ); - expect(executeCommand).not.toBeCalled(); + expect(executeCommand).not.toHaveBeenCalled(); }); [ @@ -61,8 +61,8 @@ describe("tryOpenExternalFile", () => { const uri = Uri.file("xxx"); expect(showTextDocumentSpy).toHaveBeenCalledTimes(1); expect(showTextDocumentSpy).toHaveBeenCalledWith(uri, expect.anything()); - expect(showInformationMessageSpy).toBeCalled(); - expect(executeCommand).not.toBeCalled(); + expect(showInformationMessageSpy).toHaveBeenCalled(); + expect(executeCommand).not.toHaveBeenCalled(); }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts index 1a1c560b6..12abf3c11 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts @@ -48,7 +48,7 @@ describe("helpers", () => { ); // There are no listeners registered to this readable - expect(mockReadable.on).not.toBeCalled(); + expect(mockReadable.on).not.toHaveBeenCalled(); expect(progressSpy).toBeCalledTimes(1); expect(progressSpy).toBeCalledWith({ diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts index e7fb2cd8c..f7bbb166e 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts @@ -88,7 +88,7 @@ describe("AstViewer", () => { if (expectedSelection) { expect(revealMock).toBeCalledWith(expectedSelection); } else { - expect(revealMock).not.toBeCalled(); + expect(revealMock).not.toHaveBeenCalled(); } } diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts index 5ff3e9d44..10edee9aa 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts @@ -608,8 +608,8 @@ describe("QueryHistoryManager", () => { const cancelSpy2 = jest.spyOn(inProgress2, "cancel"); await queryHistoryManager.handleCancel([inProgress1, inProgress2]); - expect(cancelSpy1).toBeCalled(); - expect(cancelSpy2).toBeCalled(); + expect(cancelSpy1).toHaveBeenCalled(); + expect(cancelSpy2).toHaveBeenCalled(); }); it("should cancel a single variant analysis", async () => { @@ -708,7 +708,7 @@ describe("QueryHistoryManager", () => { const item = localQueryHistory[4]; await queryHistoryManager.handleCopyRepoList(item); - expect(executeCommand).not.toBeCalled(); + expect(executeCommand).not.toHaveBeenCalled(); }); it("should copy repo list for a single variant analysis", async () => { @@ -731,7 +731,7 @@ describe("QueryHistoryManager", () => { const item = localQueryHistory[4]; await queryHistoryManager.handleExportResults(item); - expect(variantAnalysisManagerStub.exportResults).not.toBeCalled(); + expect(variantAnalysisManagerStub.exportResults).not.toHaveBeenCalled(); }); it("should export results for a single variant analysis", async () => { @@ -801,7 +801,7 @@ describe("QueryHistoryManager", () => { queryHistoryManager as any ).findOtherQueryToCompare(thisQuery, [thisQuery, localQueryHistory[0]]); expect(otherQuery).toBe(localQueryHistory[0]); - expect(showQuickPickSpy).not.toBeCalled(); + expect(showQuickPickSpy).not.toHaveBeenCalled(); }); it("should throw an error when a databases are not the same", async () => { @@ -850,7 +850,7 @@ describe("QueryHistoryManager", () => { await queryHistoryManager.handleCompareWith(localQueryHistory[0], [ localQueryHistory[0], ]); - expect(doCompareCallback).not.toBeCalled(); + expect(doCompareCallback).not.toHaveBeenCalled(); }); it("should throw an error when a query is not successful", async () => { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts index 70af63221..06d4a6ca8 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts @@ -245,7 +245,7 @@ describe("query-results", () => { sourceInfo as SourceInfo, ); // We do not re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); expect(results).toHaveProperty("t", "SarifInterpretationData"); expect(results).toHaveProperty("runs[0].results"); @@ -279,7 +279,7 @@ describe("query-results", () => { ); // We do not attempt to re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); }, 2 * 60 * 1000, // up to 2 minutes per test ); @@ -336,7 +336,7 @@ describe("query-results", () => { sourceInfo as SourceInfo, ); // We do not re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); expect(results).toHaveProperty("t", "SarifInterpretationData"); expect(results).toHaveProperty("runs[0].results"); @@ -400,7 +400,7 @@ describe("query-results", () => { ); // We do not attempt to re-interpret if we are reading from a SARIF file. - expect(spy).not.toBeCalled(); + expect(spy).not.toHaveBeenCalled(); }, 2 * 60 * 1000, // up to 2 minutes per test ); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts index 0daea3dca..5856c95fb 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts @@ -162,7 +162,7 @@ describe("telemetry reporting", () => { it("should reinitialize reporter when extension setting changes", async () => { await telemetryListener.initialize(); - expect(disposeSpy).not.toBeCalled(); + expect(disposeSpy).not.toHaveBeenCalled(); expect(telemetryListener._reporter).toBeDefined(); // this disables the reporter @@ -205,7 +205,7 @@ describe("telemetry reporting", () => { }, { executionTime: 1234 }, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should send a command usage event with an error", async () => { @@ -227,7 +227,7 @@ describe("telemetry reporting", () => { }, { executionTime: 1234 }, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should send a command usage event with a cli version", async () => { @@ -250,7 +250,7 @@ describe("telemetry reporting", () => { }, { executionTime: 1234 }, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); // Verify that if the cli version is not set, then the telemetry falls back to "not-set" sendTelemetryEventSpy.mockClear(); @@ -272,7 +272,7 @@ describe("telemetry reporting", () => { }, { executionTime: 5678 }, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should avoid sending an event when telemetry is disabled", async () => { @@ -282,8 +282,8 @@ describe("telemetry reporting", () => { telemetryListener.sendCommandUsage("command-id", 1234, undefined); telemetryListener.sendCommandUsage("command-id", 1234, new Error()); - expect(sendTelemetryEventSpy).not.toBeCalled(); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryEventSpy).not.toHaveBeenCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should send an event when telemetry is re-enabled", async () => { @@ -303,7 +303,7 @@ describe("telemetry reporting", () => { }, { executionTime: 1234 }, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should filter undesired properties from telemetry payload", async () => { @@ -426,7 +426,7 @@ describe("telemetry reporting", () => { await telemetryListener.initialize(); // popup should not be shown even though we have initialized telemetry - expect(showInformationMessageSpy).not.toBeCalled(); + expect(showInformationMessageSpy).not.toHaveBeenCalled(); }); // This test is failing because codeQL.canary is not a registered configuration. @@ -464,7 +464,7 @@ describe("telemetry reporting", () => { }, {}, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should send a ui-interaction telementry event with a cli version", async () => { @@ -482,7 +482,7 @@ describe("telemetry reporting", () => { }, {}, ); - expect(sendTelemetryErrorEventSpy).not.toBeCalled(); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); it("should send an error telementry event", async () => { @@ -490,7 +490,7 @@ describe("telemetry reporting", () => { telemetryListener.sendError(redactableError`test`); - expect(sendTelemetryEventSpy).not.toBeCalled(); + expect(sendTelemetryEventSpy).not.toHaveBeenCalled(); expect(sendTelemetryErrorEventSpy).toHaveBeenCalledWith( "error", { @@ -509,7 +509,7 @@ describe("telemetry reporting", () => { telemetryListener.sendError(redactableError`test`); - expect(sendTelemetryEventSpy).not.toBeCalled(); + expect(sendTelemetryEventSpy).not.toHaveBeenCalled(); expect(sendTelemetryErrorEventSpy).toHaveBeenCalledWith( "error", { @@ -529,7 +529,7 @@ describe("telemetry reporting", () => { redactableError`test message with secret information: ${42} and more ${"secret"} parts`, ); - expect(sendTelemetryEventSpy).not.toBeCalled(); + expect(sendTelemetryEventSpy).not.toHaveBeenCalled(); expect(sendTelemetryErrorEventSpy).toHaveBeenCalledWith( "error", { From abc71b5102c2604cc8e5e92984b10b4d9dad4ab7 Mon Sep 17 00:00:00 2001 From: Nora Date: Tue, 16 Jan 2024 15:28:07 +0000 Subject: [PATCH 54/77] Replace toBeCalledWith --- .../logging/output-channel-logger.test.ts | 8 +++--- .../databases/config/db-config-store.test.ts | 4 +-- .../variant-analysis-manager.test.ts | 2 +- .../variant-analysis-manager.test.ts | 8 +++--- .../local-queries/local-databases.test.ts | 28 +++++++++---------- .../query-tree-data-provider.test.ts | 4 +-- .../common/vscode/progress.test.ts | 8 +++--- .../ast-viewer/ast-builder.test.ts | 6 ++-- .../ast-viewer/ast-viewer.test.ts | 2 +- .../local-queries/query-resolver.test.ts | 2 +- .../query-history-manager.test.ts | 20 ++++++------- .../variant-analysis-history.test.ts | 4 ++- .../no-workspace/query-results.test.ts | 6 ++-- .../query-testing/test-runner.test.ts | 8 +++--- 14 files changed, 56 insertions(+), 54 deletions(-) diff --git a/extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts b/extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts index 10e6843d9..337960832 100644 --- a/extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts +++ b/extensions/ql-vscode/test/common/logging/output-channel-logger.test.ts @@ -54,12 +54,12 @@ describe("OutputChannelLogger tests", function () { it("should log to the output channel", async () => { await logger.log("xxx"); - expect(mockOutputChannel.appendLine).toBeCalledWith("xxx"); - expect(mockOutputChannel.append).not.toBeCalledWith("xxx"); + expect(mockOutputChannel.appendLine).toHaveBeenCalledWith("xxx"); + expect(mockOutputChannel.append).not.toHaveBeenCalledWith("xxx"); await logger.log("yyy", { trailingNewline: false }); - expect(mockOutputChannel.appendLine).not.toBeCalledWith("yyy"); - expect(mockOutputChannel.append).toBeCalledWith("yyy"); + expect(mockOutputChannel.appendLine).not.toHaveBeenCalledWith("yyy"); + expect(mockOutputChannel.append).toHaveBeenCalledWith("yyy"); const hucairz = createSideLogger(logger, "hucairz"); await hucairz.log("zzz"); diff --git a/extensions/ql-vscode/test/unit-tests/databases/config/db-config-store.test.ts b/extensions/ql-vscode/test/unit-tests/databases/config/db-config-store.test.ts index 33ebece8e..02e67ac7e 100644 --- a/extensions/ql-vscode/test/unit-tests/databases/config/db-config-store.test.ts +++ b/extensions/ql-vscode/test/unit-tests/databases/config/db-config-store.test.ts @@ -139,7 +139,7 @@ describe("db config store", () => { const configStore = new DbConfigStore(app, false); await configStore.initialize(); - expect(executeCommand).toBeCalledWith( + expect(executeCommand).toHaveBeenCalledWith( "setContext", "codeQLVariantAnalysisRepositories.configError", true, @@ -157,7 +157,7 @@ describe("db config store", () => { const configStore = new DbConfigStore(app, false); await configStore.initialize(); - expect(executeCommand).toBeCalledWith( + expect(executeCommand).toHaveBeenCalledWith( "setContext", "codeQLVariantAnalysisRepositories.configError", false, diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index 6274483dc..e6ebd15fd 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -610,7 +610,7 @@ describe("Variant Analysis Manager", () => { it("should return cancel if valid", async () => { await variantAnalysisManager.cancelVariantAnalysis(variantAnalysis.id); - expect(mockCancelVariantAnalysis).toBeCalledWith( + expect(mockCancelVariantAnalysis).toHaveBeenCalledWith( app.credentials, variantAnalysis, ); diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 628a2603e..168ce663b 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -105,7 +105,7 @@ describe("Variant Analysis Manager", () => { cancellationTokenSource.token, ); - expect(executeCommandSpy).toBeCalledWith( + expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.monitorNewVariantAnalysis", expect.objectContaining({ id: mockApiResponse.id, @@ -126,7 +126,7 @@ describe("Variant Analysis Manager", () => { cancellationTokenSource.token, ); - expect(executeCommandSpy).toBeCalledWith( + expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.monitorNewVariantAnalysis", expect.objectContaining({ id: mockApiResponse.id, @@ -147,7 +147,7 @@ describe("Variant Analysis Manager", () => { cancellationTokenSource.token, ); - expect(executeCommandSpy).toBeCalledWith( + expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.monitorNewVariantAnalysis", expect.objectContaining({ id: mockApiResponse.id, @@ -319,7 +319,7 @@ describe("Variant Analysis Manager", () => { ); expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); - expect(executeCommandSpy).toBeCalledWith( + expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.monitorNewVariantAnalysis", expect.objectContaining({ query: expect.objectContaining({ filePath: fileUri.fsPath }), diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts index 1b6bfe657..72bd8379a 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts @@ -130,13 +130,13 @@ describe("local databases", () => { await (databaseManager as any).addDatabaseItem(mockDbItem); expect((databaseManager as any)._databaseItems).toEqual([mockDbItem]); - expect(updateSpy).toBeCalledWith("databaseList", [ + expect(updateSpy).toHaveBeenCalledWith("databaseList", [ { options: mockDbOptions(), uri: dbLocationUri(dir).toString(true), }, ]); - expect(onDidChangeDatabaseItem).toBeCalledWith({ + expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({ item: undefined, kind: DatabaseEventKind.Add, }); @@ -147,8 +147,8 @@ describe("local databases", () => { // now remove the item await databaseManager.removeDatabaseItem(mockDbItem); expect((databaseManager as any)._databaseItems).toEqual([]); - expect(updateSpy).toBeCalledWith("databaseList", []); - expect(onDidChangeDatabaseItem).toBeCalledWith({ + expect(updateSpy).toHaveBeenCalledWith("databaseList", []); + expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({ item: undefined, kind: DatabaseEventKind.Remove, }); @@ -164,14 +164,14 @@ describe("local databases", () => { await databaseManager.renameDatabaseItem(mockDbItem, "new name"); expect(mockDbItem.name).toBe("new name"); - expect(updateSpy).toBeCalledWith("databaseList", [ + expect(updateSpy).toHaveBeenCalledWith("databaseList", [ { options: { ...mockDbOptions(), displayName: "new name" }, uri: dbLocationUri(dir).toString(true), }, ]); - expect(onDidChangeDatabaseItem).toBeCalledWith({ + expect(onDidChangeDatabaseItem).toHaveBeenCalledWith({ item: undefined, kind: DatabaseEventKind.Rename, }); @@ -187,7 +187,7 @@ describe("local databases", () => { await (databaseManager as any).addDatabaseItem(mockDbItem); expect(databaseManager.databaseItems).toEqual([mockDbItem]); - expect(updateSpy).toBeCalledWith("databaseList", [ + expect(updateSpy).toHaveBeenCalledWith("databaseList", [ { uri: dbLocationUri(dir).toString(true), options: mockDbOptions(), @@ -198,7 +198,7 @@ describe("local databases", () => { item: undefined, kind: DatabaseEventKind.Add, }; - expect(onDidChangeDatabaseItem).toBeCalledWith(mockEvent); + expect(onDidChangeDatabaseItem).toHaveBeenCalledWith(mockEvent); }); it("should add a database item source archive", async () => { @@ -234,9 +234,9 @@ describe("local databases", () => { await databaseManager.removeDatabaseItem(mockDbItem); expect(databaseManager.databaseItems).toEqual([]); - expect(updateSpy).toBeCalledWith("databaseList", []); + expect(updateSpy).toHaveBeenCalledWith("databaseList", []); // should remove the folder - expect(workspace.updateWorkspaceFolders).toBeCalledWith(0, 1); + expect(workspace.updateWorkspaceFolders).toHaveBeenCalledWith(0, 1); // should also delete the db contents await expect(pathExists(mockDbItem.databaseUri.fsPath)).resolves.toBe( @@ -262,9 +262,9 @@ describe("local databases", () => { await databaseManager.removeDatabaseItem(mockDbItem); expect(databaseManager.databaseItems).toEqual([]); - expect(updateSpy).toBeCalledWith("databaseList", []); + expect(updateSpy).toHaveBeenCalledWith("databaseList", []); // should remove the folder - expect(workspace.updateWorkspaceFolders).toBeCalledWith(0, 1); + expect(workspace.updateWorkspaceFolders).toHaveBeenCalledWith(0, 1); // should NOT delete the db contents await expect(pathExists(mockDbItem.databaseUri.fsPath)).resolves.toBe( @@ -279,12 +279,12 @@ describe("local databases", () => { await (databaseManager as any).addDatabaseItem(mockDbItem); // Should have registered this database - expect(registerSpy).toBeCalledWith(mockDbItem); + expect(registerSpy).toHaveBeenCalledWith(mockDbItem); await databaseManager.removeDatabaseItem(mockDbItem); // Should have deregistered this database - expect(deregisterSpy).toBeCalledWith(mockDbItem); + expect(deregisterSpy).toHaveBeenCalledWith(mockDbItem); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/queries-panel/query-tree-data-provider.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/queries-panel/query-tree-data-provider.test.ts index 6a7670c83..b86b3ab2b 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/queries-panel/query-tree-data-provider.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/queries-panel/query-tree-data-provider.test.ts @@ -39,7 +39,7 @@ describe("QueryTreeDataProvider", () => { ); expect(dataProvider.getChildren()).toEqual([]); - expect(executeCommand).toBeCalledWith( + expect(executeCommand).toHaveBeenCalledWith( "setContext", "codeQL.noQueries", true, @@ -118,7 +118,7 @@ describe("QueryTreeDataProvider", () => { onDidChangeQueriesEmitter.fire(); expect(dataProvider.getChildren().length).toEqual(2); - expect(executeCommand).toBeCalledWith( + expect(executeCommand).toHaveBeenCalledWith( "setContext", "codeQL.noQueries", false, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts index 12abf3c11..8ad53adef 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts @@ -18,17 +18,17 @@ describe("helpers", () => { listener({ length: secondStep }); expect(progressSpy).toBeCalledTimes(3); - expect(progressSpy).toBeCalledWith({ + expect(progressSpy).toHaveBeenCalledWith({ step: 0, maxStep: max, message: "My prefix [0.0 MB of 4.0 MB]", }); - expect(progressSpy).toBeCalledWith({ + expect(progressSpy).toHaveBeenCalledWith({ step: firstStep, maxStep: max, message: "My prefix [1.6 MB of 4.0 MB]", }); - expect(progressSpy).toBeCalledWith({ + expect(progressSpy).toHaveBeenCalledWith({ step: firstStep + secondStep, maxStep: max, message: "My prefix [3.6 MB of 4.0 MB]", @@ -51,7 +51,7 @@ describe("helpers", () => { expect(mockReadable.on).not.toHaveBeenCalled(); expect(progressSpy).toBeCalledTimes(1); - expect(progressSpy).toBeCalledWith({ + expect(progressSpy).toHaveBeenCalledWith({ step: 1, maxStep: 2, message: "My prefix (Size unknown)", diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts index 0dc0f2b6e..fd033de5f 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-builder.test.ts @@ -55,9 +55,9 @@ describe("AstBuilder", () => { const bqrsPath = path.normalize("/a/b/c/results.bqrs"); const options = { entities: ["id", "url", "string"] }; - expect(mockCli.bqrsDecode).toBeCalledWith(bqrsPath, "nodes", options); - expect(mockCli.bqrsDecode).toBeCalledWith(bqrsPath, "edges", options); - expect(mockCli.bqrsDecode).toBeCalledWith( + expect(mockCli.bqrsDecode).toHaveBeenCalledWith(bqrsPath, "nodes", options); + expect(mockCli.bqrsDecode).toHaveBeenCalledWith(bqrsPath, "edges", options); + expect(mockCli.bqrsDecode).toHaveBeenCalledWith( bqrsPath, "graphProperties", options, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts index f7bbb166e..9ecee9006 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/language-support/ast-viewer/ast-viewer.test.ts @@ -86,7 +86,7 @@ describe("AstViewer", () => { const mockEvent = createMockEvent(selectionRange, fileUri); (viewer as any).updateTreeSelection(mockEvent); if (expectedSelection) { - expect(revealMock).toBeCalledWith(expectedSelection); + expect(revealMock).toHaveBeenCalledWith(expectedSelection); } else { expect(revealMock).not.toHaveBeenCalled(); } diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/local-queries/query-resolver.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/local-queries/query-resolver.test.ts index f7fa047a5..9e4dec27b 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/local-queries/query-resolver.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/local-queries/query-resolver.test.ts @@ -49,7 +49,7 @@ describe("qlpackOfDatabase", () => { dbschemePack: "my-qlpack", dbschemePackIsLibraryPack: false, }); - expect(getPrimaryDbschemeSpy).toBeCalledWith("/path/to/database"); + expect(getPrimaryDbschemeSpy).toHaveBeenCalledWith("/path/to/database"); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts index 10edee9aa..3e773cc64 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts @@ -619,7 +619,7 @@ describe("QueryHistoryManager", () => { const inProgress1 = variantAnalysisHistory[1]; await queryHistoryManager.handleCancel([inProgress1]); - expect(cancelVariantAnalysisSpy).toBeCalledWith( + expect(cancelVariantAnalysisSpy).toHaveBeenCalledWith( inProgress1.variantAnalysis.id, ); }); @@ -632,10 +632,10 @@ describe("QueryHistoryManager", () => { const inProgress2 = variantAnalysisHistory[3]; await queryHistoryManager.handleCancel([inProgress1, inProgress2]); - expect(cancelVariantAnalysisSpy).toBeCalledWith( + expect(cancelVariantAnalysisSpy).toHaveBeenCalledWith( inProgress1.variantAnalysis.id, ); - expect(cancelVariantAnalysisSpy).toBeCalledWith( + expect(cancelVariantAnalysisSpy).toHaveBeenCalledWith( inProgress2.variantAnalysis.id, ); }); @@ -675,7 +675,7 @@ describe("QueryHistoryManager", () => { const completedVariantAnalysis = variantAnalysisHistory[0]; await queryHistoryManager.handleCancel([completedVariantAnalysis]); - expect(cancelVariantAnalysisSpy).not.toBeCalledWith( + expect(cancelVariantAnalysisSpy).not.toHaveBeenCalledWith( completedVariantAnalysis.variantAnalysis, ); }); @@ -691,10 +691,10 @@ describe("QueryHistoryManager", () => { completedVariantAnalysis, failedVariantAnalysis, ]); - expect(cancelVariantAnalysisSpy).not.toBeCalledWith( + expect(cancelVariantAnalysisSpy).not.toHaveBeenCalledWith( completedVariantAnalysis.variantAnalysis.id, ); - expect(cancelVariantAnalysisSpy).not.toBeCalledWith( + expect(cancelVariantAnalysisSpy).not.toHaveBeenCalledWith( failedVariantAnalysis.variantAnalysis.id, ); }); @@ -718,9 +718,9 @@ describe("QueryHistoryManager", () => { const item = variantAnalysisHistory[1]; await queryHistoryManager.handleCopyRepoList(item); - expect(variantAnalysisManagerStub.copyRepoListToClipboard).toBeCalledWith( - item.variantAnalysis.id, - ); + expect( + variantAnalysisManagerStub.copyRepoListToClipboard, + ).toHaveBeenCalledWith(item.variantAnalysis.id); }); }); @@ -739,7 +739,7 @@ describe("QueryHistoryManager", () => { const item = variantAnalysisHistory[1]; await queryHistoryManager.handleExportResults(item); - expect(variantAnalysisManagerStub.exportResults).toBeCalledWith( + expect(variantAnalysisManagerStub.exportResults).toHaveBeenCalledWith( item.variantAnalysis.id, ); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts index f11ba529c..6f8e4dc07 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts @@ -184,7 +184,9 @@ describe("Variant Analyses and QueryHistoryManager", () => { await qhm.readQueryHistory(); await qhm.handleItemClicked(qhm.treeDataProvider.allHistory[0]); - expect(showViewStub).toBeCalledWith(rawQueryHistory[0].variantAnalysis.id); + expect(showViewStub).toHaveBeenCalledWith( + rawQueryHistory[0].variantAnalysis.id, + ); }); it("should get the query text", async () => { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts index 06d4a6ca8..326b32d8c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-results.test.ts @@ -130,7 +130,7 @@ describe("query-results", () => { queryPath, "sortedResults-cc8589f226adc134f87f2438e10075e0667571c72342068e2281e0b3b65e1092.bqrs", ); - expect(spy).toBeCalledWith( + expect(spy).toHaveBeenCalledWith( expectedResultsPath, expectedSortedResultsPath, "a-result-set-name", @@ -189,7 +189,7 @@ describe("query-results", () => { ); expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" }); - expect(spy).toBeCalledWith( + expect(spy).toHaveBeenCalledWith( metadata, resultsPath, interpretedResultsPath, @@ -214,7 +214,7 @@ describe("query-results", () => { sourceInfo as SourceInfo, ); expect(results).toEqual({ a: "1234", t: "SarifInterpretationData" }); - expect(spy).toBeCalledWith( + expect(spy).toHaveBeenCalledWith( { kind: "my-kind", id: "dummy-id", scored: undefined }, resultsPath, interpretedResultsPath, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts index b7958a081..51aadc0cc 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts @@ -161,23 +161,23 @@ describe("test-runner", () => { ).toBeGreaterThan(openDatabaseSpy.mock.invocationCallOrder[0]); expect(removeDatabaseItemSpy).toBeCalledTimes(1); - expect(removeDatabaseItemSpy).toBeCalledWith(preTestDatabaseItem); + expect(removeDatabaseItemSpy).toHaveBeenCalledWith(preTestDatabaseItem); expect(openDatabaseSpy).toBeCalledTimes(1); - expect(openDatabaseSpy).toBeCalledWith( + expect(openDatabaseSpy).toHaveBeenCalledWith( preTestDatabaseItem.databaseUri, preTestDatabaseItem.origin, false, ); expect(renameDatabaseItemSpy).toBeCalledTimes(1); - expect(renameDatabaseItemSpy).toBeCalledWith( + expect(renameDatabaseItemSpy).toHaveBeenCalledWith( postTestDatabaseItem, preTestDatabaseItem.name, ); expect(setCurrentDatabaseItemSpy).toBeCalledTimes(1); - expect(setCurrentDatabaseItemSpy).toBeCalledWith( + expect(setCurrentDatabaseItemSpy).toHaveBeenCalledWith( postTestDatabaseItem, true, ); From 70ce93cbb3f1b470c07a59644fcff0d0c13aaf4d Mon Sep 17 00:00:00 2001 From: Nora Date: Tue, 16 Jan 2024 15:28:51 +0000 Subject: [PATCH 55/77] Replace toBeCalledTimes --- .../variant-analysis-manager.test.ts | 8 ++++---- .../variant-analysis-monitor.test.ts | 8 ++++---- .../variant-analysis-manager.test.ts | 14 +++++++------- .../local-queries/local-databases.test.ts | 16 ++++++++-------- .../codeql-cli/distribution.test.ts | 12 ++++++------ .../common/vscode/progress.test.ts | 4 ++-- .../databases/database-fetcher.test.ts | 4 ++-- .../query-history-manager.test.ts | 8 ++++---- .../variant-analysis-history.test.ts | 6 +++--- .../query-testing/test-adapter.test.ts | 8 ++++---- .../query-testing/test-runner.test.ts | 10 +++++----- .../no-workspace/telemetry.test.ts | 18 +++++++++--------- 12 files changed, 58 insertions(+), 58 deletions(-) diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts index e6ebd15fd..e90e3e6e9 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-manager.test.ts @@ -96,7 +96,7 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.rehydrateVariantAnalysis(variantAnalysis); - expect(stub).toBeCalledTimes(1); + expect(stub).toHaveBeenCalledTimes(1); }); }); @@ -423,7 +423,7 @@ describe("Variant Analysis Manager", () => { ); expect(variantAnalysisManager.downloadsQueueSize()).toBe(0); - expect(getResultsSpy).toBeCalledTimes(3); + expect(getResultsSpy).toHaveBeenCalledTimes(3); }); }); @@ -451,7 +451,7 @@ describe("Variant Analysis Manager", () => { await variantAnalysisManager.removeVariantAnalysis(dummyVariantAnalysis); - expect(removeAnalysisResultsStub).toBeCalledTimes(1); + expect(removeAnalysisResultsStub).toHaveBeenCalledTimes(1); expect(variantAnalysisManager.variantAnalysesSize).toBe(0); await expect( @@ -722,7 +722,7 @@ describe("Variant Analysis Manager", () => { variantAnalysis.id, ); - expect(writeTextStub).toBeCalledTimes(1); + expect(writeTextStub).toHaveBeenCalledTimes(1); }); it("should be valid JSON when put in object", async () => { diff --git a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts index 1de5d10ad..ac7e3957e 100644 --- a/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/activated-extension/variant-analysis/variant-analysis-monitor.test.ts @@ -121,7 +121,7 @@ describe("Variant Analysis Monitor", () => { ); await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockEecuteCommand).toBeCalledTimes(succeededRepos.length); + expect(mockEecuteCommand).toHaveBeenCalledTimes(succeededRepos.length); succeededRepos.forEach((succeededRepo, index) => { expect(mockEecuteCommand).toHaveBeenNthCalledWith( @@ -197,8 +197,8 @@ describe("Variant Analysis Monitor", () => { it("should trigger a download extension command for each repo", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(mockGetVariantAnalysis).toBeCalledTimes(4); - expect(mockEecuteCommand).toBeCalledTimes(5); + expect(mockGetVariantAnalysis).toHaveBeenCalledTimes(4); + expect(mockEecuteCommand).toHaveBeenCalledTimes(5); }); }); @@ -261,7 +261,7 @@ describe("Variant Analysis Monitor", () => { it("should only trigger the warning once per error", async () => { await variantAnalysisMonitor.monitorVariantAnalysis(variantAnalysis); - expect(logger.showWarningMessage).toBeCalledTimes(4); + expect(logger.showWarningMessage).toHaveBeenCalledTimes(4); expect(logger.showWarningMessage).toHaveBeenNthCalledWith( 1, expect.stringMatching(/No internet connection/), diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 168ce663b..f2ca2c9d2 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -113,8 +113,8 @@ describe("Variant Analysis Manager", () => { }), ); - expect(mockGetRepositoryFromNwo).toBeCalledTimes(1); - expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); + expect(mockGetRepositoryFromNwo).toHaveBeenCalledTimes(1); + expect(mockSubmitVariantAnalysis).toHaveBeenCalledTimes(1); }); it("should run a remote query that is not part of a qlpack", async () => { @@ -134,8 +134,8 @@ describe("Variant Analysis Manager", () => { }), ); - expect(mockGetRepositoryFromNwo).toBeCalledTimes(1); - expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); + expect(mockGetRepositoryFromNwo).toHaveBeenCalledTimes(1); + expect(mockSubmitVariantAnalysis).toHaveBeenCalledTimes(1); }); it("should run a remote query that is nested inside a qlpack", async () => { @@ -155,8 +155,8 @@ describe("Variant Analysis Manager", () => { }), ); - expect(mockGetRepositoryFromNwo).toBeCalledTimes(1); - expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); + expect(mockGetRepositoryFromNwo).toHaveBeenCalledTimes(1); + expect(mockSubmitVariantAnalysis).toHaveBeenCalledTimes(1); }); it("should cancel a run before uploading", async () => { @@ -318,7 +318,7 @@ describe("Variant Analysis Manager", () => { cancellationTokenSource.token, ); - expect(mockSubmitVariantAnalysis).toBeCalledTimes(1); + expect(mockSubmitVariantAnalysis).toHaveBeenCalledTimes(1); expect(executeCommandSpy).toHaveBeenCalledWith( "codeQL.monitorNewVariantAnalysis", expect.objectContaining({ diff --git a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts index 72bd8379a..f86543d62 100644 --- a/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/minimal-workspace/local-queries/local-databases.test.ts @@ -618,7 +618,7 @@ describe("local databases", () => { it("should offer the user to set up a skeleton QL pack", async () => { await (databaseManager as any).createSkeletonPacks(mockDbItem); - expect(showNeverAskAgainDialogSpy).toBeCalledTimes(1); + expect(showNeverAskAgainDialogSpy).toHaveBeenCalledTimes(1); }); it("should return early if the user refuses help", async () => { @@ -742,7 +742,7 @@ describe("local databases", () => { mockDbItem.origin, ); - expect(resolveDatabaseContentsSpy).toBeCalledTimes(2); + expect(resolveDatabaseContentsSpy).toHaveBeenCalledTimes(2); }); it("should set the database as the currently selected one", async () => { @@ -751,7 +751,7 @@ describe("local databases", () => { mockDbItem.origin, ); - expect(setCurrentDatabaseItemSpy).toBeCalledTimes(1); + expect(setCurrentDatabaseItemSpy).toHaveBeenCalledTimes(1); }); it("should not add database source archive folder when `codeQL.addingDatabases.addDatabaseSourceToWorkspace` is `false`", async () => { @@ -762,7 +762,7 @@ describe("local databases", () => { mockDbItem.origin, ); - expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(0); + expect(addDatabaseSourceArchiveFolderSpy).toHaveBeenCalledTimes(0); }); it("should add database source archive folder when `codeQL.addingDatabases.addDatabaseSourceToWorkspace` is `true`", async () => { @@ -773,7 +773,7 @@ describe("local databases", () => { mockDbItem.origin, ); - expect(addDatabaseSourceArchiveFolderSpy).toBeCalledTimes(1); + expect(addDatabaseSourceArchiveFolderSpy).toHaveBeenCalledTimes(1); }); describe("when codeQL.codespacesTemplate is set to true", () => { @@ -793,7 +793,7 @@ describe("local databases", () => { { isTutorialDatabase }, ); - expect(createSkeletonPacksSpy).toBeCalledTimes(0); + expect(createSkeletonPacksSpy).toHaveBeenCalledTimes(0); }); }); @@ -806,7 +806,7 @@ describe("local databases", () => { mockDbItem.origin, ); - expect(createSkeletonPacksSpy).toBeCalledTimes(1); + expect(createSkeletonPacksSpy).toHaveBeenCalledTimes(1); }); }); }); @@ -819,7 +819,7 @@ describe("local databases", () => { mockDbItem.databaseUri, mockDbItem.origin, ); - expect(createSkeletonPacksSpy).toBeCalledTimes(0); + expect(createSkeletonPacksSpy).toHaveBeenCalledTimes(0); }); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts index d3571cc06..189e1b12b 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/codeql-cli/distribution.test.ts @@ -114,8 +114,8 @@ describe("Launcher path", () => { expect(result).toBe(pathToCmd); // no warning or error message - expect(warnSpy).toBeCalledTimes(0); - expect(errorSpy).toBeCalledTimes(0); + expect(warnSpy).toHaveBeenCalledTimes(0); + expect(errorSpy).toHaveBeenCalledTimes(0); }); it("should warn when deprecated launcher is used, and new launcher is available", async () => { @@ -132,8 +132,8 @@ describe("Launcher path", () => { expect(result).toBe(pathToCmd); // has warning message - expect(warnSpy).toBeCalledTimes(1); - expect(errorSpy).toBeCalledTimes(0); + expect(warnSpy).toHaveBeenCalledTimes(1); + expect(errorSpy).toHaveBeenCalledTimes(0); }); it("should warn when launcher path is incorrect", async () => { @@ -147,7 +147,7 @@ describe("Launcher path", () => { expect(result).toBeUndefined(); // no error message - expect(warnSpy).toBeCalledTimes(0); - expect(errorSpy).toBeCalledTimes(1); + expect(warnSpy).toHaveBeenCalledTimes(0); + expect(errorSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts index 8ad53adef..3dad02cfb 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/common/vscode/progress.test.ts @@ -17,7 +17,7 @@ describe("helpers", () => { listener({ length: firstStep }); listener({ length: secondStep }); - expect(progressSpy).toBeCalledTimes(3); + expect(progressSpy).toHaveBeenCalledTimes(3); expect(progressSpy).toHaveBeenCalledWith({ step: 0, maxStep: max, @@ -50,7 +50,7 @@ describe("helpers", () => { // There are no listeners registered to this readable expect(mockReadable.on).not.toHaveBeenCalled(); - expect(progressSpy).toBeCalledTimes(1); + expect(progressSpy).toHaveBeenCalledTimes(1); expect(progressSpy).toHaveBeenCalledWith({ step: 1, maxStep: 2, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts index e47bbb5d6..4d38cb81e 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/databases/database-fetcher.test.ts @@ -145,7 +145,7 @@ describe("database-fetcher", () => { await expect( convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), ).rejects.toThrow(/Unable to get database/); - expect(progressSpy).toBeCalledTimes(0); + expect(progressSpy).toHaveBeenCalledTimes(0); }); // User has access to the repository, but there are no databases for any language. @@ -159,7 +159,7 @@ describe("database-fetcher", () => { await expect( convertGithubNwoToDatabaseUrl(githubRepo, octokit, progressSpy), ).rejects.toThrow(/Unable to get database/); - expect(progressSpy).toBeCalledTimes(1); + expect(progressSpy).toHaveBeenCalledTimes(1); }); describe("when language is already provided", () => { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts index 3e773cc64..69e5d3fae 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/query-history-manager.test.ts @@ -594,7 +594,7 @@ describe("QueryHistoryManager", () => { const cancelSpy = jest.spyOn(inProgress1, "cancel"); await queryHistoryManager.handleCancel([inProgress1]); - expect(cancelSpy).toBeCalledTimes(1); + expect(cancelSpy).toHaveBeenCalledTimes(1); }); it("should cancel multiple local queries", async () => { @@ -650,7 +650,7 @@ describe("QueryHistoryManager", () => { const cancelSpy = jest.spyOn(completed, "cancel"); await queryHistoryManager.handleCancel([completed]); - expect(cancelSpy).not.toBeCalledTimes(1); + expect(cancelSpy).not.toHaveBeenCalledTimes(1); }); it("should not cancel multiple local queries", async () => { @@ -664,8 +664,8 @@ describe("QueryHistoryManager", () => { const cancelSpy2 = jest.spyOn(failed, "cancel"); await queryHistoryManager.handleCancel([completed, failed]); - expect(cancelSpy).not.toBeCalledTimes(1); - expect(cancelSpy2).not.toBeCalledTimes(1); + expect(cancelSpy).not.toHaveBeenCalledTimes(1); + expect(cancelSpy2).not.toHaveBeenCalledTimes(1); }); it("should not cancel a single variant analysis", async () => { diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts index 6f8e4dc07..041409a7e 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-history/variant-analysis-history.test.ts @@ -119,7 +119,7 @@ describe("Variant Analyses and QueryHistoryManager", () => { it("should read query history that has variant analysis history items", async () => { await qhm.readQueryHistory(); - expect(rehydrateVariantAnalysisStub).toBeCalledTimes(2); + expect(rehydrateVariantAnalysisStub).toHaveBeenCalledTimes(2); expect(rehydrateVariantAnalysisStub).toHaveBeenNthCalledWith( 1, rawQueryHistory[0].variantAnalysis, @@ -142,8 +142,8 @@ describe("Variant Analyses and QueryHistoryManager", () => { // Add it back to the history qhm.addQuery(rawQueryHistory[0]); - expect(removeVariantAnalysisStub).toBeCalledTimes(1); - expect(rehydrateVariantAnalysisStub).toBeCalledTimes(2); + expect(removeVariantAnalysisStub).toHaveBeenCalledTimes(1); + expect(rehydrateVariantAnalysisStub).toHaveBeenCalledTimes(2); expect(qhm.treeDataProvider.allHistory).toEqual([ rawQueryHistory[1], rawQueryHistory[0], diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts index e293bbf1e..ee7e6148c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-adapter.test.ts @@ -95,8 +95,8 @@ describe("test-adapter", () => { const request = new TestRunRequest([rootItem]); await testManager.run(request, new CancellationTokenSource().token); - expect(enqueuedSpy).toBeCalledTimes(3); - expect(passedSpy).toBeCalledTimes(1); + expect(enqueuedSpy).toHaveBeenCalledTimes(3); + expect(passedSpy).toHaveBeenCalledTimes(1); expect(passedSpy).toHaveBeenCalledWith(childItems[0], 3000); expect(erroredSpy).toHaveBeenCalledTimes(1); expect(erroredSpy).toHaveBeenCalledWith( @@ -121,7 +121,7 @@ describe("test-adapter", () => { ], 11000, ); - expect(failedSpy).toBeCalledTimes(1); - expect(endSpy).toBeCalledTimes(1); + expect(failedSpy).toHaveBeenCalledTimes(1); + expect(endSpy).toHaveBeenCalledTimes(1); }); }); diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts index 51aadc0cc..837c28a3c 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/query-testing/test-runner.test.ts @@ -94,7 +94,7 @@ describe("test-runner", () => { eventHandlerSpy, ); - expect(eventHandlerSpy).toBeCalledTimes(3); + expect(eventHandlerSpy).toHaveBeenCalledTimes(3); expect(eventHandlerSpy).toHaveBeenNthCalledWith(1, { test: mockTestsInfo.dPath, @@ -160,23 +160,23 @@ describe("test-runner", () => { setCurrentDatabaseItemSpy.mock.invocationCallOrder[0], ).toBeGreaterThan(openDatabaseSpy.mock.invocationCallOrder[0]); - expect(removeDatabaseItemSpy).toBeCalledTimes(1); + expect(removeDatabaseItemSpy).toHaveBeenCalledTimes(1); expect(removeDatabaseItemSpy).toHaveBeenCalledWith(preTestDatabaseItem); - expect(openDatabaseSpy).toBeCalledTimes(1); + expect(openDatabaseSpy).toHaveBeenCalledTimes(1); expect(openDatabaseSpy).toHaveBeenCalledWith( preTestDatabaseItem.databaseUri, preTestDatabaseItem.origin, false, ); - expect(renameDatabaseItemSpy).toBeCalledTimes(1); + expect(renameDatabaseItemSpy).toHaveBeenCalledTimes(1); expect(renameDatabaseItemSpy).toHaveBeenCalledWith( postTestDatabaseItem, preTestDatabaseItem.name, ); - expect(setCurrentDatabaseItemSpy).toBeCalledTimes(1); + expect(setCurrentDatabaseItemSpy).toHaveBeenCalledTimes(1); expect(setCurrentDatabaseItemSpy).toHaveBeenCalledWith( postTestDatabaseItem, true, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts index 5856c95fb..356be5535 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts @@ -152,11 +152,11 @@ describe("telemetry reporting", () => { expect(telemetryListener._reporter).toBeDefined(); expect(telemetryListener._reporter).not.toBe(firstReporter); - expect(disposeSpy).toBeCalledTimes(1); + expect(disposeSpy).toHaveBeenCalledTimes(1); // initializing a third time continues to dispose await telemetryListener.initialize(); - expect(disposeSpy).toBeCalledTimes(2); + expect(disposeSpy).toHaveBeenCalledTimes(2); }); it("should reinitialize reporter when extension setting changes", async () => { @@ -170,13 +170,13 @@ describe("telemetry reporting", () => { expect(telemetryListener._reporter).toBeUndefined(); - expect(disposeSpy).toBeCalledTimes(1); + expect(disposeSpy).toHaveBeenCalledTimes(1); // creates a new reporter, but does not dispose again await enableTelemetry("codeQL.telemetry", true); expect(telemetryListener._reporter).toBeDefined(); - expect(disposeSpy).toBeCalledTimes(1); + expect(disposeSpy).toHaveBeenCalledTimes(1); }); it("should set userOprIn to false when global setting changes", async () => { @@ -361,7 +361,7 @@ describe("telemetry reporting", () => { await wait(500); // Dialog opened, user clicks "yes" and telemetry enabled - expect(showInformationMessageSpy).toBeCalledTimes(1); + expect(showInformationMessageSpy).toHaveBeenCalledTimes(1); expect(ENABLE_TELEMETRY.getValue()).toBe(true); expect(ctx.globalState.get("telemetry-request-viewed")).toBe(true); }); @@ -374,7 +374,7 @@ describe("telemetry reporting", () => { await telemetryListener.initialize(); // Dialog opened, user clicks "no" and telemetry disabled - expect(showInformationMessageSpy).toBeCalledTimes(1); + expect(showInformationMessageSpy).toHaveBeenCalledTimes(1); expect(ENABLE_TELEMETRY.getValue()).toBe(false); expect(ctx.globalState.get("telemetry-request-viewed")).toBe(true); }); @@ -387,7 +387,7 @@ describe("telemetry reporting", () => { await enableTelemetry("codeQL.telemetry", false); // Dialog opened, and user closes without interacting with it - expect(showInformationMessageSpy).toBeCalledTimes(1); + expect(showInformationMessageSpy).toHaveBeenCalledTimes(1); expect(ENABLE_TELEMETRY.getValue()).toBe(false); // dialog was canceled, so should not have marked as viewed expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false); @@ -406,7 +406,7 @@ describe("telemetry reporting", () => { // Dialog opened, and user closes without interacting with it // Telemetry state should not have changed - expect(showInformationMessageSpy).toBeCalledTimes(1); + expect(showInformationMessageSpy).toHaveBeenCalledTimes(1); expect(ENABLE_TELEMETRY.getValue()).toBe(true); // dialog was canceled, so should not have marked as viewed expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false); @@ -447,7 +447,7 @@ describe("telemetry reporting", () => { // now, we should have to click through the telemetry requestor again expect(ctx.globalState.get("telemetry-request-viewed")).toBe(false); - expect(showInformationMessageSpy).toBeCalledTimes(1); + expect(showInformationMessageSpy).toHaveBeenCalledTimes(1); }); it("should send a ui-interaction telementry event", async () => { From 39c17291aab673a9a09faf2252dc558bcbaac632 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Tue, 16 Jan 2024 16:56:27 +0100 Subject: [PATCH 56/77] Add diagnostics to suggest box This adds the ability for consumers of the suggest box to add diagnostics to the suggest box. When a diagnostic is returned for a value, the input will be shown with a red border. --- .../src/stories/common/SuggestBox.stories.tsx | 20 ++++++++ .../src/view/common/SuggestBox/SuggestBox.tsx | 48 ++++++++++++++++--- .../src/view/common/SuggestBox/diagnostics.ts | 21 ++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 extensions/ql-vscode/src/view/common/SuggestBox/diagnostics.ts diff --git a/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx b/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx index ce4c2f5c3..cb574dda1 100644 --- a/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx +++ b/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx @@ -5,6 +5,7 @@ import { styled } from "styled-components"; import { Codicon } from "../../view/common"; import { SuggestBox as SuggestBoxComponent } from "../../view/common/SuggestBox/SuggestBox"; import { useCallback, useState } from "react"; +import type { Diagnostic } from "../../view/common/SuggestBox/diagnostics"; export default { title: "Suggest Box", @@ -141,6 +142,25 @@ export const AccessPath = Template.bind({}); AccessPath.args = { options: suggestedOptions, parseValueToTokens: (value: string) => value.split("."), + validateValue: (value: string) => { + let index = value.indexOf("|"); + + const diagnostics: Diagnostic[] = []; + + while (index !== -1) { + index = value.indexOf("|", index + 1); + + diagnostics.push({ + message: "This cannot contain |", + range: { + start: index, + end: index + 1, + }, + }); + } + + return diagnostics; + }, getIcon: (option: StoryOption) => , getDetails: (option: StoryOption) => option.details, }; diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx index 0e338892f..9572d533b 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx @@ -13,16 +13,24 @@ import { useListNavigation, useRole, } from "@floating-ui/react"; -import { styled } from "styled-components"; +import { css, styled } from "styled-components"; import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"; import type { Option } from "./options"; import { findMatchingOptions } from "./options"; import { SuggestBoxItem } from "./SuggestBoxItem"; +import type { Diagnostic } from "./diagnostics"; -const Input = styled(VSCodeTextField)` +const Input = styled(VSCodeTextField)<{ $error: boolean }>` width: 430px; font-family: var(--vscode-editor-font-family); + + ${(props) => + props.$error && + css` + --dropdown-border: var(--vscode-inputValidation-errorBorder); + --focus-border: var(--vscode-inputValidation-errorBorder); + `} `; const Container = styled.div` @@ -50,7 +58,10 @@ const NoSuggestionsText = styled.div` padding-left: 22px; `; -export type SuggestBoxProps> = { +export type SuggestBoxProps< + T extends Option, + D extends Diagnostic = Diagnostic, +> = { value?: string; onChange: (value: string) => void; options: T[]; @@ -62,6 +73,12 @@ export type SuggestBoxProps> = { */ parseValueToTokens: (value: string) => string[]; + /** + * Validate the value. This is used to show syntax errors in the input. + * @param value The user-entered value to validate. + */ + validateValue?: (value: string) => D[]; + /** * Get the icon to display for an option. * @param option The option to get the icon for. @@ -83,20 +100,29 @@ export type SuggestBoxProps> = { * for easier testing. * @param props The props returned by `getReferenceProps` of {@link useInteractions} */ - renderInputComponent?: (props: Record) => ReactNode; + renderInputComponent?: ( + props: Record, + hasError: boolean, + ) => ReactNode; }; -export const SuggestBox = >({ +export const SuggestBox = < + T extends Option, + D extends Diagnostic = Diagnostic, +>({ value = "", onChange, options, parseValueToTokens, + validateValue, getIcon, getDetails, disabled, "aria-label": ariaLabel, - renderInputComponent = (props) => , -}: SuggestBoxProps) => { + renderInputComponent = (props, hasError) => ( + + ), +}: SuggestBoxProps) => { const [isOpen, setIsOpen] = useState(false); const [activeIndex, setActiveIndex] = useState(null); @@ -151,6 +177,13 @@ export const SuggestBox = >({ return findMatchingOptions(options, parseValueToTokens(value)); }, [options, value, parseValueToTokens]); + const diagnostics = useMemo( + () => validateValue?.(value) ?? [], + [validateValue, value], + ); + + const hasSyntaxError = diagnostics.length > 0; + useEffect(() => { if (disabled) { setIsOpen(false); @@ -180,6 +213,7 @@ export const SuggestBox = >({ }, disabled, }), + hasSyntaxError, )} {isOpen && ( diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/diagnostics.ts b/extensions/ql-vscode/src/view/common/SuggestBox/diagnostics.ts new file mode 100644 index 000000000..96bd1d11a --- /dev/null +++ b/extensions/ql-vscode/src/view/common/SuggestBox/diagnostics.ts @@ -0,0 +1,21 @@ +/** + * A range of characters in a value. The start position is inclusive, the end position is exclusive. + */ +type DiagnosticRange = { + /** + * Zero-based index of the first character of the token. + */ + start: number; + /** + * Zero-based index of the character after the last character of the token. + */ + end: number; +}; + +/** + * A diagnostic message. + */ +export type Diagnostic = { + range: DiagnosticRange; + message: string; +}; From ff8def3e7dafbdef5256040e3109e822badcaec7 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 16 Jan 2024 16:32:09 +0000 Subject: [PATCH 57/77] Use join instead of slash --- .../src/variant-analysis/variant-analysis-manager.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index 011a4520a..fd1cd856a 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -221,9 +221,17 @@ export class VariantAnalysisManager const packDownloadResult = await this.cliServer.packDownload([packName]); const downloadedPack = packDownloadResult.packs[0]; - const packDir = `${packDownloadResult.packDir}/${downloadedPack.name}/${downloadedPack.version}`; + const packDir = join( + packDownloadResult.packDir, + downloadedPack.name, + downloadedPack.version, + ); - const suitePath = `${packDir}/codeql-suites/${language}-code-scanning.qls`; + const suitePath = join( + packDir, + "codeql-suites", + `${language}-code-scanning.qls`, + ); const resolvedQueries = await this.cliServer.resolveQueries(suitePath); const problemQueries = From 9dc37ebf335f21097c9992aba04e5986e782cd2d Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 16 Jan 2024 16:33:16 +0000 Subject: [PATCH 58/77] Return when we detect no queries --- .../ql-vscode/src/variant-analysis/variant-analysis-manager.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index fd1cd856a..d5b8a4680 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -241,6 +241,7 @@ export class VariantAnalysisManager void this.app.logger.showErrorMessage( `Unable to trigger variant analysis. No problem queries found in published query pack: ${packName}.`, ); + return; } return withProgress((progress, token) => From 91a96da2ad4256bb78d97dd361884162f675e089 Mon Sep 17 00:00:00 2001 From: Robert Date: Tue, 16 Jan 2024 16:39:30 +0000 Subject: [PATCH 59/77] Start progress bar earlier --- .../variant-analysis-manager.ts | 79 ++++++++++++------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index d5b8a4680..c47558fbe 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -215,42 +215,65 @@ export class VariantAnalysisManager } private async runVariantAnalysisFromPublishedPack(): Promise { - const language = await askForLanguage(this.cliServer); + return withProgress(async (progress, token) => { + progress({ + maxStep: 8, + step: 0, + message: "Determining query language", + }); - const packName = `codeql/${language}-queries`; - const packDownloadResult = await this.cliServer.packDownload([packName]); - const downloadedPack = packDownloadResult.packs[0]; + const language = await askForLanguage(this.cliServer); - const packDir = join( - packDownloadResult.packDir, - downloadedPack.name, - downloadedPack.version, - ); + progress({ + maxStep: 8, + step: 1, + message: "Downloading query pack", + }); - const suitePath = join( - packDir, - "codeql-suites", - `${language}-code-scanning.qls`, - ); - const resolvedQueries = await this.cliServer.resolveQueries(suitePath); + const packName = `codeql/${language}-queries`; + const packDownloadResult = await this.cliServer.packDownload([packName]); + const downloadedPack = packDownloadResult.packs[0]; - const problemQueries = - await this.filterToOnlyProblemQueries(resolvedQueries); - - if (problemQueries.length === 0) { - void this.app.logger.showErrorMessage( - `Unable to trigger variant analysis. No problem queries found in published query pack: ${packName}.`, + const packDir = join( + packDownloadResult.packDir, + downloadedPack.name, + downloadedPack.version, ); - return; - } - return withProgress((progress, token) => - this.runVariantAnalysis( + progress({ + maxStep: 8, + step: 2, + message: "Resolving queries in pack", + }); + + const suitePath = join( + packDir, + "codeql-suites", + `${language}-code-scanning.qls`, + ); + const resolvedQueries = await this.cliServer.resolveQueries(suitePath); + + const problemQueries = + await this.filterToOnlyProblemQueries(resolvedQueries); + + if (problemQueries.length === 0) { + void this.app.logger.showErrorMessage( + `Unable to trigger variant analysis. No problem queries found in published query pack: ${packName}.`, + ); + return; + } + + await this.runVariantAnalysis( problemQueries.map((q) => Uri.file(q)), - progress, + (p) => + progress({ + ...p, + maxStep: p.maxStep + 3, + step: p.step + 3, + }), token, - ), - ); + ); + }); } private async filterToOnlyProblemQueries( From be22964113d687ac1ef88e8288cdfc75fd4f962c Mon Sep 17 00:00:00 2001 From: Shati Patel <42641846+shati-patel@users.noreply.github.com> Date: Tue, 16 Jan 2024 17:05:59 +0000 Subject: [PATCH 60/77] Send telemetry for `addDatabaseSourceToWorkspace` setting (#3238) --- extensions/ql-vscode/CHANGELOG.md | 1 + .../ql-vscode/src/common/vscode/telemetry.ts | 16 ++++++++++ extensions/ql-vscode/src/config.ts | 2 +- extensions/ql-vscode/src/extension.ts | 12 ++++++++ .../no-workspace/telemetry.test.ts | 29 ++++++++++++++++--- 5 files changed, 55 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/CHANGELOG.md b/extensions/ql-vscode/CHANGELOG.md index 6d5b4ae24..9a4f3c532 100644 --- a/extensions/ql-vscode/CHANGELOG.md +++ b/extensions/ql-vscode/CHANGELOG.md @@ -2,6 +2,7 @@ ## [UNRELEASED] +- Enable collection of telemetry for the `codeQL.addingDatabases.addDatabaseSourceToWorkspace` setting. [#3238](https://github.com/github/vscode-codeql/pull/3238) - In the CodeQL model editor, you can now select individual method rows and save changes to only the selected rows, instead of having to save the entire library model. [#3156](https://github.com/github/vscode-codeql/pull/3156) - If you run a query without having selected a database, we show a more intuitive prompt to help you select a database. [#3214](https://github.com/github/vscode-codeql/pull/3214) - The UI for browsing and running CodeQL tests has moved to use VS Code's built-in test UI. This makes the CodeQL test UI more consistent with the test UIs for other languages. diff --git a/extensions/ql-vscode/src/common/vscode/telemetry.ts b/extensions/ql-vscode/src/common/vscode/telemetry.ts index 1068ab9ee..ebad18a88 100644 --- a/extensions/ql-vscode/src/common/vscode/telemetry.ts +++ b/extensions/ql-vscode/src/common/vscode/telemetry.ts @@ -212,6 +212,22 @@ export class ExtensionTelemetryListener this.reporter.sendTelemetryErrorEvent("error", properties, {}); } + sendConfigInformation(config: Record): void { + if (!this.reporter) { + return; + } + + this.reporter.sendTelemetryEvent( + "config", + { + ...config, + isCanary: isCanary().toString(), + cliVersion: this.cliVersionStr, + }, + {}, + ); + } + /** * Displays a popup asking the user if they want to enable telemetry * for this extension. diff --git a/extensions/ql-vscode/src/config.ts b/extensions/ql-vscode/src/config.ts index 7145c6b23..095b53d96 100644 --- a/extensions/ql-vscode/src/config.ts +++ b/extensions/ql-vscode/src/config.ts @@ -653,7 +653,7 @@ export function allowHttp(): boolean { ); } -const ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING = new Setting( +export const ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING = new Setting( "addDatabaseSourceToWorkspace", ADDING_DATABASES_SETTING, ); diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 58be99fad..a599ad76b 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -23,6 +23,8 @@ import { } from "./common/vscode/archive-filesystem-provider"; import { CliVersionConstraint, CodeQLCliServer } from "./codeql-cli/cli"; import { + ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING, + addDatabaseSourceToWorkspace, CliConfigListener, DistributionConfigListener, GitHubDatabaseConfigListener, @@ -302,6 +304,14 @@ const codeQlVersionRange = DEFAULT_DISTRIBUTION_VERSION_RANGE; // before silently being refused to upgrade. const MIN_VERSION = "1.82.0"; +function sendConfigTelemetryData() { + const config: Record = {}; + config[ADD_DATABASE_SOURCE_TO_WORKSPACE_SETTING.qualifiedName] = + addDatabaseSourceToWorkspace().toString(); + + telemetryListener?.sendConfigInformation(config); +} + /** * Returns the CodeQLExtensionInterface, or an empty object if the interface is not * available after activation is complete. This will happen if there is no cli @@ -329,6 +339,8 @@ export async function activate( const app = new ExtensionApp(ctx); + sendConfigTelemetryData(); + const quickEvalCodeLensProvider = new QuickEvalCodeLensProvider(); languages.registerCodeLensProvider( { scheme: "file", language: "ql" }, diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts index 356be5535..93e51b45a 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/telemetry.test.ts @@ -450,7 +450,7 @@ describe("telemetry reporting", () => { expect(showInformationMessageSpy).toHaveBeenCalledTimes(1); }); - it("should send a ui-interaction telementry event", async () => { + it("should send a ui-interaction telemetry event", async () => { await telemetryListener.initialize(); telemetryListener.sendUIInteraction("test"); @@ -467,7 +467,7 @@ describe("telemetry reporting", () => { expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); - it("should send a ui-interaction telementry event with a cli version", async () => { + it("should send a ui-interaction telemetry event with a cli version", async () => { await telemetryListener.initialize(); telemetryListener.cliVersion = new SemVer("1.2.3"); @@ -485,7 +485,7 @@ describe("telemetry reporting", () => { expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); }); - it("should send an error telementry event", async () => { + it("should send an error telemetry event", async () => { await telemetryListener.initialize(); telemetryListener.sendError(redactableError`test`); @@ -503,7 +503,7 @@ describe("telemetry reporting", () => { ); }); - it("should send an error telementry event with a cli version", async () => { + it("should send an error telemetry event with a cli version", async () => { await telemetryListener.initialize(); telemetryListener.cliVersion = new SemVer("1.2.3"); @@ -543,6 +543,27 @@ describe("telemetry reporting", () => { ); }); + it("should send config telemetry event", async () => { + await telemetryListener.initialize(); + + telemetryListener.sendConfigInformation({ + testKey: "testValue", + testKey2: "42", + }); + + expect(sendTelemetryEventSpy).toHaveBeenCalledWith( + "config", + { + testKey: "testValue", + testKey2: "42", + isCanary: "false", + cliVersion: "not-set", + }, + {}, + ); + expect(sendTelemetryErrorEventSpy).not.toHaveBeenCalled(); + }); + async function enableTelemetry(section: string, value: boolean | undefined) { await workspace .getConfiguration(section) From 1cb46d26040ab6e70f0c2b150e854c85c0e72d65 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 17 Jan 2024 11:37:07 +0100 Subject: [PATCH 61/77] Rename ide-server.ts to language-client.ts --- extensions/ql-vscode/src/language-support/index.ts | 2 +- .../src/language-support/{ide-server.ts => language-client.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename extensions/ql-vscode/src/language-support/{ide-server.ts => language-client.ts} (100%) diff --git a/extensions/ql-vscode/src/language-support/index.ts b/extensions/ql-vscode/src/language-support/index.ts index c6e57e6c0..9b101343b 100644 --- a/extensions/ql-vscode/src/language-support/index.ts +++ b/extensions/ql-vscode/src/language-support/index.ts @@ -5,6 +5,6 @@ export * from "./contextual/key-type"; export * from "./contextual/location-finder"; export * from "./contextual/query-resolver"; export * from "./contextual/template-provider"; -export * from "./ide-server"; +export * from "./language-client"; export * from "./language-support"; export * from "./query-editor"; diff --git a/extensions/ql-vscode/src/language-support/ide-server.ts b/extensions/ql-vscode/src/language-support/language-client.ts similarity index 100% rename from extensions/ql-vscode/src/language-support/ide-server.ts rename to extensions/ql-vscode/src/language-support/language-client.ts From 479aa683eef2b36b88eca21ede2140ba99363657 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Wed, 17 Jan 2024 11:40:00 +0100 Subject: [PATCH 62/77] Rename IDE server to language client --- extensions/ql-vscode/src/codeql-cli/cli.ts | 2 +- .../src/common/logging/vscode/loggers.ts | 2 +- extensions/ql-vscode/src/extension.ts | 28 +++++++++---------- .../src/language-support/language-client.ts | 28 ++++++++++--------- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index fc2b43fba..a8bb874be 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -1657,7 +1657,7 @@ function isEnvTrue(name: string): boolean { ); } -export function shouldDebugIdeServer() { +export function shouldDebugLanguageServer() { return isEnvTrue("IDE_SERVER_JAVA_DEBUG"); } diff --git a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts index ff91b00fb..8d4401dfe 100644 --- a/extensions/ql-vscode/src/common/logging/vscode/loggers.ts +++ b/extensions/ql-vscode/src/common/logging/vscode/loggers.ts @@ -11,7 +11,7 @@ export const extLogger = new OutputChannelLogger("CodeQL Extension Log"); export const queryServerLogger = new OutputChannelLogger("CodeQL Query Server"); // Logger for messages from the language server. -export const ideServerLogger = new OutputChannelLogger( +export const languageServerLogger = new OutputChannelLogger( "CodeQL Language Server", ); diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index a599ad76b..e422dfcae 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -34,7 +34,7 @@ import { } from "./config"; import { AstViewer, - createIDEServer, + createLanguageClient, getQueryEditorCommands, install, TemplatePrintAstProvider, @@ -84,7 +84,7 @@ import { import type { ProgressReporter } from "./common/logging/vscode"; import { extLogger, - ideServerLogger, + languageServerLogger, queryServerLogger, } from "./common/logging/vscode"; import { QueryHistoryManager } from "./query-history/query-history-manager"; @@ -171,7 +171,7 @@ function getCommands( app: App, cliServer: CodeQLCliServer, queryRunner: QueryRunner, - ideServer: LanguageClient, + languageClient: LanguageClient, ): BaseCommands { const getCliVersion = async () => { try { @@ -189,10 +189,10 @@ function getCommands( await Promise.all([ queryRunner.restartQueryServer(progress, token), async () => { - if (ideServer.isRunning()) { - await ideServer.restart(); + if (languageClient.isRunning()) { + await languageClient.restart(); } else { - await ideServer.start(); + await languageClient.start(); } }, ]); @@ -942,7 +942,7 @@ async function activateWithInstalledDistribution( ctx.subscriptions.push(tmpDirDisposal); void extLogger.log("Initializing CodeQL language server."); - const ideServer = createIDEServer(qlConfigurationListener); + const languageClient = createLanguageClient(qlConfigurationListener); const localQueries = new LocalQueries( app, @@ -1008,7 +1008,7 @@ async function activateWithInstalledDistribution( void extLogger.log("Registering top-level command palette commands."); const allCommands: AllExtensionCommands = { - ...getCommands(app, cliServer, qs, ideServer), + ...getCommands(app, cliServer, qs, languageClient), ...getQueryEditorCommands({ commandManager: app.commands, queryRunner: qs, @@ -1055,21 +1055,21 @@ async function activateWithInstalledDistribution( } void extLogger.log("Starting language server."); - await ideServer.start(); + await languageClient.start(); ctx.subscriptions.push({ dispose: () => { - void ideServer.stop(); + void languageClient.stop(); }, }); - // Handle visibility changes in the ideserver + // Handle visibility changes in the CodeQL language client. if (await cliServer.cliConstraints.supportsVisibilityNotifications()) { Window.onDidChangeVisibleTextEditors((editors) => { - ideServer.notifyVisibilityChange(editors); + languageClient.notifyVisibilityChange(editors); }); // Send an inital notification to the language server // to set the initial state of the visible editors. - ideServer.notifyVisibilityChange(Window.visibleTextEditors); + languageClient.notifyVisibilityChange(Window.visibleTextEditors); } // Jump-to-definition and find-references @@ -1251,7 +1251,7 @@ function getContextStoragePath(ctx: ExtensionContext) { async function initializeLogging(ctx: ExtensionContext): Promise { ctx.subscriptions.push(extLogger); ctx.subscriptions.push(queryServerLogger); - ctx.subscriptions.push(ideServerLogger); + ctx.subscriptions.push(languageServerLogger); } const checkForUpdatesCommand: keyof PreActivationCommands = diff --git a/extensions/ql-vscode/src/language-support/language-client.ts b/extensions/ql-vscode/src/language-support/language-client.ts index 889f7667c..7b27a3fda 100644 --- a/extensions/ql-vscode/src/language-support/language-client.ts +++ b/extensions/ql-vscode/src/language-support/language-client.ts @@ -2,32 +2,32 @@ import type { TextEditor } from "vscode"; import { ProgressLocation, window } from "vscode"; import type { StreamInfo } from "vscode-languageclient/node"; import { LanguageClient, NotificationType } from "vscode-languageclient/node"; -import { shouldDebugIdeServer, spawnServer } from "../codeql-cli/cli"; +import { shouldDebugLanguageServer, spawnServer } from "../codeql-cli/cli"; import type { QueryServerConfig } from "../config"; -import { ideServerLogger } from "../common/logging/vscode"; +import { languageServerLogger } from "../common/logging/vscode"; /** - * Managing the language server for CodeQL. + * Managing the language client and corresponding server process for CodeQL. */ /** - * Create a new CodeQL language server. + * Create a new CodeQL language client connected to a language server. */ -export function createIDEServer( +export function createLanguageClient( config: QueryServerConfig, ): CodeQLLanguageClient { return new CodeQLLanguageClient(config); } /** - * CodeQL language server. + * CodeQL language client. */ export class CodeQLLanguageClient extends LanguageClient { constructor(config: QueryServerConfig) { super( "codeQL.lsp", "CodeQL Language Server", - () => spawnIdeServer(config), + () => spawnLanguageServer(config), { documentSelector: [ { language: "ql", scheme: "file" }, @@ -38,7 +38,7 @@ export class CodeQLLanguageClient extends LanguageClient { configurationSection: "codeQL", }, // Ensure that language server exceptions are logged to the same channel as its output. - outputChannel: ideServerLogger.outputChannel, + outputChannel: languageServerLogger.outputChannel, }, true, ); @@ -55,12 +55,14 @@ export class CodeQLLanguageClient extends LanguageClient { } /** Starts a new CodeQL language server process, sending progress messages to the status bar. */ -async function spawnIdeServer(config: QueryServerConfig): Promise { +async function spawnLanguageServer( + config: QueryServerConfig, +): Promise { return window.withProgress( { title: "CodeQL language server", location: ProgressLocation.Window }, async (progressReporter, _) => { const args = ["--check-errors", "ON_CHANGE"]; - if (shouldDebugIdeServer()) { + if (shouldDebugLanguageServer()) { args.push( "-J=-agentlib:jdwp=transport=dt_socket,address=localhost:9009,server=y,suspend=n,quiet=y", ); @@ -70,11 +72,11 @@ async function spawnIdeServer(config: QueryServerConfig): Promise { "CodeQL language server", ["execute", "language-server"], args, - ideServerLogger, + languageServerLogger, (data) => - ideServerLogger.log(data.toString(), { trailingNewline: false }), + languageServerLogger.log(data.toString(), { trailingNewline: false }), (data) => - ideServerLogger.log(data.toString(), { trailingNewline: false }), + languageServerLogger.log(data.toString(), { trailingNewline: false }), progressReporter, ); return { writer: child.stdin!, reader: child.stdout! }; From 97980371c84f4cad5dc4bf958cef6a42e45b1329 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Wed, 17 Jan 2024 13:52:03 +0000 Subject: [PATCH 63/77] Skip multi-query MRVAs from query history (#3249) --- .../src/query-history/query-history-manager.ts | 5 +++++ .../src/variant-analysis/shared/variant-analysis.ts | 10 ++++++++++ .../src/variant-analysis/variant-analysis-manager.ts | 8 ++++++++ .../src/variant-analysis/variant-analysis-mapper.ts | 4 +++- 4 files changed, 26 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/query-history/query-history-manager.ts b/extensions/ql-vscode/src/query-history/query-history-manager.ts index 61de46c56..ea5806a05 100644 --- a/extensions/ql-vscode/src/query-history/query-history-manager.ts +++ b/extensions/ql-vscode/src/query-history/query-history-manager.ts @@ -373,6 +373,11 @@ export class QueryHistoryManager extends DisposableObject { const variantAnalysisAddedSubscription = this.variantAnalysisManager.onVariantAnalysisAdded( async (variantAnalysis) => { + if (variantAnalysis.queries !== undefined) { + // This is a variant analysis that contains multiple queries, which + // is not fully supported yet. So we ignore it from the query history. + return; + } this.addQuery({ t: "variant-analysis", status: QueryStatus.InProgress, diff --git a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts index 31c4e0863..1c862b793 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -12,6 +12,7 @@ export interface VariantAnalysis { text: string; kind?: string; }; + queries?: VariantAnalysisQueries; databases: { repositories?: string[]; repositoryLists?: string[]; @@ -144,6 +145,7 @@ export interface VariantAnalysisSubmission { // Base64 encoded query pack. pack: string; }; + queries?: VariantAnalysisQueries; databases: { repositories?: string[]; repositoryLists?: string[]; @@ -151,6 +153,14 @@ export interface VariantAnalysisSubmission { }; } +// Experimental information about the queries that are +// going to be run as part of the variant analysis. +// For now, this is just the query language, but it's +// unclear what it will look like in the future. +export interface VariantAnalysisQueries { + language: QueryLanguage; +} + export async function isVariantAnalysisComplete( variantAnalysis: VariantAnalysis, artifactDownloaded: ( diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index c47558fbe..e14575e47 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -347,6 +347,13 @@ export class VariantAnalysisManager const queryText = await readFile(queryFile, "utf8"); + const queries = + uris.length === 1 + ? undefined + : { + language: variantAnalysisLanguage, + }; + const variantAnalysisSubmission: VariantAnalysisSubmission = { startTime: queryStartTime, actionRepoRef: actionBranch, @@ -359,6 +366,7 @@ export class VariantAnalysisManager text: queryText, kind: queryMetadata?.kind, }, + queries, databases: { repositories: repoSelection.repositories, repositoryLists: repoSelection.repositoryLists, diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts index 3a3db0f87..f6e1cb581 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-mapper.ts @@ -36,6 +36,7 @@ export function mapVariantAnalysis( text: submission.query.text, kind: submission.query.kind, }, + queries: submission.queries, databases: submission.databases, executionStartTime: submission.startTime, }, @@ -46,7 +47,7 @@ export function mapVariantAnalysis( export function mapUpdatedVariantAnalysis( previousVariantAnalysis: Pick< VariantAnalysis, - "query" | "databases" | "executionStartTime" + "query" | "queries" | "databases" | "executionStartTime" >, response: ApiVariantAnalysis, ): VariantAnalysis { @@ -73,6 +74,7 @@ export function mapUpdatedVariantAnalysis( private: response.controller_repo.private, }, query: previousVariantAnalysis.query, + queries: previousVariantAnalysis.queries, databases: previousVariantAnalysis.databases, executionStartTime: previousVariantAnalysis.executionStartTime, createdAt: response.created_at, From 1010239c297aaaea8b49578c7172a93fea921fb4 Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 17 Jan 2024 14:25:27 +0000 Subject: [PATCH 64/77] Add a test of runVariantAnalysisFromPublishedPack --- .../variant-analysis-manager.ts | 2 +- .../variant-analysis-manager.test.ts | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts index c47558fbe..4aacddfb0 100644 --- a/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts +++ b/extensions/ql-vscode/src/variant-analysis/variant-analysis-manager.ts @@ -214,7 +214,7 @@ export class VariantAnalysisManager } } - private async runVariantAnalysisFromPublishedPack(): Promise { + public async runVariantAnalysisFromPublishedPack(): Promise { return withProgress(async (progress, token) => { progress({ maxStep: 8, diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index f2ca2c9d2..9770ade4f 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -388,4 +388,37 @@ describe("Variant Analysis Manager", () => { } } }); + + describe("runVariantAnalysisFromPublishedPack", () => { + it("should download pack for correct language and identify problem queries", async () => { + const showQuickPickSpy = jest + .spyOn(window, "showQuickPick") + .mockResolvedValue( + mockedQuickPickItem({ + label: "JavaScript", + description: "javascript", + language: "javascript", + }), + ); + + const runVariantAnalysisMock = jest.fn(); + variantAnalysisManager.runVariantAnalysis = runVariantAnalysisMock; + + await variantAnalysisManager.runVariantAnalysisFromPublishedPack(); + + expect(showQuickPickSpy).toHaveBeenCalledTimes(1); + expect(runVariantAnalysisMock).toHaveBeenCalledTimes(1); + + const queries: Uri[] = runVariantAnalysisMock.mock.calls[0][0]; + // Should include queries. Just check that at least one known query exists. + // It doesn't particularly matter which query we check for. + expect( + queries.find((q) => q.fsPath.includes("CWE-201/PostMessageStar.ql")), + ).toBeDefined(); + // Should not include non-problem queries. + expect( + queries.find((q) => q.fsPath.includes("LinesOfCode.ql")), + ).not.toBeDefined(); + }); + }); }); From 6b1a056ae6f2265da0a61946678ef5c0e1b83a4f Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 17 Jan 2024 15:35:38 +0000 Subject: [PATCH 65/77] Avoid using path separator in test data --- .../variant-analysis/variant-analysis-manager.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index 9770ade4f..b45c15087 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -413,7 +413,7 @@ describe("Variant Analysis Manager", () => { // Should include queries. Just check that at least one known query exists. // It doesn't particularly matter which query we check for. expect( - queries.find((q) => q.fsPath.includes("CWE-201/PostMessageStar.ql")), + queries.find((q) => q.fsPath.includes("PostMessageStar.ql")), ).toBeDefined(); // Should not include non-problem queries. expect( From 55306c9a93b4d8200dd1f63c88ee6bea93b0aaba Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jan 2024 12:47:06 -0500 Subject: [PATCH 66/77] Fix PR feedback --- extensions/ql-vscode/src/codeql-cli/cli.ts | 21 -------- .../ql-vscode/src/common/short-paths.ts | 54 ++++++++++--------- .../src/variant-analysis/run-remote-query.ts | 3 +- 3 files changed, 32 insertions(+), 46 deletions(-) diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 1db2fa0ca..a3be391a4 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -1420,27 +1420,6 @@ export class CodeQLCliServer implements Disposable { ); } - public async packCreate( - dir: string, - workspaceFolders: string[], - outputPath: string, - moreOptions: string[], - ): Promise { - const args = [ - "--output", - outputPath, - dir, - ...moreOptions, - ...this.getAdditionalPacksArg(workspaceFolders), - ]; - - return this.runJsonCodeQlCliCommandWithAuthentication( - ["pack", "create"], - args, - "Creating pack", - ); - } - async packBundle( dir: string, workspaceFolders: string[], diff --git a/extensions/ql-vscode/src/common/short-paths.ts b/extensions/ql-vscode/src/common/short-paths.ts index 27eac829c..805075c99 100644 --- a/extensions/ql-vscode/src/common/short-paths.ts +++ b/extensions/ql-vscode/src/common/short-paths.ts @@ -1,11 +1,7 @@ import { platform } from "os"; import { basename, dirname, join, normalize, resolve } from "path"; import { lstat, readdir } from "fs/promises"; -import { extLogger } from "./logging/vscode"; - -async function log(message: string): Promise { - await extLogger.log(message); -} +import type { BaseLogger } from "./logging"; /** * Expand a single short path component @@ -16,8 +12,9 @@ async function log(message: string): Promise { async function expandShortPathComponent( dir: string, shortBase: string, + logger: BaseLogger, ): Promise { - await log(`Expanding short path component: ${shortBase}`); + void logger.log(`Expanding short path component: ${shortBase}`); const fullPath = join(dir, shortBase); @@ -25,36 +22,36 @@ async function expandShortPathComponent( const stats = await lstat(fullPath, { bigint: true }); if (stats.dev === BigInt(0) || stats.ino === BigInt(0)) { // No inode info, so we won't be able to find this in the directory listing. - await log(`No inode info available. Skipping.`); + void logger.log(`No inode info available. Skipping.`); return shortBase; } - await log(`dev/inode: ${stats.dev}/${stats.ino}`); + void logger.log(`dev/inode: ${stats.dev}/${stats.ino}`); try { // Enumerate the children of the parent directory, and try to find one with the same dev/inode. const children = await readdir(dir); for (const child of children) { - await log(`considering child: ${child}`); + void logger.log(`considering child: ${child}`); try { const childStats = await lstat(join(dir, child), { bigint: true }); - await log(`child dev/inode: ${childStats.dev}/${childStats.ino}`); + void logger.log(`child dev/inode: ${childStats.dev}/${childStats.ino}`); if (childStats.dev === stats.dev && childStats.ino === stats.ino) { // Found a match. - await log(`Found a match: ${child}`); + void logger.log(`Found a match: ${child}`); return child; } } catch (e) { // Can't read stats for the child, so skip it. - await log(`Error reading stats for child: ${e}`); + void logger.log(`Error reading stats for child: ${e}`); } } } catch (e) { // Can't read the directory, so we won't be able to find this in the directory listing. - await log(`Error reading directory: ${e}`); + void logger.log(`Error reading directory: ${e}`); return shortBase; } - await log(`No match found. Returning original.`); + void logger.log(`No match found. Returning original.`); return shortBase; } @@ -63,49 +60,58 @@ async function expandShortPathComponent( * @param shortPath The path to expand. * @returns The expanded path. */ -async function expandShortPathRecursive(shortPath: string): Promise { +async function expandShortPathRecursive( + shortPath: string, + logger: BaseLogger, +): Promise { const shortBase = basename(shortPath); if (shortBase.length === 0) { // We've reached the root. return shortPath; } - const dir = await expandShortPathRecursive(dirname(shortPath)); - await log(`dir: ${dir}`); - await log(`base: ${shortBase}`); + const dir = await expandShortPathRecursive(dirname(shortPath), logger); + void logger.log(`dir: ${dir}`); + void logger.log(`base: ${shortBase}`); if (shortBase.indexOf("~") < 0) { // This component doesn't have a short name, so just append it to the (long) parent. - await log(`Component is not a short name`); + void logger.log(`Component is not a short name`); return join(dir, shortBase); } // This component looks like it has a short name, so try to expand it. - const longBase = await expandShortPathComponent(dir, shortBase); + const longBase = await expandShortPathComponent(dir, shortBase, logger); return join(dir, longBase); } /** * Expands a path that potentially contains 8.3 short names (e.g. "C:\PROGRA~1" instead of "C:\Program Files"). + * + * See https://en.wikipedia.org/wiki/8.3_filename if you're not familiar with Windows 8.3 short names. + * * @param shortPath The path to expand. * @returns A normalized, absolute path, with any short components expanded. */ -export async function expandShortPaths(shortPath: string): Promise { +export async function expandShortPaths( + shortPath: string, + logger: BaseLogger, +): Promise { const absoluteShortPath = normalize(resolve(shortPath)); if (platform() !== "win32") { // POSIX doesn't have short paths. return absoluteShortPath; } - await log(`Expanding short paths in: ${absoluteShortPath}`); + void logger.log(`Expanding short paths in: ${absoluteShortPath}`); // A quick check to see if there might be any short components. // There might be a case where a short component doesn't contain a `~`, but if there is, I haven't // found it. // This may find long components that happen to have a '~', but that's OK. if (absoluteShortPath.indexOf("~") < 0) { // No short components to expand. - await log(`Skipping due to no short components`); + void logger.log(`Skipping due to no short components`); return absoluteShortPath; } - return await expandShortPathRecursive(absoluteShortPath); + return await expandShortPathRecursive(absoluteShortPath, logger); } diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 5f0dea969..13caced7a 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -295,13 +295,14 @@ async function createRemoteQueriesTempDirectory(): Promise { // archive if the pack path contains any 8.3 components. const remoteQueryDir = { ...shortRemoteQueryDir, - path: await expandShortPaths(shortRemoteQueryDir.path), + path: await expandShortPaths(shortRemoteQueryDir.path, extLogger), }; const queryPackDir = join(remoteQueryDir.path, "query-pack"); await mkdirp(queryPackDir); const compiledPackDir = join(remoteQueryDir.path, "compiled-pack"); const bundleFile = await expandShortPaths( await getPackedBundlePath(tmpDir.name), + extLogger, ); return { remoteQueryDir, queryPackDir, compiledPackDir, bundleFile }; } From 1d275985d0aa010ad3a841865927ba98d9746db4 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jan 2024 12:49:20 -0500 Subject: [PATCH 67/77] Fix PR feedback --- .../ql-vscode/src/common/short-paths.ts | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/extensions/ql-vscode/src/common/short-paths.ts b/extensions/ql-vscode/src/common/short-paths.ts index 805075c99..838dac031 100644 --- a/extensions/ql-vscode/src/common/short-paths.ts +++ b/extensions/ql-vscode/src/common/short-paths.ts @@ -3,6 +3,38 @@ import { basename, dirname, join, normalize, resolve } from "path"; import { lstat, readdir } from "fs/promises"; import type { BaseLogger } from "./logging"; +/** + * Expands a path that potentially contains 8.3 short names (e.g. "C:\PROGRA~1" instead of "C:\Program Files"). + * + * See https://en.wikipedia.org/wiki/8.3_filename if you're not familiar with Windows 8.3 short names. + * + * @param shortPath The path to expand. + * @returns A normalized, absolute path, with any short components expanded. + */ +export async function expandShortPaths( + shortPath: string, + logger: BaseLogger, +): Promise { + const absoluteShortPath = normalize(resolve(shortPath)); + if (platform() !== "win32") { + // POSIX doesn't have short paths. + return absoluteShortPath; + } + + void logger.log(`Expanding short paths in: ${absoluteShortPath}`); + // A quick check to see if there might be any short components. + // There might be a case where a short component doesn't contain a `~`, but if there is, I haven't + // found it. + // This may find long components that happen to have a '~', but that's OK. + if (absoluteShortPath.indexOf("~") < 0) { + // No short components to expand. + void logger.log(`Skipping due to no short components`); + return absoluteShortPath; + } + + return await expandShortPathRecursive(absoluteShortPath, logger); +} + /** * Expand a single short path component * @param dir The absolute path of the directory containing the short path component. @@ -83,35 +115,3 @@ async function expandShortPathRecursive( const longBase = await expandShortPathComponent(dir, shortBase, logger); return join(dir, longBase); } - -/** - * Expands a path that potentially contains 8.3 short names (e.g. "C:\PROGRA~1" instead of "C:\Program Files"). - * - * See https://en.wikipedia.org/wiki/8.3_filename if you're not familiar with Windows 8.3 short names. - * - * @param shortPath The path to expand. - * @returns A normalized, absolute path, with any short components expanded. - */ -export async function expandShortPaths( - shortPath: string, - logger: BaseLogger, -): Promise { - const absoluteShortPath = normalize(resolve(shortPath)); - if (platform() !== "win32") { - // POSIX doesn't have short paths. - return absoluteShortPath; - } - - void logger.log(`Expanding short paths in: ${absoluteShortPath}`); - // A quick check to see if there might be any short components. - // There might be a case where a short component doesn't contain a `~`, but if there is, I haven't - // found it. - // This may find long components that happen to have a '~', but that's OK. - if (absoluteShortPath.indexOf("~") < 0) { - // No short components to expand. - void logger.log(`Skipping due to no short components`); - return absoluteShortPath; - } - - return await expandShortPathRecursive(absoluteShortPath, logger); -} From 65fbb6bca8cd179569acfeff3a4eed041ce1c8ed Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jan 2024 14:20:14 -0500 Subject: [PATCH 68/77] Fix PR feedback --- extensions/ql-vscode/src/codeql-cli/cli.ts | 22 ++++++--- .../src/variant-analysis/run-remote-query.ts | 20 ++++----- .../test/matchers/toExistInCodeQLPack.ts | 43 ++++++++++++++++++ .../variant-analysis-manager.test.ts | 45 ++----------------- extensions/ql-vscode/test/vscode-tests/cli.ts | 9 ++-- 5 files changed, 78 insertions(+), 61 deletions(-) create mode 100644 extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index a3be391a4..5f3ebab70 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -1420,16 +1420,28 @@ export class CodeQLCliServer implements Disposable { ); } + /** + * Compile a CodeQL pack and bundle it into a single file. + * + * @param sourcePackDir The directory of the input CodeQL pack. + * @param workspaceFolders The workspace folders to search for additional packs. + * @param outputBundleFile The path to the output bundle file. + * @param outputPackDir The directory to contain the unbundled output pack. + * @param moreOptions Additional options to be passed to `codeql pack bundle`. + */ async packBundle( - dir: string, + sourcePackDir: string, workspaceFolders: string[], - outputPath: string, + outputBundleFile: string, + outputPackDir: string, moreOptions: string[], ): Promise { const args = [ "-o", - outputPath, - dir, + outputBundleFile, + sourcePackDir, + "--pack-path", + outputPackDir, ...moreOptions, ...this.getAdditionalPacksArg(workspaceFolders), ]; @@ -1492,7 +1504,7 @@ export class CodeQLCliServer implements Disposable { return (await this.getVersionAndFeatures()).features; } - public async getVersionAndFeatures(): Promise { + private async getVersionAndFeatures(): Promise { if (!this._versionAndFeatures) { try { const newVersionAndFeatures = await this.refreshVersion(); diff --git a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts index 13caced7a..85a40552a 100644 --- a/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts +++ b/extensions/ql-vscode/src/variant-analysis/run-remote-query.ts @@ -79,11 +79,10 @@ async function generateQueryPack( ? await askForLanguage(cliServer) // open popup to ask for language if not already hardcoded : await findLanguage(cliServer, Uri.file(queryFile)); if (!language) { - throw new UserCancellationException("Could not determine language."); + throw new UserCancellationException("Could not determine language"); } let queryPackDir: string; - let precompilationOpts: string[]; let needsInstall: boolean; if (mustSynthesizePack) { // This section applies whether or not the CLI supports MRVA pack creation directly. @@ -136,8 +135,7 @@ async function generateQueryPack( await cliServer.clearCache(); } - // Clear the CLI cache so that the most recent qlpack lock file is used. - await cliServer.clearCache(); + let precompilationOpts: string[]; if (cliSupportsMrvaPackCreate) { precompilationOpts = [ "--mrva", @@ -151,11 +149,11 @@ async function generateQueryPack( if (await cliServer.cliConstraints.usesGlobalCompilationCache()) { precompilationOpts = ["--qlx"]; } else { - const ccache = join(originalPackRoot, ".cache"); + const cache = join(originalPackRoot, ".cache"); precompilationOpts = [ "--qlx", "--no-default-compilation-cache", - `--compilation-cache=${ccache}`, + `--compilation-cache=${cache}`, ]; } @@ -168,11 +166,13 @@ async function generateQueryPack( void extLogger.log( `Compiling and bundling query pack from ${queryPackDir} to ${bundlePath}. (This may take a while.)`, ); - await cliServer.packBundle(queryPackDir, workspaceFolders, bundlePath, [ - "--pack-path", + await cliServer.packBundle( + queryPackDir, + workspaceFolders, + bundlePath, tmpDir.compiledPackDir, - ...precompilationOpts, - ]); + precompilationOpts, + ); const base64Pack = (await readFile(bundlePath)).toString("base64"); return { base64Pack, diff --git a/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts b/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts new file mode 100644 index 000000000..5021c6bdb --- /dev/null +++ b/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts @@ -0,0 +1,43 @@ +import { expect } from "@jest/globals"; +import type { ExpectationResult } from "expect"; +import type { QueryPackFS } from "../vscode-tests/utils/bundled-pack-helpers"; +import { EOL } from "os"; + +/** + * Custom Jest matcher to check if a file exists in a query pack. + */ +function toExistInPack( + this: jest.MatcherContext, + actual: unknown, + packFS: QueryPackFS, +): ExpectationResult { + if (typeof actual !== "string") { + throw new TypeError( + `Expected actual value to be a string. Found ${typeof actual}`, + ); + } + + const pass = packFS.fileExists(actual); + if (pass) { + return { + pass: true, + message: () => `expected ${actual} not to exist in pack`, + }; + } else { + const files = packFS.allFiles(); + const filesString = files.length > 0 ? files.join(EOL) : ""; + return { + pass: false, + message: () => + `expected ${actual} to exist in pack.\nThe following files were found in the pack:\n${filesString}`, + }; + } +} + +expect.extend({ toExistInPack }); + +declare module "expect" { + interface Matchers { + toExistInCodeQLPack(packFS: QueryPackFS): R; + } +} diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index ca1287ea1..ee2bc14b6 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -20,50 +20,11 @@ import { ExtensionApp } from "../../../../src/common/vscode/vscode-app"; import { DbConfigStore } from "../../../../src/databases/config/db-config-store"; import { mockedQuickPickItem } from "../../utils/mocking.helpers"; import { QueryLanguage } from "../../../../src/common/query-language"; -import type { QueryPackFS } from "../../utils/bundled-pack-helpers"; import { readBundledPack } from "../../utils/bundled-pack-helpers"; import { load } from "js-yaml"; import type { ExtensionPackMetadata } from "../../../../src/model-editor/extension-pack-metadata"; import type { QlPackLockFile } from "../../../../src/packaging/qlpack-lock-file"; import { expect } from "@jest/globals"; -import type { ExpectationResult } from "expect"; - -/** - * Custom Jest matcher to check if a file exists in a query pack. - */ -function toExistInPack( - this: jest.MatcherContext, - actual: unknown, - packFS: QueryPackFS, -): ExpectationResult { - if (typeof actual !== "string") { - throw new TypeError("Expected actual value to be a string."); - } - - const pass = packFS.fileExists(actual); - const files = packFS.allFiles(); - const filesString = files.length > 0 ? files.join("\n") : ""; - if (pass) { - return { - pass: true, - message: () => `expected ${actual} not to exist in pack`, - }; - } else { - return { - pass: false, - message: () => - `expected ${actual} to exist in pack.\nThe following files were found in the pack:\n${filesString}`, - }; - } -} - -expect.extend({ toExistInPack }); - -declare module "expect" { - interface Matchers { - toExistInPack(packFS: QueryPackFS): R; - } -} describe("Variant Analysis Manager", () => { let cli: CodeQLCliServer; @@ -371,14 +332,14 @@ describe("Variant Analysis Manager", () => { const packFS = await readBundledPack(request.query.pack); filesThatExist.forEach((file) => { - expect(file).toExistInPack(packFS); + expect(file).toExistInCodeQLPack(packFS); }); qlxFilesThatExist.forEach((file) => { - expect(file).toExistInPack(packFS); + expect(file).toExistInCodeQLPack(packFS); }); filesThatDoNotExist.forEach((file) => { - expect(file).not.toExistInPack(packFS); + expect(file).not.toExistInCodeQLPack(packFS); }); expect( diff --git a/extensions/ql-vscode/test/vscode-tests/cli.ts b/extensions/ql-vscode/test/vscode-tests/cli.ts index c1110dfe8..8ed3a9b4f 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli.ts @@ -1,3 +1,4 @@ +import { dirname } from "path"; import { workspace } from "vscode"; /** @@ -6,10 +7,10 @@ import { workspace } from "vscode"; */ function hasCodeQL() { const folders = workspace.workspaceFolders; - return !!folders?.some( - (folder) => - folder.uri.path.endsWith("/codeql") || folder.uri.path.endsWith("/ql"), - ); + return !!folders?.some((folder) => { + const name = dirname(folder.uri.fsPath); + return name === "codeql" || name === "ql"; + }); } // describeWithCodeQL will be equal to describe if the CodeQL libraries are From d7e555248140f5b50bcc0fc5295df0c03c9e69c4 Mon Sep 17 00:00:00 2001 From: Dave Bartolomeo Date: Wed, 17 Jan 2024 17:48:44 -0500 Subject: [PATCH 69/77] Fix broken test code --- .../test/matchers/toExistInCodeQLPack.ts | 29 ++++++++++++------- .../variant-analysis-manager.test.ts | 3 +- extensions/ql-vscode/test/vscode-tests/cli.ts | 4 +-- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts b/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts index 5021c6bdb..622c41200 100644 --- a/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts +++ b/extensions/ql-vscode/test/matchers/toExistInCodeQLPack.ts @@ -1,16 +1,16 @@ import { expect } from "@jest/globals"; -import type { ExpectationResult } from "expect"; +import type { MatcherFunction } from "expect"; import type { QueryPackFS } from "../vscode-tests/utils/bundled-pack-helpers"; import { EOL } from "os"; /** * Custom Jest matcher to check if a file exists in a query pack. */ -function toExistInPack( - this: jest.MatcherContext, - actual: unknown, - packFS: QueryPackFS, -): ExpectationResult { +// eslint-disable-next-line func-style -- We need to set the type of this function +const toExistInCodeQLPack: MatcherFunction<[packFS: QueryPackFS]> = function ( + actual, + packFS, +) { if (typeof actual !== "string") { throw new TypeError( `Expected actual value to be a string. Found ${typeof actual}`, @@ -32,12 +32,19 @@ function toExistInPack( `expected ${actual} to exist in pack.\nThe following files were found in the pack:\n${filesString}`, }; } -} +}; -expect.extend({ toExistInPack }); +expect.extend({ toExistInCodeQLPack }); -declare module "expect" { - interface Matchers { - toExistInCodeQLPack(packFS: QueryPackFS): R; +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace -- We need to extend this global declaration + namespace jest { + interface AsymmetricMatchers { + toExistInCodeQLPack(packFS: QueryPackFS): void; + } + + interface Matchers { + toExistInCodeQLPack(packFS: QueryPackFS): R; + } } } diff --git a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts index ee2bc14b6..e3eb13d4f 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli-integration/variant-analysis/variant-analysis-manager.test.ts @@ -24,7 +24,8 @@ import { readBundledPack } from "../../utils/bundled-pack-helpers"; import { load } from "js-yaml"; import type { ExtensionPackMetadata } from "../../../../src/model-editor/extension-pack-metadata"; import type { QlPackLockFile } from "../../../../src/packaging/qlpack-lock-file"; -import { expect } from "@jest/globals"; +//import { expect } from "@jest/globals"; +import "../../../matchers/toExistInCodeQLPack"; describe("Variant Analysis Manager", () => { let cli: CodeQLCliServer; diff --git a/extensions/ql-vscode/test/vscode-tests/cli.ts b/extensions/ql-vscode/test/vscode-tests/cli.ts index 8ed3a9b4f..9dca8eaf1 100644 --- a/extensions/ql-vscode/test/vscode-tests/cli.ts +++ b/extensions/ql-vscode/test/vscode-tests/cli.ts @@ -1,4 +1,4 @@ -import { dirname } from "path"; +import { basename } from "path"; import { workspace } from "vscode"; /** @@ -8,7 +8,7 @@ import { workspace } from "vscode"; function hasCodeQL() { const folders = workspace.workspaceFolders; return !!folders?.some((folder) => { - const name = dirname(folder.uri.fsPath); + const name = basename(folder.uri.fsPath); return name === "codeql" || name === "ql"; }); } From 4e81d410867512aa2a0650acc03b699be3fc5c31 Mon Sep 17 00:00:00 2001 From: Koen Vlaswinkel Date: Thu, 18 Jan 2024 10:03:45 +0100 Subject: [PATCH 70/77] Add comment about Storybook test diagnostics --- extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx b/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx index cb574dda1..b7acb398f 100644 --- a/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx +++ b/extensions/ql-vscode/src/stories/common/SuggestBox.stories.tsx @@ -148,6 +148,8 @@ AccessPath.args = { const diagnostics: Diagnostic[] = []; while (index !== -1) { + // For testing in this Storybook, disallow pipe characters to avoid a dependency on the + // real access path validation. index = value.indexOf("|", index + 1); diagnostics.push({ From 978e00a2528fc48d5fb312985d78a7a96dfb9252 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:28:48 +0000 Subject: [PATCH 71/77] Bump actions/dependency-review-action from 3 to 4 Bumps [actions/dependency-review-action](https://github.com/actions/dependency-review-action) from 3 to 4. - [Release notes](https://github.com/actions/dependency-review-action/releases) - [Commits](https://github.com/actions/dependency-review-action/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/dependency-review-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/dependency-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 853bc7bdb..47d874e86 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -13,4 +13,4 @@ jobs: - name: 'Checkout Repository' uses: actions/checkout@v4 - name: 'Dependency Review' - uses: actions/dependency-review-action@v3 + uses: actions/dependency-review-action@v4 From 8beaa7cbc0b2af57a7e7a5448c147b2f1f553bd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:47:43 +0000 Subject: [PATCH 72/77] Bump @typescript-eslint/eslint-plugin in /extensions/ql-vscode Bumps [@typescript-eslint/eslint-plugin](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/eslint-plugin) from 6.18.0 to 6.19.0. - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v6.19.0/packages/eslint-plugin) --- updated-dependencies: - dependency-name: "@typescript-eslint/eslint-plugin" dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 120 ++++++++++++------------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 61 insertions(+), 61 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index e62b4f67c..02dcc0112 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -92,7 +92,7 @@ "@types/unzipper": "^0.10.1", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", - "@typescript-eslint/eslint-plugin": "^6.18.0", + "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.16.0", "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", @@ -9839,16 +9839,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.0.tgz", - "integrity": "sha512-3lqEvQUdCozi6d1mddWqd+kf8KxmGq2Plzx36BlkjuQe3rSTm/O98cLf0A4uDO+a5N1KD2SeEEl6fW97YHY+6w==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.19.0.tgz", + "integrity": "sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.18.0", - "@typescript-eslint/type-utils": "6.18.0", - "@typescript-eslint/utils": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/type-utils": "6.19.0", + "@typescript-eslint/utils": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -9874,13 +9874,13 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.0.tgz", - "integrity": "sha512-o/UoDT2NgOJ2VfHpfr+KBY2ErWvCySNUIX/X7O9g8Zzt/tXdpfEU43qbNk8LVuWUT2E0ptzTWXh79i74PP0twA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -9891,9 +9891,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", - "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -9904,12 +9904,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", - "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -10107,13 +10107,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.0.tgz", - "integrity": "sha512-ZeMtrXnGmTcHciJN1+u2CigWEEXgy1ufoxtWcHORt5kGvpjjIlK9MUhzHm4RM8iVy6dqSaZA/6PVkX6+r+ChjQ==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.19.0.tgz", + "integrity": "sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.18.0", - "@typescript-eslint/utils": "6.18.0", + "@typescript-eslint/typescript-estree": "6.19.0", + "@typescript-eslint/utils": "6.19.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -10134,9 +10134,9 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", - "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10147,13 +10147,13 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.0.tgz", - "integrity": "sha512-klNvl+Ql4NsBNGB4W9TZ2Od03lm7aGvTbs0wYaFYsplVPhr+oeXjlPZCDI4U9jgJIDK38W1FKhacCFzCC+nbIg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10175,12 +10175,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", - "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -10281,17 +10281,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.0.tgz", - "integrity": "sha512-wiKKCbUeDPGaYEYQh1S580dGxJ/V9HI7K5sbGAVklyf+o5g3O+adnS4UNJajplF4e7z2q0uVBaTdT/yLb4XAVA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.19.0.tgz", + "integrity": "sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.18.0", - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/typescript-estree": "6.18.0", + "@typescript-eslint/scope-manager": "6.19.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/typescript-estree": "6.19.0", "semver": "^7.5.4" }, "engines": { @@ -10306,13 +10306,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.0.tgz", - "integrity": "sha512-o/UoDT2NgOJ2VfHpfr+KBY2ErWvCySNUIX/X7O9g8Zzt/tXdpfEU43qbNk8LVuWUT2E0ptzTWXh79i74PP0twA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.19.0.tgz", + "integrity": "sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0" + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10323,9 +10323,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.0.tgz", - "integrity": "sha512-/RFVIccwkwSdW/1zeMx3hADShWbgBxBnV/qSrex6607isYjj05t36P6LyONgqdUrNLl5TYU8NIKdHUYpFvExkA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.19.0.tgz", + "integrity": "sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -10336,13 +10336,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.0.tgz", - "integrity": "sha512-klNvl+Ql4NsBNGB4W9TZ2Od03lm7aGvTbs0wYaFYsplVPhr+oeXjlPZCDI4U9jgJIDK38W1FKhacCFzCC+nbIg==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.19.0.tgz", + "integrity": "sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", - "@typescript-eslint/visitor-keys": "6.18.0", + "@typescript-eslint/types": "6.19.0", + "@typescript-eslint/visitor-keys": "6.19.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10364,12 +10364,12 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.0.tgz", - "integrity": "sha512-1wetAlSZpewRDb2h9p/Q8kRjdGuqdTAQbkJIOUMLug2LBLG+QOjiWoSj6/3B/hA9/tVTFFdtiKvAYoYnSRW/RA==", + "version": "6.19.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.19.0.tgz", + "integrity": "sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.18.0", + "@typescript-eslint/types": "6.19.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5227fd8aa..711fadfae 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2010,7 +2010,7 @@ "@types/unzipper": "^0.10.1", "@types/vscode": "^1.82.0", "@types/yauzl": "^2.10.3", - "@typescript-eslint/eslint-plugin": "^6.18.0", + "@typescript-eslint/eslint-plugin": "^6.19.0", "@typescript-eslint/parser": "^6.16.0", "@vscode/test-electron": "^2.2.0", "@vscode/vsce": "^2.19.0", From c89382ceb732d905737469fb34675f16a6e44cfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:48:04 +0000 Subject: [PATCH 73/77] Bump @storybook/addon-a11y from 7.6.4 to 7.6.9 in /extensions/ql-vscode Bumps [@storybook/addon-a11y](https://github.com/storybookjs/storybook/tree/HEAD/code/addons/a11y) from 7.6.4 to 7.6.9. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/addons/a11y) --- updated-dependencies: - dependency-name: "@storybook/addon-a11y" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 23 ++++++++++++++++++----- extensions/ql-vscode/package.json | 2 +- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index e62b4f67c..cb5738305 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -53,7 +53,7 @@ "@faker-js/faker": "^8.0.2", "@github/markdownlint-github": "^0.6.0", "@octokit/plugin-throttling": "^8.0.0", - "@storybook/addon-a11y": "^7.4.6", + "@storybook/addon-a11y": "^7.6.9", "@storybook/addon-actions": "^7.1.0", "@storybook/addon-essentials": "^7.1.0", "@storybook/addon-interactions": "^7.1.0", @@ -5192,12 +5192,12 @@ } }, "node_modules/@storybook/addon-a11y": { - "version": "7.6.4", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.6.4.tgz", - "integrity": "sha512-NwROJMes3D1WVPSmASVnHtDCIFQCF3DoPJEpzpnUYFxCR2IQzqPSlf9jptRkot6XjL5XwVX5mV2KxC2lKA1cfg==", + "version": "7.6.9", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-7.6.9.tgz", + "integrity": "sha512-TMr6X8rd0qIAoNHP4Sb+YcQR13lB1uEWNv7lFJc7lcZVUV9ZtxqrQ3lbZDrPaTU1rpBoWXNUGMKjd6Teq9WDAw==", "dev": true, "dependencies": { - "@storybook/addon-highlight": "7.6.4", + "@storybook/addon-highlight": "7.6.9", "axe-core": "^4.2.0" }, "funding": { @@ -5205,6 +5205,19 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/addon-a11y/node_modules/@storybook/addon-highlight": { + "version": "7.6.9", + "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-7.6.9.tgz", + "integrity": "sha512-TgfUdZqG+X6lJfZJvMkSsG+UfiEkh1xAXC628RTcVjVx5uopuezdASLU2jpLbyBc9Dmvp3j0XaEJph50n9sEUQ==", + "dev": true, + "dependencies": { + "@storybook/global": "^5.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@storybook/addon-actions": { "version": "7.6.4", "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-7.6.4.tgz", diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5227fd8aa..215407ce6 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1971,7 +1971,7 @@ "@faker-js/faker": "^8.0.2", "@github/markdownlint-github": "^0.6.0", "@octokit/plugin-throttling": "^8.0.0", - "@storybook/addon-a11y": "^7.4.6", + "@storybook/addon-a11y": "^7.6.9", "@storybook/addon-actions": "^7.1.0", "@storybook/addon-essentials": "^7.1.0", "@storybook/addon-interactions": "^7.1.0", From ccfba04d9702eca46f0518d54902e717655d0b75 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 13:49:17 +0000 Subject: [PATCH 74/77] Bump @storybook/theming from 7.6.7 to 7.6.9 in /extensions/ql-vscode Bumps [@storybook/theming](https://github.com/storybookjs/storybook/tree/HEAD/code/lib/theming) from 7.6.7 to 7.6.9. - [Release notes](https://github.com/storybookjs/storybook/releases) - [Changelog](https://github.com/storybookjs/storybook/blob/next/CHANGELOG.md) - [Commits](https://github.com/storybookjs/storybook/commits/v7.6.9/code/lib/theming) --- updated-dependencies: - dependency-name: "@storybook/theming" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 56 ++++++++++++++++++++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index e62b4f67c..f61245ef4 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -63,7 +63,7 @@ "@storybook/manager-api": "^7.6.7", "@storybook/react": "^7.1.0", "@storybook/react-webpack5": "^7.6.7", - "@storybook/theming": "^7.6.7", + "@storybook/theming": "^7.6.9", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.0.0", @@ -6620,6 +6620,26 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/components/node_modules/@storybook/theming": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.7.tgz", + "integrity": "sha512-+42rfC4rZtWVAXJ7JBUQKnQ6vWBXJVHZ9HtNUWzQLPR9sJSMmHnnSMV6y5tizGgZqmBnAIkuoYk+Tt6NfwUmSA==", + "dev": true, + "dependencies": { + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@storybook/client-logger": "7.6.7", + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@storybook/components/node_modules/@storybook/types": { "version": "7.6.7", "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.7.tgz", @@ -7448,6 +7468,26 @@ "url": "https://opencollective.com/storybook" } }, + "node_modules/@storybook/manager-api/node_modules/@storybook/theming": { + "version": "7.6.7", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.7.tgz", + "integrity": "sha512-+42rfC4rZtWVAXJ7JBUQKnQ6vWBXJVHZ9HtNUWzQLPR9sJSMmHnnSMV6y5tizGgZqmBnAIkuoYk+Tt6NfwUmSA==", + "dev": true, + "dependencies": { + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", + "@storybook/client-logger": "7.6.7", + "@storybook/global": "^5.0.0", + "memoizerific": "^1.11.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@storybook/manager-api/node_modules/@storybook/types": { "version": "7.6.7", "resolved": "https://registry.npmjs.org/@storybook/types/-/types-7.6.7.tgz", @@ -8328,13 +8368,13 @@ } }, "node_modules/@storybook/theming": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.7.tgz", - "integrity": "sha512-+42rfC4rZtWVAXJ7JBUQKnQ6vWBXJVHZ9HtNUWzQLPR9sJSMmHnnSMV6y5tizGgZqmBnAIkuoYk+Tt6NfwUmSA==", + "version": "7.6.9", + "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-7.6.9.tgz", + "integrity": "sha512-S2tow/l2HJFL7im+ovFQE0nLCzy/39qZU30/WVc8gM2dfM7Gsn6M4xiXu23BEwJHnCP8TIOBiCDN1JkOcOvvgg==", "dev": true, "dependencies": { "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@storybook/client-logger": "7.6.7", + "@storybook/client-logger": "7.6.9", "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3" }, @@ -8348,9 +8388,9 @@ } }, "node_modules/@storybook/theming/node_modules/@storybook/client-logger": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.7.tgz", - "integrity": "sha512-A16zpWgsa0gSdXMR9P3bWVdC9u/1B1oG4H7Z1+JhNzgnL3CdyOYO0qFSiAtNBso4nOjIAJVb6/AoBzdRhmSVQg==", + "version": "7.6.9", + "resolved": "https://registry.npmjs.org/@storybook/client-logger/-/client-logger-7.6.9.tgz", + "integrity": "sha512-Xm6fa6AR3cjxabauMldBv/66OOp5IhDiUEpp4D/a7hXfvCWqwmjVJ6EPz9WzkMhcPbMJr8vWJBaS3glkFqsRng==", "dev": true, "dependencies": { "@storybook/global": "^5.0.0" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 5227fd8aa..800f3c902 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -1981,7 +1981,7 @@ "@storybook/manager-api": "^7.6.7", "@storybook/react": "^7.1.0", "@storybook/react-webpack5": "^7.6.7", - "@storybook/theming": "^7.6.7", + "@storybook/theming": "^7.6.9", "@testing-library/dom": "^9.3.4", "@testing-library/jest-dom": "^6.2.0", "@testing-library/react": "^14.0.0", From f1a915e46cff37be8a4569414457fabb7dc7dcca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 12:56:46 -0800 Subject: [PATCH 75/77] Bump css-loader from 6.8.1 to 6.9.0 in /extensions/ql-vscode (#3256) Bumps [css-loader](https://github.com/webpack-contrib/css-loader) from 6.8.1 to 6.9.0. - [Release notes](https://github.com/webpack-contrib/css-loader/releases) - [Changelog](https://github.com/webpack-contrib/css-loader/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/css-loader/compare/v6.8.1...v6.9.0) --- updated-dependencies: - dependency-name: css-loader dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/ql-vscode/package-lock.json | 20 ++++++++++---------- extensions/ql-vscode/package.json | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index 7d4f848ea..e72683471 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -100,7 +100,7 @@ "applicationinsights": "^2.9.2", "cosmiconfig": "^9.0.0", "cross-env": "^7.0.3", - "css-loader": "^6.8.1", + "css-loader": "^6.9.0", "del": "^6.0.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.0.0", @@ -13741,19 +13741,19 @@ } }, "node_modules/css-loader": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz", - "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.9.0.tgz", + "integrity": "sha512-3I5Nu4ytWlHvOP6zItjiHlefBNtrH+oehq8tnQa2kO305qpVyx9XNIT1CXIj5bgCJs7qICBCkgCYxQLKPANoLA==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", - "postcss": "^8.4.21", + "postcss": "^8.4.31", "postcss-modules-extract-imports": "^3.0.0", "postcss-modules-local-by-default": "^4.0.3", - "postcss-modules-scope": "^3.0.0", + "postcss-modules-scope": "^3.1.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", - "semver": "^7.3.8" + "semver": "^7.5.4" }, "engines": { "node": ">= 12.13.0" @@ -26924,9 +26924,9 @@ } }, "node_modules/postcss-modules-scope": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", - "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.0.tgz", + "integrity": "sha512-SaIbK8XW+MZbd0xHPf7kdfA/3eOt7vxJ72IRecn3EzuZVLr1r0orzf0MX/pN8m+NMDoo6X/SQd8oeKqGZd8PXg==", "dev": true, "dependencies": { "postcss-selector-parser": "^6.0.4" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 874cd0459..3df7cb0e2 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2018,7 +2018,7 @@ "applicationinsights": "^2.9.2", "cosmiconfig": "^9.0.0", "cross-env": "^7.0.3", - "css-loader": "^6.8.1", + "css-loader": "^6.9.0", "del": "^6.0.0", "eslint": "^8.56.0", "eslint-config-prettier": "^9.0.0", From a2ebc69d3aa2f43184f61246d9eaecde3eb27a85 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 20:58:32 +0000 Subject: [PATCH 76/77] Bump prettier from 3.1.1 to 3.2.4 in /extensions/ql-vscode Bumps [prettier](https://github.com/prettier/prettier) from 3.1.1 to 3.2.4. - [Release notes](https://github.com/prettier/prettier/releases) - [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md) - [Commits](https://github.com/prettier/prettier/compare/3.1.1...3.2.4) --- updated-dependencies: - dependency-name: prettier dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- extensions/ql-vscode/package-lock.json | 8 ++++---- extensions/ql-vscode/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/extensions/ql-vscode/package-lock.json b/extensions/ql-vscode/package-lock.json index e72683471..6c3f1526c 100644 --- a/extensions/ql-vscode/package-lock.json +++ b/extensions/ql-vscode/package-lock.json @@ -129,7 +129,7 @@ "mini-css-extract-plugin": "^2.6.1", "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", - "prettier": "^3.0.0", + "prettier": "^3.2.4", "storybook": "^7.6.7", "tar-stream": "^3.0.0", "through2": "^4.0.2", @@ -27025,9 +27025,9 @@ } }, "node_modules/prettier": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz", - "integrity": "sha512-22UbSzg8luF4UuZtzgiUOfcGM8s4tjBv6dJRT7j275NXsy2jb4aJa4NNveul5x4eqlF1wuhuR2RElK71RvmVaw==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.4.tgz", + "integrity": "sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" diff --git a/extensions/ql-vscode/package.json b/extensions/ql-vscode/package.json index 3df7cb0e2..9e64bc55e 100644 --- a/extensions/ql-vscode/package.json +++ b/extensions/ql-vscode/package.json @@ -2047,7 +2047,7 @@ "mini-css-extract-plugin": "^2.6.1", "npm-run-all": "^4.1.5", "patch-package": "^8.0.0", - "prettier": "^3.0.0", + "prettier": "^3.2.4", "storybook": "^7.6.7", "tar-stream": "^3.0.0", "through2": "^4.0.2", From fc41360bad566818095dbfe6d54bf682b310cecb Mon Sep 17 00:00:00 2001 From: Nora Date: Fri, 19 Jan 2024 10:15:00 +0000 Subject: [PATCH 77/77] Check in npm run format --- .../ql-vscode/scripts/util/vscode-versions.ts | 5 +++-- extensions/ql-vscode/src/common/readonly.ts | 17 +++++++++-------- extensions/ql-vscode/src/extension.ts | 5 +++-- .../shared/variant-analysis-filter-sort.ts | 4 ++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/extensions/ql-vscode/scripts/util/vscode-versions.ts b/extensions/ql-vscode/scripts/util/vscode-versions.ts index d18c247f6..ce7853620 100644 --- a/extensions/ql-vscode/scripts/util/vscode-versions.ts +++ b/extensions/ql-vscode/scripts/util/vscode-versions.ts @@ -45,8 +45,9 @@ export async function getVersionInformation( vscodeVersion: string, ): Promise { const vsCodePackageJson = await getVsCodePackageJson(vscodeVersion); - const electronVersion = minVersion(vsCodePackageJson.devDependencies.electron) - ?.version; + const electronVersion = minVersion( + vsCodePackageJson.devDependencies.electron, + )?.version; if (!electronVersion) { throw new Error("Could not find Electron version"); } diff --git a/extensions/ql-vscode/src/common/readonly.ts b/extensions/ql-vscode/src/common/readonly.ts index d6bf75098..19a4ad963 100644 --- a/extensions/ql-vscode/src/common/readonly.ts +++ b/extensions/ql-vscode/src/common/readonly.ts @@ -1,11 +1,12 @@ -export type DeepReadonly = T extends Array - ? DeepReadonlyArray - : // eslint-disable-next-line @typescript-eslint/ban-types - T extends Function - ? T - : T extends object - ? DeepReadonlyObject - : T; +export type DeepReadonly = + T extends Array + ? DeepReadonlyArray + : // eslint-disable-next-line @typescript-eslint/ban-types + T extends Function + ? T + : T extends object + ? DeepReadonlyObject + : T; interface DeepReadonlyArray extends ReadonlyArray> {} diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index 5e409398d..8e4680139 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -215,8 +215,9 @@ function getCommands( "codeQL.restartLegacyQueryServerOnConfigChange": restartQueryServer, "codeQL.restartQueryServerOnExternalConfigChange": restartQueryServer, "codeQL.copyVersion": async () => { - const text = `CodeQL extension version: ${extension?.packageJSON - .version} \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${platform()} ${arch()}`; + const text = `CodeQL extension version: ${ + extension?.packageJSON.version + } \nCodeQL CLI version: ${await getCliVersion()} \nPlatform: ${platform()} ${arch()}`; await env.clipboard.writeText(text); void showAndLogInformationMessage(extLogger, text); }, diff --git a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis-filter-sort.ts b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis-filter-sort.ts index 9d2d153bc..794439e13 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis-filter-sort.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis-filter-sort.ts @@ -146,8 +146,8 @@ export function filterAndSortRepositoriesWithResults< filterSortState.repositoryIds.length > 0 ) { return repositories - .filter( - (repo) => filterSortState.repositoryIds?.includes(repo.repository.id), + .filter((repo) => + filterSortState.repositoryIds?.includes(repo.repository.id), ) .sort(compareWithResults(filterSortState)); }