From e71ea8d975761c0432c6d623e53fe1220e06968a Mon Sep 17 00:00:00 2001 From: Robert Date: Wed, 13 Aug 2025 16:17:53 +0000 Subject: [PATCH 1/2] Enable the @typescript-eslint/no-unsafe-return rule --- extensions/ql-vscode/eslint.config.mjs | 1 - extensions/ql-vscode/src/codeql-cli/cli.ts | 2 +- .../ql-vscode/src/common/mock-gh-api/recorder.ts | 2 +- extensions/ql-vscode/src/extension.ts | 6 +++--- .../src/variant-analysis/shared/variant-analysis.ts | 8 +++++--- .../src/view/common/SuggestBox/SuggestBox.tsx | 8 ++++++-- .../src/view/common/SuggestBox/useEffectEvent.ts | 1 + extensions/ql-vscode/test/mock-memento.ts | 2 +- extensions/ql-vscode/test/mocked-object.ts | 2 ++ .../test/unit-tests/common/disposable-object.test.ts | 4 ++-- .../unit-tests/common/invocation-rate-limiter.test.ts | 2 +- .../unit-tests/variant-analysis/custom-errors.test.ts | 5 +++-- .../no-workspace/codeql-cli/distribution.test.ts | 2 +- .../ql-vscode/test/vscode-tests/no-workspace/index.ts | 6 +++--- .../language-support/ast-viewer/ast-builder.test.ts | 10 +++++++--- .../no-workspace/query-testing/test-runner.test.ts | 3 ++- 16 files changed, 39 insertions(+), 25 deletions(-) diff --git a/extensions/ql-vscode/eslint.config.mjs b/extensions/ql-vscode/eslint.config.mjs index ba67c3a224a..9496fb03876 100644 --- a/extensions/ql-vscode/eslint.config.mjs +++ b/extensions/ql-vscode/eslint.config.mjs @@ -70,7 +70,6 @@ export default tseslint.config( // Rules disabled during eslint 9 migration "github/filenames-match-regex": "off", "@typescript-eslint/restrict-template-expressions": "off", - "@typescript-eslint/no-unsafe-return": "off", "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-argument": "off", "@typescript-eslint/no-unsafe-member-access": "off", diff --git a/extensions/ql-vscode/src/codeql-cli/cli.ts b/extensions/ql-vscode/src/codeql-cli/cli.ts index 1ef1e4a8d40..2074f0d7dec 100644 --- a/extensions/ql-vscode/src/codeql-cli/cli.ts +++ b/extensions/ql-vscode/src/codeql-cli/cli.ts @@ -979,7 +979,7 @@ export class CodeQLCliServer implements Disposable { subcommandArgs, "Resolving query by language", ), - ); + ) as QueryInfoByLanguage; } /** diff --git a/extensions/ql-vscode/src/common/mock-gh-api/recorder.ts b/extensions/ql-vscode/src/common/mock-gh-api/recorder.ts index a2d3c34c72c..4f82b0f5493 100644 --- a/extensions/ql-vscode/src/common/mock-gh-api/recorder.ts +++ b/extensions/ql-vscode/src/common/mock-gh-api/recorder.ts @@ -283,7 +283,7 @@ async function jsonResponseBody(response: Response): Promise { const body = await responseBody(response); const text = new TextDecoder("utf-8").decode(body); - return JSON.parse(text); + return JSON.parse(text) as T; } function shouldWriteBodyToFile( diff --git a/extensions/ql-vscode/src/extension.ts b/extensions/ql-vscode/src/extension.ts index b6766f3baff..3edd7b8944f 100644 --- a/extensions/ql-vscode/src/extension.ts +++ b/extensions/ql-vscode/src/extension.ts @@ -251,7 +251,7 @@ function registerErrorStubs( stubGenerator: (command: string) => () => Promise, ): void { // Remove existing stubs - errorStubs.forEach((stub) => stub.dispose()); + errorStubs.forEach((stub) => void stub.dispose()); if (extension === undefined) { throw new Error(`Can't find extension ${extensionId}`); @@ -757,7 +757,7 @@ async function activateWithInstalledDistribution( beganMainExtensionActivation = true; // Remove any error stubs command handlers left over from first part // of activation. - errorStubs.forEach((stub) => stub.dispose()); + errorStubs.forEach((stub) => void stub.dispose()); void extLogger.log("Initializing configuration listener..."); const qlConfigurationListener = @@ -1167,7 +1167,7 @@ async function activateWithInstalledDistribution( databaseUI, variantAnalysisManager, dispose: () => { - ctx.subscriptions.forEach((d) => d.dispose()); + ctx.subscriptions.forEach((d) => void d.dispose()); }, }; } 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 90f09ba15e8..5bb97368058 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -287,9 +287,11 @@ export function getSkippedRepoCount( return 0; } - return Object.values(skippedRepos).reduce( - (acc, group) => acc + group.repositoryCount, - 0, + return ( + (skippedRepos.accessMismatchRepos?.repositoryCount ?? 0) + + (skippedRepos.notFoundRepos?.repositoryCount ?? 0) + + (skippedRepos.noCodeqlDbRepos?.repositoryCount ?? 0) + + (skippedRepos.overLimitRepos?.repositoryCount ?? 0) ); } diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx index f59d5f96093..facfeedf0dd 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx +++ b/extensions/ql-vscode/src/view/common/SuggestBox/SuggestBox.tsx @@ -22,11 +22,15 @@ import type { Diagnostic } from "./diagnostics"; import { useOpenKey } from "./useOpenKey"; import { VscodeTextfield } from "@vscode-elements/react-elements"; -const Input = styled(VscodeTextfield)<{ $error: boolean }>` +interface InputProps { + $error: boolean; +} + +const Input = styled(VscodeTextfield)` width: 100%; font-family: var(--vscode-editor-font-family); - ${(props) => + ${(props: InputProps) => props.$error && css` --dropdown-border: var(--vscode-inputValidation-errorBorder); diff --git a/extensions/ql-vscode/src/view/common/SuggestBox/useEffectEvent.ts b/extensions/ql-vscode/src/view/common/SuggestBox/useEffectEvent.ts index 13707487ad1..3bd2f92f4e0 100644 --- a/extensions/ql-vscode/src/view/common/SuggestBox/useEffectEvent.ts +++ b/extensions/ql-vscode/src/view/common/SuggestBox/useEffectEvent.ts @@ -18,6 +18,7 @@ export function useEffectEvent any>(callback: T) { }); return useCallback<(...args: Parameters) => ReturnType>( + // eslint-disable-next-line @typescript-eslint/no-unsafe-return (...args) => ref.current(...args), [], ) as T; diff --git a/extensions/ql-vscode/test/mock-memento.ts b/extensions/ql-vscode/test/mock-memento.ts index 24e9ea8362b..8d2a036ff81 100644 --- a/extensions/ql-vscode/test/mock-memento.ts +++ b/extensions/ql-vscode/test/mock-memento.ts @@ -17,7 +17,7 @@ class MockMemento implements Memento { public get(key: string): T | undefined; public get(key: string, defaultValue: T): T; - public get(key: any, defaultValue?: any): T | undefined { + public get(key: any, defaultValue?: T): T | undefined { return this.map.get(key) || defaultValue; } diff --git a/extensions/ql-vscode/test/mocked-object.ts b/extensions/ql-vscode/test/mocked-object.ts index e20caba295b..bd4374a843b 100644 --- a/extensions/ql-vscode/test/mocked-object.ts +++ b/extensions/ql-vscode/test/mocked-object.ts @@ -25,9 +25,11 @@ export function mockedObject( return new Proxy({} as unknown as T, { get: (_target, prop) => { if (prop in props) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return (props as any)[prop]; } if (dynamicProperties && prop in dynamicProperties) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return (dynamicProperties as any)[prop](); } 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 b211141e39c..381bf838d2c 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 @@ -77,10 +77,10 @@ describe("DisposableObject and DisposeHandler", () => { expect(disposable1.dispose).toHaveBeenCalled(); }); - it("ahould use a dispose handler", () => { + it("should use a dispose handler", () => { const handler = (d: any) => d === disposable1 || d === disposable3 || d === nestedDisposableObject - ? d.dispose(handler) + ? void d.dispose(handler) : void 0; disposableObject.push(disposable1); diff --git a/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts b/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts index 18590963a86..d29b60261b8 100644 --- a/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts +++ b/extensions/ql-vscode/test/unit-tests/common/invocation-rate-limiter.test.ts @@ -40,7 +40,7 @@ describe("Invocation rate limiter", () => { * @return The stored value or the defaultValue. */ get(key: string, defaultValue?: T): T | undefined { - return this.map.has(key) ? this.map.get(key) : defaultValue; + return this.map.has(key) ? (this.map.get(key) as T) : defaultValue; } /** diff --git a/extensions/ql-vscode/test/unit-tests/variant-analysis/custom-errors.test.ts b/extensions/ql-vscode/test/unit-tests/variant-analysis/custom-errors.test.ts index 6980ab36c0c..e35442480df 100644 --- a/extensions/ql-vscode/test/unit-tests/variant-analysis/custom-errors.test.ts +++ b/extensions/ql-vscode/test/unit-tests/variant-analysis/custom-errors.test.ts @@ -173,8 +173,9 @@ function toErrorMessage(data: any) { if (Array.isArray(data.errors)) { return `${data.message}: ${data.errors.map(JSON.stringify).join(", ")}`; } - - return data.message; + if (typeof data.message === "string") { + return data.message as string; + } } // istanbul ignore next - just in case 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 aa86d1a9ed1..965aebb1d21 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 @@ -38,7 +38,7 @@ import type { Release } from "../../../../src/codeql-cli/distribution/release"; import { zip } from "zip-a-folder"; jest.mock("os", () => { - const original = jest.requireActual("os"); + const original: typeof os = jest.requireActual("os"); return { ...original, platform: jest.fn(), diff --git a/extensions/ql-vscode/test/vscode-tests/no-workspace/index.ts b/extensions/ql-vscode/test/vscode-tests/no-workspace/index.ts index c39abbbaf72..8e0e4764a4a 100644 --- a/extensions/ql-vscode/test/vscode-tests/no-workspace/index.ts +++ b/extensions/ql-vscode/test/vscode-tests/no-workspace/index.ts @@ -4,12 +4,12 @@ export function createMockExtensionContext(): ExtensionContext { return { globalState: { _state: {} as Record, - get(key: string) { - return this._state[key]; + get(key: string): T | undefined { + return this._state[key] as T | undefined; }, update(key: string, val: any) { this._state[key] = val; }, }, - } as any; + } as unknown as ExtensionContext; } 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 6275abd6b7b..df108005c21 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 @@ -5,6 +5,7 @@ import { Uri } from "vscode"; import { mockDatabaseItem, mockedObject } from "../../../utils/mocking.helpers"; import path from "path"; import { AstBuilder } from "../../../../../src/language-support"; +import type { DecodedBqrsChunk } from "../../../../../src/common/bqrs-cli-types"; /** * @@ -29,7 +30,7 @@ int disable_interrupts(void) describe("AstBuilder", () => { let mockCli: CodeQLCliServer; - let overrides: Record | undefined>; + let overrides: Record; beforeEach(() => { mockCli = mockedObject({ @@ -132,6 +133,7 @@ describe("AstBuilder", () => { it("should fail when graphProperties are not correct", async () => { overrides.graphProperties = { tuples: [["semmle.graphKind", "hucairz"]], + columns: [], }; const astBuilder = createAstBuilder(); @@ -149,7 +151,9 @@ describe("AstBuilder", () => { ); } - function mockDecode(resultSet: "nodes" | "edges" | "graphProperties") { + async function mockDecode( + resultSet: "nodes" | "edges" | "graphProperties", + ): Promise { if (overrides[resultSet]) { return overrides[resultSet]; } @@ -166,7 +170,7 @@ describe("AstBuilder", () => { `${__dirname}/../../data/language-support/ast-viewer/ast-builder.json`, "utf8", ), - )[index]; + )[index] as DecodedBqrsChunk; } else { throw new Error(`Invalid resultSet: ${resultSet}`); } 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 ea33088f87a..62d5a98dc64 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 @@ -1,4 +1,5 @@ import { CancellationTokenSource, Uri } from "vscode"; +import type * as fsExtra from "fs-extra"; import type { CodeQLCliServer } from "../../../../src/codeql-cli/cli"; import type { DatabaseItem, @@ -15,7 +16,7 @@ import { } from "./test-runner-helpers"; jest.mock("fs-extra", () => { - const original = jest.requireActual("fs-extra"); + const original: typeof fsExtra = jest.requireActual("fs-extra"); return { ...original, access: jest.fn(), From ac398f4fc8e71b6b46e9bdbb2a347abe2418405a Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 14 Aug 2025 10:58:28 +0000 Subject: [PATCH 2/2] Use reduce to getSkippedRepoCount --- .../src/variant-analysis/shared/variant-analysis.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) 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 5bb97368058..a83a5f2bbf6 100644 --- a/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts +++ b/extensions/ql-vscode/src/variant-analysis/shared/variant-analysis.ts @@ -287,11 +287,10 @@ export function getSkippedRepoCount( return 0; } - return ( - (skippedRepos.accessMismatchRepos?.repositoryCount ?? 0) + - (skippedRepos.notFoundRepos?.repositoryCount ?? 0) + - (skippedRepos.noCodeqlDbRepos?.repositoryCount ?? 0) + - (skippedRepos.overLimitRepos?.repositoryCount ?? 0) + return Object.values(skippedRepos).reduce( + (acc, group: VariantAnalysisSkippedRepositoryGroup) => + acc + group.repositoryCount, + 0, ); }