diff --git a/.golangci.example.yml b/.golangci.example.yml index 86b4b57c24c1..4f4c1e52042c 100644 --- a/.golangci.example.yml +++ b/.golangci.example.yml @@ -66,8 +66,8 @@ run: # Define the Go version limit. # Mainly related to generics support in go1.18. - # Default: 1.17 - go: 1.18 + # Default: use Go version from the go.mod file, fallback on the env var `GOVERSION`, fallback on 1.17 + go: '1.18' # output configuration options diff --git a/.golangci.yml b/.golangci.yml index a17d2b119bad..4427980c5835 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -130,6 +130,7 @@ issues: run: timeout: 5m + go: '1.17' # TODO(ldez): we force to use an old version of Go for the CI and the tests. skip-dirs: - test/testdata_etc - internal/cache diff --git a/pkg/commands/executor.go b/pkg/commands/executor.go index 0ad37dde6ffc..622b5372bff8 100644 --- a/pkg/commands/executor.go +++ b/pkg/commands/executor.go @@ -110,6 +110,10 @@ func NewExecutor(version, commit, date string) *Executor { e.log.Fatalf("Can't read config: %s", err) } + if commandLineCfg.Run.Go == "" && e.cfg.Run.Go == "" { + e.cfg.Run.Go = config.DetectGoVersion() + } + // recreate after getting config e.DBManager = lintersdb.NewManager(e.cfg, e.log).WithCustomLinters() diff --git a/pkg/commands/run.go b/pkg/commands/run.go index 61f86ee52368..f4716d184819 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -95,7 +95,7 @@ func initFlagSet(fs *pflag.FlagSet, cfg *config.Config, m *lintersdb.Manager, is "Modules download mode. If not empty, passed as -mod= to go tools") fs.IntVar(&rc.ExitCodeIfIssuesFound, "issues-exit-code", exitcodes.IssuesFound, wh("Exit code when issues were found")) - fs.StringVar(&rc.Go, "go", "1.17", wh("Targeted Go version")) + fs.StringVar(&rc.Go, "go", "", wh("Targeted Go version")) fs.StringSliceVar(&rc.BuildTags, "build-tags", nil, wh("Build tags")) fs.DurationVar(&rc.Timeout, "deadline", defaultTimeout, wh("Deadline for total work")) diff --git a/pkg/config/config.go b/pkg/config/config.go index 49df4e495228..9536c80cd0d5 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,5 +1,13 @@ package config +import ( + "os" + "strings" + + hcversion "github.com/hashicorp/go-version" + "github.com/ldez/gomoddirectives" +) + // Config encapsulates the config data specified in the golangci yaml config file. type Config struct { cfgDir string // The directory containing the golangci config file. @@ -31,3 +39,32 @@ func NewDefault() *Config { type Version struct { Format string `mapstructure:"format"` } + +func IsGreaterThanOrEqualGo118(v string) bool { + v1, err := hcversion.NewVersion(strings.TrimPrefix(v, "go")) + if err != nil { + return false + } + + limit, err := hcversion.NewVersion("1.18") + if err != nil { + return false + } + + return v1.GreaterThanOrEqual(limit) +} + +func DetectGoVersion() string { + file, _ := gomoddirectives.GetModuleFile() + + if file != nil && file.Go != nil && file.Go.Version != "" { + return file.Go.Version + } + + v := os.Getenv("GOVERSION") + if v != "" { + return v + } + + return "1.17" +} diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 2ae8550e71b4..3f984ce3724e 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -374,7 +374,8 @@ type GoSecSettings struct { } type GovetSettings struct { - CheckShadowing bool `mapstructure:"check-shadowing"` + Go string `mapstructure:"-"` + CheckShadowing bool `mapstructure:"check-shadowing"` Settings map[string]map[string]interface{} Enable []string @@ -383,7 +384,7 @@ type GovetSettings struct { DisableAll bool `mapstructure:"disable-all"` } -func (cfg GovetSettings) Validate() error { +func (cfg *GovetSettings) Validate() error { if cfg.EnableAll && cfg.DisableAll { return errors.New("enable-all and disable-all can't be combined") } diff --git a/pkg/golinters/govet.go b/pkg/golinters/govet.go index b3860e01705c..c7a58af610f8 100644 --- a/pkg/golinters/govet.go +++ b/pkg/golinters/govet.go @@ -119,7 +119,46 @@ var ( } ) +func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter { + var settings map[string]map[string]interface{} + if cfg != nil { + settings = cfg.Settings + } + return goanalysis.NewLinter( + "govet", + "Vet examines Go source code and reports suspicious constructs, "+ + "such as Printf calls whose arguments do not align with the format string", + analyzersFromConfig(cfg), + settings, + ).WithLoadMode(goanalysis.LoadModeTypesInfo) +} + +func analyzersFromConfig(cfg *config.GovetSettings) []*analysis.Analyzer { + if cfg == nil { + return defaultAnalyzers + } + + if cfg.CheckShadowing { + // Keeping for backward compatibility. + cfg.Enable = append(cfg.Enable, shadow.Analyzer.Name) + } + + var enabledAnalyzers []*analysis.Analyzer + for _, a := range allAnalyzers { + if isAnalyzerEnabled(a.Name, cfg, defaultAnalyzers) { + enabledAnalyzers = append(enabledAnalyzers, a) + } + } + + return enabledAnalyzers +} + func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers []*analysis.Analyzer) bool { + if (name == nilness.Analyzer.Name || name == unusedwrite.Analyzer.Name) && + config.IsGreaterThanOrEqualGo118(cfg.Go) { + return false + } + if cfg.EnableAll { for _, n := range cfg.Disable { if n == name { @@ -128,20 +167,24 @@ func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers } return true } + // Raw for loops should be OK on small slice lengths. for _, n := range cfg.Enable { if n == name { return true } } + for _, n := range cfg.Disable { if n == name { return false } } + if cfg.DisableAll { return false } + for _, a := range defaultAnalyzers { if a.Name == name { return true @@ -149,37 +192,3 @@ func isAnalyzerEnabled(name string, cfg *config.GovetSettings, defaultAnalyzers } return false } - -func analyzersFromConfig(cfg *config.GovetSettings) []*analysis.Analyzer { - if cfg == nil { - return defaultAnalyzers - } - - if cfg.CheckShadowing { - // Keeping for backward compatibility. - cfg.Enable = append(cfg.Enable, shadow.Analyzer.Name) - } - - var enabledAnalyzers []*analysis.Analyzer - for _, a := range allAnalyzers { - if isAnalyzerEnabled(a.Name, cfg, defaultAnalyzers) { - enabledAnalyzers = append(enabledAnalyzers, a) - } - } - - return enabledAnalyzers -} - -func NewGovet(cfg *config.GovetSettings) *goanalysis.Linter { - var settings map[string]map[string]interface{} - if cfg != nil { - settings = cfg.Settings - } - return goanalysis.NewLinter( - "govet", - "Vet examines Go source code and reports suspicious constructs, "+ - "such as Printf calls whose arguments do not align with the format string", - analyzersFromConfig(cfg), - settings, - ).WithLoadMode(goanalysis.LoadModeTypesInfo) -} diff --git a/pkg/lint/linter/config.go b/pkg/lint/linter/config.go index e86a5dc44b9b..c8e065542e2a 100644 --- a/pkg/lint/linter/config.go +++ b/pkg/lint/linter/config.go @@ -1,9 +1,6 @@ package linter import ( - "strings" - - hcversion "github.com/hashicorp/go-version" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/packages" @@ -126,7 +123,7 @@ func (lc *Config) Name() string { } func (lc *Config) WithNoopFallback(cfg *config.Config) *Config { - if isGreaterThanOrEqualGo118(cfg) { + if cfg != nil && config.IsGreaterThanOrEqualGo118(cfg.Run.Go) { lc.Linter = &Noop{ name: lc.Linter.Name(), desc: lc.Linter.Desc(), @@ -134,6 +131,9 @@ func (lc *Config) WithNoopFallback(cfg *config.Config) *Config { return nil, nil }, } + + lc.LoadMode = 0 + return lc.WithLoadFiles() } return lc @@ -145,21 +145,3 @@ func NewConfig(linter Linter) *Config { } return lc.WithLoadFiles() } - -func isGreaterThanOrEqualGo118(cfg *config.Config) bool { - if cfg == nil { - return false - } - - v1, err := hcversion.NewVersion(strings.TrimPrefix(cfg.Run.Go, "go")) - if err != nil { - return false - } - - limit, err := hcversion.NewVersion("1.18") - if err != nil { - return false - } - - return v1.GreaterThanOrEqual(limit) -} diff --git a/pkg/lint/linter/linter.go b/pkg/lint/linter/linter.go index 04f0f6aeb81b..01e2196911e0 100644 --- a/pkg/lint/linter/linter.go +++ b/pkg/lint/linter/linter.go @@ -22,7 +22,6 @@ type Noop struct { func (n Noop) Run(_ context.Context, lintCtx *Context) ([]result.Issue, error) { lintCtx.Log.Warnf("%s is disabled because of go1.18."+ - " If you are not using go1.18, you can set `go: go1.17` in the `run` section."+ " You can track the evolution of the go1.18 support by following the https://github.com/golangci/golangci-lint/issues/2649.", n.name) return nil, nil } diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 8cef0a6d3359..e82a3ae161eb 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -164,6 +164,10 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { unusedCfg = &m.cfg.LintersSettings.Unused varnamelenCfg = &m.cfg.LintersSettings.Varnamelen wrapcheckCfg = &m.cfg.LintersSettings.Wrapcheck + + if govetCfg != nil { + govetCfg.Go = m.cfg.Run.Go + } } const megacheckName = "megacheck" @@ -446,7 +450,8 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithLoadForGoAnalysis(). WithPresets(linter.PresetStyle). WithURL("https://github.com/mvdan/interfacer"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", ""), + Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", ""). + WithNoopFallback(m.cfg), linter.NewConfig(golinters.NewIreturn(ireturnCfg)). WithSince("v1.43.0"). diff --git a/test/fix_test.go b/test/fix_test.go index 3d1144d5e6d7..ce61d85f2d59 100644 --- a/test/fix_test.go +++ b/test/fix_test.go @@ -43,6 +43,7 @@ func TestFix(t *testing.T) { t.Parallel() args := []string{ + "--go=1.17", // TODO(ldez): we force to use an old version of Go for the CI and the tests. "--disable-all", "--print-issued-lines=false", "--print-linter-name=false", "--out-format=line-number", "--allow-parallel-runners", "--fix", input, diff --git a/test/linters_test.go b/test/linters_test.go index a80baa6a511c..19c315ffef57 100644 --- a/test/linters_test.go +++ b/test/linters_test.go @@ -179,6 +179,7 @@ func saveConfig(t *testing.T, cfg map[string]interface{}) (cfgPath string, finis func testOneSource(t *testing.T, sourcePath string) { args := []string{ "run", + "--go=1.17", // TODO(ldez): we force to use an old version of Go for the CI and the tests. "--allow-parallel-runners", "--disable-all", "--print-issued-lines=false", diff --git a/test/testshared/testshared.go b/test/testshared/testshared.go index fa70c23131ae..9291532be429 100644 --- a/test/testshared/testshared.go +++ b/test/testshared/testshared.go @@ -98,7 +98,10 @@ func (r *LintRunner) Run(args ...string) *RunResult { func (r *LintRunner) RunCommand(command string, args ...string) *RunResult { r.Install() - runArgs := append([]string{command}, "--internal-cmd-test") + runArgs := append([]string{command}, + "--go=1.17", // TODO(ldez): we force to use an old version of Go for the CI and the tests. + "--internal-cmd-test", + ) runArgs = append(runArgs, args...) defer func(startedAt time.Time) {