Skip to content

Commit 7bbbe77

Browse files
authored
feat: automatic Go version detection (#2669)
* feat: disable unsupported go1.18 govet analyzers * fix: inactivate interfacer with go1.18
1 parent da0a6b3 commit 7bbbe77

File tree

13 files changed

+107
-64
lines changed

13 files changed

+107
-64
lines changed

.golangci.example.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ run:
6666

6767
# Define the Go version limit.
6868
# Mainly related to generics support in go1.18.
69-
# Default: 1.17
70-
go: 1.18
69+
# Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.17
70+
go: '1.18'
7171

7272

7373
# output configuration options

.golangci.yml

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ issues:
130130

131131
run:
132132
timeout: 5m
133+
go: '1.17' # TODO(ldez): we force to use an old version of Go for the CI and the tests.
133134
skip-dirs:
134135
- test/testdata_etc
135136
- internal/cache

pkg/commands/executor.go

+4
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ func NewExecutor(version, commit, date string) *Executor {
110110
e.log.Fatalf("Can't read config: %s", err)
111111
}
112112

113+
if commandLineCfg.Run.Go == "" && e.cfg.Run.Go == "" {
114+
e.cfg.Run.Go = config.DetectGoVersion()
115+
}
116+
113117
// recreate after getting config
114118
e.DBManager = lintersdb.NewManager(e.cfg, e.log).WithCustomLinters()
115119

pkg/commands/run.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, is
9595
"Modules download mode. If not empty, passed as -mod=<mode> to go tools")
9696
fs.IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code",
9797
exitcodes.IssuesFound, wh("Exit code when issues were found"))
98-
fs.StringVar(&rc.Go, "go", "1.17", wh("Targeted Go version"))
98+
fs.StringVar(&rc.Go, "go", "", wh("Targeted Go version"))
9999
fs.StringSliceVar(&rc.BuildTags, "build-tags", nil, wh("Build tags"))
100100

101101
fs.DurationVar(&rc.Timeout, "deadline", defaultTimeout, wh("Deadline for total work"))

pkg/config/config.go

+37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
package config
22

3+
import (
4+
"os"
5+
"strings"
6+
7+
hcversion "github.com/hashicorp/go-version"
8+
"github.com/ldez/gomoddirectives"
9+
)
10+
311
// Config encapsulates the config data specified in the golangci yaml config file.
412
type Config struct {
513
cfgDir string // The directory containing the golangci config file.
@@ -31,3 +39,32 @@ func NewDefault() *Config {
3139
type Version struct {
3240
Format string `mapstructure:"format"`
3341
}
42+
43+
func IsGreaterThanOrEqualGo118(v string) bool {
44+
v1, err := hcversion.NewVersion(strings.TrimPrefix(v, "go"))
45+
if err != nil {
46+
return false
47+
}
48+
49+
limit, err := hcversion.NewVersion("1.18")
50+
if err != nil {
51+
return false
52+
}
53+
54+
return v1.GreaterThanOrEqual(limit)
55+
}
56+
57+
func DetectGoVersion() string {
58+
file, _ := gomoddirectives.GetModuleFile()
59+
60+
if file != nil && file.Go != nil && file.Go.Version != "" {
61+
return file.Go.Version
62+
}
63+
64+
v := os.Getenv("GOVERSION")
65+
if v != "" {
66+
return v
67+
}
68+
69+
return "1.17"
70+
}

pkg/config/linters_settings.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,8 @@ type GoSecSettings struct {
374374
}
375375

376376
type GovetSettings struct {
377-
CheckShadowing bool `mapstructure:"check-shadowing"`
377+
Go string `mapstructure:"-"`
378+
CheckShadowing bool `mapstructure:"check-shadowing"`
378379
Settings map[string]map[string]interface{}
379380

380381
Enable []string
@@ -383,7 +384,7 @@ type GovetSettings struct {
383384
DisableAll bool `mapstructure:"disable-all"`
384385
}
385386

386-
func (cfg GovetSettings) Validate() error {
387+
func (cfg *GovetSettings) Validate() error {
387388
if cfg.EnableAll && cfg.DisableAll {
388389
return errors.New("enable-all and disable-all can't be combined")
389390
}

pkg/golinters/govet.go

+43-34
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,46 @@ var (
119119
}
120120
)
121121

122+
func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter {
123+
var settings map[string]map[string]interface{}
124+
if cfg != nil {
125+
settings = cfg.Settings
126+
}
127+
return goanalysis.NewLinter(
128+
"govet",
129+
"Vet examines Go source code and reports suspicious constructs, "+
130+
"such as Printf calls whose arguments do not align with the format string",
131+
analyzersFromConfig(cfg),
132+
settings,
133+
).WithLoadMode(goanalysis.LoadModeTypesInfo)
134+
}
135+
136+
func analyzersFromConfig(cfg *config.GovetSettings) []*analysis.Analyzer {
137+
if cfg == nil {
138+
return defaultAnalyzers
139+
}
140+
141+
if cfg.CheckShadowing {
142+
// Keeping for backward compatibility.
143+
cfg.Enable = append(cfg.Enable, shadow.Analyzer.Name)
144+
}
145+
146+
var enabledAnalyzers []*analysis.Analyzer
147+
for _, a := range allAnalyzers {
148+
if isAnalyzerEnabled(a.Name, cfg, defaultAnalyzers) {
149+
enabledAnalyzers = append(enabledAnalyzers, a)
150+
}
151+
}
152+
153+
return enabledAnalyzers
154+
}
155+
122156
func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers []*analysis.Analyzer) bool {
157+
if (name == nilness.Analyzer.Name || name == unusedwrite.Analyzer.Name) &&
158+
config.IsGreaterThanOrEqualGo118(cfg.Go) {
159+
return false
160+
}
161+
123162
if cfg.EnableAll {
124163
for _, n := range cfg.Disable {
125164
if n == name {
@@ -128,58 +167,28 @@ func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers
128167
}
129168
return true
130169
}
170+
131171
// Raw for loops should be OK on small slice lengths.
132172
for _, n := range cfg.Enable {
133173
if n == name {
134174
return true
135175
}
136176
}
177+
137178
for _, n := range cfg.Disable {
138179
if n == name {
139180
return false
140181
}
141182
}
183+
142184
if cfg.DisableAll {
143185
return false
144186
}
187+
145188
for _, a := range defaultAnalyzers {
146189
if a.Name == name {
147190
return true
148191
}
149192
}
150193
return false
151194
}
152-
153-
func analyzersFromConfig(cfg *config.GovetSettings) []*analysis.Analyzer {
154-
if cfg == nil {
155-
return defaultAnalyzers
156-
}
157-
158-
if cfg.CheckShadowing {
159-
// Keeping for backward compatibility.
160-
cfg.Enable = append(cfg.Enable, shadow.Analyzer.Name)
161-
}
162-
163-
var enabledAnalyzers []*analysis.Analyzer
164-
for _, a := range allAnalyzers {
165-
if isAnalyzerEnabled(a.Name, cfg, defaultAnalyzers) {
166-
enabledAnalyzers = append(enabledAnalyzers, a)
167-
}
168-
}
169-
170-
return enabledAnalyzers
171-
}
172-
173-
func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter {
174-
var settings map[string]map[string]interface{}
175-
if cfg != nil {
176-
settings = cfg.Settings
177-
}
178-
return goanalysis.NewLinter(
179-
"govet",
180-
"Vet examines Go source code and reports suspicious constructs, "+
181-
"such as Printf calls whose arguments do not align with the format string",
182-
analyzersFromConfig(cfg),
183-
settings,
184-
).WithLoadMode(goanalysis.LoadModeTypesInfo)
185-
}

pkg/lint/linter/config.go

+4-22
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package linter
22

33
import (
4-
"strings"
5-
6-
hcversion "github.com/hashicorp/go-version"
74
"golang.org/x/tools/go/analysis"
85
"golang.org/x/tools/go/packages"
96

@@ -126,14 +123,17 @@ func (lc *Config) Name() string {
126123
}
127124

128125
func (lc *Config) WithNoopFallback(cfg *config.Config) *Config {
129-
if isGreaterThanOrEqualGo118(cfg) {
126+
if cfg != nil && config.IsGreaterThanOrEqualGo118(cfg.Run.Go) {
130127
lc.Linter = &Noop{
131128
name: lc.Linter.Name(),
132129
desc: lc.Linter.Desc(),
133130
run: func(pass *analysis.Pass) (interface{}, error) {
134131
return nil, nil
135132
},
136133
}
134+
135+
lc.LoadMode = 0
136+
return lc.WithLoadFiles()
137137
}
138138

139139
return lc
@@ -145,21 +145,3 @@ func NewConfig(linter Linter) *Config {
145145
}
146146
return lc.WithLoadFiles()
147147
}
148-
149-
func isGreaterThanOrEqualGo118(cfg *config.Config) bool {
150-
if cfg == nil {
151-
return false
152-
}
153-
154-
v1, err := hcversion.NewVersion(strings.TrimPrefix(cfg.Run.Go, "go"))
155-
if err != nil {
156-
return false
157-
}
158-
159-
limit, err := hcversion.NewVersion("1.18")
160-
if err != nil {
161-
return false
162-
}
163-
164-
return v1.GreaterThanOrEqual(limit)
165-
}

pkg/lint/linter/linter.go

-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ type Noop struct {
2222

2323
func (n Noop) Run(_ context.Context, lintCtx *Context) ([]result.Issue, error) {
2424
lintCtx.Log.Warnf("%s is disabled because of go1.18."+
25-
" If you are not using go1.18, you can set `go: go1.17` in the `run` section."+
2625
" You can track the evolution of the go1.18 support by following the https://github.com/golangci/golangci-lint/issues/2649.", n.name)
2726
return nil, nil
2827
}

pkg/lint/lintersdb/manager.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
164164
unusedCfg = &m.cfg.LintersSettings.Unused
165165
varnamelenCfg = &m.cfg.LintersSettings.Varnamelen
166166
wrapcheckCfg = &m.cfg.LintersSettings.Wrapcheck
167+
168+
if govetCfg != nil {
169+
govetCfg.Go = m.cfg.Run.Go
170+
}
167171
}
168172

169173
const megacheckName = "megacheck"
@@ -446,7 +450,8 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
446450
WithLoadForGoAnalysis().
447451
WithPresets(linter.PresetStyle).
448452
WithURL("https://github.com/mvdan/interfacer").
449-
Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", ""),
453+
Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", "").
454+
WithNoopFallback(m.cfg),
450455

451456
linter.NewConfig(golinters.NewIreturn(ireturnCfg)).
452457
WithSince("v1.43.0").

test/fix_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ func TestFix(t *testing.T) {
4343
t.Parallel()
4444

4545
args := []string{
46+
"--go=1.17", // TODO(ldez): we force to use an old version of Go for the CI and the tests.
4647
"--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number",
4748
"--allow-parallel-runners", "--fix",
4849
input,

test/linters_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finis
179179
func testOneSource(t *testing.T, sourcePath string) {
180180
args := []string{
181181
"run",
182+
"--go=1.17", // TODO(ldez): we force to use an old version of Go for the CI and the tests.
182183
"--allow-parallel-runners",
183184
"--disable-all",
184185
"--print-issued-lines=false",

test/testshared/testshared.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ func (r *LintRunner) Run(args ...string) *RunResult {
9898
func (r *LintRunner) RunCommand(command string, args ...string) *RunResult {
9999
r.Install()
100100

101-
runArgs := append([]string{command}, "--internal-cmd-test")
101+
runArgs := append([]string{command},
102+
"--go=1.17", // TODO(ldez): we force to use an old version of Go for the CI and the tests.
103+
"--internal-cmd-test",
104+
)
102105
runArgs = append(runArgs, args...)
103106

104107
defer func(startedAt time.Time) {

0 commit comments

Comments
 (0)