diff --git a/docs/src/docs/contributing/architecture.mdx b/docs/src/docs/contributing/architecture.mdx index a449156673a7..ad4dc3fd2481 100644 --- a/docs/src/docs/contributing/architecture.mdx +++ b/docs/src/docs/contributing/architecture.mdx @@ -22,11 +22,18 @@ graph LR +## Init + +The configuration is loaded from file and flags by `config.Loader` inside `PersistentPreRun` (or `PreRun`) of the commands that require configuration. + +The linter database (`linterdb.Manager`) is fill based on the configuration: +- The linters ("internals" and plugins) are built by `linterdb.LinterBuilder` and `linterdb.PluginBuilder` builders. +- The configuration is validated by `linterdb.Validator`. + ## Load Packages Loading packages is listing all packages and their recursive dependencies for analysis. -Also, depending on the enabled linters set some parsing of the source code can be performed -at this step. +Also, depending on the enabled linters set some parsing of the source code can be performed at this step. Packages loading starts here: @@ -79,10 +86,10 @@ and outputs list of packages and requested information about them: filenames, ty First, we need to find all enabled linters. All linters are registered here: -```go title=pkg/lint/lintersdb/manager.go -func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { +```go title=pkg/lint/lintersdb/builder_linter.go +func (b LinterBuilder) Build(cfg *config.Config) []*linter.Config { // ... - linters = append(linters, + return []*linter.Config{ // ... linter.NewConfig(golinters.NewBodyclose()). WithSince("v1.18.0"). @@ -97,26 +104,25 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { WithPresets(linter.PresetBugs, linter.PresetMetaLinter). WithAlternativeNames("vet", "vetshadow"). WithURL("https://pkg.go.dev/cmd/vet"), + // ... } - // ... } ``` We filter requested in config and command-line linters in `EnabledSet`: -```go title=pkg/lint/lintersdb/enabled_set.go -func (es EnabledSet) GetEnabledLintersMap() (map[string]*linter.Config, error) +```go title=pkg/lint/lintersdb/manager.go +func (m *Manager) GetEnabledLintersMap() (map[string]*linter.Config, error) ``` We merge enabled linters into one `MetaLinter` to improve execution time if we can: -```go title=pkg/lint/lintersdb/enabled_set.go -// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters -// into a fewer number of linters. E.g. some go/analysis linters can be optimized into -// one metalinter for data reuse and speed up. -func (es EnabledSet) GetOptimizedLinters() ([]*linter.Config, error) { +```go titlepkg/lint/lintersdb/manager.go +// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters into a fewer number of linters. +// E.g. some go/analysis linters can be optimized into one metalinter for data reuse and speed up. +func (m *Manager) GetOptimizedLinters() ([]*linter.Config, error) { // ... - es.combineGoAnalysisLinters(resultLintersSet) + m.combineGoAnalysisLinters(resultLintersSet) // ... } ``` @@ -133,9 +139,11 @@ type MetaLinter struct { Currently, all linters except `unused` can be merged into this meta linter. The `unused` isn't merged because it has high memory usage. -Linters execution starts in `runAnalyzers`. It's the most complex part of the `golangci-lint`. -We use custom [go/analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis) runner there. It runs as much as it can in parallel. It lazy-loads as much as it can -to reduce memory usage. Also, it sets all heavyweight data to `nil` as becomes unneeded to save memory. +Linters execution starts in `runAnalyzers`. +It's the most complex part of the `golangci-lint`. +We use custom [go/analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis) runner there. +It runs as much as it can in parallel. It lazy-loads as much as it can to reduce memory usage. +Also, it sets all heavyweight data to `nil` as becomes unneeded to save memory. We don't use existing [multichecker](https://pkg.go.dev/golang.org/x/tools/go/analysis/multichecker) because it doesn't use caching and doesn't have some important performance optimizations. diff --git a/docs/src/docs/contributing/new-linters.mdx b/docs/src/docs/contributing/new-linters.mdx index ec82ec88d097..8e2551cb48c6 100644 --- a/docs/src/docs/contributing/new-linters.mdx +++ b/docs/src/docs/contributing/new-linters.mdx @@ -28,8 +28,8 @@ After that: Look at other linters in this directory. Implement linter integration and check that test passes. 3. Add the new struct for the linter (which you've implemented in `pkg/golinters/{yourlintername}.go`) to the - list of all supported linters in [`pkg/lint/lintersdb/manager.go`](https://github.com/golangci/golangci-lint/blob/master/pkg/lint/lintersdb/manager.go) - to the function `GetAllSupportedLinterConfigs`. + list of all supported linters in [`pkg/lint/lintersdb/builder_linter.go`](https://github.com/golangci/golangci-lint/blob/master/pkg/lint/lintersdb/builder_linter.go) + to the method `LinterBuilder.Build`. - Add `WithSince("next_version")`, where `next_version` must be replaced by the next minor version. (ex: v1.2.0 if the current version is v1.1.0) 4. Find out what options do you need to configure for the linter. For example, `nakedret` has only 1 option: [`max-func-lines`](https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml). diff --git a/pkg/commands/help.go b/pkg/commands/help.go index d6f0ce11ce83..655d1e675fe0 100644 --- a/pkg/commands/help.go +++ b/pkg/commands/help.go @@ -41,7 +41,7 @@ func newHelpCommand(logger logutils.Log) *helpCommand { Args: cobra.NoArgs, ValidArgsFunction: cobra.NoFileCompletions, Run: c.execute, - PreRun: c.preRun, + PreRunE: c.preRunE, }, ) @@ -50,10 +50,18 @@ func newHelpCommand(logger logutils.Log) *helpCommand { return c } -func (c *helpCommand) preRun(_ *cobra.Command, _ []string) { +func (c *helpCommand) preRunE(_ *cobra.Command, _ []string) error { // The command doesn't depend on the real configuration. // It just needs the list of all plugins and all presets. - c.dbManager = lintersdb.NewManager(config.NewDefault(), c.log) + dbManager, err := lintersdb.NewManager(c.log.Child(logutils.DebugKeyLintersDB), config.NewDefault(), + lintersdb.NewPluginBuilder(c.log), lintersdb.NewLinterBuilder()) + if err != nil { + return err + } + + c.dbManager = dbManager + + return nil } func (c *helpCommand) execute(_ *cobra.Command, _ []string) { diff --git a/pkg/commands/linters.go b/pkg/commands/linters.go index 02e9447d4ef3..30d1958c1ee6 100644 --- a/pkg/commands/linters.go +++ b/pkg/commands/linters.go @@ -27,8 +27,7 @@ type lintersCommand struct { log logutils.Log - dbManager *lintersdb.Manager - enabledLintersSet *lintersdb.EnabledSet + dbManager *lintersdb.Manager } func newLintersCommand(logger logutils.Log, cfg *config.Config) *lintersCommand { @@ -65,15 +64,19 @@ func (c *lintersCommand) preRunE(cmd *cobra.Command, _ []string) error { return fmt.Errorf("can't load config: %w", err) } - c.dbManager = lintersdb.NewManager(c.cfg, c.log) - c.enabledLintersSet = lintersdb.NewEnabledSet(c.dbManager, - lintersdb.NewValidator(c.dbManager), c.log.Child(logutils.DebugKeyLintersDB), c.cfg) + dbManager, err := lintersdb.NewManager(c.log.Child(logutils.DebugKeyLintersDB), c.cfg, + lintersdb.NewPluginBuilder(c.log), lintersdb.NewLinterBuilder()) + if err != nil { + return err + } + + c.dbManager = dbManager return nil } func (c *lintersCommand) execute(_ *cobra.Command, _ []string) error { - enabledLintersMap, err := c.enabledLintersSet.GetEnabledLintersMap() + enabledLintersMap, err := c.dbManager.GetEnabledLintersMap() if err != nil { return fmt.Errorf("can't get enabled linters: %w", err) } diff --git a/pkg/commands/run.go b/pkg/commands/run.go index a4ba89058714..e47af1ab6393 100644 --- a/pkg/commands/run.go +++ b/pkg/commands/run.go @@ -80,8 +80,7 @@ type runCommand struct { buildInfo BuildInfo - dbManager *lintersdb.Manager - enabledLintersSet *lintersdb.EnabledSet + dbManager *lintersdb.Manager log logutils.Log debugf logutils.DebugFunc @@ -171,9 +170,13 @@ func (c *runCommand) persistentPostRunE(_ *cobra.Command, _ []string) error { } func (c *runCommand) preRunE(_ *cobra.Command, _ []string) error { - c.dbManager = lintersdb.NewManager(c.cfg, c.log) - c.enabledLintersSet = lintersdb.NewEnabledSet(c.dbManager, - lintersdb.NewValidator(c.dbManager), c.log.Child(logutils.DebugKeyLintersDB), c.cfg) + dbManager, err := lintersdb.NewManager(c.log.Child(logutils.DebugKeyLintersDB), c.cfg, + lintersdb.NewPluginBuilder(c.log), lintersdb.NewLinterBuilder()) + if err != nil { + return err + } + + c.dbManager = dbManager c.goenv = goutil.NewEnv(c.log.Child(logutils.DebugKeyGoEnv)) @@ -340,12 +343,12 @@ func (c *runCommand) runAndPrint(ctx context.Context, args []string) error { func (c *runCommand) runAnalysis(ctx context.Context, args []string) ([]result.Issue, error) { c.cfg.Run.Args = args - lintersToRun, err := c.enabledLintersSet.GetOptimizedLinters() + lintersToRun, err := c.dbManager.GetOptimizedLinters() if err != nil { return nil, err } - enabledLintersMap, err := c.enabledLintersSet.GetEnabledLintersMap() + enabledLintersMap, err := c.dbManager.GetEnabledLintersMap() if err != nil { return nil, err } @@ -361,8 +364,8 @@ func (c *runCommand) runAnalysis(ctx context.Context, args []string) ([]result.I } lintCtx.Log = c.log.Child(logutils.DebugKeyLintersContext) - runner, err := lint.NewRunner(c.cfg, c.log.Child(logutils.DebugKeyRunner), - c.goenv, c.enabledLintersSet, c.lineCache, c.fileCache, c.dbManager, lintCtx.Packages) + runner, err := lint.NewRunner(c.log.Child(logutils.DebugKeyRunner), + c.cfg, c.goenv, c.lineCache, c.fileCache, c.dbManager, lintCtx.Packages) if err != nil { return nil, err } diff --git a/pkg/commands/version.go b/pkg/commands/version.go index 124ad7f1a339..a03e46e221c8 100644 --- a/pkg/commands/version.go +++ b/pkg/commands/version.go @@ -25,8 +25,8 @@ type versionInfo struct { } type versionOptions struct { - Format string `mapstructure:"format"` - Debug bool `mapstructure:"debug"` + Format string + Debug bool } type versionCommand struct { diff --git a/pkg/config/config.go b/pkg/config/config.go index 9a4cc97f03f8..3bf61e89b0d3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,9 +1,8 @@ package config import ( - "errors" - "fmt" "os" + "regexp" "strings" hcversion "github.com/hashicorp/go-version" @@ -33,18 +32,16 @@ func (c *Config) GetConfigDir() string { } func (c *Config) Validate() error { - for i, rule := range c.Issues.ExcludeRules { - if err := rule.Validate(); err != nil { - return fmt.Errorf("error in exclude rule #%d: %w", i, err) - } + validators := []func() error{ + c.Issues.Validate, + c.Severity.Validate, + c.LintersSettings.Validate, + c.Linters.Validate, } - if len(c.Severity.Rules) > 0 && c.Severity.Default == "" { - return errors.New("can't set severity rule option: no default severity defined") - } - for i, rule := range c.Severity.Rules { - if err := rule.Validate(); err != nil { - return fmt.Errorf("error in severity rule #%d: %w", i, err) + for _, v := range validators { + if err := v(); err != nil { + return err } } @@ -90,3 +87,22 @@ func detectGoVersion() string { return "1.17" } + +// Trims the Go version to keep only M.m. +// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0). +// The version can also include information which we want to remove (ex: 1.21alpha1) +// https://go.dev/doc/toolchain#versions +// This a problem with staticcheck and gocritic. +func trimGoVersion(v string) string { + if v == "" { + return "" + } + + exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`) + + if exp.MatchString(v) { + return exp.FindStringSubmatch(v)[1] + } + + return v +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index ac101b95fb53..0fe1855cc19e 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -84,3 +84,52 @@ func TestIsGoGreaterThanOrEqual(t *testing.T) { }) } } + +func Test_trimGoVersion(t *testing.T) { + testCases := []struct { + desc string + version string + expected string + }{ + { + desc: "patched version", + version: "1.22.0", + expected: "1.22", + }, + { + desc: "minor version", + version: "1.22", + expected: "1.22", + }, + { + desc: "RC version", + version: "1.22rc1", + expected: "1.22", + }, + { + desc: "alpha version", + version: "1.22alpha1", + expected: "1.22", + }, + { + desc: "beta version", + version: "1.22beta1", + expected: "1.22", + }, + { + desc: "semver RC version", + version: "1.22.0-rc1", + expected: "1.22", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + version := trimGoVersion(test.version) + assert.Equal(t, test.expected, version) + }) + } +} diff --git a/pkg/config/issues.go b/pkg/config/issues.go index 8725bde82e97..2e14f6cc5d59 100644 --- a/pkg/config/issues.go +++ b/pkg/config/issues.go @@ -122,6 +122,16 @@ type Issues struct { NeedFix bool `mapstructure:"fix"` } +func (i *Issues) Validate() error { + for i, rule := range i.ExcludeRules { + if err := rule.Validate(); err != nil { + return fmt.Errorf("error in exclude rule #%d: %w", i, err) + } + } + + return nil +} + type ExcludeRule struct { BaseRule `mapstructure:",squash"` } diff --git a/pkg/config/linters.go b/pkg/config/linters.go index ccbdc123a1b5..5c2628272c1f 100644 --- a/pkg/config/linters.go +++ b/pkg/config/linters.go @@ -1,5 +1,10 @@ package config +import ( + "errors" + "fmt" +) + type Linters struct { Enable []string Disable []string @@ -9,3 +14,52 @@ type Linters struct { Presets []string } + +func (l *Linters) Validate() error { + if err := l.validateAllDisableEnableOptions(); err != nil { + return err + } + + if err := l.validateDisabledAndEnabledAtOneMoment(); err != nil { + return err + } + + return nil +} + +func (l *Linters) validateAllDisableEnableOptions() error { + if l.EnableAll && l.DisableAll { + return errors.New("--enable-all and --disable-all options must not be combined") + } + + if l.DisableAll { + if len(l.Enable) == 0 && len(l.Presets) == 0 { + return errors.New("all linters were disabled, but no one linter was enabled: must enable at least one") + } + + if len(l.Disable) != 0 { + return errors.New("can't combine options --disable-all and --disable") + } + } + + if l.EnableAll && len(l.Enable) != 0 && !l.Fast { + return errors.New("can't combine options --enable-all and --enable") + } + + return nil +} + +func (l *Linters) validateDisabledAndEnabledAtOneMoment() error { + enabledLintersSet := map[string]bool{} + for _, name := range l.Enable { + enabledLintersSet[name] = true + } + + for _, name := range l.Disable { + if enabledLintersSet[name] { + return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name) + } + } + + return nil +} diff --git a/pkg/config/linters_settings.go b/pkg/config/linters_settings.go index 57d126d940e1..e55e5ad25a12 100644 --- a/pkg/config/linters_settings.go +++ b/pkg/config/linters_settings.go @@ -291,6 +291,10 @@ type LintersSettings struct { Custom map[string]CustomLinterSettings } +func (s *LintersSettings) Validate() error { + return s.Govet.Validate() +} + type AsasalintSettings struct { Exclude []string `mapstructure:"exclude"` UseBuiltinExclusions bool `mapstructure:"use-builtin-exclusions"` @@ -606,15 +610,14 @@ type GovetSettings struct { } func (cfg *GovetSettings) Validate() error { - // TODO(ldez) need to be move into the linter file. if cfg.EnableAll && cfg.DisableAll { - return errors.New("enable-all and disable-all can't be combined") + return errors.New("govet: enable-all and disable-all can't be combined") } if cfg.EnableAll && len(cfg.Enable) != 0 { - return errors.New("enable-all and enable can't be combined") + return errors.New("govet: enable-all and enable can't be combined") } if cfg.DisableAll && len(cfg.Disable) != 0 { - return errors.New("disable-all and disable can't be combined") + return errors.New("govet: disable-all and disable can't be combined") } return nil } diff --git a/pkg/config/linters_test.go b/pkg/config/linters_test.go new file mode 100644 index 000000000000..5be75600541f --- /dev/null +++ b/pkg/config/linters_test.go @@ -0,0 +1,214 @@ +package config + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestLinters_validateDisabledAndEnabledAtOneMoment(t *testing.T) { + testCases := []struct { + desc string + cfg *Linters + }{ + { + desc: "2 different sets", + cfg: &Linters{ + Enable: []string{"dupl", "gofmt", "misspell"}, + Disable: []string{"goimports", "gosec", "nolintlint"}, + }, + }, + { + desc: "only enable", + cfg: &Linters{ + Enable: []string{"goimports", "gosec", "nolintlint"}, + Disable: nil, + }, + }, + { + desc: "only disable", + cfg: &Linters{ + Enable: nil, + Disable: []string{"dupl", "gofmt", "misspell"}, + }, + }, + { + desc: "no sets", + cfg: &Linters{ + Enable: nil, + Disable: nil, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + err := test.cfg.validateDisabledAndEnabledAtOneMoment() + require.NoError(t, err) + }) + } +} + +func TestLinters_validateDisabledAndEnabledAtOneMoment_error(t *testing.T) { + testCases := []struct { + desc string + cfg *Linters + expected string + }{ + { + desc: "disable one linter of the enabled linters", + cfg: &Linters{ + Enable: []string{"dupl", "gofmt", "misspell"}, + Disable: []string{"dupl", "gosec", "nolintlint"}, + }, + expected: `linter "dupl" can't be disabled and enabled at one moment`, + }, + { + desc: "disable multiple enabled linters", + cfg: &Linters{ + Enable: []string{"dupl", "gofmt", "misspell"}, + Disable: []string{"dupl", "gofmt", "misspell"}, + }, + expected: `linter "dupl" can't be disabled and enabled at one moment`, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + err := test.cfg.validateDisabledAndEnabledAtOneMoment() + require.Error(t, err) + + require.EqualError(t, err, test.expected) + }) + } +} + +func TestLinters_validateAllDisableEnableOptions(t *testing.T) { + testCases := []struct { + desc string + cfg *Linters + }{ + { + desc: "nothing", + cfg: &Linters{}, + }, + { + desc: "enable and disable", + cfg: &Linters{ + Enable: []string{"goimports", "gosec", "nolintlint"}, + EnableAll: false, + Disable: []string{"dupl", "gofmt", "misspell"}, + DisableAll: false, + }, + }, + { + desc: "disable-all and enable", + cfg: &Linters{ + Enable: []string{"goimports", "gosec", "nolintlint"}, + EnableAll: false, + Disable: nil, + DisableAll: true, + }, + }, + { + desc: "enable-all and disable", + cfg: &Linters{ + Enable: nil, + EnableAll: true, + Disable: []string{"goimports", "gosec", "nolintlint"}, + DisableAll: false, + }, + }, + { + desc: "enable-all and enable and fast", + cfg: &Linters{ + Enable: []string{"dupl", "gofmt", "misspell"}, + EnableAll: true, + Disable: nil, + DisableAll: false, + Fast: true, + }, + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + err := test.cfg.validateAllDisableEnableOptions() + require.NoError(t, err) + }) + } +} + +func TestLinters_validateAllDisableEnableOptions_error(t *testing.T) { + testCases := []struct { + desc string + cfg *Linters + expected string + }{ + { + desc: "enable-all and disable-all", + cfg: &Linters{ + Enable: nil, + EnableAll: true, + Disable: nil, + DisableAll: true, + Fast: false, + }, + expected: "--enable-all and --disable-all options must not be combined", + }, + { + desc: "disable-all and disable no enable no preset", + cfg: &Linters{ + Enable: nil, + EnableAll: false, + Disable: []string{"dupl", "gofmt", "misspell"}, + DisableAll: true, + Fast: false, + }, + expected: "all linters were disabled, but no one linter was enabled: must enable at least one", + }, + { + desc: "disable-all and disable with enable", + cfg: &Linters{ + Enable: []string{"nolintlint"}, + EnableAll: false, + Disable: []string{"dupl", "gofmt", "misspell"}, + DisableAll: true, + Fast: false, + }, + expected: "can't combine options --disable-all and --disable", + }, + { + desc: "enable-all and enable", + cfg: &Linters{ + Enable: []string{"dupl", "gofmt", "misspell"}, + EnableAll: true, + Disable: nil, + DisableAll: false, + Fast: false, + }, + expected: "can't combine options --enable-all and --enable", + }, + } + + for _, test := range testCases { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + + err := test.cfg.validateAllDisableEnableOptions() + require.Error(t, err) + + require.EqualError(t, err, test.expected) + }) + } +} diff --git a/pkg/config/loader.go b/pkg/config/loader.go index 789796d8f6ea..3884cad462d7 100644 --- a/pkg/config/loader.go +++ b/pkg/config/loader.go @@ -59,11 +59,37 @@ func (l *Loader) Load() error { l.applyStringSliceHack() + l.handleGoVersion() + + return nil +} + +func (l *Loader) handleGoVersion() { if l.cfg.Run.Go == "" { l.cfg.Run.Go = detectGoVersion() } - return nil + l.cfg.LintersSettings.Govet.Go = l.cfg.Run.Go + + l.cfg.LintersSettings.ParallelTest.Go = l.cfg.Run.Go + + trimmedGoVersion := trimGoVersion(l.cfg.Run.Go) + + l.cfg.LintersSettings.Gocritic.Go = trimmedGoVersion + if l.cfg.LintersSettings.Gofumpt.LangVersion == "" { + l.cfg.LintersSettings.Gofumpt.LangVersion = l.cfg.Run.Go + } + + // staticcheck related linters. + if l.cfg.LintersSettings.Staticcheck.GoVersion == "" { + l.cfg.LintersSettings.Staticcheck.GoVersion = trimmedGoVersion + } + if l.cfg.LintersSettings.Gosimple.GoVersion == "" { + l.cfg.LintersSettings.Gosimple.GoVersion = trimmedGoVersion + } + if l.cfg.LintersSettings.Stylecheck.GoVersion != "" { + l.cfg.LintersSettings.Stylecheck.GoVersion = trimmedGoVersion + } } func (l *Loader) setConfigFile() error { @@ -161,11 +187,7 @@ func (l *Loader) parseConfig() error { // Load configuration from flags only. err = l.viper.Unmarshal(l.cfg) if err != nil { - return err - } - - if err = l.cfg.Validate(); err != nil { - return fmt.Errorf("can't validate config: %w", err) + return fmt.Errorf("can't unmarshal config by viper (flags): %w", err) } return nil @@ -181,11 +203,7 @@ func (l *Loader) parseConfig() error { // Load configuration from all sources (flags, file). if err := l.viper.Unmarshal(l.cfg, fileDecoderHook()); err != nil { - return fmt.Errorf("can't unmarshal config by viper: %w", err) - } - - if err := l.cfg.Validate(); err != nil { - return fmt.Errorf("can't validate config: %w", err) + return fmt.Errorf("can't unmarshal config by viper (flags, file): %w", err) } if l.cfg.InternalTest { // just for testing purposes: to detect config file usage diff --git a/pkg/config/severity.go b/pkg/config/severity.go index 3068a0ed69ce..91a6503d1d52 100644 --- a/pkg/config/severity.go +++ b/pkg/config/severity.go @@ -1,5 +1,10 @@ package config +import ( + "errors" + "fmt" +) + const severityRuleMinConditionsCount = 1 type Severity struct { @@ -8,6 +13,20 @@ type Severity struct { Rules []SeverityRule `mapstructure:"rules"` } +func (s *Severity) Validate() error { + if len(s.Rules) > 0 && s.Default == "" { + return errors.New("can't set severity rule option: no default severity defined") + } + + for i, rule := range s.Rules { + if err := rule.Validate(); err != nil { + return fmt.Errorf("error in severity rule #%d: %w", i, err) + } + } + + return nil +} + type SeverityRule struct { BaseRule `mapstructure:",squash"` Severity string diff --git a/pkg/golinters/govet.go b/pkg/golinters/govet.go index 2ed8f252f606..066f7e682a3d 100644 --- a/pkg/golinters/govet.go +++ b/pkg/golinters/govet.go @@ -139,11 +139,6 @@ var ( func NewGovet(settings *config.GovetSettings) *goanalysis.Linter { var conf map[string]map[string]any if settings != nil { - err := settings.Validate() - if err != nil { - linterLogger.Fatalf("govet configuration: %v", err) - } - conf = settings.Settings } diff --git a/pkg/lint/lintersdb/builder_linter.go b/pkg/lint/lintersdb/builder_linter.go new file mode 100644 index 000000000000..5fe538f6f3ca --- /dev/null +++ b/pkg/lint/lintersdb/builder_linter.go @@ -0,0 +1,712 @@ +package lintersdb + +import ( + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters" + "github.com/golangci/golangci-lint/pkg/lint/linter" +) + +// LinterBuilder builds the "internal" linters based on the configuration. +type LinterBuilder struct{} + +// NewLinterBuilder creates a new LinterBuilder. +func NewLinterBuilder() *LinterBuilder { + return &LinterBuilder{} +} + +// Build loads all the "internal" linters. +// The configuration is use for the linter settings. +func (b LinterBuilder) Build(cfg *config.Config) []*linter.Config { + if cfg == nil { + return nil + } + + const megacheckName = "megacheck" + + // The linters are sorted in the alphabetical order (case-insensitive). + // When a new linter is added the version in `WithSince(...)` must be the next minor version of golangci-lint. + return []*linter.Config{ + linter.NewConfig(golinters.NewAsasalint(&cfg.LintersSettings.Asasalint)). + WithSince("1.47.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/alingse/asasalint"), + + linter.NewConfig(golinters.NewAsciicheck()). + WithSince("v1.26.0"). + WithPresets(linter.PresetBugs, linter.PresetStyle). + WithURL("https://github.com/tdakkota/asciicheck"), + + linter.NewConfig(golinters.NewBiDiChkFuncName(&cfg.LintersSettings.BiDiChk)). + WithSince("1.43.0"). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/breml/bidichk"), + + linter.NewConfig(golinters.NewBodyclose()). + WithSince("v1.18.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetPerformance, linter.PresetBugs). + WithURL("https://github.com/timakin/bodyclose"), + + linter.NewConfig(golinters.NewContainedCtx()). + WithSince("1.44.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/sivchari/containedctx"), + + linter.NewConfig(golinters.NewContextCheck()). + WithSince("v1.43.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/kkHAIKE/contextcheck"), + + linter.NewConfig(golinters.NewCopyLoopVar()). + WithSince("v1.57.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/karamaru-alpha/copyloopvar"). + WithNoopFallback(cfg, linter.IsGoLowerThanGo122()), + + linter.NewConfig(golinters.NewCyclop(&cfg.LintersSettings.Cyclop)). + WithSince("v1.37.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/bkielbasa/cyclop"), + + linter.NewConfig(golinters.NewDecorder(&cfg.LintersSettings.Decorder)). + WithSince("v1.44.0"). + WithPresets(linter.PresetFormatting, linter.PresetStyle). + WithURL("https://gitlab.com/bosi/decorder"), + + linter.NewConfig(golinters.NewDeadcode()). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetUnused). + WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"). + Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), + + linter.NewConfig(golinters.NewDepguard(&cfg.LintersSettings.Depguard)). + WithSince("v1.4.0"). + WithPresets(linter.PresetStyle, linter.PresetImport, linter.PresetModule). + WithURL("https://github.com/OpenPeeDeeP/depguard"), + + linter.NewConfig(golinters.NewDogsled(&cfg.LintersSettings.Dogsled)). + WithSince("v1.19.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/alexkohler/dogsled"), + + linter.NewConfig(golinters.NewDupl(&cfg.LintersSettings.Dupl)). + WithSince("v1.0.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/mibk/dupl"), + + linter.NewConfig(golinters.NewDupWord(&cfg.LintersSettings.DupWord)). + WithSince("1.50.0"). + WithPresets(linter.PresetComment). + WithAutoFix(). + WithURL("https://github.com/Abirdcfly/dupword"), + + linter.NewConfig(golinters.NewDurationCheck()). + WithSince("v1.37.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/charithe/durationcheck"), + + linter.NewConfig(golinters.NewErrcheck(&cfg.LintersSettings.Errcheck)). + WithEnabledByDefault(). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs, linter.PresetError). + WithURL("https://github.com/kisielk/errcheck"), + + linter.NewConfig(golinters.NewErrChkJSONFuncName(&cfg.LintersSettings.ErrChkJSON)). + WithSince("1.44.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/breml/errchkjson"), + + linter.NewConfig(golinters.NewErrName()). + WithSince("v1.42.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/Antonboom/errname"), + + linter.NewConfig(golinters.NewErrorLint(&cfg.LintersSettings.ErrorLint)). + WithSince("v1.32.0"). + WithPresets(linter.PresetBugs, linter.PresetError). + WithLoadForGoAnalysis(). + WithURL("https://github.com/polyfloyd/go-errorlint"), + + linter.NewConfig(golinters.NewExecInQuery()). + WithSince("v1.46.0"). + WithPresets(linter.PresetSQL). + WithLoadForGoAnalysis(). + WithURL("https://github.com/lufeee/execinquery"), + + linter.NewConfig(golinters.NewExhaustive(&cfg.LintersSettings.Exhaustive)). + WithSince(" v1.28.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/nishanths/exhaustive"), + + linter.NewConfig(golinters.NewExhaustiveStruct(&cfg.LintersSettings.ExhaustiveStruct)). + WithSince("v1.32.0"). + WithPresets(linter.PresetStyle, linter.PresetTest). + WithLoadForGoAnalysis(). + WithURL("https://github.com/mbilski/exhaustivestruct"). + Deprecated("The owner seems to have abandoned the linter.", "v1.46.0", "exhaustruct"), + + linter.NewConfig(golinters.NewExhaustruct(&cfg.LintersSettings.Exhaustruct)). + WithSince("v1.46.0"). + WithPresets(linter.PresetStyle, linter.PresetTest). + WithLoadForGoAnalysis(). + WithURL("https://github.com/GaijinEntertainment/go-exhaustruct"), + + linter.NewConfig(golinters.NewExportLoopRef()). + WithSince("v1.28.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/kyoh86/exportloopref"), + + linter.NewConfig(golinters.NewForbidigo(&cfg.LintersSettings.Forbidigo)). + WithSince("v1.34.0"). + WithPresets(linter.PresetStyle). + // Strictly speaking, + // the additional information is only needed when forbidigoCfg.AnalyzeTypes is chosen by the user. + // But we don't know that here in all cases (sometimes config is not loaded), + // so we have to assume that it is needed to be on the safe side. + WithLoadForGoAnalysis(). + WithURL("https://github.com/ashanbrown/forbidigo"), + + linter.NewConfig(golinters.NewForceTypeAssert()). + WithSince("v1.38.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/gostaticanalysis/forcetypeassert"), + + linter.NewConfig(golinters.NewFunlen(&cfg.LintersSettings.Funlen)). + WithSince("v1.18.0"). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/ultraware/funlen"), + + linter.NewConfig(golinters.NewGci(&cfg.LintersSettings.Gci)). + WithSince("v1.30.0"). + WithPresets(linter.PresetFormatting, linter.PresetImport). + WithURL("https://github.com/daixiang0/gci"), + + linter.NewConfig(golinters.NewGinkgoLinter(&cfg.LintersSettings.GinkgoLinter)). + WithSince("v1.51.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/nunnatsa/ginkgolinter"), + + linter.NewConfig(golinters.NewGoCheckCompilerDirectives()). + WithSince("v1.51.0"). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/leighmcculloch/gocheckcompilerdirectives"), + + linter.NewConfig(golinters.NewGochecknoglobals()). + WithSince("v1.12.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/leighmcculloch/gochecknoglobals"), + + linter.NewConfig(golinters.NewGochecknoinits()). + WithSince("v1.12.0"). + WithPresets(linter.PresetStyle), + + linter.NewConfig(golinters.NewGoCheckSumType()). + WithSince("v1.55.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/alecthomas/go-check-sumtype"), + + linter.NewConfig(golinters.NewGocognit(&cfg.LintersSettings.Gocognit)). + WithSince("v1.20.0"). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/uudashr/gocognit"), + + linter.NewConfig(golinters.NewGoconst(&cfg.LintersSettings.Goconst)). + WithSince("v1.0.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/jgautheron/goconst"), + + linter.NewConfig(golinters.NewGoCritic(&cfg.LintersSettings.Gocritic, cfg)). + WithSince("v1.12.0"). + WithPresets(linter.PresetStyle, linter.PresetMetaLinter). + WithLoadForGoAnalysis(). + WithURL("https://github.com/go-critic/go-critic"), + + linter.NewConfig(golinters.NewGocyclo(&cfg.LintersSettings.Gocyclo)). + WithSince("v1.0.0"). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/fzipp/gocyclo"), + + linter.NewConfig(golinters.NewGodot(&cfg.LintersSettings.Godot)). + WithSince("v1.25.0"). + WithPresets(linter.PresetStyle, linter.PresetComment). + WithAutoFix(). + WithURL("https://github.com/tetafro/godot"), + + linter.NewConfig(golinters.NewGodox(&cfg.LintersSettings.Godox)). + WithSince("v1.19.0"). + WithPresets(linter.PresetStyle, linter.PresetComment). + WithURL("https://github.com/matoous/godox"), + + linter.NewConfig(golinters.NewGoerr113()). + WithSince("v1.26.0"). + WithPresets(linter.PresetStyle, linter.PresetError). + WithLoadForGoAnalysis(). + WithURL("https://github.com/Djarvur/go-err113"), + + linter.NewConfig(golinters.NewGofmt(&cfg.LintersSettings.Gofmt)). + WithSince("v1.0.0"). + WithPresets(linter.PresetFormatting). + WithAutoFix(). + WithURL("https://pkg.go.dev/cmd/gofmt"), + + linter.NewConfig(golinters.NewGofumpt(&cfg.LintersSettings.Gofumpt)). + WithSince("v1.28.0"). + WithPresets(linter.PresetFormatting). + WithAutoFix(). + WithURL("https://github.com/mvdan/gofumpt"), + + linter.NewConfig(golinters.NewGoHeader(&cfg.LintersSettings.Goheader)). + WithSince("v1.28.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/denis-tingaikin/go-header"), + + linter.NewConfig(golinters.NewGoimports(&cfg.LintersSettings.Goimports)). + WithSince("v1.20.0"). + WithPresets(linter.PresetFormatting, linter.PresetImport). + WithAutoFix(). + WithURL("https://pkg.go.dev/golang.org/x/tools/cmd/goimports"), + + linter.NewConfig(golinters.NewGolint(&cfg.LintersSettings.Golint)). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/golang/lint"). + Deprecated("The repository of the linter has been archived by the owner.", "v1.41.0", "revive"), + + linter.NewConfig(golinters.NewGoMND(&cfg.LintersSettings.Gomnd)). + WithSince("v1.22.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/tommy-muehle/go-mnd"), + + linter.NewConfig(golinters.NewGoModDirectives(&cfg.LintersSettings.GoModDirectives)). + WithSince("v1.39.0"). + WithPresets(linter.PresetStyle, linter.PresetModule). + WithURL("https://github.com/ldez/gomoddirectives"), + + linter.NewConfig(golinters.NewGomodguard(&cfg.LintersSettings.Gomodguard)). + WithSince("v1.25.0"). + WithPresets(linter.PresetStyle, linter.PresetImport, linter.PresetModule). + WithURL("https://github.com/ryancurrah/gomodguard"), + + linter.NewConfig(golinters.NewGoPrintfFuncName()). + WithSince("v1.23.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/jirfag/go-printf-func-name"), + + linter.NewConfig(golinters.NewGosec(&cfg.LintersSettings.Gosec)). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/securego/gosec"). + WithAlternativeNames("gas"), + + linter.NewConfig(golinters.NewGosimple(&cfg.LintersSettings.Gosimple)). + WithEnabledByDefault(). + WithSince("v1.20.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithAlternativeNames(megacheckName). + WithURL("https://github.com/dominikh/go-tools/tree/master/simple"), + + linter.NewConfig(golinters.NewGosmopolitan(&cfg.LintersSettings.Gosmopolitan)). + WithSince("v1.53.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/xen0n/gosmopolitan"), + + linter.NewConfig(golinters.NewGovet(&cfg.LintersSettings.Govet)). + WithEnabledByDefault(). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs, linter.PresetMetaLinter). + WithAlternativeNames("vet", "vetshadow"). + WithURL("https://pkg.go.dev/cmd/vet"), + + linter.NewConfig(golinters.NewGrouper(&cfg.LintersSettings.Grouper)). + WithSince("v1.44.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/leonklingele/grouper"), + + linter.NewConfig(golinters.NewIfshort(&cfg.LintersSettings.Ifshort)). + WithSince("v1.36.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/esimonov/ifshort"). + Deprecated("The repository of the linter has been deprecated by the owner.", "v1.48.0", ""), + + linter.NewConfig(golinters.NewImportAs(&cfg.LintersSettings.ImportAs)). + WithSince("v1.38.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/julz/importas"), + + linter.NewConfig(golinters.NewINamedParam(&cfg.LintersSettings.Inamedparam)). + WithSince("v1.55.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/macabu/inamedparam"), + + linter.NewConfig(golinters.NewIneffassign()). + WithEnabledByDefault(). + WithSince("v1.0.0"). + WithPresets(linter.PresetUnused). + WithURL("https://github.com/gordonklaus/ineffassign"), + + linter.NewConfig(golinters.NewInterfaceBloat(&cfg.LintersSettings.InterfaceBloat)). + WithSince("v1.49.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/sashamelentyev/interfacebloat"), + + linter.NewConfig(golinters.NewInterfacer()). + WithSince("v1.0.0"). + 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", ""), + + linter.NewConfig(golinters.NewIntrange()). + WithSince("v1.57.0"). + WithURL("https://github.com/ckaznocha/intrange"). + WithNoopFallback(cfg, linter.IsGoLowerThanGo122()), + + linter.NewConfig(golinters.NewIreturn(&cfg.LintersSettings.Ireturn)). + WithSince("v1.43.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/butuzov/ireturn"), + + linter.NewConfig(golinters.NewLLL(&cfg.LintersSettings.Lll)). + WithSince("v1.8.0"). + WithPresets(linter.PresetStyle), + + linter.NewConfig(golinters.NewLoggerCheck(&cfg.LintersSettings.LoggerCheck)). + WithSince("v1.49.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle, linter.PresetBugs). + WithAlternativeNames("logrlint"). + WithURL("https://github.com/timonwong/loggercheck"), + + linter.NewConfig(golinters.NewMaintIdx(&cfg.LintersSettings.MaintIdx)). + WithSince("v1.44.0"). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/yagipy/maintidx"), + + linter.NewConfig(golinters.NewMakezero(&cfg.LintersSettings.Makezero)). + WithSince("v1.34.0"). + WithPresets(linter.PresetStyle, linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ashanbrown/makezero"), + + linter.NewConfig(golinters.NewMaligned(&cfg.LintersSettings.Maligned)). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetPerformance). + WithURL("https://github.com/mdempsky/maligned"). + Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", "govet 'fieldalignment'"), + + linter.NewConfig(golinters.NewMirror()). + WithSince("v1.53.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/butuzov/mirror"), + + linter.NewConfig(golinters.NewMisspell(&cfg.LintersSettings.Misspell)). + WithSince("v1.8.0"). + WithPresets(linter.PresetStyle, linter.PresetComment). + WithAutoFix(). + WithURL("https://github.com/client9/misspell"), + + linter.NewConfig(golinters.NewMustTag(&cfg.LintersSettings.MustTag)). + WithSince("v1.51.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle, linter.PresetBugs). + WithURL("https://github.com/go-simpler/musttag"), + + linter.NewConfig(golinters.NewNakedret(&cfg.LintersSettings.Nakedret)). + WithSince("v1.19.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/alexkohler/nakedret"), + + linter.NewConfig(golinters.NewNestif(&cfg.LintersSettings.Nestif)). + WithSince("v1.25.0"). + WithPresets(linter.PresetComplexity). + WithURL("https://github.com/nakabonne/nestif"), + + linter.NewConfig(golinters.NewNilErr()). + WithSince("v1.38.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/gostaticanalysis/nilerr"), + + linter.NewConfig(golinters.NewNilNil(&cfg.LintersSettings.NilNil)). + WithSince("v1.43.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/Antonboom/nilnil"), + + linter.NewConfig(golinters.NewNLReturn(&cfg.LintersSettings.Nlreturn)). + WithSince("v1.30.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/ssgreg/nlreturn"), + + linter.NewConfig(golinters.NewNoctx()). + WithSince("v1.28.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetPerformance, linter.PresetBugs). + WithURL("https://github.com/sonatard/noctx"), + + linter.NewConfig(golinters.NewNoNamedReturns(&cfg.LintersSettings.NoNamedReturns)). + WithSince("v1.46.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/firefart/nonamedreturns"), + + linter.NewConfig(golinters.NewNoSnakeCase()). + WithSince("v1.47.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/sivchari/nosnakecase"). + Deprecated("The repository of the linter has been deprecated by the owner.", "v1.48.1", "revive(var-naming)"), + + linter.NewConfig(golinters.NewNoSprintfHostPort()). + WithSince("v1.46.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/stbenjam/no-sprintf-host-port"), + + linter.NewConfig(golinters.NewParallelTest(&cfg.LintersSettings.ParallelTest)). + WithSince("v1.33.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle, linter.PresetTest). + WithURL("https://github.com/kunwardeep/paralleltest"), + + linter.NewConfig(golinters.NewPerfSprint(&cfg.LintersSettings.PerfSprint)). + WithSince("v1.55.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetPerformance). + WithURL("https://github.com/catenacyber/perfsprint"), + + linter.NewConfig(golinters.NewPreAlloc(&cfg.LintersSettings.Prealloc)). + WithSince("v1.19.0"). + WithPresets(linter.PresetPerformance). + WithURL("https://github.com/alexkohler/prealloc"), + + linter.NewConfig(golinters.NewPredeclared(&cfg.LintersSettings.Predeclared)). + WithSince("v1.35.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/nishanths/predeclared"), + + linter.NewConfig(golinters.NewPromlinter(&cfg.LintersSettings.Promlinter)). + WithSince("v1.40.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/yeya24/promlinter"), + + linter.NewConfig(golinters.NewProtoGetter(&cfg.LintersSettings.ProtoGetter)). + WithSince("v1.55.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithAutoFix(). + WithURL("https://github.com/ghostiam/protogetter"), + + linter.NewConfig(golinters.NewReassign(&cfg.LintersSettings.Reassign)). + WithSince("1.49.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/curioswitch/go-reassign"), + + linter.NewConfig(golinters.NewRevive(&cfg.LintersSettings.Revive)). + WithSince("v1.37.0"). + WithPresets(linter.PresetStyle, linter.PresetMetaLinter). + ConsiderSlow(). + WithURL("https://github.com/mgechev/revive"), + + linter.NewConfig(golinters.NewRowsErrCheck(&cfg.LintersSettings.RowsErrCheck)). + WithSince("v1.23.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs, linter.PresetSQL). + WithURL("https://github.com/jingyugao/rowserrcheck"), + + linter.NewConfig(golinters.NewSlogLint(&cfg.LintersSettings.SlogLint)). + WithSince("v1.55.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle, linter.PresetFormatting). + WithURL("https://github.com/go-simpler/sloglint"), + + linter.NewConfig(golinters.NewScopelint()). + WithSince("v1.12.0"). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/kyoh86/scopelint"). + Deprecated("The repository of the linter has been deprecated by the owner.", "v1.39.0", "exportloopref"), + + linter.NewConfig(golinters.NewSQLCloseCheck()). + WithSince("v1.28.0"). + WithPresets(linter.PresetBugs, linter.PresetSQL). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ryanrolds/sqlclosecheck"), + + linter.NewConfig(golinters.NewSpancheck(&cfg.LintersSettings.Spancheck)). + WithSince("v1.56.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs). + WithURL("https://github.com/jjti/go-spancheck"), + + linter.NewConfig(golinters.NewStaticcheck(&cfg.LintersSettings.Staticcheck)). + WithEnabledByDefault(). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs, linter.PresetMetaLinter). + WithAlternativeNames(megacheckName). + WithURL("https://staticcheck.io/"), + + linter.NewConfig(golinters.NewStructcheck(&cfg.LintersSettings.Structcheck)). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetUnused). + WithURL("https://github.com/opennota/check"). + Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), + + linter.NewConfig(golinters.NewStylecheck(&cfg.LintersSettings.Stylecheck)). + WithSince("v1.20.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/dominikh/go-tools/tree/master/stylecheck"), + + linter.NewConfig(golinters.NewTagAlign(&cfg.LintersSettings.TagAlign)). + WithSince("v1.53.0"). + WithPresets(linter.PresetStyle, linter.PresetFormatting). + WithAutoFix(). + WithURL("https://github.com/4meepo/tagalign"), + + linter.NewConfig(golinters.NewTagliatelle(&cfg.LintersSettings.Tagliatelle)). + WithSince("v1.40.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/ldez/tagliatelle"), + + linter.NewConfig(golinters.NewTenv(&cfg.LintersSettings.Tenv)). + WithSince("v1.43.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/sivchari/tenv"), + + linter.NewConfig(golinters.NewTestableexamples()). + WithSince("v1.50.0"). + WithPresets(linter.PresetTest). + WithURL("https://github.com/maratori/testableexamples"), + + linter.NewConfig(golinters.NewTestifylint(&cfg.LintersSettings.Testifylint)). + WithSince("v1.55.0"). + WithPresets(linter.PresetTest, linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/Antonboom/testifylint"), + + linter.NewConfig(golinters.NewTestpackage(&cfg.LintersSettings.Testpackage)). + WithSince("v1.25.0"). + WithPresets(linter.PresetStyle, linter.PresetTest). + WithURL("https://github.com/maratori/testpackage"), + + linter.NewConfig(golinters.NewThelper(&cfg.LintersSettings.Thelper)). + WithSince("v1.34.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/kulti/thelper"), + + linter.NewConfig(golinters.NewTparallel()). + WithSince("v1.32.0"). + WithPresets(linter.PresetStyle, linter.PresetTest). + WithLoadForGoAnalysis(). + WithURL("https://github.com/moricho/tparallel"), + + linter.NewConfig(golinters.NewTypecheck()). + WithInternal(). + WithEnabledByDefault(). + WithSince("v1.3.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetBugs). + WithURL(""), + + linter.NewConfig(golinters.NewUnconvert()). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/mdempsky/unconvert"), + + linter.NewConfig(golinters.NewUnparam(&cfg.LintersSettings.Unparam)). + WithSince("v1.9.0"). + WithPresets(linter.PresetUnused). + WithLoadForGoAnalysis(). + WithURL("https://github.com/mvdan/unparam"), + + linter.NewConfig(golinters.NewUnused(&cfg.LintersSettings.Unused, &cfg.LintersSettings.Staticcheck)). + WithEnabledByDefault(). + WithSince("v1.20.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetUnused). + WithAlternativeNames(megacheckName). + ConsiderSlow(). + WithChangeTypes(). + WithURL("https://github.com/dominikh/go-tools/tree/master/unused"), + + linter.NewConfig(golinters.NewUseStdlibVars(&cfg.LintersSettings.UseStdlibVars)). + WithSince("v1.48.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/sashamelentyev/usestdlibvars"), + + linter.NewConfig(golinters.NewVarcheck(&cfg.LintersSettings.Varcheck)). + WithSince("v1.0.0"). + WithLoadForGoAnalysis(). + WithPresets(linter.PresetUnused). + WithURL("https://github.com/opennota/check"). + Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), + + linter.NewConfig(golinters.NewVarnamelen(&cfg.LintersSettings.Varnamelen)). + WithSince("v1.43.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/blizzy78/varnamelen"), + + linter.NewConfig(golinters.NewWastedAssign()). + WithSince("v1.38.0"). + WithPresets(linter.PresetStyle). + WithLoadForGoAnalysis(). + WithURL("https://github.com/sanposhiho/wastedassign"), + + linter.NewConfig(golinters.NewWhitespace(&cfg.LintersSettings.Whitespace)). + WithSince("v1.19.0"). + WithPresets(linter.PresetStyle). + WithAutoFix(). + WithURL("https://github.com/ultraware/whitespace"), + + linter.NewConfig(golinters.NewWrapcheck(&cfg.LintersSettings.Wrapcheck)). + WithSince("v1.32.0"). + WithPresets(linter.PresetStyle, linter.PresetError). + WithLoadForGoAnalysis(). + WithURL("https://github.com/tomarrell/wrapcheck"), + + linter.NewConfig(golinters.NewWSL(&cfg.LintersSettings.WSL)). + WithSince("v1.20.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/bombsimon/wsl"), + + linter.NewConfig(golinters.NewZerologLint()). + WithSince("v1.53.0"). + WithPresets(linter.PresetBugs). + WithLoadForGoAnalysis(). + WithURL("https://github.com/ykadowak/zerologlint"), + + // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives + linter.NewConfig(golinters.NewNoLintLint(&cfg.LintersSettings.NoLintLint)). + WithSince("v1.26.0"). + WithPresets(linter.PresetStyle). + WithURL("https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint/README.md"), + } +} diff --git a/pkg/lint/lintersdb/custom_linters.go b/pkg/lint/lintersdb/builder_plugin.go similarity index 61% rename from pkg/lint/lintersdb/custom_linters.go rename to pkg/lint/lintersdb/builder_plugin.go index 76e8fc58922b..401a645c3e76 100644 --- a/pkg/lint/lintersdb/custom_linters.go +++ b/pkg/lint/lintersdb/builder_plugin.go @@ -11,24 +11,35 @@ import ( "github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/logutils" ) type AnalyzerPlugin interface { GetAnalyzers() []*analysis.Analyzer } -// getCustomLinterConfigs loads private linters that are specified in the golangci config file. -func (m *Manager) getCustomLinterConfigs() []*linter.Config { - if m.cfg == nil || m.log == nil { +// PluginBuilder builds the custom linters (plugins) based on the configuration. +type PluginBuilder struct { + log logutils.Log +} + +// NewPluginBuilder creates new PluginBuilder. +func NewPluginBuilder(log logutils.Log) *PluginBuilder { + return &PluginBuilder{log: log} +} + +// Build loads custom linters that are specified in the golangci-lint config file. +func (b *PluginBuilder) Build(cfg *config.Config) []*linter.Config { + if cfg == nil || b.log == nil { return nil } var linters []*linter.Config - for name, settings := range m.cfg.LintersSettings.Custom { - lc, err := m.loadCustomLinterConfig(name, settings) + for name, settings := range cfg.LintersSettings.Custom { + lc, err := b.loadConfig(cfg, name, settings) if err != nil { - m.log.Errorf("Unable to load custom analyzer %s:%s, %v", name, settings.Path, err) + b.log.Errorf("Unable to load custom analyzer %s:%s, %v", name, settings.Path, err) } else { linters = append(linters, lc) } @@ -37,15 +48,15 @@ func (m *Manager) getCustomLinterConfigs() []*linter.Config { return linters } -// loadCustomLinterConfig loads the configuration of private linters. +// loadConfig loads the configuration of private linters. // Private linters are dynamically loaded from .so plugin files. -func (m *Manager) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { - analyzers, err := m.getAnalyzerPlugin(settings.Path, settings.Settings) +func (b *PluginBuilder) loadConfig(cfg *config.Config, name string, settings config.CustomLinterSettings) (*linter.Config, error) { + analyzers, err := b.getAnalyzerPlugin(cfg, settings.Path, settings.Settings) if err != nil { return nil, err } - m.log.Infof("Loaded %s: %s", settings.Path, name) + b.log.Infof("Loaded %s: %s", settings.Path, name) customLinter := goanalysis.NewLinter(name, settings.Description, analyzers, nil). WithLoadMode(goanalysis.LoadModeTypesInfo) @@ -63,10 +74,10 @@ func (m *Manager) loadCustomLinterConfig(name string, settings config.CustomLint // and returns the 'AnalyzerPlugin' interface implemented by the private plugin. // An error is returned if the private linter cannot be loaded // or the linter does not implement the AnalyzerPlugin interface. -func (m *Manager) getAnalyzerPlugin(path string, settings any) ([]*analysis.Analyzer, error) { +func (b *PluginBuilder) getAnalyzerPlugin(cfg *config.Config, path string, settings any) ([]*analysis.Analyzer, error) { if !filepath.IsAbs(path) { // resolve non-absolute paths relative to config file's directory - path = filepath.Join(m.cfg.GetConfigDir(), path) + path = filepath.Join(cfg.GetConfigDir(), path) } plug, err := plugin.Open(path) @@ -74,7 +85,7 @@ func (m *Manager) getAnalyzerPlugin(path string, settings any) ([]*analysis.Anal return nil, err } - analyzers, err := m.lookupPlugin(plug, settings) + analyzers, err := b.lookupPlugin(plug, settings) if err != nil { return nil, fmt.Errorf("lookup plugin %s: %w", path, err) } @@ -82,10 +93,10 @@ func (m *Manager) getAnalyzerPlugin(path string, settings any) ([]*analysis.Anal return analyzers, nil } -func (m *Manager) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.Analyzer, error) { +func (b *PluginBuilder) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.Analyzer, error) { symbol, err := plug.Lookup("New") if err != nil { - analyzers, errP := m.lookupAnalyzerPlugin(plug) + analyzers, errP := b.lookupAnalyzerPlugin(plug) if errP != nil { return nil, errors.Join(err, errP) } @@ -102,13 +113,13 @@ func (m *Manager) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.A return constructor(settings) } -func (m *Manager) lookupAnalyzerPlugin(plug *plugin.Plugin) ([]*analysis.Analyzer, error) { +func (b *PluginBuilder) lookupAnalyzerPlugin(plug *plugin.Plugin) ([]*analysis.Analyzer, error) { symbol, err := plug.Lookup("AnalyzerPlugin") if err != nil { return nil, err } - m.log.Warnf("plugin: 'AnalyzerPlugin' plugins are deprecated, please use the new plugin signature: " + + b.log.Warnf("plugin: 'AnalyzerPlugin' plugins are deprecated, please use the new plugin signature: " + "https://golangci-lint.run/contributing/new-linters/#create-a-plugin") analyzerPlugin, ok := symbol.(AnalyzerPlugin) diff --git a/pkg/lint/lintersdb/enabled_set.go b/pkg/lint/lintersdb/enabled_set.go deleted file mode 100644 index 0cdafb7a6cf1..000000000000 --- a/pkg/lint/lintersdb/enabled_set.go +++ /dev/null @@ -1,224 +0,0 @@ -package lintersdb - -import ( - "os" - "sort" - - "golang.org/x/exp/maps" - - "github.com/golangci/golangci-lint/pkg/config" - "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" - "github.com/golangci/golangci-lint/pkg/lint/linter" - "github.com/golangci/golangci-lint/pkg/logutils" -) - -// EnvTestRun value: "1" -const EnvTestRun = "GL_TEST_RUN" - -type EnabledSet struct { - m *Manager - v *Validator - log logutils.Log - cfg *config.Config - debugf logutils.DebugFunc -} - -func NewEnabledSet(m *Manager, v *Validator, log logutils.Log, cfg *config.Config) *EnabledSet { - return &EnabledSet{ - m: m, - v: v, - log: log, - cfg: cfg, - debugf: logutils.Debug(logutils.DebugKeyEnabledLinters), - } -} - -//nolint:gocyclo // the complexity cannot be reduced. -func (es EnabledSet) build(lcfg *config.Linters, enabledByDefaultLinters []*linter.Config) map[string]*linter.Config { - es.debugf("Linters config: %#v", lcfg) - - resultLintersSet := map[string]*linter.Config{} - switch { - case len(lcfg.Presets) != 0: - break // imply --disable-all - case lcfg.EnableAll: - resultLintersSet = linterConfigsToMap(es.m.GetAllSupportedLinterConfigs()) - case lcfg.DisableAll: - break - default: - resultLintersSet = linterConfigsToMap(enabledByDefaultLinters) - } - - // --presets can only add linters to default set - for _, p := range lcfg.Presets { - for _, lc := range es.m.GetAllLinterConfigsForPreset(p) { - lc := lc - resultLintersSet[lc.Name()] = lc - } - } - - // --fast removes slow linters from current set. - // It should be after --presets to be able to run only fast linters in preset. - // It should be before --enable and --disable to be able to enable or disable specific linter. - if lcfg.Fast { - for name, lc := range resultLintersSet { - if lc.IsSlowLinter() { - delete(resultLintersSet, name) - } - } - } - - for _, name := range lcfg.Enable { - for _, lc := range es.m.GetLinterConfigs(name) { - // it's important to use lc.Name() nor name because name can be alias - resultLintersSet[lc.Name()] = lc - } - } - - for _, name := range lcfg.Disable { - for _, lc := range es.m.GetLinterConfigs(name) { - // it's important to use lc.Name() nor name because name can be alias - delete(resultLintersSet, lc.Name()) - } - } - - // typecheck is not a real linter and cannot be disabled. - if _, ok := resultLintersSet["typecheck"]; !ok && (es.cfg == nil || !es.cfg.InternalCmdTest) { - for _, lc := range es.m.GetLinterConfigs("typecheck") { - // it's important to use lc.Name() nor name because name can be alias - resultLintersSet[lc.Name()] = lc - } - } - - return resultLintersSet -} - -func (es EnabledSet) GetEnabledLintersMap() (map[string]*linter.Config, error) { - if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil { - return nil, err - } - - enabledLinters := es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters()) - if os.Getenv(EnvTestRun) == "1" { - es.verbosePrintLintersStatus(enabledLinters) - } - return enabledLinters, nil -} - -// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters -// into a fewer number of linters. E.g. some go/analysis linters can be optimized into -// one metalinter for data reuse and speed up. -func (es EnabledSet) GetOptimizedLinters() ([]*linter.Config, error) { - if err := es.v.validateEnabledDisabledLintersConfig(&es.cfg.Linters); err != nil { - return nil, err - } - - resultLintersSet := es.build(&es.cfg.Linters, es.m.GetAllEnabledByDefaultLinters()) - es.verbosePrintLintersStatus(resultLintersSet) - es.combineGoAnalysisLinters(resultLintersSet) - - resultLinters := maps.Values(resultLintersSet) - - // Make order of execution of linters (go/analysis metalinter and unused) stable. - sort.Slice(resultLinters, func(i, j int) bool { - a, b := resultLinters[i], resultLinters[j] - - if b.Name() == linter.LastLinter { - return true - } - - if a.Name() == linter.LastLinter { - return false - } - - if a.DoesChangeTypes != b.DoesChangeTypes { - return b.DoesChangeTypes // move type-changing linters to the end to optimize speed - } - return a.Name() < b.Name() - }) - - return resultLinters, nil -} - -func (es EnabledSet) combineGoAnalysisLinters(linters map[string]*linter.Config) { - var goanalysisLinters []*goanalysis.Linter - goanalysisPresets := map[string]bool{} - for _, lc := range linters { - lnt, ok := lc.Linter.(*goanalysis.Linter) - if !ok { - continue - } - - if lnt.LoadMode() == goanalysis.LoadModeWholeProgram { - // It's ineffective by CPU and memory to run whole-program and incremental analyzers at once. - continue - } - - goanalysisLinters = append(goanalysisLinters, lnt) - - for _, p := range lc.InPresets { - goanalysisPresets[p] = true - } - } - - if len(goanalysisLinters) <= 1 { - es.debugf("Didn't combine go/analysis linters: got only %d linters", len(goanalysisLinters)) - return - } - - for _, lnt := range goanalysisLinters { - delete(linters, lnt.Name()) - } - - // Make order of execution of go/analysis analyzers stable. - sort.Slice(goanalysisLinters, func(i, j int) bool { - a, b := goanalysisLinters[i], goanalysisLinters[j] - - if b.Name() == linter.LastLinter { - return true - } - - if a.Name() == linter.LastLinter { - return false - } - - return a.Name() <= b.Name() - }) - - ml := goanalysis.NewMetaLinter(goanalysisLinters) - - presets := maps.Keys(goanalysisPresets) - sort.Strings(presets) - - mlConfig := &linter.Config{ - Linter: ml, - EnabledByDefault: false, - InPresets: presets, - AlternativeNames: nil, - OriginalURL: "", - } - - mlConfig = mlConfig.WithLoadForGoAnalysis() - - linters[ml.Name()] = mlConfig - - es.debugf("Combined %d go/analysis linters into one metalinter", len(goanalysisLinters)) -} - -func (es EnabledSet) verbosePrintLintersStatus(lcs map[string]*linter.Config) { - var linterNames []string - for _, lc := range lcs { - if lc.Internal { - continue - } - - linterNames = append(linterNames, lc.Name()) - } - sort.StringSlice(linterNames).Sort() - es.log.Infof("Active %d linters: %s", len(linterNames), linterNames) - - if len(es.cfg.Linters.Presets) != 0 { - sort.StringSlice(es.cfg.Linters.Presets).Sort() - es.log.Infof("Active presets: %s", es.cfg.Linters.Presets) - } -} diff --git a/pkg/lint/lintersdb/enabled_set_test.go b/pkg/lint/lintersdb/enabled_set_test.go deleted file mode 100644 index d4fd75c410a4..000000000000 --- a/pkg/lint/lintersdb/enabled_set_test.go +++ /dev/null @@ -1,263 +0,0 @@ -package lintersdb - -import ( - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - - "github.com/golangci/golangci-lint/pkg/config" - "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" - "github.com/golangci/golangci-lint/pkg/lint/linter" - "github.com/golangci/golangci-lint/pkg/logutils" -) - -type dummyLogger struct{} - -func (d dummyLogger) Fatalf(_ string, _ ...any) {} - -func (d dummyLogger) Panicf(_ string, _ ...any) {} - -func (d dummyLogger) Errorf(_ string, _ ...any) {} - -func (d dummyLogger) Warnf(_ string, _ ...any) {} - -func (d dummyLogger) Infof(_ string, _ ...any) {} - -func (d dummyLogger) Child(_ string) logutils.Log { - return nil -} - -func (d dummyLogger) SetLevel(_ logutils.LogLevel) {} - -func TestEnabledSet_GetEnabledLintersMap(t *testing.T) { - m := NewManager(nil, nil) - - cfg := config.NewDefault() - - cfg.Linters.DisableAll = true - cfg.Linters.Enable = []string{"gofmt"} - - es := NewEnabledSet(m, NewValidator(m), dummyLogger{}, cfg) - - lintersMap, err := es.GetEnabledLintersMap() - require.NoError(t, err) - - gofmtConfigs := m.GetLinterConfigs("gofmt") - typecheckConfigs := m.GetLinterConfigs("typecheck") - - expected := map[string]*linter.Config{ - "gofmt": gofmtConfigs[0], - "typecheck": typecheckConfigs[0], - } - - assert.Equal(t, expected, lintersMap) -} - -func TestEnabledSet_GetOptimizedLinters(t *testing.T) { - m := NewManager(nil, nil) - - cfg := config.NewDefault() - - cfg.Linters.DisableAll = true - cfg.Linters.Enable = []string{"gofmt"} - - es := NewEnabledSet(m, NewValidator(m), dummyLogger{}, cfg) - - optimizedLinters, err := es.GetOptimizedLinters() - require.NoError(t, err) - - gofmtConfigs := m.GetLinterConfigs("gofmt") - typecheckConfigs := m.GetLinterConfigs("typecheck") - - var gaLinters []*goanalysis.Linter - for _, l := range gofmtConfigs { - gaLinters = append(gaLinters, l.Linter.(*goanalysis.Linter)) - } - for _, l := range typecheckConfigs { - gaLinters = append(gaLinters, l.Linter.(*goanalysis.Linter)) - } - - mlConfig := &linter.Config{ - Linter: goanalysis.NewMetaLinter(gaLinters), - InPresets: []string{"bugs", "format"}, - } - - expected := []*linter.Config{mlConfig.WithLoadForGoAnalysis()} - - assert.Equal(t, expected, optimizedLinters) -} - -func TestEnabledSet_build(t *testing.T) { - type cs struct { - cfg config.Linters - name string // test case name - def []string // enabled by default linters - exp []string // alphabetically ordered enabled linter names - } - - allMegacheckLinterNames := []string{"gosimple", "staticcheck", "unused"} - - cases := []cs{ - { - cfg: config.Linters{ - Disable: []string{"megacheck"}, - }, - name: "disable all linters from megacheck", - def: allMegacheckLinterNames, - exp: []string{"typecheck"}, // all disabled - }, - { - cfg: config.Linters{ - Disable: []string{"staticcheck"}, - }, - name: "disable only staticcheck", - def: allMegacheckLinterNames, - exp: []string{"gosimple", "typecheck", "unused"}, - }, - { - name: "don't merge into megacheck", - def: allMegacheckLinterNames, - exp: []string{"gosimple", "staticcheck", "typecheck", "unused"}, - }, - { - name: "expand megacheck", - cfg: config.Linters{ - Enable: []string{"megacheck"}, - }, - def: nil, - exp: []string{"gosimple", "staticcheck", "typecheck", "unused"}, - }, - { - name: "don't disable anything", - def: []string{"gofmt", "govet", "typecheck"}, - exp: []string{"gofmt", "govet", "typecheck"}, - }, - { - name: "enable gosec by gas alias", - cfg: config.Linters{ - Enable: []string{"gas"}, - }, - exp: []string{"gosec", "typecheck"}, - }, - { - name: "enable gosec by primary name", - cfg: config.Linters{ - Enable: []string{"gosec"}, - }, - exp: []string{"gosec", "typecheck"}, - }, - { - name: "enable gosec by both names", - cfg: config.Linters{ - Enable: []string{"gosec", "gas"}, - }, - exp: []string{"gosec", "typecheck"}, - }, - { - name: "disable gosec by gas alias", - cfg: config.Linters{ - Disable: []string{"gas"}, - }, - def: []string{"gosec"}, - exp: []string{"typecheck"}, - }, - { - name: "disable gosec by primary name", - cfg: config.Linters{ - Disable: []string{"gosec"}, - }, - def: []string{"gosec"}, - exp: []string{"typecheck"}, - }, - } - - m := NewManager(nil, nil) - es := NewEnabledSet(m, NewValidator(m), dummyLogger{}, nil) - - for _, c := range cases { - c := c - t.Run(c.name, func(t *testing.T) { - var defaultLinters []*linter.Config - for _, ln := range c.def { - lcs := m.GetLinterConfigs(ln) - assert.NotNil(t, lcs, ln) - defaultLinters = append(defaultLinters, lcs...) - } - - els := es.build(&c.cfg, defaultLinters) - var enabledLinters []string - for ln, lc := range els { - assert.Equal(t, ln, lc.Name()) - enabledLinters = append(enabledLinters, ln) - } - - assert.ElementsMatch(t, c.exp, enabledLinters) - }) - } -} - -func TestEnabledSet_combineGoAnalysisLinters(t *testing.T) { - m := NewManager(nil, nil) - - es := NewEnabledSet(m, NewValidator(m), dummyLogger{}, config.NewDefault()) - - foo := goanalysis.NewLinter("foo", "example foo", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo) - bar := goanalysis.NewLinter("bar", "example bar", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo) - - testCases := []struct { - desc string - linters map[string]*linter.Config - expected map[string]*linter.Config - }{ - { - desc: "no combined, one linter", - linters: map[string]*linter.Config{ - "foo": { - Linter: foo, - InPresets: []string{"A"}, - }, - }, - expected: map[string]*linter.Config{ - "foo": { - Linter: foo, - InPresets: []string{"A"}, - }, - }, - }, - { - desc: "combined, several linters", - linters: map[string]*linter.Config{ - "foo": { - Linter: foo, - InPresets: []string{"A"}, - }, - "bar": { - Linter: bar, - InPresets: []string{"B"}, - }, - }, - expected: func() map[string]*linter.Config { - mlConfig := &linter.Config{ - Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{bar, foo}), - InPresets: []string{"A", "B"}, - } - - return map[string]*linter.Config{ - "goanalysis_metalinter": mlConfig.WithLoadForGoAnalysis(), - } - }(), - }, - } - - for _, test := range testCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - es.combineGoAnalysisLinters(test.linters) - - assert.Equal(t, test.expected, test.linters) - }) - } -} diff --git a/pkg/lint/lintersdb/manager.go b/pkg/lint/lintersdb/manager.go index 3a972c7a43aa..a55ef9a39244 100644 --- a/pkg/lint/lintersdb/manager.go +++ b/pkg/lint/lintersdb/manager.go @@ -1,936 +1,137 @@ package lintersdb import ( - "regexp" + "os" + "slices" + "sort" + + "golang.org/x/exp/maps" "github.com/golangci/golangci-lint/pkg/config" - "github.com/golangci/golangci-lint/pkg/golinters" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/logutils" ) +// EnvTestRun value: "1" +const EnvTestRun = "GL_TEST_RUN" + +type Builder interface { + Build(cfg *config.Config) []*linter.Config +} + +// Manager is a type of database for all linters (internals or plugins). +// It provides methods to access to the linter sets. type Manager struct { + log logutils.Log + debugf logutils.DebugFunc + cfg *config.Config - log logutils.Log - nameToLCs map[string][]*linter.Config - customLinters []*linter.Config + linters []*linter.Config + + nameToLCs map[string][]*linter.Config } -func NewManager(cfg *config.Config, log logutils.Log) *Manager { - m := &Manager{cfg: cfg, log: log} - m.customLinters = m.getCustomLinterConfigs() +// NewManager creates a new Manager. +// This constructor will call the builders to build and store the linters. +func NewManager(log logutils.Log, cfg *config.Config, builders ...Builder) (*Manager, error) { + m := &Manager{ + log: log, + debugf: logutils.Debug(logutils.DebugKeyEnabledLinters), + nameToLCs: make(map[string][]*linter.Config), + } + + m.cfg = cfg + if cfg == nil { + m.cfg = config.NewDefault() + } - nameToLCs := make(map[string][]*linter.Config) - for _, lc := range m.GetAllSupportedLinterConfigs() { + for _, builder := range builders { + m.linters = append(m.linters, builder.Build(m.cfg)...) + } + + for _, lc := range m.linters { for _, name := range lc.AllNames() { - nameToLCs[name] = append(nameToLCs[name], lc) + m.nameToLCs[name] = append(m.nameToLCs[name], lc) } } - m.nameToLCs = nameToLCs + err := NewValidator(m).Validate(m.cfg) + if err != nil { + return nil, err + } - return m + return m, nil } func (m *Manager) GetLinterConfigs(name string) []*linter.Config { return m.nameToLCs[name] } -//nolint:funlen func (m *Manager) GetAllSupportedLinterConfigs() []*linter.Config { - var ( - asasalintCfg *config.AsasalintSettings - bidichkCfg *config.BiDiChkSettings - cyclopCfg *config.Cyclop - decorderCfg *config.DecorderSettings - depGuardCfg *config.DepGuardSettings - dogsledCfg *config.DogsledSettings - duplCfg *config.DuplSettings - dupwordCfg *config.DupWordSettings - errcheckCfg *config.ErrcheckSettings - errchkjsonCfg *config.ErrChkJSONSettings - errorlintCfg *config.ErrorLintSettings - exhaustiveCfg *config.ExhaustiveSettings - exhaustiveStructCfg *config.ExhaustiveStructSettings - exhaustructCfg *config.ExhaustructSettings - forbidigoCfg *config.ForbidigoSettings - funlenCfg *config.FunlenSettings - gciCfg *config.GciSettings - ginkgolinterCfg *config.GinkgoLinterSettings - gocognitCfg *config.GocognitSettings - goconstCfg *config.GoConstSettings - gocriticCfg *config.GoCriticSettings - gocycloCfg *config.GoCycloSettings - godotCfg *config.GodotSettings - godoxCfg *config.GodoxSettings - gofmtCfg *config.GoFmtSettings - gofumptCfg *config.GofumptSettings - goheaderCfg *config.GoHeaderSettings - goimportsCfg *config.GoImportsSettings - golintCfg *config.GoLintSettings - goMndCfg *config.GoMndSettings - goModDirectivesCfg *config.GoModDirectivesSettings - gomodguardCfg *config.GoModGuardSettings - gosecCfg *config.GoSecSettings - gosimpleCfg *config.StaticCheckSettings - gosmopolitanCfg *config.GosmopolitanSettings - govetCfg *config.GovetSettings - grouperCfg *config.GrouperSettings - ifshortCfg *config.IfshortSettings - importAsCfg *config.ImportAsSettings - inamedparamCfg *config.INamedParamSettings - interfaceBloatCfg *config.InterfaceBloatSettings - ireturnCfg *config.IreturnSettings - lllCfg *config.LllSettings - loggerCheckCfg *config.LoggerCheckSettings - maintIdxCfg *config.MaintIdxSettings - makezeroCfg *config.MakezeroSettings - malignedCfg *config.MalignedSettings - misspellCfg *config.MisspellSettings - musttagCfg *config.MustTagSettings - nakedretCfg *config.NakedretSettings - nestifCfg *config.NestifSettings - nilNilCfg *config.NilNilSettings - nlreturnCfg *config.NlreturnSettings - noLintLintCfg *config.NoLintLintSettings - noNamedReturnsCfg *config.NoNamedReturnsSettings - parallelTestCfg *config.ParallelTestSettings - perfSprintCfg *config.PerfSprintSettings - preallocCfg *config.PreallocSettings - predeclaredCfg *config.PredeclaredSettings - promlinterCfg *config.PromlinterSettings - protogetterCfg *config.ProtoGetterSettings - reassignCfg *config.ReassignSettings - reviveCfg *config.ReviveSettings - rowserrcheckCfg *config.RowsErrCheckSettings - sloglintCfg *config.SlogLintSettings - spancheckCfg *config.SpancheckSettings - staticcheckCfg *config.StaticCheckSettings - structcheckCfg *config.StructCheckSettings - stylecheckCfg *config.StaticCheckSettings - tagalignCfg *config.TagAlignSettings - tagliatelleCfg *config.TagliatelleSettings - tenvCfg *config.TenvSettings - testifylintCfg *config.TestifylintSettings - testpackageCfg *config.TestpackageSettings - thelperCfg *config.ThelperSettings - unparamCfg *config.UnparamSettings - unusedCfg *config.UnusedSettings - usestdlibvars *config.UseStdlibVarsSettings - varcheckCfg *config.VarCheckSettings - varnamelenCfg *config.VarnamelenSettings - whitespaceCfg *config.WhitespaceSettings - wrapcheckCfg *config.WrapcheckSettings - wslCfg *config.WSLSettings - ) - - if m.cfg != nil { - asasalintCfg = &m.cfg.LintersSettings.Asasalint - bidichkCfg = &m.cfg.LintersSettings.BiDiChk - cyclopCfg = &m.cfg.LintersSettings.Cyclop - decorderCfg = &m.cfg.LintersSettings.Decorder - depGuardCfg = &m.cfg.LintersSettings.Depguard - dogsledCfg = &m.cfg.LintersSettings.Dogsled - duplCfg = &m.cfg.LintersSettings.Dupl - dupwordCfg = &m.cfg.LintersSettings.DupWord - errcheckCfg = &m.cfg.LintersSettings.Errcheck - errchkjsonCfg = &m.cfg.LintersSettings.ErrChkJSON - errorlintCfg = &m.cfg.LintersSettings.ErrorLint - exhaustiveCfg = &m.cfg.LintersSettings.Exhaustive - exhaustiveStructCfg = &m.cfg.LintersSettings.ExhaustiveStruct - exhaustructCfg = &m.cfg.LintersSettings.Exhaustruct - forbidigoCfg = &m.cfg.LintersSettings.Forbidigo - funlenCfg = &m.cfg.LintersSettings.Funlen - gciCfg = &m.cfg.LintersSettings.Gci - ginkgolinterCfg = &m.cfg.LintersSettings.GinkgoLinter - gocognitCfg = &m.cfg.LintersSettings.Gocognit - goconstCfg = &m.cfg.LintersSettings.Goconst - gocriticCfg = &m.cfg.LintersSettings.Gocritic - gocycloCfg = &m.cfg.LintersSettings.Gocyclo - godotCfg = &m.cfg.LintersSettings.Godot - godoxCfg = &m.cfg.LintersSettings.Godox - gofmtCfg = &m.cfg.LintersSettings.Gofmt - gofumptCfg = &m.cfg.LintersSettings.Gofumpt - goheaderCfg = &m.cfg.LintersSettings.Goheader - goimportsCfg = &m.cfg.LintersSettings.Goimports - golintCfg = &m.cfg.LintersSettings.Golint - goMndCfg = &m.cfg.LintersSettings.Gomnd - goModDirectivesCfg = &m.cfg.LintersSettings.GoModDirectives - gomodguardCfg = &m.cfg.LintersSettings.Gomodguard - gosecCfg = &m.cfg.LintersSettings.Gosec - gosimpleCfg = &m.cfg.LintersSettings.Gosimple - gosmopolitanCfg = &m.cfg.LintersSettings.Gosmopolitan - govetCfg = &m.cfg.LintersSettings.Govet - grouperCfg = &m.cfg.LintersSettings.Grouper - ifshortCfg = &m.cfg.LintersSettings.Ifshort - importAsCfg = &m.cfg.LintersSettings.ImportAs - inamedparamCfg = &m.cfg.LintersSettings.Inamedparam - interfaceBloatCfg = &m.cfg.LintersSettings.InterfaceBloat - ireturnCfg = &m.cfg.LintersSettings.Ireturn - lllCfg = &m.cfg.LintersSettings.Lll - loggerCheckCfg = &m.cfg.LintersSettings.LoggerCheck - maintIdxCfg = &m.cfg.LintersSettings.MaintIdx - makezeroCfg = &m.cfg.LintersSettings.Makezero - malignedCfg = &m.cfg.LintersSettings.Maligned - misspellCfg = &m.cfg.LintersSettings.Misspell - musttagCfg = &m.cfg.LintersSettings.MustTag - nakedretCfg = &m.cfg.LintersSettings.Nakedret - nestifCfg = &m.cfg.LintersSettings.Nestif - nilNilCfg = &m.cfg.LintersSettings.NilNil - nlreturnCfg = &m.cfg.LintersSettings.Nlreturn - noLintLintCfg = &m.cfg.LintersSettings.NoLintLint - noNamedReturnsCfg = &m.cfg.LintersSettings.NoNamedReturns - parallelTestCfg = &m.cfg.LintersSettings.ParallelTest - perfSprintCfg = &m.cfg.LintersSettings.PerfSprint - preallocCfg = &m.cfg.LintersSettings.Prealloc - predeclaredCfg = &m.cfg.LintersSettings.Predeclared - promlinterCfg = &m.cfg.LintersSettings.Promlinter - protogetterCfg = &m.cfg.LintersSettings.ProtoGetter - reassignCfg = &m.cfg.LintersSettings.Reassign - reviveCfg = &m.cfg.LintersSettings.Revive - rowserrcheckCfg = &m.cfg.LintersSettings.RowsErrCheck - sloglintCfg = &m.cfg.LintersSettings.SlogLint - spancheckCfg = &m.cfg.LintersSettings.Spancheck - staticcheckCfg = &m.cfg.LintersSettings.Staticcheck - structcheckCfg = &m.cfg.LintersSettings.Structcheck - stylecheckCfg = &m.cfg.LintersSettings.Stylecheck - tagalignCfg = &m.cfg.LintersSettings.TagAlign - tagliatelleCfg = &m.cfg.LintersSettings.Tagliatelle - tenvCfg = &m.cfg.LintersSettings.Tenv - testifylintCfg = &m.cfg.LintersSettings.Testifylint - testpackageCfg = &m.cfg.LintersSettings.Testpackage - thelperCfg = &m.cfg.LintersSettings.Thelper - unparamCfg = &m.cfg.LintersSettings.Unparam - unusedCfg = &m.cfg.LintersSettings.Unused - usestdlibvars = &m.cfg.LintersSettings.UseStdlibVars - varcheckCfg = &m.cfg.LintersSettings.Varcheck - varnamelenCfg = &m.cfg.LintersSettings.Varnamelen - whitespaceCfg = &m.cfg.LintersSettings.Whitespace - wrapcheckCfg = &m.cfg.LintersSettings.Wrapcheck - wslCfg = &m.cfg.LintersSettings.WSL - - govetCfg.Go = m.cfg.Run.Go - - parallelTestCfg.Go = m.cfg.Run.Go - - gocriticCfg.Go = trimGoVersion(m.cfg.Run.Go) - - if gofumptCfg.LangVersion == "" { - gofumptCfg.LangVersion = m.cfg.Run.Go + return m.linters +} + +func (m *Manager) GetAllLinterConfigsForPreset(p string) []*linter.Config { + var ret []*linter.Config + for _, lc := range m.linters { + if lc.IsDeprecated() { + continue } - // staticcheck related linters. - if staticcheckCfg.GoVersion == "" { - staticcheckCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) + if slices.Contains(lc.InPresets, p) { + ret = append(ret, lc) } - if gosimpleCfg.GoVersion == "" { - gosimpleCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) + } + + return ret +} + +func (m *Manager) GetEnabledLintersMap() (map[string]*linter.Config, error) { + enabledLinters := m.build(m.GetAllEnabledByDefaultLinters()) + + if os.Getenv(EnvTestRun) == "1" { + m.verbosePrintLintersStatus(enabledLinters) + } + + return enabledLinters, nil +} + +// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters into a fewer number of linters. +// E.g. some go/analysis linters can be optimized into one metalinter for data reuse and speed up. +func (m *Manager) GetOptimizedLinters() ([]*linter.Config, error) { + resultLintersSet := m.build(m.GetAllEnabledByDefaultLinters()) + m.verbosePrintLintersStatus(resultLintersSet) + + m.combineGoAnalysisLinters(resultLintersSet) + + resultLinters := maps.Values(resultLintersSet) + + // Make order of execution of linters (go/analysis metalinter and unused) stable. + sort.Slice(resultLinters, func(i, j int) bool { + a, b := resultLinters[i], resultLinters[j] + + if b.Name() == linter.LastLinter { + return true } - if stylecheckCfg.GoVersion != "" { - stylecheckCfg.GoVersion = trimGoVersion(m.cfg.Run.Go) + + if a.Name() == linter.LastLinter { + return false } - } - const megacheckName = "megacheck" - - var linters []*linter.Config - linters = append(linters, m.customLinters...) - - // The linters are sorted in the alphabetical order (case-insensitive). - // When a new linter is added the version in `WithSince(...)` must be the next minor version of golangci-lint. - linters = append(linters, - linter.NewConfig(golinters.NewAsasalint(asasalintCfg)). - WithSince("1.47.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/alingse/asasalint"), - - linter.NewConfig(golinters.NewAsciicheck()). - WithSince("v1.26.0"). - WithPresets(linter.PresetBugs, linter.PresetStyle). - WithURL("https://github.com/tdakkota/asciicheck"), - - linter.NewConfig(golinters.NewBiDiChkFuncName(bidichkCfg)). - WithSince("1.43.0"). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/breml/bidichk"), - - linter.NewConfig(golinters.NewBodyclose()). - WithSince("v1.18.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetPerformance, linter.PresetBugs). - WithURL("https://github.com/timakin/bodyclose"), - - linter.NewConfig(golinters.NewContainedCtx()). - WithSince("1.44.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/sivchari/containedctx"), - - linter.NewConfig(golinters.NewContextCheck()). - WithSince("v1.43.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/kkHAIKE/contextcheck"), - - linter.NewConfig(golinters.NewCopyLoopVar()). - WithSince("v1.57.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/karamaru-alpha/copyloopvar"). - WithNoopFallback(m.cfg, linter.IsGoLowerThanGo122()), - - linter.NewConfig(golinters.NewCyclop(cyclopCfg)). - WithSince("v1.37.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetComplexity). - WithURL("https://github.com/bkielbasa/cyclop"), - - linter.NewConfig(golinters.NewDecorder(decorderCfg)). - WithSince("v1.44.0"). - WithPresets(linter.PresetFormatting, linter.PresetStyle). - WithURL("https://gitlab.com/bosi/decorder"), - - linter.NewConfig(golinters.NewDeadcode()). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetUnused). - WithURL("https://github.com/remyoudompheng/go-misc/tree/master/deadcode"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), - - linter.NewConfig(golinters.NewDepguard(depGuardCfg)). - WithSince("v1.4.0"). - WithPresets(linter.PresetStyle, linter.PresetImport, linter.PresetModule). - WithURL("https://github.com/OpenPeeDeeP/depguard"), - - linter.NewConfig(golinters.NewDogsled(dogsledCfg)). - WithSince("v1.19.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/alexkohler/dogsled"), - - linter.NewConfig(golinters.NewDupl(duplCfg)). - WithSince("v1.0.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/mibk/dupl"), - - linter.NewConfig(golinters.NewDupWord(dupwordCfg)). - WithSince("1.50.0"). - WithPresets(linter.PresetComment). - WithAutoFix(). - WithURL("https://github.com/Abirdcfly/dupword"), - - linter.NewConfig(golinters.NewDurationCheck()). - WithSince("v1.37.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/charithe/durationcheck"), - - linter.NewConfig(golinters.NewErrcheck(errcheckCfg)). - WithEnabledByDefault(). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs, linter.PresetError). - WithURL("https://github.com/kisielk/errcheck"), - - linter.NewConfig(golinters.NewErrChkJSONFuncName(errchkjsonCfg)). - WithSince("1.44.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/breml/errchkjson"), - - linter.NewConfig(golinters.NewErrName()). - WithSince("v1.42.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/Antonboom/errname"), - - linter.NewConfig(golinters.NewErrorLint(errorlintCfg)). - WithSince("v1.32.0"). - WithPresets(linter.PresetBugs, linter.PresetError). - WithLoadForGoAnalysis(). - WithURL("https://github.com/polyfloyd/go-errorlint"), - - linter.NewConfig(golinters.NewExecInQuery()). - WithSince("v1.46.0"). - WithPresets(linter.PresetSQL). - WithLoadForGoAnalysis(). - WithURL("https://github.com/lufeee/execinquery"), - - linter.NewConfig(golinters.NewExhaustive(exhaustiveCfg)). - WithSince(" v1.28.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/nishanths/exhaustive"), - - linter.NewConfig(golinters.NewExhaustiveStruct(exhaustiveStructCfg)). - WithSince("v1.32.0"). - WithPresets(linter.PresetStyle, linter.PresetTest). - WithLoadForGoAnalysis(). - WithURL("https://github.com/mbilski/exhaustivestruct"). - Deprecated("The owner seems to have abandoned the linter.", "v1.46.0", "exhaustruct"), - - linter.NewConfig(golinters.NewExhaustruct(exhaustructCfg)). - WithSince("v1.46.0"). - WithPresets(linter.PresetStyle, linter.PresetTest). - WithLoadForGoAnalysis(). - WithURL("https://github.com/GaijinEntertainment/go-exhaustruct"), - - linter.NewConfig(golinters.NewExportLoopRef()). - WithSince("v1.28.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/kyoh86/exportloopref"), - - linter.NewConfig(golinters.NewForbidigo(forbidigoCfg)). - WithSince("v1.34.0"). - WithPresets(linter.PresetStyle). - // Strictly speaking, - // the additional information is only needed when forbidigoCfg.AnalyzeTypes is chosen by the user. - // But we don't know that here in all cases (sometimes config is not loaded), - // so we have to assume that it is needed to be on the safe side. - WithLoadForGoAnalysis(). - WithURL("https://github.com/ashanbrown/forbidigo"), - - linter.NewConfig(golinters.NewForceTypeAssert()). - WithSince("v1.38.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/gostaticanalysis/forcetypeassert"), - - linter.NewConfig(golinters.NewFunlen(funlenCfg)). - WithSince("v1.18.0"). - WithPresets(linter.PresetComplexity). - WithURL("https://github.com/ultraware/funlen"), - - linter.NewConfig(golinters.NewGci(gciCfg)). - WithSince("v1.30.0"). - WithPresets(linter.PresetFormatting, linter.PresetImport). - WithURL("https://github.com/daixiang0/gci"), - - linter.NewConfig(golinters.NewGinkgoLinter(ginkgolinterCfg)). - WithSince("v1.51.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/nunnatsa/ginkgolinter"), - - linter.NewConfig(golinters.NewGoCheckCompilerDirectives()). - WithSince("v1.51.0"). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/leighmcculloch/gocheckcompilerdirectives"), - - linter.NewConfig(golinters.NewGochecknoglobals()). - WithSince("v1.12.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/leighmcculloch/gochecknoglobals"), - - linter.NewConfig(golinters.NewGochecknoinits()). - WithSince("v1.12.0"). - WithPresets(linter.PresetStyle), - - linter.NewConfig(golinters.NewGoCheckSumType()). - WithSince("v1.55.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/alecthomas/go-check-sumtype"), - - linter.NewConfig(golinters.NewGocognit(gocognitCfg)). - WithSince("v1.20.0"). - WithPresets(linter.PresetComplexity). - WithURL("https://github.com/uudashr/gocognit"), - - linter.NewConfig(golinters.NewGoconst(goconstCfg)). - WithSince("v1.0.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/jgautheron/goconst"), - - linter.NewConfig(golinters.NewGoCritic(gocriticCfg, m.cfg)). - WithSince("v1.12.0"). - WithPresets(linter.PresetStyle, linter.PresetMetaLinter). - WithLoadForGoAnalysis(). - WithURL("https://github.com/go-critic/go-critic"), - - linter.NewConfig(golinters.NewGocyclo(gocycloCfg)). - WithSince("v1.0.0"). - WithPresets(linter.PresetComplexity). - WithURL("https://github.com/fzipp/gocyclo"), - - linter.NewConfig(golinters.NewGodot(godotCfg)). - WithSince("v1.25.0"). - WithPresets(linter.PresetStyle, linter.PresetComment). - WithAutoFix(). - WithURL("https://github.com/tetafro/godot"), - - linter.NewConfig(golinters.NewGodox(godoxCfg)). - WithSince("v1.19.0"). - WithPresets(linter.PresetStyle, linter.PresetComment). - WithURL("https://github.com/matoous/godox"), - - linter.NewConfig(golinters.NewGoerr113()). - WithSince("v1.26.0"). - WithPresets(linter.PresetStyle, linter.PresetError). - WithLoadForGoAnalysis(). - WithURL("https://github.com/Djarvur/go-err113"), - - linter.NewConfig(golinters.NewGofmt(gofmtCfg)). - WithSince("v1.0.0"). - WithPresets(linter.PresetFormatting). - WithAutoFix(). - WithURL("https://pkg.go.dev/cmd/gofmt"), - - linter.NewConfig(golinters.NewGofumpt(gofumptCfg)). - WithSince("v1.28.0"). - WithPresets(linter.PresetFormatting). - WithAutoFix(). - WithURL("https://github.com/mvdan/gofumpt"), - - linter.NewConfig(golinters.NewGoHeader(goheaderCfg)). - WithSince("v1.28.0"). - WithPresets(linter.PresetStyle). - WithAutoFix(). - WithURL("https://github.com/denis-tingaikin/go-header"), - - linter.NewConfig(golinters.NewGoimports(goimportsCfg)). - WithSince("v1.20.0"). - WithPresets(linter.PresetFormatting, linter.PresetImport). - WithAutoFix(). - WithURL("https://pkg.go.dev/golang.org/x/tools/cmd/goimports"), - - linter.NewConfig(golinters.NewGolint(golintCfg)). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/golang/lint"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.41.0", "revive"), - - linter.NewConfig(golinters.NewGoMND(goMndCfg)). - WithSince("v1.22.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/tommy-muehle/go-mnd"), - - linter.NewConfig(golinters.NewGoModDirectives(goModDirectivesCfg)). - WithSince("v1.39.0"). - WithPresets(linter.PresetStyle, linter.PresetModule). - WithURL("https://github.com/ldez/gomoddirectives"), - - linter.NewConfig(golinters.NewGomodguard(gomodguardCfg)). - WithSince("v1.25.0"). - WithPresets(linter.PresetStyle, linter.PresetImport, linter.PresetModule). - WithURL("https://github.com/ryancurrah/gomodguard"), - - linter.NewConfig(golinters.NewGoPrintfFuncName()). - WithSince("v1.23.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/jirfag/go-printf-func-name"), - - linter.NewConfig(golinters.NewGosec(gosecCfg)). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/securego/gosec"). - WithAlternativeNames("gas"), - - linter.NewConfig(golinters.NewGosimple(gosimpleCfg)). - WithEnabledByDefault(). - WithSince("v1.20.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithAlternativeNames(megacheckName). - WithURL("https://github.com/dominikh/go-tools/tree/master/simple"), - - linter.NewConfig(golinters.NewGosmopolitan(gosmopolitanCfg)). - WithSince("v1.53.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/xen0n/gosmopolitan"), - - linter.NewConfig(golinters.NewGovet(govetCfg)). - WithEnabledByDefault(). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs, linter.PresetMetaLinter). - WithAlternativeNames("vet", "vetshadow"). - WithURL("https://pkg.go.dev/cmd/vet"), - - linter.NewConfig(golinters.NewGrouper(grouperCfg)). - WithSince("v1.44.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/leonklingele/grouper"), - - linter.NewConfig(golinters.NewIfshort(ifshortCfg)). - WithSince("v1.36.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/esimonov/ifshort"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.48.0", ""), - - linter.NewConfig(golinters.NewImportAs(importAsCfg)). - WithSince("v1.38.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/julz/importas"), - - linter.NewConfig(golinters.NewINamedParam(inamedparamCfg)). - WithSince("v1.55.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/macabu/inamedparam"), - - linter.NewConfig(golinters.NewIneffassign()). - WithEnabledByDefault(). - WithSince("v1.0.0"). - WithPresets(linter.PresetUnused). - WithURL("https://github.com/gordonklaus/ineffassign"), - - linter.NewConfig(golinters.NewInterfaceBloat(interfaceBloatCfg)). - WithSince("v1.49.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/sashamelentyev/interfacebloat"), - - linter.NewConfig(golinters.NewInterfacer()). - WithSince("v1.0.0"). - 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", ""), - - linter.NewConfig(golinters.NewIntrange()). - WithSince("v1.57.0"). - WithURL("https://github.com/ckaznocha/intrange"). - WithNoopFallback(m.cfg, linter.IsGoLowerThanGo122()), - - linter.NewConfig(golinters.NewIreturn(ireturnCfg)). - WithSince("v1.43.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/butuzov/ireturn"), - - linter.NewConfig(golinters.NewLLL(lllCfg)). - WithSince("v1.8.0"). - WithPresets(linter.PresetStyle), - - linter.NewConfig(golinters.NewLoggerCheck(loggerCheckCfg)). - WithSince("v1.49.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle, linter.PresetBugs). - WithAlternativeNames("logrlint"). - WithURL("https://github.com/timonwong/loggercheck"), - - linter.NewConfig(golinters.NewMaintIdx(maintIdxCfg)). - WithSince("v1.44.0"). - WithPresets(linter.PresetComplexity). - WithURL("https://github.com/yagipy/maintidx"), - - linter.NewConfig(golinters.NewMakezero(makezeroCfg)). - WithSince("v1.34.0"). - WithPresets(linter.PresetStyle, linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/ashanbrown/makezero"), - - linter.NewConfig(golinters.NewMaligned(malignedCfg)). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetPerformance). - WithURL("https://github.com/mdempsky/maligned"). - Deprecated("The repository of the linter has been archived by the owner.", "v1.38.0", "govet 'fieldalignment'"), - - linter.NewConfig(golinters.NewMirror()). - WithSince("v1.53.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/butuzov/mirror"), - - linter.NewConfig(golinters.NewMisspell(misspellCfg)). - WithSince("v1.8.0"). - WithPresets(linter.PresetStyle, linter.PresetComment). - WithAutoFix(). - WithURL("https://github.com/client9/misspell"), - - linter.NewConfig(golinters.NewMustTag(musttagCfg)). - WithSince("v1.51.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle, linter.PresetBugs). - WithURL("https://github.com/go-simpler/musttag"), - - linter.NewConfig(golinters.NewNakedret(nakedretCfg)). - WithSince("v1.19.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/alexkohler/nakedret"), - - linter.NewConfig(golinters.NewNestif(nestifCfg)). - WithSince("v1.25.0"). - WithPresets(linter.PresetComplexity). - WithURL("https://github.com/nakabonne/nestif"), - - linter.NewConfig(golinters.NewNilErr()). - WithSince("v1.38.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/gostaticanalysis/nilerr"), - - linter.NewConfig(golinters.NewNilNil(nilNilCfg)). - WithSince("v1.43.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/Antonboom/nilnil"), - - linter.NewConfig(golinters.NewNLReturn(nlreturnCfg)). - WithSince("v1.30.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/ssgreg/nlreturn"), - - linter.NewConfig(golinters.NewNoctx()). - WithSince("v1.28.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetPerformance, linter.PresetBugs). - WithURL("https://github.com/sonatard/noctx"), - - linter.NewConfig(golinters.NewNoNamedReturns(noNamedReturnsCfg)). - WithSince("v1.46.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/firefart/nonamedreturns"), - - linter.NewConfig(golinters.NewNoSnakeCase()). - WithSince("v1.47.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/sivchari/nosnakecase"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.48.1", "revive(var-naming)"), - - linter.NewConfig(golinters.NewNoSprintfHostPort()). - WithSince("v1.46.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/stbenjam/no-sprintf-host-port"), - - linter.NewConfig(golinters.NewParallelTest(parallelTestCfg)). - WithSince("v1.33.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle, linter.PresetTest). - WithURL("https://github.com/kunwardeep/paralleltest"), - - linter.NewConfig(golinters.NewPerfSprint(perfSprintCfg)). - WithSince("v1.55.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetPerformance). - WithURL("https://github.com/catenacyber/perfsprint"), - - linter.NewConfig(golinters.NewPreAlloc(preallocCfg)). - WithSince("v1.19.0"). - WithPresets(linter.PresetPerformance). - WithURL("https://github.com/alexkohler/prealloc"), - - linter.NewConfig(golinters.NewPredeclared(predeclaredCfg)). - WithSince("v1.35.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/nishanths/predeclared"), - - linter.NewConfig(golinters.NewPromlinter(promlinterCfg)). - WithSince("v1.40.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/yeya24/promlinter"), - - linter.NewConfig(golinters.NewProtoGetter(protogetterCfg)). - WithSince("v1.55.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithAutoFix(). - WithURL("https://github.com/ghostiam/protogetter"), - - linter.NewConfig(golinters.NewReassign(reassignCfg)). - WithSince("1.49.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/curioswitch/go-reassign"), - - linter.NewConfig(golinters.NewRevive(reviveCfg)). - WithSince("v1.37.0"). - WithPresets(linter.PresetStyle, linter.PresetMetaLinter). - ConsiderSlow(). - WithURL("https://github.com/mgechev/revive"), - - linter.NewConfig(golinters.NewRowsErrCheck(rowserrcheckCfg)). - WithSince("v1.23.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs, linter.PresetSQL). - WithURL("https://github.com/jingyugao/rowserrcheck"), - - linter.NewConfig(golinters.NewSlogLint(sloglintCfg)). - WithSince("v1.55.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle, linter.PresetFormatting). - WithURL("https://github.com/go-simpler/sloglint"), - - linter.NewConfig(golinters.NewScopelint()). - WithSince("v1.12.0"). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/kyoh86/scopelint"). - Deprecated("The repository of the linter has been deprecated by the owner.", "v1.39.0", "exportloopref"), - - linter.NewConfig(golinters.NewSQLCloseCheck()). - WithSince("v1.28.0"). - WithPresets(linter.PresetBugs, linter.PresetSQL). - WithLoadForGoAnalysis(). - WithURL("https://github.com/ryanrolds/sqlclosecheck"), - - linter.NewConfig(golinters.NewSpancheck(spancheckCfg)). - WithSince("v1.56.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs). - WithURL("https://github.com/jjti/go-spancheck"), - - linter.NewConfig(golinters.NewStaticcheck(staticcheckCfg)). - WithEnabledByDefault(). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs, linter.PresetMetaLinter). - WithAlternativeNames(megacheckName). - WithURL("https://staticcheck.io/"), - - linter.NewConfig(golinters.NewStructcheck(structcheckCfg)). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetUnused). - WithURL("https://github.com/opennota/check"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), - - linter.NewConfig(golinters.NewStylecheck(stylecheckCfg)). - WithSince("v1.20.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/dominikh/go-tools/tree/master/stylecheck"), - - linter.NewConfig(golinters.NewTagAlign(tagalignCfg)). - WithSince("v1.53.0"). - WithPresets(linter.PresetStyle, linter.PresetFormatting). - WithAutoFix(). - WithURL("https://github.com/4meepo/tagalign"), - - linter.NewConfig(golinters.NewTagliatelle(tagliatelleCfg)). - WithSince("v1.40.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/ldez/tagliatelle"), - - linter.NewConfig(golinters.NewTenv(tenvCfg)). - WithSince("v1.43.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/sivchari/tenv"), - - linter.NewConfig(golinters.NewTestableexamples()). - WithSince("v1.50.0"). - WithPresets(linter.PresetTest). - WithURL("https://github.com/maratori/testableexamples"), - - linter.NewConfig(golinters.NewTestifylint(testifylintCfg)). - WithSince("v1.55.0"). - WithPresets(linter.PresetTest, linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/Antonboom/testifylint"), - - linter.NewConfig(golinters.NewTestpackage(testpackageCfg)). - WithSince("v1.25.0"). - WithPresets(linter.PresetStyle, linter.PresetTest). - WithURL("https://github.com/maratori/testpackage"), - - linter.NewConfig(golinters.NewThelper(thelperCfg)). - WithSince("v1.34.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/kulti/thelper"), - - linter.NewConfig(golinters.NewTparallel()). - WithSince("v1.32.0"). - WithPresets(linter.PresetStyle, linter.PresetTest). - WithLoadForGoAnalysis(). - WithURL("https://github.com/moricho/tparallel"), - - linter.NewConfig(golinters.NewTypecheck()). - WithInternal(). - WithEnabledByDefault(). - WithSince("v1.3.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetBugs). - WithURL(""), - - linter.NewConfig(golinters.NewUnconvert()). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/mdempsky/unconvert"), - - linter.NewConfig(golinters.NewUnparam(unparamCfg)). - WithSince("v1.9.0"). - WithPresets(linter.PresetUnused). - WithLoadForGoAnalysis(). - WithURL("https://github.com/mvdan/unparam"), - - linter.NewConfig(golinters.NewUnused(unusedCfg, staticcheckCfg)). - WithEnabledByDefault(). - WithSince("v1.20.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetUnused). - WithAlternativeNames(megacheckName). - ConsiderSlow(). - WithChangeTypes(). - WithURL("https://github.com/dominikh/go-tools/tree/master/unused"), - - linter.NewConfig(golinters.NewUseStdlibVars(usestdlibvars)). - WithSince("v1.48.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/sashamelentyev/usestdlibvars"), - - linter.NewConfig(golinters.NewVarcheck(varcheckCfg)). - WithSince("v1.0.0"). - WithLoadForGoAnalysis(). - WithPresets(linter.PresetUnused). - WithURL("https://github.com/opennota/check"). - Deprecated("The owner seems to have abandoned the linter.", "v1.49.0", "unused"), - - linter.NewConfig(golinters.NewVarnamelen(varnamelenCfg)). - WithSince("v1.43.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/blizzy78/varnamelen"), - - linter.NewConfig(golinters.NewWastedAssign()). - WithSince("v1.38.0"). - WithPresets(linter.PresetStyle). - WithLoadForGoAnalysis(). - WithURL("https://github.com/sanposhiho/wastedassign"), - - linter.NewConfig(golinters.NewWhitespace(whitespaceCfg)). - WithSince("v1.19.0"). - WithPresets(linter.PresetStyle). - WithAutoFix(). - WithURL("https://github.com/ultraware/whitespace"), - - linter.NewConfig(golinters.NewWrapcheck(wrapcheckCfg)). - WithSince("v1.32.0"). - WithPresets(linter.PresetStyle, linter.PresetError). - WithLoadForGoAnalysis(). - WithURL("https://github.com/tomarrell/wrapcheck"), - - linter.NewConfig(golinters.NewWSL(wslCfg)). - WithSince("v1.20.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/bombsimon/wsl"), - - linter.NewConfig(golinters.NewZerologLint()). - WithSince("v1.53.0"). - WithPresets(linter.PresetBugs). - WithLoadForGoAnalysis(). - WithURL("https://github.com/ykadowak/zerologlint"), - - // nolintlint must be last because it looks at the results of all the previous linters for unused nolint directives - linter.NewConfig(golinters.NewNoLintLint(noLintLintCfg)). - WithSince("v1.26.0"). - WithPresets(linter.PresetStyle). - WithURL("https://github.com/golangci/golangci-lint/blob/master/pkg/golinters/nolintlint/README.md"), - ) - - return linters + if a.DoesChangeTypes != b.DoesChangeTypes { + return b.DoesChangeTypes // move type-changing linters to the end to optimize speed + } + return a.Name() < b.Name() + }) + + return resultLinters, nil } func (m *Manager) GetAllEnabledByDefaultLinters() []*linter.Config { var ret []*linter.Config - for _, lc := range m.GetAllSupportedLinterConfigs() { + for _, lc := range m.linters { if lc.EnabledByDefault { ret = append(ret, lc) } @@ -939,32 +140,143 @@ func (m *Manager) GetAllEnabledByDefaultLinters() []*linter.Config { return ret } -func (m *Manager) GetAllLinterConfigsForPreset(p string) []*linter.Config { - var ret []*linter.Config - for _, lc := range m.GetAllSupportedLinterConfigs() { - if lc.IsDeprecated() { - continue +//nolint:gocyclo // the complexity cannot be reduced. +func (m *Manager) build(enabledByDefaultLinters []*linter.Config) map[string]*linter.Config { + m.debugf("Linters config: %#v", m.cfg.Linters) + + resultLintersSet := map[string]*linter.Config{} + switch { + case m.cfg.Linters.DisableAll: + // no default linters + case len(m.cfg.Linters.Presets) != 0: + // imply --disable-all + case m.cfg.Linters.EnableAll: + resultLintersSet = linterConfigsToMap(m.linters) + default: + resultLintersSet = linterConfigsToMap(enabledByDefaultLinters) + } + + // --presets can only add linters to default set + for _, p := range m.cfg.Linters.Presets { + for _, lc := range m.GetAllLinterConfigsForPreset(p) { + lc := lc + resultLintersSet[lc.Name()] = lc } + } - for _, ip := range lc.InPresets { - if p == ip { - ret = append(ret, lc) - break + // --fast removes slow linters from current set. + // It should be after --presets to be able to run only fast linters in preset. + // It should be before --enable and --disable to be able to enable or disable specific linter. + if m.cfg.Linters.Fast { + for name, lc := range resultLintersSet { + if lc.IsSlowLinter() { + delete(resultLintersSet, name) } } } - return ret + for _, name := range m.cfg.Linters.Enable { + for _, lc := range m.GetLinterConfigs(name) { + // it's important to use lc.Name() nor name because name can be alias + resultLintersSet[lc.Name()] = lc + } + } + + for _, name := range m.cfg.Linters.Disable { + for _, lc := range m.GetLinterConfigs(name) { + // it's important to use lc.Name() nor name because name can be alias + delete(resultLintersSet, lc.Name()) + } + } + + // typecheck is not a real linter and cannot be disabled. + if _, ok := resultLintersSet["typecheck"]; !ok && (m.cfg == nil || !m.cfg.InternalCmdTest) { + for _, lc := range m.GetLinterConfigs("typecheck") { + // it's important to use lc.Name() nor name because name can be alias + resultLintersSet[lc.Name()] = lc + } + } + + return resultLintersSet } -func linterConfigsToMap(lcs []*linter.Config) map[string]*linter.Config { - ret := map[string]*linter.Config{} +func (m *Manager) combineGoAnalysisLinters(linters map[string]*linter.Config) { + var goanalysisLinters []*goanalysis.Linter + goanalysisPresets := map[string]bool{} + for _, lc := range linters { + lnt, ok := lc.Linter.(*goanalysis.Linter) + if !ok { + continue + } + if lnt.LoadMode() == goanalysis.LoadModeWholeProgram { + // It's ineffective by CPU and memory to run whole-program and incremental analyzers at once. + continue + } + goanalysisLinters = append(goanalysisLinters, lnt) + for _, p := range lc.InPresets { + goanalysisPresets[p] = true + } + } + + if len(goanalysisLinters) <= 1 { + m.debugf("Didn't combine go/analysis linters: got only %d linters", len(goanalysisLinters)) + return + } + + for _, lnt := range goanalysisLinters { + delete(linters, lnt.Name()) + } + + // Make order of execution of go/analysis analyzers stable. + sort.Slice(goanalysisLinters, func(i, j int) bool { + a, b := goanalysisLinters[i], goanalysisLinters[j] + + if b.Name() == linter.LastLinter { + return true + } + + if a.Name() == linter.LastLinter { + return false + } + + return a.Name() <= b.Name() + }) + + ml := goanalysis.NewMetaLinter(goanalysisLinters) + + presets := maps.Keys(goanalysisPresets) + sort.Strings(presets) + + mlConfig := &linter.Config{ + Linter: ml, + EnabledByDefault: false, + InPresets: presets, + AlternativeNames: nil, + OriginalURL: "", + } + + mlConfig = mlConfig.WithLoadForGoAnalysis() + + linters[ml.Name()] = mlConfig + m.debugf("Combined %d go/analysis linters into one metalinter", len(goanalysisLinters)) +} + +func (m *Manager) verbosePrintLintersStatus(lcs map[string]*linter.Config) { + var linterNames []string for _, lc := range lcs { - lc := lc // local copy - ret[lc.Name()] = lc + if lc.Internal { + continue + } + + linterNames = append(linterNames, lc.Name()) } + sort.Strings(linterNames) + m.log.Infof("Active %d linters: %s", len(linterNames), linterNames) - return ret + if len(m.cfg.Linters.Presets) != 0 { + sort.Strings(m.cfg.Linters.Presets) + m.log.Infof("Active presets: %s", m.cfg.Linters.Presets) + } } func AllPresets() []string { @@ -985,20 +297,11 @@ func AllPresets() []string { } } -// Trims the Go version to keep only M.m. -// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0). -// https://go.dev/doc/toolchain#versions -// This a problem with staticcheck and gocritic. -func trimGoVersion(v string) string { - if v == "" { - return "" - } - - exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`) - - if exp.MatchString(v) { - return exp.FindStringSubmatch(v)[1] +func linterConfigsToMap(lcs []*linter.Config) map[string]*linter.Config { + ret := map[string]*linter.Config{} + for _, lc := range lcs { + ret[lc.Name()] = lc } - return v + return ret } diff --git a/pkg/lint/lintersdb/manager_test.go b/pkg/lint/lintersdb/manager_test.go index 0b05c9ec39d7..87fffa82f9cf 100644 --- a/pkg/lint/lintersdb/manager_test.go +++ b/pkg/lint/lintersdb/manager_test.go @@ -4,43 +4,223 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/golangci/golangci-lint/pkg/config" + "github.com/golangci/golangci-lint/pkg/golinters/goanalysis" + "github.com/golangci/golangci-lint/pkg/lint/linter" + "github.com/golangci/golangci-lint/pkg/logutils" ) -func Test_trimGoVersion(t *testing.T) { - testCases := []struct { - desc string - version string - expected string - }{ +func TestManager_GetEnabledLintersMap(t *testing.T) { + cfg := config.NewDefault() + cfg.Linters.DisableAll = true + cfg.Linters.Enable = []string{"gofmt"} + + m, err := NewManager(logutils.NewStderrLog("skip"), cfg, NewLinterBuilder()) + require.NoError(t, err) + + lintersMap, err := m.GetEnabledLintersMap() + require.NoError(t, err) + + gofmtConfigs := m.GetLinterConfigs("gofmt") + typecheckConfigs := m.GetLinterConfigs("typecheck") + + expected := map[string]*linter.Config{ + "gofmt": gofmtConfigs[0], + "typecheck": typecheckConfigs[0], + } + + assert.Equal(t, expected, lintersMap) +} + +func TestManager_GetOptimizedLinters(t *testing.T) { + cfg := config.NewDefault() + cfg.Linters.DisableAll = true + cfg.Linters.Enable = []string{"gofmt"} + + m, err := NewManager(logutils.NewStderrLog("skip"), cfg, NewLinterBuilder()) + require.NoError(t, err) + + optimizedLinters, err := m.GetOptimizedLinters() + require.NoError(t, err) + + var gaLinters []*goanalysis.Linter + for _, l := range m.GetLinterConfigs("gofmt") { + gaLinters = append(gaLinters, l.Linter.(*goanalysis.Linter)) + } + for _, l := range m.GetLinterConfigs("typecheck") { + gaLinters = append(gaLinters, l.Linter.(*goanalysis.Linter)) + } + + mlConfig := &linter.Config{ + Linter: goanalysis.NewMetaLinter(gaLinters), + InPresets: []string{"bugs", "format"}, + } + + expected := []*linter.Config{mlConfig.WithLoadForGoAnalysis()} + + assert.Equal(t, expected, optimizedLinters) +} + +func TestManager_build(t *testing.T) { + type cs struct { + cfg config.Linters + name string // test case name + def []string // enabled by default linters + exp []string // alphabetically ordered enabled linter names + } + + allMegacheckLinterNames := []string{"gosimple", "staticcheck", "unused"} + + cases := []cs{ + { + cfg: config.Linters{ + Disable: []string{"megacheck"}, + }, + name: "disable all linters from megacheck", + def: allMegacheckLinterNames, + exp: []string{"typecheck"}, // all disabled + }, + { + cfg: config.Linters{ + Disable: []string{"staticcheck"}, + }, + name: "disable only staticcheck", + def: allMegacheckLinterNames, + exp: []string{"gosimple", "typecheck", "unused"}, + }, + { + name: "don't merge into megacheck", + def: allMegacheckLinterNames, + exp: []string{"gosimple", "staticcheck", "typecheck", "unused"}, + }, + { + name: "expand megacheck", + cfg: config.Linters{ + Enable: []string{"megacheck"}, + }, + def: nil, + exp: []string{"gosimple", "staticcheck", "typecheck", "unused"}, + }, { - desc: "patched version", - version: "1.22.0", - expected: "1.22", + name: "don't disable anything", + def: []string{"gofmt", "govet", "typecheck"}, + exp: []string{"gofmt", "govet", "typecheck"}, }, { - desc: "minor version", - version: "1.22", - expected: "1.22", + name: "enable gosec by gas alias", + cfg: config.Linters{ + Enable: []string{"gas"}, + }, + exp: []string{"gosec", "typecheck"}, }, { - desc: "RC version", - version: "1.22rc1", - expected: "1.22", + name: "enable gosec by primary name", + cfg: config.Linters{ + Enable: []string{"gosec"}, + }, + exp: []string{"gosec", "typecheck"}, }, { - desc: "alpha version", - version: "1.22alpha1", - expected: "1.22", + name: "enable gosec by both names", + cfg: config.Linters{ + Enable: []string{"gosec", "gas"}, + }, + exp: []string{"gosec", "typecheck"}, }, { - desc: "beta version", - version: "1.22beta1", - expected: "1.22", + name: "disable gosec by gas alias", + cfg: config.Linters{ + Disable: []string{"gas"}, + }, + def: []string{"gosec"}, + exp: []string{"typecheck"}, }, { - desc: "semver RC version", - version: "1.22.0-rc1", - expected: "1.22", + name: "disable gosec by primary name", + cfg: config.Linters{ + Disable: []string{"gosec"}, + }, + def: []string{"gosec"}, + exp: []string{"typecheck"}, + }, + } + + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + m, err := NewManager(logutils.NewStderrLog("skip"), &config.Config{Linters: c.cfg}, NewLinterBuilder()) + require.NoError(t, err) + + var defaultLinters []*linter.Config + for _, ln := range c.def { + lcs := m.GetLinterConfigs(ln) + assert.NotNil(t, lcs, ln) + defaultLinters = append(defaultLinters, lcs...) + } + + els := m.build(defaultLinters) + var enabledLinters []string + for ln, lc := range els { + assert.Equal(t, ln, lc.Name()) + enabledLinters = append(enabledLinters, ln) + } + + assert.ElementsMatch(t, c.exp, enabledLinters) + }) + } +} + +func TestManager_combineGoAnalysisLinters(t *testing.T) { + m, err := NewManager(nil, nil) + require.NoError(t, err) + + foo := goanalysis.NewLinter("foo", "example foo", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo) + bar := goanalysis.NewLinter("bar", "example bar", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo) + + testCases := []struct { + desc string + linters map[string]*linter.Config + expected map[string]*linter.Config + }{ + { + desc: "no combined, one linter", + linters: map[string]*linter.Config{ + "foo": { + Linter: foo, + InPresets: []string{"A"}, + }, + }, + expected: map[string]*linter.Config{ + "foo": { + Linter: foo, + InPresets: []string{"A"}, + }, + }, + }, + { + desc: "combined, several linters", + linters: map[string]*linter.Config{ + "foo": { + Linter: foo, + InPresets: []string{"A"}, + }, + "bar": { + Linter: bar, + InPresets: []string{"B"}, + }, + }, + expected: func() map[string]*linter.Config { + mlConfig := &linter.Config{ + Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{bar, foo}), + InPresets: []string{"A", "B"}, + } + + return map[string]*linter.Config{ + "goanalysis_metalinter": mlConfig.WithLoadForGoAnalysis(), + } + }(), }, } @@ -49,8 +229,9 @@ func Test_trimGoVersion(t *testing.T) { t.Run(test.desc, func(t *testing.T) { t.Parallel() - version := trimGoVersion(test.version) - assert.Equal(t, test.expected, version) + m.combineGoAnalysisLinters(test.linters) + + assert.Equal(t, test.expected, test.linters) }) } } diff --git a/pkg/lint/lintersdb/validator.go b/pkg/lint/lintersdb/validator.go index 9ea568f1a44e..364d5082a5e6 100644 --- a/pkg/lint/lintersdb/validator.go +++ b/pkg/lint/lintersdb/validator.go @@ -14,9 +14,29 @@ type Validator struct { } func NewValidator(m *Manager) *Validator { - return &Validator{ - m: m, + return &Validator{m: m} +} + +// Validate validates the configuration by calling all other validators for different +// sections in the configuration and then some additional linter validation functions. +func (v Validator) Validate(cfg *config.Config) error { + err := cfg.Validate() + if err != nil { + return err + } + + validators := []func(cfg *config.Linters) error{ + v.validateLintersNames, + v.validatePresets, + } + + for _, v := range validators { + if err := v(&cfg.Linters); err != nil { + return err + } } + + return nil } func (v Validator) validateLintersNames(cfg *config.Linters) error { @@ -55,56 +75,3 @@ func (v Validator) validatePresets(cfg *config.Linters) error { return nil } - -func (v Validator) validateAllDisableEnableOptions(cfg *config.Linters) error { - if cfg.EnableAll && cfg.DisableAll { - return errors.New("--enable-all and --disable-all options must not be combined") - } - - if cfg.DisableAll { - if len(cfg.Enable) == 0 && len(cfg.Presets) == 0 { - return errors.New("all linters were disabled, but no one linter was enabled: must enable at least one") - } - - if len(cfg.Disable) != 0 { - return fmt.Errorf("can't combine options --disable-all and --disable %s", cfg.Disable[0]) - } - } - - if cfg.EnableAll && len(cfg.Enable) != 0 && !cfg.Fast { - return fmt.Errorf("can't combine options --enable-all and --enable %s", cfg.Enable[0]) - } - - return nil -} - -func (v Validator) validateDisabledAndEnabledAtOneMoment(cfg *config.Linters) error { - enabledLintersSet := map[string]bool{} - for _, name := range cfg.Enable { - enabledLintersSet[name] = true - } - - for _, name := range cfg.Disable { - if enabledLintersSet[name] { - return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name) - } - } - - return nil -} - -func (v Validator) validateEnabledDisabledLintersConfig(cfg *config.Linters) error { - validators := []func(cfg *config.Linters) error{ - v.validateLintersNames, - v.validatePresets, - v.validateAllDisableEnableOptions, - v.validateDisabledAndEnabledAtOneMoment, - } - for _, v := range validators { - if err := v(cfg); err != nil { - return err - } - } - - return nil -} diff --git a/pkg/lint/lintersdb/validator_test.go b/pkg/lint/lintersdb/validator_test.go index 944fadd77663..1bfeb7078cb0 100644 --- a/pkg/lint/lintersdb/validator_test.go +++ b/pkg/lint/lintersdb/validator_test.go @@ -53,72 +53,6 @@ var validatePresetsErrorTestCases = []validateErrorTestCase{ }, } -var validateDisabledAndEnabledAtOneMomentErrorTestCases = []validateErrorTestCase{ - { - desc: "disable one linter of the enabled linters", - cfg: &config.Linters{ - Enable: []string{"dupl", "gofmt", "misspell"}, - Disable: []string{"dupl", "gosec", "nolintlint"}, - }, - expected: `linter "dupl" can't be disabled and enabled at one moment`, - }, - { - desc: "disable multiple enabled linters", - cfg: &config.Linters{ - Enable: []string{"dupl", "gofmt", "misspell"}, - Disable: []string{"dupl", "gofmt", "misspell"}, - }, - expected: `linter "dupl" can't be disabled and enabled at one moment`, - }, -} - -var validateAllDisableEnableOptionsErrorTestCases = []validateErrorTestCase{ - { - desc: "enable-all and disable-all", - cfg: &config.Linters{ - Enable: nil, - EnableAll: true, - Disable: nil, - DisableAll: true, - Fast: false, - }, - expected: "--enable-all and --disable-all options must not be combined", - }, - { - desc: "disable-all and disable no enable no preset", - cfg: &config.Linters{ - Enable: nil, - EnableAll: false, - Disable: []string{"dupl", "gofmt", "misspell"}, - DisableAll: true, - Fast: false, - }, - expected: "all linters were disabled, but no one linter was enabled: must enable at least one", - }, - { - desc: "disable-all and disable with enable", - cfg: &config.Linters{ - Enable: []string{"nolintlint"}, - EnableAll: false, - Disable: []string{"dupl", "gofmt", "misspell"}, - DisableAll: true, - Fast: false, - }, - expected: "can't combine options --disable-all and --disable dupl", - }, - { - desc: "enable-all and enable", - cfg: &config.Linters{ - Enable: []string{"dupl", "gofmt", "misspell"}, - EnableAll: true, - Disable: nil, - DisableAll: false, - Fast: false, - }, - expected: "can't combine options --enable-all and --enable dupl", - }, -} - type validatorTestCase struct { desc string cfg *config.Linters @@ -172,116 +106,43 @@ var validatePresetsTestCases = []validatorTestCase{ }, } -var validateDisabledAndEnabledAtOneMomentTestCases = []validatorTestCase{ - { - desc: "2 different sets", - cfg: &config.Linters{ - Enable: []string{"dupl", "gofmt", "misspell"}, - Disable: []string{"goimports", "gosec", "nolintlint"}, - }, - }, - { - desc: "only enable", - cfg: &config.Linters{ - Enable: []string{"goimports", "gosec", "nolintlint"}, - Disable: nil, - }, - }, - { - desc: "only disable", - cfg: &config.Linters{ - Enable: nil, - Disable: []string{"dupl", "gofmt", "misspell"}, - }, - }, - { - desc: "no sets", - cfg: &config.Linters{ - Enable: nil, - Disable: nil, - }, - }, -} - -var validateAllDisableEnableOptionsTestCases = []validatorTestCase{ - { - desc: "nothing", - cfg: &config.Linters{}, - }, - { - desc: "enable and disable", - cfg: &config.Linters{ - Enable: []string{"goimports", "gosec", "nolintlint"}, - EnableAll: false, - Disable: []string{"dupl", "gofmt", "misspell"}, - DisableAll: false, - }, - }, - { - desc: "disable-all and enable", - cfg: &config.Linters{ - Enable: []string{"goimports", "gosec", "nolintlint"}, - EnableAll: false, - Disable: nil, - DisableAll: true, - }, - }, - { - desc: "enable-all and disable", - cfg: &config.Linters{ - Enable: nil, - EnableAll: true, - Disable: []string{"goimports", "gosec", "nolintlint"}, - DisableAll: false, - }, - }, - { - desc: "enable-all and enable and fast", - cfg: &config.Linters{ - Enable: []string{"dupl", "gofmt", "misspell"}, - EnableAll: true, - Disable: nil, - DisableAll: false, - Fast: true, - }, - }, -} +func TestValidator_Validate(t *testing.T) { + m, err := NewManager(nil, nil, NewLinterBuilder()) + require.NoError(t, err) -func TestValidator_validateEnabledDisabledLintersConfig(t *testing.T) { - v := NewValidator(NewManager(nil, nil)) + v := NewValidator(m) var testCases []validatorTestCase testCases = append(testCases, validateLintersNamesTestCases...) testCases = append(testCases, validatePresetsTestCases...) - testCases = append(testCases, validateDisabledAndEnabledAtOneMomentTestCases...) - testCases = append(testCases, validateAllDisableEnableOptionsTestCases...) for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() - err := v.validateEnabledDisabledLintersConfig(test.cfg) + err := v.Validate(&config.Config{Linters: *test.cfg}) require.NoError(t, err) }) } } -func TestValidator_validateEnabledDisabledLintersConfig_error(t *testing.T) { - v := NewValidator(NewManager(nil, nil)) +func TestValidator_Validate_error(t *testing.T) { + m, err := NewManager(nil, nil, NewLinterBuilder()) + require.NoError(t, err) + + v := NewValidator(m) var testCases []validateErrorTestCase testCases = append(testCases, validateLintersNamesErrorTestCases...) testCases = append(testCases, validatePresetsErrorTestCases...) - testCases = append(testCases, validateDisabledAndEnabledAtOneMomentErrorTestCases...) - testCases = append(testCases, validateAllDisableEnableOptionsErrorTestCases...) for _, test := range testCases { test := test t.Run(test.desc, func(t *testing.T) { t.Parallel() - err := v.validateEnabledDisabledLintersConfig(test.cfg) + err := v.Validate(&config.Config{Linters: *test.cfg}) require.Error(t, err) require.EqualError(t, err, test.expected) @@ -290,7 +151,10 @@ func TestValidator_validateEnabledDisabledLintersConfig_error(t *testing.T) { } func TestValidator_validateLintersNames(t *testing.T) { - v := NewValidator(NewManager(nil, nil)) + m, err := NewManager(nil, nil, NewLinterBuilder()) + require.NoError(t, err) + + v := NewValidator(m) for _, test := range validateLintersNamesTestCases { test := test @@ -304,7 +168,10 @@ func TestValidator_validateLintersNames(t *testing.T) { } func TestValidator_validateLintersNames_error(t *testing.T) { - v := NewValidator(NewManager(nil, nil)) + m, err := NewManager(nil, nil, NewLinterBuilder()) + require.NoError(t, err) + + v := NewValidator(m) for _, test := range validateLintersNamesErrorTestCases { test := test @@ -348,63 +215,3 @@ func TestValidator_validatePresets_error(t *testing.T) { }) } } - -func TestValidator_validateDisabledAndEnabledAtOneMoment(t *testing.T) { - v := NewValidator(nil) - - for _, test := range validateDisabledAndEnabledAtOneMomentTestCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - err := v.validateDisabledAndEnabledAtOneMoment(test.cfg) - require.NoError(t, err) - }) - } -} - -func TestValidator_validateDisabledAndEnabledAtOneMoment_error(t *testing.T) { - v := NewValidator(nil) - - for _, test := range validateDisabledAndEnabledAtOneMomentErrorTestCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - err := v.validateDisabledAndEnabledAtOneMoment(test.cfg) - require.Error(t, err) - - require.EqualError(t, err, test.expected) - }) - } -} - -func TestValidator_validateAllDisableEnableOptions(t *testing.T) { - v := NewValidator(nil) - - for _, test := range validateAllDisableEnableOptionsTestCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - err := v.validateAllDisableEnableOptions(test.cfg) - require.NoError(t, err) - }) - } -} - -func TestValidator_validateAllDisableEnableOptions_error(t *testing.T) { - v := NewValidator(nil) - - for _, test := range validateAllDisableEnableOptionsErrorTestCases { - test := test - t.Run(test.desc, func(t *testing.T) { - t.Parallel() - - err := v.validateAllDisableEnableOptions(test.cfg) - require.Error(t, err) - - require.EqualError(t, err, test.expected) - }) - } -} diff --git a/pkg/lint/runner.go b/pkg/lint/runner.go index e7cb17555a5b..d5571c236def 100644 --- a/pkg/lint/runner.go +++ b/pkg/lint/runner.go @@ -27,8 +27,7 @@ type Runner struct { Log logutils.Log } -func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env, - es *lintersdb.EnabledSet, +func NewRunner(log logutils.Log, cfg *config.Config, goenv *goutil.Env, lineCache *fsutils.LineCache, fileCache *fsutils.FileCache, dbManager *lintersdb.Manager, pkgs []*gopackages.Package) (*Runner, error) { // Beware that some processors need to add the path prefix when working with paths @@ -50,7 +49,7 @@ func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env, return nil, err } - enabledLinters, err := es.GetEnabledLintersMap() + enabledLinters, err := dbManager.GetEnabledLintersMap() if err != nil { return nil, fmt.Errorf("failed to get enabled linters: %w", err) } diff --git a/pkg/result/processors/nolint_test.go b/pkg/result/processors/nolint_test.go index cd70189e94be..1588a2e14520 100644 --- a/pkg/result/processors/nolint_test.go +++ b/pkg/result/processors/nolint_test.go @@ -34,7 +34,10 @@ func newNolint2FileIssue(line int) result.Issue { } func newTestNolintProcessor(log logutils.Log) *Nolint { - return NewNolint(log, lintersdb.NewManager(nil, nil), nil) + dbManager, _ := lintersdb.NewManager(log, config.NewDefault(), + lintersdb.NewPluginBuilder(log), lintersdb.NewLinterBuilder()) + + return NewNolint(log, dbManager, nil) } func getMockLog() *logutils.MockLog { @@ -283,11 +286,11 @@ func TestNolintUnused(t *testing.T) { enabledSetLog.On("Infof", "Active %d linters: %s", len(enabledLinters), enabledLinters) cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: enabledLinters}} - dbManager := lintersdb.NewManager(cfg, nil) - enabledLintersSet := lintersdb.NewEnabledSet(dbManager, lintersdb.NewValidator(dbManager), enabledSetLog, cfg) + dbManager, err := lintersdb.NewManager(enabledSetLog, cfg, lintersdb.NewLinterBuilder()) + require.NoError(t, err) - enabledLintersMap, err := enabledLintersSet.GetEnabledLintersMap() + enabledLintersMap, err := dbManager.GetEnabledLintersMap() require.NoError(t, err) return NewNolint(log, dbManager, enabledLintersMap) @@ -347,11 +350,13 @@ func TestNolintUnused(t *testing.T) { enabledSetLog.On("Infof", "Active %d linters: %s", 1, []string{"nolintlint"}) cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: []string{"nolintlint"}}} - dbManager := lintersdb.NewManager(cfg, nil) - enabledLintersSet := lintersdb.NewEnabledSet(dbManager, lintersdb.NewValidator(dbManager), enabledSetLog, cfg) - enabledLintersMap, err := enabledLintersSet.GetEnabledLintersMap() + dbManager, err := lintersdb.NewManager(enabledSetLog, cfg, lintersdb.NewLinterBuilder()) require.NoError(t, err) + + enabledLintersMap, err := dbManager.GetEnabledLintersMap() + require.NoError(t, err) + p := NewNolint(log, dbManager, enabledLintersMap) defer p.Finish() diff --git a/scripts/expand_website_templates/main.go b/scripts/expand_website_templates/main.go index e4c5a20b30df..4056429637c1 100644 --- a/scripts/expand_website_templates/main.go +++ b/scripts/expand_website_templates/main.go @@ -204,8 +204,11 @@ func getDefaultExclusions() string { } func getLintersListMarkdown(enabled bool) string { + dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) + + lcs := dbManager.GetAllSupportedLinterConfigs() + var neededLcs []*linter.Config - lcs := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() for _, lc := range lcs { if lc.Internal { continue @@ -322,8 +325,9 @@ type authorDetails struct { func getThanksList() string { addedAuthors := map[string]*authorDetails{} + dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) - for _, lc := range lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() { + for _, lc := range dbManager.GetAllSupportedLinterConfigs() { if lc.Internal { continue } @@ -491,7 +495,8 @@ func extractExampleSnippets(example []byte) (*SettingSnippets, error) { } func getLintersSettingSections(node, nextNode *yaml.Node) (string, error) { - lcs := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() + dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) + lcs := dbManager.GetAllSupportedLinterConfigs() var lintersDesc = make(map[string]string) for _, lc := range lcs { diff --git a/test/enabled_linters_test.go b/test/enabled_linters_test.go index e2589ef894c2..0224223156c4 100644 --- a/test/enabled_linters_test.go +++ b/test/enabled_linters_test.go @@ -7,6 +7,8 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" + "github.com/golangci/golangci-lint/pkg/lint/lintersdb" "github.com/golangci/golangci-lint/test/testshared" ) @@ -29,7 +31,7 @@ func TestEnabledLinters(t *testing.T) { disable: - govet `, - enabledLinters: getEnabledByDefaultFastLintersExcept("govet"), + enabledLinters: getEnabledByDefaultFastLintersExcept(t, "govet"), }, { name: "enable revive in config", @@ -38,12 +40,12 @@ func TestEnabledLinters(t *testing.T) { enable: - revive `, - enabledLinters: getEnabledByDefaultFastLintersWith("revive"), + enabledLinters: getEnabledByDefaultFastLintersWith(t, "revive"), }, { name: "disable govet in cmd", args: []string{"-Dgovet"}, - enabledLinters: getEnabledByDefaultFastLintersExcept("govet"), + enabledLinters: getEnabledByDefaultFastLintersExcept(t, "govet"), }, { name: "enable gofmt in cmd and enable revive in config", @@ -53,7 +55,7 @@ func TestEnabledLinters(t *testing.T) { enable: - revive `, - enabledLinters: getEnabledByDefaultFastLintersWith("revive", "gofmt"), + enabledLinters: getEnabledByDefaultFastLintersWith(t, "revive", "gofmt"), }, { name: "fast option in config", @@ -61,7 +63,7 @@ func TestEnabledLinters(t *testing.T) { linters: fast: true `, - enabledLinters: getEnabledByDefaultFastLintersWith(), + enabledLinters: getEnabledByDefaultFastLintersWith(t), noImplicitFast: true, }, { @@ -70,13 +72,13 @@ func TestEnabledLinters(t *testing.T) { linters: fast: false `, - enabledLinters: getEnabledByDefaultLinters(), + enabledLinters: getEnabledByDefaultLinters(t), noImplicitFast: true, }, { name: "set fast option in command-line", args: []string{"--fast"}, - enabledLinters: getEnabledByDefaultFastLintersWith(), + enabledLinters: getEnabledByDefaultFastLintersWith(t), noImplicitFast: true, }, { @@ -86,7 +88,7 @@ func TestEnabledLinters(t *testing.T) { fast: false `, args: []string{"--fast"}, - enabledLinters: getEnabledByDefaultFastLintersWith(), + enabledLinters: getEnabledByDefaultFastLintersWith(t), noImplicitFast: true, }, { @@ -96,13 +98,13 @@ func TestEnabledLinters(t *testing.T) { fast: true `, args: []string{"--fast=false"}, - enabledLinters: getEnabledByDefaultLinters(), + enabledLinters: getEnabledByDefaultLinters(t), noImplicitFast: true, }, { name: "fast option combined with enable and enable-all", args: []string{"--enable-all", "--fast", "--enable=unused"}, - enabledLinters: getAllFastLintersWith("unused"), + enabledLinters: getAllFastLintersWith(t, "unused"), noImplicitFast: true, }, } @@ -127,7 +129,7 @@ func TestEnabledLinters(t *testing.T) { Runner(). Run() - sort.StringSlice(c.enabledLinters).Sort() + sort.Strings(c.enabledLinters) r.ExpectOutputContains(fmt.Sprintf("Active %d linters: [%s]", len(c.enabledLinters), strings.Join(c.enabledLinters, " "))) @@ -135,8 +137,12 @@ func TestEnabledLinters(t *testing.T) { } } -func getEnabledByDefaultFastLintersExcept(except ...string) []string { - m := lintersdb.NewManager(nil, nil) +func getEnabledByDefaultFastLintersExcept(t *testing.T, except ...string) []string { + t.Helper() + + m, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) + require.NoError(t, err) + ebdl := m.GetAllEnabledByDefaultLinters() var ret []string for _, lc := range ebdl { @@ -152,8 +158,13 @@ func getEnabledByDefaultFastLintersExcept(except ...string) []string { return ret } -func getAllFastLintersWith(with ...string) []string { - linters := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() +func getAllFastLintersWith(t *testing.T, with ...string) []string { + t.Helper() + + dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) + require.NoError(t, err) + + linters := dbManager.GetAllSupportedLinterConfigs() ret := append([]string{}, with...) for _, lc := range linters { if lc.IsSlowLinter() { @@ -165,8 +176,13 @@ func getAllFastLintersWith(with ...string) []string { return ret } -func getEnabledByDefaultLinters() []string { - ebdl := lintersdb.NewManager(nil, nil).GetAllEnabledByDefaultLinters() +func getEnabledByDefaultLinters(t *testing.T) []string { + t.Helper() + + dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) + require.NoError(t, err) + + ebdl := dbManager.GetAllEnabledByDefaultLinters() var ret []string for _, lc := range ebdl { if lc.Internal { @@ -179,8 +195,13 @@ func getEnabledByDefaultLinters() []string { return ret } -func getEnabledByDefaultFastLintersWith(with ...string) []string { - ebdl := lintersdb.NewManager(nil, nil).GetAllEnabledByDefaultLinters() +func getEnabledByDefaultFastLintersWith(t *testing.T, with ...string) []string { + t.Helper() + + dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder()) + require.NoError(t, err) + + ebdl := dbManager.GetAllEnabledByDefaultLinters() ret := append([]string{}, with...) for _, lc := range ebdl { if lc.IsSlowLinter() {