Skip to content

Commit 5e6ba19

Browse files
logica0419h9jiang
authored andcommitted
extension/src/goLint: adding golangci-lint-v2 to lintTool
This change adds support for golangci-lint v2 to the extension. golangci-lint deleted some flags this extension used during the update, so v1 and v2 must be handled differently. Fixes #3732 Change-Id: I4bb1ec4e6d806227254d7f5de9bbdc00cde1afa6 Reviewed-on: https://go-review.googlesource.com/c/vscode-go/+/660415 Reviewed-by: Dmitri Shuralyov <[email protected]> kokoro-CI: kokoro <[email protected]> Reviewed-by: Hongxiang Jiang <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent f122b06 commit 5e6ba19

File tree

8 files changed

+104
-15
lines changed

8 files changed

+104
-15
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
99

1010
* Introduced quick pick separator in command `Go: Choose Go Environment` showing
1111
diff between options locally discovered and options available for download.
12+
* Added support for golangci-lint v2 ([Issue 3732](https://github.com/golang/vscode-go/issues/3732))
13+
* Added a new lint tool, `golangci-lint-v2`. It's added as an installable tool, so you can install it via the `Go: Install/Update Tools` command.
14+
* You can switch v1 and v2 per workspace by using `golangci-lint` and `golangci-lint-v2` option. You must keep the `golangci-lint` executable version on your machine to v1 for that.
15+
* You can also use `golangci-lint` executable updated to v2. Just keep using the `golangci-lint` option for that.
16+
* Looking for a way to format your code with golangci-lint v2 on VS Code? Check the [golangci-lint documentation](https://golangci-lint.run/welcome/integrations/#visual-studio-code).
1217

1318
### Fixes
1419

docs/settings.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ Default: `"package"`
362362
### `go.lintTool`
363363

364364
Specifies Lint tool name.<br/>
365-
Allowed Options: `staticcheck`, `golint`, `golangci-lint`, `revive`
365+
Allowed Options: `staticcheck`, `golint`, `golangci-lint`, `golangci-lint-v2`, `revive`
366366

367367
Default: `"staticcheck"`
368368
### `go.logging.level (deprecated)`

extension/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -1174,6 +1174,7 @@
11741174
"staticcheck",
11751175
"golint",
11761176
"golangci-lint",
1177+
"golangci-lint-v2",
11771178
"revive"
11781179
]
11791180
},

extension/src/goInstallTools.ts

+26
Original file line numberDiff line numberDiff line change
@@ -221,15 +221,21 @@ export async function installTools(
221221
}
222222
outputChannel.appendLine(envMsg);
223223

224+
let installingPath = '';
224225
let installingMsg = `Installing ${missing.length} ${missing.length > 1 ? 'tools' : 'tool'} at `;
225226
if (envForTools['GOBIN']) {
227+
installingPath = envForTools['GOBIN'];
226228
installingMsg += `the configured GOBIN: ${envForTools['GOBIN']}`;
227229
} else {
228230
const p = toolsGopath
229231
?.split(path.delimiter)
230232
.map((e) => path.join(e, 'bin'))
231233
.join(path.delimiter);
232234
installingMsg += `${p}`;
235+
236+
if (p) {
237+
installingPath = p;
238+
}
233239
}
234240

235241
outputChannel.appendLine(installingMsg);
@@ -246,13 +252,33 @@ export async function installTools(
246252
const failures: { tool: ToolAtVersion; reason: string }[] = [];
247253
const tm = options?.toolsManager ?? defaultToolsManager;
248254
for (const tool of missing) {
255+
// v2, v3... of the tools are installed with the same name as v1,
256+
// but must be able to co-exist with other major versions in the GOBIN.
257+
// Thus, we install it in a tmp directory and copy it to the GOBIN.
258+
// See detail: golang/vscode-go#3732
259+
if (tool.name.match('-v\\d+$')) {
260+
envForTools['GOBIN'] = path.join(installingPath, 'tmp');
261+
}
262+
249263
const failed = await tm.installTool(tool, goForInstall, envForTools);
250264
if (failed) {
251265
failures.push({ tool, reason: failed });
252266
} else if (tool.name === 'gopls' && !skipRestartGopls) {
253267
// Restart the language server if a new binary has been installed.
254268
vscode.commands.executeCommand('go.languageserver.restart', RestartReason.INSTALLATION);
255269
}
270+
271+
if (tool.name.match('-v\\d+$')) {
272+
// grep the tool name without version.
273+
const toolName = tool.name.match('^(?<tool>.+)-v\\d+$')?.groups?.tool;
274+
if (!toolName) {
275+
failures.push({ tool, reason: 'failed to grep tool name with regex' });
276+
continue;
277+
}
278+
279+
fs.copyFileSync(path.join(installingPath, 'tmp', toolName), path.join(installingPath, tool.name));
280+
fs.rmdirSync(path.join(installingPath, 'tmp'), { recursive: true });
281+
}
256282
}
257283

258284
// Report detailed information about any failures.

extension/src/goLint.ts

+50-13
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import { getGoConfig, getGoplsConfig } from './config';
1010
import { toolExecutionEnvironment } from './goEnv';
1111
import { diagnosticsStatusBarItem, outputChannel } from './goStatus';
1212
import { goplsStaticcheckEnabled } from './goTools';
13-
import { getWorkspaceFolderPath, handleDiagnosticErrors, ICheckResult, resolvePath, runTool } from './util';
13+
import { inspectGoToolVersion } from './goInstallTools';
14+
import { getBinPath, getWorkspaceFolderPath, handleDiagnosticErrors, ICheckResult, resolvePath, runTool } from './util';
1415

1516
/**
1617
* Runs linter on the current file, package or workspace.
@@ -63,7 +64,7 @@ export function lintCode(scope?: string): CommandFactory {
6364
* @param goConfig Configuration for the Go extension.
6465
* @param scope Scope in which to run the linter.
6566
*/
66-
export function goLint(
67+
export async function goLint(
6768
fileUri: vscode.Uri | undefined,
6869
goConfig: vscode.WorkspaceConfiguration,
6970
goplsConfig: vscode.WorkspaceConfiguration,
@@ -112,24 +113,60 @@ export function goLint(
112113
}
113114
args.push(flag);
114115
});
115-
if (lintTool === 'golangci-lint') {
116+
if (lintTool.startsWith('golangci-lint')) {
117+
let version: number;
118+
if (lintTool === 'golangci-lint-v2') {
119+
version = 2;
120+
} else {
121+
const { moduleVersion } = await inspectGoToolVersion(getBinPath(lintTool));
122+
// if moduleVersion is undefined, treat it as version=1
123+
// if moduleVersion is higher than v1 (v2, v3...), treat it as version=2
124+
!moduleVersion || moduleVersion.startsWith('v1') ? (version = 1) : (version = 2);
125+
}
126+
127+
// append common flags
116128
if (args.indexOf('run') === -1) {
117129
args.unshift('run');
118130
}
119-
if (args.indexOf('--print-issued-lines=false') === -1) {
120-
// print only file:number:column
121-
args.push('--print-issued-lines=false');
122-
}
123-
if (args.indexOf('--out-format=colored-line-number') === -1) {
124-
// print file:number:column.
125-
// Explicit override in case .golangci.yml calls for a format we don't understand
126-
args.push('--out-format=colored-line-number');
127-
}
128-
if (args.indexOf('--issues-exit-code=') === -1) {
131+
if (args.indexOf('--issues-exit-code=0') === -1) {
129132
// adds an explicit no-error-code return argument, to avoid npm error
130133
// message detection logic. See golang/vscode-go/issues/411
131134
args.push('--issues-exit-code=0');
132135
}
136+
switch (version) {
137+
case 1: // append golangci-lint v1 flags
138+
if (args.indexOf('--print-issued-lines=false') === -1) {
139+
// print only file:number:column
140+
args.push('--print-issued-lines=false');
141+
}
142+
if (args.indexOf('--out-format=colored-line-number') === -1) {
143+
// print file:number:column.
144+
// Explicit override in case .golangci.yml calls for a format we don't understand
145+
args.push('--out-format=colored-line-number');
146+
}
147+
break;
148+
149+
case 2: // append golangci-lint v2 flags
150+
if (args.indexOf('--output.text.print-issued-lines=false') === -1) {
151+
// print only file:number:column
152+
args.push('--output.text.print-issued-lines=false');
153+
}
154+
if (args.indexOf('--show-stats=false') === -1) {
155+
// print only file:number:column
156+
args.push('--show-stats=false');
157+
}
158+
if (args.indexOf('--output.text.path=stdout') === -1) {
159+
// print file:number:column.
160+
// Explicit override in case .golangci.yml calls for a format we don't understand
161+
args.push('--output.text.path=stdout');
162+
}
163+
if (args.indexOf('--output.text.colors=true') === -1) {
164+
// print file:number:column.
165+
// Explicit override in case .golangci.yml calls for a format we don't understand
166+
args.push('--output.text.colors=true');
167+
}
168+
break;
169+
}
133170
}
134171

135172
if (scope === 'workspace' && currentWorkspace) {

extension/src/goTools.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ export function getConfiguredTools(goConfig: { [key: string]: any }, goplsConfig
156156
if (goConfig['lintTool'] !== 'staticcheck' || !goplsStaticheckEnabled) {
157157
maybeAddTool(goConfig['lintTool']);
158158
}
159-
return tools;
159+
160+
// Remove duplicates since tools like golangci-lint v2 are both linter and formatter
161+
return tools.filter((v, i, self) => self.indexOf(v) === i);
160162
}
161163

162164
export function goplsStaticcheckEnabled(

extension/src/goToolsInformation.ts

+9
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,15 @@ export const allToolsInformation: { [key: string]: Tool } = {
8585
description: 'Linter',
8686
minimumGoVersion: semver.coerce('1.20')
8787
},
88+
'golangci-lint-v2': {
89+
name: 'golangci-lint-v2',
90+
importPath: 'github.com/golangci/golangci-lint/v2/cmd/golangci-lint',
91+
modulePath: 'github.com/golangci/golangci-lint/v2',
92+
replacedByGopls: false,
93+
isImportant: true,
94+
description: 'Linter',
95+
minimumGoVersion: semver.coerce('1.23')
96+
},
8897
'revive': {
8998
name: 'revive',
9099
importPath: 'github.com/mgechev/revive',

extension/tools/allTools.ts.in

+9
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,15 @@ export const allToolsInformation: { [key: string]: Tool } = {
8383
description: 'Linter',
8484
minimumGoVersion: semver.coerce('1.20')
8585
},
86+
'golangci-lint-v2': {
87+
name: 'golangci-lint-v2',
88+
importPath: 'github.com/golangci/golangci-lint/v2/cmd/golangci-lint',
89+
modulePath: 'github.com/golangci/golangci-lint/v2',
90+
replacedByGopls: false,
91+
isImportant: true,
92+
description: 'Linter',
93+
minimumGoVersion: semver.coerce('1.23')
94+
},
8695
'revive': {
8796
name: 'revive',
8897
importPath: 'github.com/mgechev/revive',

0 commit comments

Comments
 (0)