extension/test: add regression test for golang/vscode-go#3511 Also move the diagnostics test to the same file. For golang/vscode-go#3511 Change-Id: I4797671b9955ce1d4d774b34474729d179d6cba1 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/812360 Auto-Submit: Hongxiang Jiang <hxjiang@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Madeline Kalil <mkalil@google.com>
diff --git a/extension/test/gopls/diagnostics.test.ts b/extension/test/gopls/diagnostics.test.ts new file mode 100644 index 0000000..c83ee4e --- /dev/null +++ b/extension/test/gopls/diagnostics.test.ts
@@ -0,0 +1,323 @@ +/*--------------------------------------------------------- + * Copyright 2026 The Go Authors. All rights reserved. + * Licensed under the MIT License. See LICENSE in the project root for license information. + *--------------------------------------------------------*/ + +import assert from 'assert'; +import os = require('os'); +import * as path from 'path'; +import sinon from 'sinon'; +import * as vscode from 'vscode'; +import * as config from '../../src/config'; +import { GoExtensionContext } from '../../src/context'; +import { handleErrors, ICheckResult } from '../../src/diagnostics/diagnostics'; +import { lintCode } from '../../src/diagnostics/goLint'; +import { MockWorkspaceConfiguration } from '../integration/mocks/configuration'; +import { Env } from './goplsTestEnv.utils'; + +interface expectedDiagnostic { + line: number; + source: string; + severity: vscode.DiagnosticSeverity; +} + +function compareDiags(a: vscode.Diagnostic, b: vscode.Diagnostic): number { + if (a.range.start.line !== b.range.start.line) { + return a.range.start.line - b.range.start.line; + } + if (a.range.start.character !== b.range.start.character) { + return a.range.start.character - b.range.start.character; + } + if (a.severity !== b.severity) { + return a.severity - b.severity; + } + return (a.source ?? '').localeCompare(b.source ?? ''); +} + +suite('Diagnostic consolidation - unit', () => { + let goCtx: GoExtensionContext; + + const fileURI = vscode.Uri.file(path.join(os.tmpdir(), 'diagnostic_priority_test.go')); // fake file + const filePath = fileURI.fsPath; + + interface TestDiagnosticTestCase { + name: string; + diags: { + gopls?: ICheckResult[]; + build?: ICheckResult[]; + vet?: ICheckResult[]; + lint?: ICheckResult[]; + }; + want: expectedDiagnostic[]; + } + + const testCases: TestDiagnosticTestCase[] = [ + { + name: 'Symmetric priority masking (Gopls > Build > Vet > Lint)', + diags: { + gopls: [{ file: filePath, line: 10, msg: 'unmasked - highest priority', severity: 'warning' }], + build: [ + { file: filePath, line: 10, msg: 'masked by gopls', severity: 'warning' }, + { file: filePath, line: 20, msg: 'unmasked - no higher priority', severity: 'warning' } + ], + vet: [ + { file: filePath, line: 10, msg: 'masked by gopls', severity: 'warning' }, + { file: filePath, line: 20, msg: 'masked by build', severity: 'warning' }, + { file: filePath, line: 30, msg: 'unmasked - no higher priority', severity: 'warning' } + ], + lint: [ + { file: filePath, line: 10, msg: 'masked by gopls', severity: 'warning' }, + { file: filePath, line: 20, msg: 'masked by build', severity: 'warning' }, + { file: filePath, line: 30, msg: 'masked by vet', severity: 'warning' }, + { file: filePath, line: 40, msg: 'unmasked - no higher priority', severity: 'warning' } + ] + }, + want: [ + { line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Warning }, + { line: 20, source: 'build-test', severity: vscode.DiagnosticSeverity.Warning }, + { line: 30, source: 'vet-test', severity: vscode.DiagnosticSeverity.Warning }, + { line: 40, source: 'lint-test', severity: vscode.DiagnosticSeverity.Warning } + ] + }, + { + name: 'Diagnostics with columns and severity', + diags: { + gopls: [{ file: filePath, line: 10, col: 5, msg: 'unmasked - highest priority', severity: 'error' }], + build: [ + { file: filePath, line: 10, col: 5, msg: 'masked by gopls', severity: 'error' }, + { file: filePath, line: 20, col: 12, msg: 'unmasked - no higher priority', severity: 'error' } + ], + vet: [ + { file: filePath, line: 20, col: 12, msg: 'masked by build', severity: 'warning' }, + { file: filePath, line: 30, col: 8, msg: 'unmasked - no higher priority', severity: 'warning' } + ], + lint: [ + { file: filePath, line: 30, col: 8, msg: 'masked by vet', severity: 'warning' }, + { file: filePath, line: 40, col: 15, msg: 'unmasked - no higher priority', severity: 'warning' } + ] + }, + want: [ + { line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Error }, + { line: 20, source: 'build-test', severity: vscode.DiagnosticSeverity.Error }, + { line: 30, source: 'vet-test', severity: vscode.DiagnosticSeverity.Warning }, + { line: 40, source: 'lint-test', severity: vscode.DiagnosticSeverity.Warning } + ] + }, + // TODO(hxjiang): update test case once dedup based on line and column + { + name: 'Same line, different columns, same severity', + diags: { + gopls: [{ file: filePath, line: 10, col: 5, msg: 'unmasked - highest priority', severity: 'error' }], + build: [ + { file: filePath, line: 10, col: 5, msg: 'masked by gopls', severity: 'error' }, + { file: filePath, line: 10, col: 15, msg: 'masked by gopls', severity: 'error' } + ], + vet: [{ file: filePath, line: 10, col: 25, msg: 'masked by gopls', severity: 'error' }], + lint: [{ file: filePath, line: 10, col: 35, msg: 'masked by gopls', severity: 'error' }] + }, + want: [{ line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Error }] + }, + { + name: 'Same line and column, lower priority has higher severity', + diags: { + gopls: [{ file: filePath, line: 10, col: 5, msg: 'unmasked - highest priority', severity: 'warning' }], + lint: [ + { file: filePath, line: 10, col: 5, msg: 'masked by gopls', severity: 'warning' }, + { + file: filePath, + line: 10, + col: 5, + msg: 'unmasked - higher severity than gopls warning', + severity: 'error' + } + ] + }, + want: [ + { line: 10, source: 'lint-test', severity: vscode.DiagnosticSeverity.Error }, + { line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Warning } + ] + } + ]; + + setup(() => { + goCtx = { + languageClient: { diagnostics: vscode.languages.createDiagnosticCollection('gopls-test') } as any, + buildDiagnosticCollection: vscode.languages.createDiagnosticCollection('build-test'), + vetDiagnosticCollection: vscode.languages.createDiagnosticCollection('vet-test'), + lintDiagnosticCollection: vscode.languages.createDiagnosticCollection('lint-test') + }; + }); + + teardown(() => { + goCtx.languageClient?.diagnostics?.dispose(); + goCtx.buildDiagnosticCollection?.dispose(); + goCtx.vetDiagnosticCollection?.dispose(); + goCtx.lintDiagnosticCollection?.dispose(); + }); + + for (const tc of testCases) { + for (let round = 1; round <= 5; round++) { + test(`${tc.name} (round ${round})`, async () => { + const collections = [ + { key: 'gopls' as const, collection: goCtx.languageClient!.diagnostics! }, + { key: 'build' as const, collection: goCtx.buildDiagnosticCollection! }, + { key: 'vet' as const, collection: goCtx.vetDiagnosticCollection! }, + { key: 'lint' as const, collection: goCtx.lintDiagnosticCollection! } + ]; + + // Simulate 4 concurrent diagnostic providers reporting diags + // independently with random delays to verify eventual consistency. + const tasks = collections.map(({ key, collection }) => { + const errors = tc.diags[key] || []; + return new Promise<void>((resolve) => { + const delay = Math.floor(Math.random() * 15); + setTimeout(() => { + handleErrors(goCtx, undefined, errors, collection); + resolve(); + }, delay); + }); + }); + + // Wait for all concurrent diagnostic providers to finish reporting. + await Promise.all(tasks); + + // Read diagnostics directly from the "PROBLEMS" tab. + const problems = vscode.languages.getDiagnostics(fileURI); + const sorted = [...problems].sort(compareDiags); + + assert.strictEqual( + sorted.length, + tc.want.length, + `[${tc.name}] Expected ${tc.want.length} diagnostics, got ${sorted.length}: ${JSON.stringify(sorted.map((p) => ({ line: p.range.start.line + 1, source: p.source, msg: p.message })))}` + ); + + for (let i = 0; i < tc.want.length; i++) { + const want = tc.want[i]; + const got = sorted[i]; + assert.strictEqual(got.range.start.line, want.line - 1, `[${tc.name}] Line mismatch at index ${i}`); + assert.strictEqual(got.source, want.source, `[${tc.name}] Source mismatch at index ${i}`); + assert.strictEqual(got.severity, want.severity, `[${tc.name}] Severity mismatch at index ${i}`); + } + }); + } + } +}); + +// Regression tests for golang/vscode-go#3511. +suite('Diagnostic consolidation - regression (#3511)', function () { + this.timeout(30000); + const projectDir = path.join(__dirname, '..', '..', '..'); + const testdataDir = path.join(projectDir, 'test', 'testdata', 'diagnosticsTest'); + let env: Env; + + async function pollDiagnostics(uri: vscode.Uri, predicate: (diags: vscode.Diagnostic[]) => boolean): Promise<void> { + const start = Date.now(); + // Polling deadline of 10s. Analyzing a module with only a few files + // should finish within 10s for both gopls and external linters. + while (Date.now() - start < 10000) { + const problems = vscode.languages.getDiagnostics(uri); + if (predicate(problems)) { + return; + } + await new Promise((resolve) => setTimeout(resolve, 50)); + } + assert.fail( + `timed out waiting for expected diags on ${path.basename(uri.fsPath)}, got: ${JSON.stringify( + vscode.languages + .getDiagnostics(uri) + .map((p) => ({ line: p.range.start.line + 1, source: p.source, msg: p.message })) + )}` + ); + } + + suiteSetup(async () => { + env = new Env(); + const goplsConfig = new MockWorkspaceConfiguration( + config.getGoplsConfig(), + new Map<string, any>([ + ['ui.diagnostic.staticcheck', true], + ['ui.diagnostic.analyses', { ST1017: true }] + ]) + ); + sinon.stub(config, 'getGoplsConfig').returns(goplsConfig); + + const goConfig = new MockWorkspaceConfiguration( + config.getGoConfig(), + new Map<string, any>([ + ['lintTool', 'golangci-lint-v2'], + ['lintOnSave', 'package'] + ]) + ); + sinon.stub(config, 'getGoConfig').returns(goConfig); + + env.goCtx.lintDiagnosticCollection = vscode.languages.createDiagnosticCollection('go-lint'); + + // Start gopls with workspace + await env.startGopls(path.join(testdataDir, 'masked.go'), undefined, testdataDir); + + // Open both documents to trigger gopls diagnostics + const { doc: coexistDoc } = await env.openDoc(path.join(testdataDir, 'coexist.go')); + const { doc: maskedDoc } = await env.openDoc(path.join(testdataDir, 'masked.go')); + await vscode.window.showTextDocument(coexistDoc); + + // Run linter once for the package + lintCode('package')(undefined as any, env.goCtx)(); + + // Wait until diagnostics from both gopls and linter are ready + await pollDiagnostics(coexistDoc.uri, (diags) => diags.length >= 2); + await pollDiagnostics(maskedDoc.uri, (diags) => diags.length >= 1); + }); + + suiteTeardown(async () => { + sinon.restore(); + env.goCtx.lintDiagnosticCollection?.dispose(); + await env.teardown(); + env.flushTrace(false); + }); + + interface DiagnosticTestCase { + name: string; + fileName: string; + want: expectedDiagnostic[]; + } + + const testCases: DiagnosticTestCase[] = [ + { + name: 'coexist', + fileName: 'coexist.go', + // golangci-lint-v2 report a more severe diags so that persist. + want: [ + { line: 8, source: 'any', severity: vscode.DiagnosticSeverity.Hint }, + { line: 8, source: 'go-lint', severity: vscode.DiagnosticSeverity.Warning } + ] + }, + { + name: 'masked', + fileName: 'masked.go', + // golangci-lint-v2 will report the same diags but prefer gopls'. + want: [{ line: 4, source: 'ST1017', severity: vscode.DiagnosticSeverity.Warning }] + } + ]; + + for (const tc of testCases) { + test(tc.name, () => { + const uri = vscode.Uri.file(path.join(testdataDir, tc.fileName)); + const problems = vscode.languages.getDiagnostics(uri); + const sorted = [...problems].sort(compareDiags); + + assert.strictEqual( + sorted.length, + tc.want.length, + `[${tc.name}] Expected ${tc.want.length} diagnostics, got ${sorted.length}: ${JSON.stringify(sorted.map((p) => ({ line: p.range.start.line + 1, source: p.source, msg: p.message })))}` + ); + + for (let i = 0; i < tc.want.length; i++) { + const want = tc.want[i]; + const got = sorted[i]; + assert.strictEqual(got.range.start.line, want.line - 1, `[${tc.name}] Line mismatch at index ${i}`); + assert.strictEqual(got.source, want.source, `[${tc.name}] Source mismatch at index ${i}`); + assert.strictEqual(got.severity, want.severity, `[${tc.name}] Severity mismatch at index ${i}`); + } + }); + } +});
diff --git a/extension/test/integration/diagnostics.test.ts b/extension/test/integration/diagnostics.test.ts deleted file mode 100644 index 825cf70..0000000 --- a/extension/test/integration/diagnostics.test.ts +++ /dev/null
@@ -1,196 +0,0 @@ -/*--------------------------------------------------------- - * Copyright 2026 The Go Authors. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for license information. - *--------------------------------------------------------*/ - -import assert from 'assert'; -import os = require('os'); -import path = require('path'); -import * as vscode from 'vscode'; -import { GoExtensionContext } from '../../src/context'; -import { handleErrors, ICheckResult } from '../../src/diagnostics/diagnostics'; - -interface DiagnosticTestCase { - name: string; - diags: { - gopls?: ICheckResult[]; - build?: ICheckResult[]; - vet?: ICheckResult[]; - lint?: ICheckResult[]; - }; - want: { - line: number; - source: string; - severity: vscode.DiagnosticSeverity; - }[]; -} - -suite('Diagnostic consolidation', () => { - let goCtx: GoExtensionContext; - - const fileURI = vscode.Uri.file(path.join(os.tmpdir(), 'diagnostic_priority_test.go')); // fake file - const filePath = fileURI.fsPath; - - const testCases: DiagnosticTestCase[] = [ - { - name: 'Symmetric priority masking (Gopls > Build > Vet > Lint)', - diags: { - gopls: [{ file: filePath, line: 10, msg: 'unmasked - highest priority', severity: 'warning' }], - build: [ - { file: filePath, line: 10, msg: 'masked by gopls', severity: 'warning' }, - { file: filePath, line: 20, msg: 'unmasked - no higher priority', severity: 'warning' } - ], - vet: [ - { file: filePath, line: 10, msg: 'masked by gopls', severity: 'warning' }, - { file: filePath, line: 20, msg: 'masked by build', severity: 'warning' }, - { file: filePath, line: 30, msg: 'unmasked - no higher priority', severity: 'warning' } - ], - lint: [ - { file: filePath, line: 10, msg: 'masked by gopls', severity: 'warning' }, - { file: filePath, line: 20, msg: 'masked by build', severity: 'warning' }, - { file: filePath, line: 30, msg: 'masked by vet', severity: 'warning' }, - { file: filePath, line: 40, msg: 'unmasked - no higher priority', severity: 'warning' } - ] - }, - want: [ - { line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Warning }, - { line: 20, source: 'build-test', severity: vscode.DiagnosticSeverity.Warning }, - { line: 30, source: 'vet-test', severity: vscode.DiagnosticSeverity.Warning }, - { line: 40, source: 'lint-test', severity: vscode.DiagnosticSeverity.Warning } - ] - }, - { - name: 'Diagnostics with columns and severity', - diags: { - gopls: [{ file: filePath, line: 10, col: 5, msg: 'unmasked - highest priority', severity: 'error' }], - build: [ - { file: filePath, line: 10, col: 5, msg: 'masked by gopls', severity: 'error' }, - { file: filePath, line: 20, col: 12, msg: 'unmasked - no higher priority', severity: 'error' } - ], - vet: [ - { file: filePath, line: 20, col: 12, msg: 'masked by build', severity: 'warning' }, - { file: filePath, line: 30, col: 8, msg: 'unmasked - no higher priority', severity: 'warning' } - ], - lint: [ - { file: filePath, line: 30, col: 8, msg: 'masked by vet', severity: 'warning' }, - { file: filePath, line: 40, col: 15, msg: 'unmasked - no higher priority', severity: 'warning' } - ] - }, - want: [ - { line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Error }, - { line: 20, source: 'build-test', severity: vscode.DiagnosticSeverity.Error }, - { line: 30, source: 'vet-test', severity: vscode.DiagnosticSeverity.Warning }, - { line: 40, source: 'lint-test', severity: vscode.DiagnosticSeverity.Warning } - ] - }, - // TODO(hxjiang): update test case once dedup based on line and column - { - name: 'Same line, different columns, same severity', - diags: { - gopls: [{ file: filePath, line: 10, col: 5, msg: 'unmasked - highest priority', severity: 'error' }], - build: [ - { file: filePath, line: 10, col: 5, msg: 'masked by gopls', severity: 'error' }, - { file: filePath, line: 10, col: 15, msg: 'masked by gopls', severity: 'error' } - ], - vet: [{ file: filePath, line: 10, col: 25, msg: 'masked by gopls', severity: 'error' }], - lint: [{ file: filePath, line: 10, col: 35, msg: 'masked by gopls', severity: 'error' }] - }, - want: [{ line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Error }] - }, - { - name: 'Same line and column, lower priority has higher severity', - diags: { - gopls: [{ file: filePath, line: 10, col: 5, msg: 'unmasked - highest priority', severity: 'warning' }], - lint: [ - { file: filePath, line: 10, col: 5, msg: 'masked by gopls', severity: 'warning' }, - { - file: filePath, - line: 10, - col: 5, - msg: 'unmasked - higher severity than gopls warning', - severity: 'error' - } - ] - }, - want: [ - { line: 10, source: 'lint-test', severity: vscode.DiagnosticSeverity.Error }, - { line: 10, source: 'gopls-test', severity: vscode.DiagnosticSeverity.Warning } - ] - } - ]; - - setup(() => { - goCtx = { - languageClient: { diagnostics: vscode.languages.createDiagnosticCollection('gopls-test') } as any, - buildDiagnosticCollection: vscode.languages.createDiagnosticCollection('build-test'), - vetDiagnosticCollection: vscode.languages.createDiagnosticCollection('vet-test'), - lintDiagnosticCollection: vscode.languages.createDiagnosticCollection('lint-test') - }; - }); - - teardown(() => { - goCtx.languageClient?.diagnostics?.dispose(); - goCtx.buildDiagnosticCollection?.dispose(); - goCtx.vetDiagnosticCollection?.dispose(); - goCtx.lintDiagnosticCollection?.dispose(); - }); - - for (const tc of testCases) { - for (let round = 1; round <= 5; round++) { - test(`${tc.name} (round ${round})`, async () => { - const collections = [ - { key: 'gopls' as const, collection: goCtx.languageClient!.diagnostics! }, - { key: 'build' as const, collection: goCtx.buildDiagnosticCollection! }, - { key: 'vet' as const, collection: goCtx.vetDiagnosticCollection! }, - { key: 'lint' as const, collection: goCtx.lintDiagnosticCollection! } - ]; - - // Simulate 4 concurrent diagnostic providers reporting diags - // independently with random delays to verify eventual consistency. - const tasks = collections.map(({ key, collection }) => { - const errors = tc.diags[key] || []; - return new Promise<void>((resolve) => { - const delay = Math.floor(Math.random() * 15); - setTimeout(() => { - handleErrors(goCtx, undefined, errors, collection); - resolve(); - }, delay); - }); - }); - - // Wait for all concurrent diagnostic providers to finish reporting. - await Promise.all(tasks); - - // Read diagnostics directly from the "PROBLEMS" tab. - const problems = vscode.languages.getDiagnostics(fileURI); - - const sorted = [...problems].sort((a, b) => { - if (a.range.start.line !== b.range.start.line) { - return a.range.start.line - b.range.start.line; - } - if (a.range.start.character !== b.range.start.character) { - return a.range.start.character - b.range.start.character; - } - if (a.severity !== b.severity) { - return a.severity - b.severity; - } - return a.source!.localeCompare(b.source!); - }); - - assert.strictEqual( - sorted.length, - tc.want.length, - `[${tc.name}] Expected ${tc.want.length} diagnostics in problem tab, got ${sorted.length}: ${JSON.stringify(sorted.map((p) => ({ source: p.source, msg: p.message })))}` - ); - - for (let i = 0; i < tc.want.length; i++) { - const want = tc.want[i]; - const got = sorted[i]; - assert.strictEqual(got.range.start.line, want.line - 1, `[${tc.name}] Line mismatch at index ${i}`); - assert.strictEqual(got.source, want.source, `[${tc.name}] Source mismatch at index ${i}`); - assert.strictEqual(got.severity, want.severity, `[${tc.name}] Severity mismatch at index ${i}`); - } - }); - } - } -});
diff --git a/extension/test/testdata/diagnosticsTest/coexist.go b/extension/test/testdata/diagnosticsTest/coexist.go new file mode 100644 index 0000000..7eb93c5 --- /dev/null +++ b/extension/test/testdata/diagnosticsTest/coexist.go
@@ -0,0 +1,8 @@ +package main + +import ( + "fmt" + "os" +) + +func Save(v interface{}, path string) { os.WriteFile(path, fmt.Append(nil, v), 0o600) }
diff --git a/extension/test/testdata/diagnosticsTest/go.mod b/extension/test/testdata/diagnosticsTest/go.mod new file mode 100644 index 0000000..6dbf814 --- /dev/null +++ b/extension/test/testdata/diagnosticsTest/go.mod
@@ -0,0 +1,3 @@ +module example.com/diagnosticstest + +go 1.20
diff --git a/extension/test/testdata/diagnosticsTest/masked.go b/extension/test/testdata/diagnosticsTest/masked.go new file mode 100644 index 0000000..70a6873 --- /dev/null +++ b/extension/test/testdata/diagnosticsTest/masked.go
@@ -0,0 +1,7 @@ +package main + +func Yoda(a int) { + if 42 == a { + _ = a + } +}
diff --git a/extension/tools/allTools.ts.in b/extension/tools/allTools.ts.in index 4e5f99f..19eabef 100644 --- a/extension/tools/allTools.ts.in +++ b/extension/tools/allTools.ts.in
@@ -107,7 +107,8 @@ replacedByGopls: false, isImportant: true, description: 'Linter', - minimumGoVersion: semver.coerce('1.23') + minimumGoVersion: semver.coerce('1.23'), + defaultVersion: 'v2.12.2' } ], [
diff --git a/extension/tools/installtools/main.go b/extension/tools/installtools/main.go index 8696345..81fe64d 100644 --- a/extension/tools/installtools/main.go +++ b/extension/tools/installtools/main.go
@@ -24,41 +24,20 @@ "strings" ) -// finalVersion encodes the fact that the specified tool version -// is the known last version that can be buildable with goMinorVersion. -type finalVersion struct { - goMinorVersion int - version string -} - var tools = []struct { path string dest string preferPreview bool - // versions is a list of supportedVersions sorted by - // goMinorVersion. If we want to pin a tool's version - // add a fake entry with a large goMinorVersion - // value and the pinned tool version as the last entry. - // Nil of empty list indicates we can use the `latest` version. - versions []finalVersion + version string // pinned version, or empty string to use "latest" (or preview if preferPreview) }{ // TODO: auto-generate based on allTools.ts.in. - {"golang.org/x/tools/gopls", "", true, nil}, - {"github.com/cweill/gotests/gotests", "", false, nil}, - {"github.com/haya14busa/goplay/cmd/goplay", "", false, nil}, - {"honnef.co/go/tools/cmd/staticcheck", "", false, []finalVersion{{21, "v0.4.7"}}}, - {"github.com/go-delve/delve/cmd/dlv", "", false, nil}, -} - -// pickVersion returns the version to install based on the supported -// version list. -func pickVersion(goMinorVersion int, versions []finalVersion, defaultVersion string) string { - for _, v := range versions { - if goMinorVersion <= v.goMinorVersion { - return v.version - } - } - return defaultVersion + {"golang.org/x/tools/gopls", "", true, ""}, + {"github.com/cweill/gotests/gotests", "", false, ""}, + {"github.com/haya14busa/goplay/cmd/goplay", "", false, ""}, + {"honnef.co/go/tools/cmd/staticcheck", "", false, ""}, + // For regression test: golang/vscode-go#3511 + {"github.com/golangci/golangci-lint/v2/cmd/golangci-lint", "golangci-lint-v2", false, "v2.12.2"}, + {"github.com/go-delve/delve/cmd/dlv", "", false, ""}, } func main() { @@ -66,16 +45,13 @@ if err != nil { exitf("failed to find go version: %v", err) } - if ver < 21 { - exitf("unsupported go version: 1.%v", ver) - } fmt.Printf("installing tools for go1.%d...\n", ver) bin, err := goBin() if err != nil { exitf("failed to determine go tool installation directory: %v", err) } - err = installTools(bin, ver) + err = installTools(bin) if err != nil { exitf("failed to install tools: %v", err) } @@ -129,13 +105,16 @@ return filepath.Join(gopaths[0], "bin"), nil } -func installTools(binDir string, goMinorVersion int) error { +func installTools(binDir string) error { installCmd := "install" // For tools installation, ensure GOTOOLCHAIN=auto. env := append(os.Environ(), "GO111MODULE=on", "GOTOOLCHAIN=auto") for _, tool := range tools { - ver := pickVersion(goMinorVersion, tool.versions, pickLatest(tool.path, tool.preferPreview)) + ver := tool.version + if ver == "" { + ver = pickLatest(tool.path, tool.preferPreview) + } path := tool.path + "@" + ver cmd := exec.Command("go", installCmd, path) cmd.Env = env
diff --git a/extension/tools/installtools/main_test.go b/extension/tools/installtools/main_test.go deleted file mode 100644 index d8b3d8d..0000000 --- a/extension/tools/installtools/main_test.go +++ /dev/null
@@ -1,46 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Binary installtools is a helper that installs Go tools extension tests depend on. -package main - -import "testing" - -func Test_pickVersion(t *testing.T) { - tests := []struct { - name string - versions []finalVersion - want map[int]string - }{ - { - name: "nil", - versions: nil, - want: map[int]string{15: "latest", 16: "latest", 17: "latest", 18: "latest"}, - }, - { - name: "one_entry", - versions: []finalVersion{ - {16, "v0.2.2"}, - }, - want: map[int]string{15: "v0.2.2", 16: "v0.2.2", 17: "latest", 18: "latest"}, - }, - { - name: "two_entries", - versions: []finalVersion{ - {16, "v0.2.2"}, - {17, "v0.3.0"}, - }, - want: map[int]string{15: "v0.2.2", 16: "v0.2.2", 17: "v0.3.0", 18: "latest"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - for goMinorVersion, want := range tt.want { - if got := pickVersion(goMinorVersion, tt.versions, "latest"); got != want { - t.Errorf("pickVersion(go 1.%v) = %v, want %v", goMinorVersion, got, want) - } - } - }) - } -}