dev: rewrite linters Manager ()

This commit is contained in:
Ludovic Fernandez 2024-03-02 21:43:28 +01:00 committed by GitHub
parent 26f8088b38
commit b14d05cdb4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 1749 additions and 1825 deletions

View file

@ -22,11 +22,18 @@ graph LR
</ResponsiveContainer> </ResponsiveContainer>
## 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 ## Load Packages
Loading packages is listing all packages and their recursive dependencies for analysis. 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 Also, depending on the enabled linters set some parsing of the source code can be performed at this step.
at this step.
Packages loading starts here: 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: First, we need to find all enabled linters. All linters are registered here:
```go title=pkg/lint/lintersdb/manager.go ```go title=pkg/lint/lintersdb/builder_linter.go
func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config { func (b LinterBuilder) Build(cfg *config.Config) []*linter.Config {
// ... // ...
linters = append(linters, return []*linter.Config{
// ... // ...
linter.NewConfig(golinters.NewBodyclose()). linter.NewConfig(golinters.NewBodyclose()).
WithSince("v1.18.0"). WithSince("v1.18.0").
@ -97,26 +104,25 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithPresets(linter.PresetBugs, linter.PresetMetaLinter). WithPresets(linter.PresetBugs, linter.PresetMetaLinter).
WithAlternativeNames("vet", "vetshadow"). WithAlternativeNames("vet", "vetshadow").
WithURL("https://pkg.go.dev/cmd/vet"), WithURL("https://pkg.go.dev/cmd/vet"),
// ...
} }
// ...
} }
``` ```
We filter requested in config and command-line linters in `EnabledSet`: We filter requested in config and command-line linters in `EnabledSet`:
```go title=pkg/lint/lintersdb/enabled_set.go ```go title=pkg/lint/lintersdb/manager.go
func (es EnabledSet) GetEnabledLintersMap() (map[string]*linter.Config, error) func (m *Manager) GetEnabledLintersMap() (map[string]*linter.Config, error)
``` ```
We merge enabled linters into one `MetaLinter` to improve execution time if we can: We merge enabled linters into one `MetaLinter` to improve execution time if we can:
```go title=pkg/lint/lintersdb/enabled_set.go ```go titlepkg/lint/lintersdb/manager.go
// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters // GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters into a fewer number of linters.
// into a fewer number of linters. E.g. some go/analysis linters can be optimized into // E.g. some go/analysis linters can be optimized into one metalinter for data reuse and speed up.
// one metalinter for data reuse and speed up. func (m *Manager) GetOptimizedLinters() ([]*linter.Config, error) {
func (es EnabledSet) 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. Currently, all linters except `unused` can be merged into this meta linter.
The `unused` isn't merged because it has high memory usage. 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`. Linters execution starts in `runAnalyzers`.
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 It's the most complex part of the `golangci-lint`.
to reduce memory usage. Also, it sets all heavyweight data to `nil` as becomes unneeded to save memory. 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 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. it doesn't use caching and doesn't have some important performance optimizations.

View file

@ -28,8 +28,8 @@ After that:
Look at other linters in this directory. Look at other linters in this directory.
Implement linter integration and check that test passes. 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 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) 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 function `GetAllSupportedLinterConfigs`. 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) - 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. 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). For example, `nakedret` has only 1 option: [`max-func-lines`](https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml).

View file

@ -41,7 +41,7 @@ func newHelpCommand(logger logutils.Log) *helpCommand {
Args: cobra.NoArgs, Args: cobra.NoArgs,
ValidArgsFunction: cobra.NoFileCompletions, ValidArgsFunction: cobra.NoFileCompletions,
Run: c.execute, Run: c.execute,
PreRun: c.preRun, PreRunE: c.preRunE,
}, },
) )
@ -50,10 +50,18 @@ func newHelpCommand(logger logutils.Log) *helpCommand {
return c 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. // The command doesn't depend on the real configuration.
// It just needs the list of all plugins and all presets. // 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) { func (c *helpCommand) execute(_ *cobra.Command, _ []string) {

View file

@ -27,8 +27,7 @@ type lintersCommand struct {
log logutils.Log log logutils.Log
dbManager *lintersdb.Manager dbManager *lintersdb.Manager
enabledLintersSet *lintersdb.EnabledSet
} }
func newLintersCommand(logger logutils.Log, cfg *config.Config) *lintersCommand { 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) return fmt.Errorf("can't load config: %w", err)
} }
c.dbManager = lintersdb.NewManager(c.cfg, c.log) dbManager, err := lintersdb.NewManager(c.log.Child(logutils.DebugKeyLintersDB), c.cfg,
c.enabledLintersSet = lintersdb.NewEnabledSet(c.dbManager, lintersdb.NewPluginBuilder(c.log), lintersdb.NewLinterBuilder())
lintersdb.NewValidator(c.dbManager), c.log.Child(logutils.DebugKeyLintersDB), c.cfg) if err != nil {
return err
}
c.dbManager = dbManager
return nil return nil
} }
func (c *lintersCommand) execute(_ *cobra.Command, _ []string) error { func (c *lintersCommand) execute(_ *cobra.Command, _ []string) error {
enabledLintersMap, err := c.enabledLintersSet.GetEnabledLintersMap() enabledLintersMap, err := c.dbManager.GetEnabledLintersMap()
if err != nil { if err != nil {
return fmt.Errorf("can't get enabled linters: %w", err) return fmt.Errorf("can't get enabled linters: %w", err)
} }

View file

@ -80,8 +80,7 @@ type runCommand struct {
buildInfo BuildInfo buildInfo BuildInfo
dbManager *lintersdb.Manager dbManager *lintersdb.Manager
enabledLintersSet *lintersdb.EnabledSet
log logutils.Log log logutils.Log
debugf logutils.DebugFunc debugf logutils.DebugFunc
@ -171,9 +170,13 @@ func (c *runCommand) persistentPostRunE(_ *cobra.Command, _ []string) error {
} }
func (c *runCommand) preRunE(_ *cobra.Command, _ []string) error { func (c *runCommand) preRunE(_ *cobra.Command, _ []string) error {
c.dbManager = lintersdb.NewManager(c.cfg, c.log) dbManager, err := lintersdb.NewManager(c.log.Child(logutils.DebugKeyLintersDB), c.cfg,
c.enabledLintersSet = lintersdb.NewEnabledSet(c.dbManager, lintersdb.NewPluginBuilder(c.log), lintersdb.NewLinterBuilder())
lintersdb.NewValidator(c.dbManager), c.log.Child(logutils.DebugKeyLintersDB), c.cfg) if err != nil {
return err
}
c.dbManager = dbManager
c.goenv = goutil.NewEnv(c.log.Child(logutils.DebugKeyGoEnv)) 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) { func (c *runCommand) runAnalysis(ctx context.Context, args []string) ([]result.Issue, error) {
c.cfg.Run.Args = args c.cfg.Run.Args = args
lintersToRun, err := c.enabledLintersSet.GetOptimizedLinters() lintersToRun, err := c.dbManager.GetOptimizedLinters()
if err != nil { if err != nil {
return nil, err return nil, err
} }
enabledLintersMap, err := c.enabledLintersSet.GetEnabledLintersMap() enabledLintersMap, err := c.dbManager.GetEnabledLintersMap()
if err != nil { if err != nil {
return nil, err 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) lintCtx.Log = c.log.Child(logutils.DebugKeyLintersContext)
runner, err := lint.NewRunner(c.cfg, c.log.Child(logutils.DebugKeyRunner), runner, err := lint.NewRunner(c.log.Child(logutils.DebugKeyRunner),
c.goenv, c.enabledLintersSet, c.lineCache, c.fileCache, c.dbManager, lintCtx.Packages) c.cfg, c.goenv, c.lineCache, c.fileCache, c.dbManager, lintCtx.Packages)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -25,8 +25,8 @@ type versionInfo struct {
} }
type versionOptions struct { type versionOptions struct {
Format string `mapstructure:"format"` Format string
Debug bool `mapstructure:"debug"` Debug bool
} }
type versionCommand struct { type versionCommand struct {

View file

@ -1,9 +1,8 @@
package config package config
import ( import (
"errors"
"fmt"
"os" "os"
"regexp"
"strings" "strings"
hcversion "github.com/hashicorp/go-version" hcversion "github.com/hashicorp/go-version"
@ -33,18 +32,16 @@ func (c *Config) GetConfigDir() string {
} }
func (c *Config) Validate() error { func (c *Config) Validate() error {
for i, rule := range c.Issues.ExcludeRules { validators := []func() error{
if err := rule.Validate(); err != nil { c.Issues.Validate,
return fmt.Errorf("error in exclude rule #%d: %w", i, err) c.Severity.Validate,
} c.LintersSettings.Validate,
c.Linters.Validate,
} }
if len(c.Severity.Rules) > 0 && c.Severity.Default == "" { for _, v := range validators {
return errors.New("can't set severity rule option: no default severity defined") if err := v(); err != nil {
} return err
for i, rule := range c.Severity.Rules {
if err := rule.Validate(); err != nil {
return fmt.Errorf("error in severity rule #%d: %w", i, err)
} }
} }
@ -90,3 +87,22 @@ func detectGoVersion() string {
return "1.17" 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
}

View file

@ -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)
})
}
}

View file

@ -122,6 +122,16 @@ type Issues struct {
NeedFix bool `mapstructure:"fix"` 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 { type ExcludeRule struct {
BaseRule `mapstructure:",squash"` BaseRule `mapstructure:",squash"`
} }

View file

@ -1,5 +1,10 @@
package config package config
import (
"errors"
"fmt"
)
type Linters struct { type Linters struct {
Enable []string Enable []string
Disable []string Disable []string
@ -9,3 +14,52 @@ type Linters struct {
Presets []string 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
}

View file

@ -291,6 +291,10 @@ type LintersSettings struct {
Custom map[string]CustomLinterSettings Custom map[string]CustomLinterSettings
} }
func (s *LintersSettings) Validate() error {
return s.Govet.Validate()
}
type AsasalintSettings struct { type AsasalintSettings struct {
Exclude []string `mapstructure:"exclude"` Exclude []string `mapstructure:"exclude"`
UseBuiltinExclusions bool `mapstructure:"use-builtin-exclusions"` UseBuiltinExclusions bool `mapstructure:"use-builtin-exclusions"`
@ -606,15 +610,14 @@ type GovetSettings struct {
} }
func (cfg *GovetSettings) Validate() error { func (cfg *GovetSettings) Validate() error {
// TODO(ldez) need to be move into the linter file.
if cfg.EnableAll && cfg.DisableAll { 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 { 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 { 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 return nil
} }

214
pkg/config/linters_test.go Normal file
View file

@ -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)
})
}
}

View file

@ -59,11 +59,37 @@ func (l *Loader) Load() error {
l.applyStringSliceHack() l.applyStringSliceHack()
l.handleGoVersion()
return nil
}
func (l *Loader) handleGoVersion() {
if l.cfg.Run.Go == "" { if l.cfg.Run.Go == "" {
l.cfg.Run.Go = detectGoVersion() 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 { func (l *Loader) setConfigFile() error {
@ -161,11 +187,7 @@ func (l *Loader) parseConfig() error {
// Load configuration from flags only. // Load configuration from flags only.
err = l.viper.Unmarshal(l.cfg) err = l.viper.Unmarshal(l.cfg)
if err != nil { if err != nil {
return err return fmt.Errorf("can't unmarshal config by viper (flags): %w", err)
}
if err = l.cfg.Validate(); err != nil {
return fmt.Errorf("can't validate config: %w", err)
} }
return nil return nil
@ -181,11 +203,7 @@ func (l *Loader) parseConfig() error {
// Load configuration from all sources (flags, file). // Load configuration from all sources (flags, file).
if err := l.viper.Unmarshal(l.cfg, fileDecoderHook()); err != nil { if err := l.viper.Unmarshal(l.cfg, fileDecoderHook()); err != nil {
return fmt.Errorf("can't unmarshal config by viper: %w", err) return fmt.Errorf("can't unmarshal config by viper (flags, file): %w", err)
}
if err := l.cfg.Validate(); err != nil {
return fmt.Errorf("can't validate config: %w", err)
} }
if l.cfg.InternalTest { // just for testing purposes: to detect config file usage if l.cfg.InternalTest { // just for testing purposes: to detect config file usage

View file

@ -1,5 +1,10 @@
package config package config
import (
"errors"
"fmt"
)
const severityRuleMinConditionsCount = 1 const severityRuleMinConditionsCount = 1
type Severity struct { type Severity struct {
@ -8,6 +13,20 @@ type Severity struct {
Rules []SeverityRule `mapstructure:"rules"` 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 { type SeverityRule struct {
BaseRule `mapstructure:",squash"` BaseRule `mapstructure:",squash"`
Severity string Severity string

View file

@ -139,11 +139,6 @@ var (
func NewGovet(settings *config.GovetSettings) *goanalysis.Linter { func NewGovet(settings *config.GovetSettings) *goanalysis.Linter {
var conf map[string]map[string]any var conf map[string]map[string]any
if settings != nil { if settings != nil {
err := settings.Validate()
if err != nil {
linterLogger.Fatalf("govet configuration: %v", err)
}
conf = settings.Settings conf = settings.Settings
} }

View file

@ -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"),
}
}

View file

@ -11,24 +11,35 @@ import (
"github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/golinters/goanalysis" "github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter" "github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/logutils"
) )
type AnalyzerPlugin interface { type AnalyzerPlugin interface {
GetAnalyzers() []*analysis.Analyzer GetAnalyzers() []*analysis.Analyzer
} }
// getCustomLinterConfigs loads private linters that are specified in the golangci config file. // PluginBuilder builds the custom linters (plugins) based on the configuration.
func (m *Manager) getCustomLinterConfigs() []*linter.Config { type PluginBuilder struct {
if m.cfg == nil || m.log == nil { 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 return nil
} }
var linters []*linter.Config var linters []*linter.Config
for name, settings := range m.cfg.LintersSettings.Custom { for name, settings := range cfg.LintersSettings.Custom {
lc, err := m.loadCustomLinterConfig(name, settings) lc, err := b.loadConfig(cfg, name, settings)
if err != nil { 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 { } else {
linters = append(linters, lc) linters = append(linters, lc)
} }
@ -37,15 +48,15 @@ func (m *Manager) getCustomLinterConfigs() []*linter.Config {
return linters 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. // Private linters are dynamically loaded from .so plugin files.
func (m *Manager) loadCustomLinterConfig(name string, settings config.CustomLinterSettings) (*linter.Config, error) { func (b *PluginBuilder) loadConfig(cfg *config.Config, name string, settings config.CustomLinterSettings) (*linter.Config, error) {
analyzers, err := m.getAnalyzerPlugin(settings.Path, settings.Settings) analyzers, err := b.getAnalyzerPlugin(cfg, settings.Path, settings.Settings)
if err != nil { if err != nil {
return nil, err 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). customLinter := goanalysis.NewLinter(name, settings.Description, analyzers, nil).
WithLoadMode(goanalysis.LoadModeTypesInfo) 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. // and returns the 'AnalyzerPlugin' interface implemented by the private plugin.
// An error is returned if the private linter cannot be loaded // An error is returned if the private linter cannot be loaded
// or the linter does not implement the AnalyzerPlugin interface. // 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) { if !filepath.IsAbs(path) {
// resolve non-absolute paths relative to config file's directory // 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) plug, err := plugin.Open(path)
@ -74,7 +85,7 @@ func (m *Manager) getAnalyzerPlugin(path string, settings any) ([]*analysis.Anal
return nil, err return nil, err
} }
analyzers, err := m.lookupPlugin(plug, settings) analyzers, err := b.lookupPlugin(plug, settings)
if err != nil { if err != nil {
return nil, fmt.Errorf("lookup plugin %s: %w", path, err) 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 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") symbol, err := plug.Lookup("New")
if err != nil { if err != nil {
analyzers, errP := m.lookupAnalyzerPlugin(plug) analyzers, errP := b.lookupAnalyzerPlugin(plug)
if errP != nil { if errP != nil {
return nil, errors.Join(err, errP) return nil, errors.Join(err, errP)
} }
@ -102,13 +113,13 @@ func (m *Manager) lookupPlugin(plug *plugin.Plugin, settings any) ([]*analysis.A
return constructor(settings) 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") symbol, err := plug.Lookup("AnalyzerPlugin")
if err != nil { if err != nil {
return nil, err 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") "https://golangci-lint.run/contributing/new-linters/#create-a-plugin")
analyzerPlugin, ok := symbol.(AnalyzerPlugin) analyzerPlugin, ok := symbol.(AnalyzerPlugin)

View file

@ -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)
}
}

View file

@ -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)
})
}
}

File diff suppressed because it is too large Load diff

View file

@ -4,43 +4,223 @@ import (
"testing" "testing"
"github.com/stretchr/testify/assert" "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) { 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"},
},
{
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"},
},
}
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 { testCases := []struct {
desc string desc string
version string linters map[string]*linter.Config
expected string expected map[string]*linter.Config
}{ }{
{ {
desc: "patched version", desc: "no combined, one linter",
version: "1.22.0", linters: map[string]*linter.Config{
expected: "1.22", "foo": {
Linter: foo,
InPresets: []string{"A"},
},
},
expected: map[string]*linter.Config{
"foo": {
Linter: foo,
InPresets: []string{"A"},
},
},
}, },
{ {
desc: "minor version", desc: "combined, several linters",
version: "1.22", linters: map[string]*linter.Config{
expected: "1.22", "foo": {
}, Linter: foo,
{ InPresets: []string{"A"},
desc: "RC version", },
version: "1.22rc1", "bar": {
expected: "1.22", Linter: bar,
}, InPresets: []string{"B"},
{ },
desc: "alpha version", },
version: "1.22alpha1", expected: func() map[string]*linter.Config {
expected: "1.22", mlConfig := &linter.Config{
}, Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{bar, foo}),
{ InPresets: []string{"A", "B"},
desc: "beta version", }
version: "1.22beta1",
expected: "1.22", return map[string]*linter.Config{
}, "goanalysis_metalinter": mlConfig.WithLoadForGoAnalysis(),
{ }
desc: "semver RC version", }(),
version: "1.22.0-rc1",
expected: "1.22",
}, },
} }
@ -49,8 +229,9 @@ func Test_trimGoVersion(t *testing.T) {
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
version := trimGoVersion(test.version) m.combineGoAnalysisLinters(test.linters)
assert.Equal(t, test.expected, version)
assert.Equal(t, test.expected, test.linters)
}) })
} }
} }

View file

@ -14,9 +14,29 @@ type Validator struct {
} }
func NewValidator(m *Manager) *Validator { func NewValidator(m *Manager) *Validator {
return &Validator{ return &Validator{m: m}
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 { func (v Validator) validateLintersNames(cfg *config.Linters) error {
@ -55,56 +75,3 @@ func (v Validator) validatePresets(cfg *config.Linters) error {
return nil 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
}

View file

@ -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 { type validatorTestCase struct {
desc string desc string
cfg *config.Linters cfg *config.Linters
@ -172,116 +106,43 @@ var validatePresetsTestCases = []validatorTestCase{
}, },
} }
var validateDisabledAndEnabledAtOneMomentTestCases = []validatorTestCase{ func TestValidator_Validate(t *testing.T) {
{ m, err := NewManager(nil, nil, NewLinterBuilder())
desc: "2 different sets", require.NoError(t, err)
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{ v := NewValidator(m)
{
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_validateEnabledDisabledLintersConfig(t *testing.T) {
v := NewValidator(NewManager(nil, nil))
var testCases []validatorTestCase var testCases []validatorTestCase
testCases = append(testCases, validateLintersNamesTestCases...) testCases = append(testCases, validateLintersNamesTestCases...)
testCases = append(testCases, validatePresetsTestCases...) testCases = append(testCases, validatePresetsTestCases...)
testCases = append(testCases, validateDisabledAndEnabledAtOneMomentTestCases...)
testCases = append(testCases, validateAllDisableEnableOptionsTestCases...)
for _, test := range testCases { for _, test := range testCases {
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
err := v.validateEnabledDisabledLintersConfig(test.cfg) err := v.Validate(&config.Config{Linters: *test.cfg})
require.NoError(t, err) require.NoError(t, err)
}) })
} }
} }
func TestValidator_validateEnabledDisabledLintersConfig_error(t *testing.T) { func TestValidator_Validate_error(t *testing.T) {
v := NewValidator(NewManager(nil, nil)) m, err := NewManager(nil, nil, NewLinterBuilder())
require.NoError(t, err)
v := NewValidator(m)
var testCases []validateErrorTestCase var testCases []validateErrorTestCase
testCases = append(testCases, validateLintersNamesErrorTestCases...) testCases = append(testCases, validateLintersNamesErrorTestCases...)
testCases = append(testCases, validatePresetsErrorTestCases...) testCases = append(testCases, validatePresetsErrorTestCases...)
testCases = append(testCases, validateDisabledAndEnabledAtOneMomentErrorTestCases...)
testCases = append(testCases, validateAllDisableEnableOptionsErrorTestCases...)
for _, test := range testCases { for _, test := range testCases {
test := test test := test
t.Run(test.desc, func(t *testing.T) { t.Run(test.desc, func(t *testing.T) {
t.Parallel() t.Parallel()
err := v.validateEnabledDisabledLintersConfig(test.cfg) err := v.Validate(&config.Config{Linters: *test.cfg})
require.Error(t, err) require.Error(t, err)
require.EqualError(t, err, test.expected) require.EqualError(t, err, test.expected)
@ -290,7 +151,10 @@ func TestValidator_validateEnabledDisabledLintersConfig_error(t *testing.T) {
} }
func TestValidator_validateLintersNames(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 { for _, test := range validateLintersNamesTestCases {
test := test test := test
@ -304,7 +168,10 @@ func TestValidator_validateLintersNames(t *testing.T) {
} }
func TestValidator_validateLintersNames_error(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 { for _, test := range validateLintersNamesErrorTestCases {
test := test 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)
})
}
}

View file

@ -27,8 +27,7 @@ type Runner struct {
Log logutils.Log Log logutils.Log
} }
func NewRunner(cfg *config.Config, log logutils.Log, goenv *goutil.Env, func NewRunner(log logutils.Log, cfg *config.Config, goenv *goutil.Env,
es *lintersdb.EnabledSet,
lineCache *fsutils.LineCache, fileCache *fsutils.FileCache, lineCache *fsutils.LineCache, fileCache *fsutils.FileCache,
dbManager *lintersdb.Manager, pkgs []*gopackages.Package) (*Runner, error) { dbManager *lintersdb.Manager, pkgs []*gopackages.Package) (*Runner, error) {
// Beware that some processors need to add the path prefix when working with paths // 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 return nil, err
} }
enabledLinters, err := es.GetEnabledLintersMap() enabledLinters, err := dbManager.GetEnabledLintersMap()
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get enabled linters: %w", err) return nil, fmt.Errorf("failed to get enabled linters: %w", err)
} }

View file

@ -34,7 +34,10 @@ func newNolint2FileIssue(line int) result.Issue {
} }
func newTestNolintProcessor(log logutils.Log) *Nolint { 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 { func getMockLog() *logutils.MockLog {
@ -283,11 +286,11 @@ func TestNolintUnused(t *testing.T) {
enabledSetLog.On("Infof", "Active %d linters: %s", len(enabledLinters), enabledLinters) enabledSetLog.On("Infof", "Active %d linters: %s", len(enabledLinters), enabledLinters)
cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: 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) require.NoError(t, err)
return NewNolint(log, dbManager, enabledLintersMap) return NewNolint(log, dbManager, enabledLintersMap)
@ -347,11 +350,13 @@ func TestNolintUnused(t *testing.T) {
enabledSetLog.On("Infof", "Active %d linters: %s", 1, []string{"nolintlint"}) enabledSetLog.On("Infof", "Active %d linters: %s", 1, []string{"nolintlint"})
cfg := &config.Config{Linters: config.Linters{DisableAll: true, Enable: []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) require.NoError(t, err)
enabledLintersMap, err := dbManager.GetEnabledLintersMap()
require.NoError(t, err)
p := NewNolint(log, dbManager, enabledLintersMap) p := NewNolint(log, dbManager, enabledLintersMap)
defer p.Finish() defer p.Finish()

View file

@ -204,8 +204,11 @@ func getDefaultExclusions() string {
} }
func getLintersListMarkdown(enabled bool) string { func getLintersListMarkdown(enabled bool) string {
dbManager, _ := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
lcs := dbManager.GetAllSupportedLinterConfigs()
var neededLcs []*linter.Config var neededLcs []*linter.Config
lcs := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs()
for _, lc := range lcs { for _, lc := range lcs {
if lc.Internal { if lc.Internal {
continue continue
@ -322,8 +325,9 @@ type authorDetails struct {
func getThanksList() string { func getThanksList() string {
addedAuthors := map[string]*authorDetails{} 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 { if lc.Internal {
continue continue
} }
@ -491,7 +495,8 @@ func extractExampleSnippets(example []byte) (*SettingSnippets, error) {
} }
func getLintersSettingSections(node, nextNode *yaml.Node) (string, 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) var lintersDesc = make(map[string]string)
for _, lc := range lcs { for _, lc := range lcs {

View file

@ -7,6 +7,8 @@ import (
"strings" "strings"
"testing" "testing"
"github.com/stretchr/testify/require"
"github.com/golangci/golangci-lint/pkg/lint/lintersdb" "github.com/golangci/golangci-lint/pkg/lint/lintersdb"
"github.com/golangci/golangci-lint/test/testshared" "github.com/golangci/golangci-lint/test/testshared"
) )
@ -29,7 +31,7 @@ func TestEnabledLinters(t *testing.T) {
disable: disable:
- govet - govet
`, `,
enabledLinters: getEnabledByDefaultFastLintersExcept("govet"), enabledLinters: getEnabledByDefaultFastLintersExcept(t, "govet"),
}, },
{ {
name: "enable revive in config", name: "enable revive in config",
@ -38,12 +40,12 @@ func TestEnabledLinters(t *testing.T) {
enable: enable:
- revive - revive
`, `,
enabledLinters: getEnabledByDefaultFastLintersWith("revive"), enabledLinters: getEnabledByDefaultFastLintersWith(t, "revive"),
}, },
{ {
name: "disable govet in cmd", name: "disable govet in cmd",
args: []string{"-Dgovet"}, args: []string{"-Dgovet"},
enabledLinters: getEnabledByDefaultFastLintersExcept("govet"), enabledLinters: getEnabledByDefaultFastLintersExcept(t, "govet"),
}, },
{ {
name: "enable gofmt in cmd and enable revive in config", name: "enable gofmt in cmd and enable revive in config",
@ -53,7 +55,7 @@ func TestEnabledLinters(t *testing.T) {
enable: enable:
- revive - revive
`, `,
enabledLinters: getEnabledByDefaultFastLintersWith("revive", "gofmt"), enabledLinters: getEnabledByDefaultFastLintersWith(t, "revive", "gofmt"),
}, },
{ {
name: "fast option in config", name: "fast option in config",
@ -61,7 +63,7 @@ func TestEnabledLinters(t *testing.T) {
linters: linters:
fast: true fast: true
`, `,
enabledLinters: getEnabledByDefaultFastLintersWith(), enabledLinters: getEnabledByDefaultFastLintersWith(t),
noImplicitFast: true, noImplicitFast: true,
}, },
{ {
@ -70,13 +72,13 @@ func TestEnabledLinters(t *testing.T) {
linters: linters:
fast: false fast: false
`, `,
enabledLinters: getEnabledByDefaultLinters(), enabledLinters: getEnabledByDefaultLinters(t),
noImplicitFast: true, noImplicitFast: true,
}, },
{ {
name: "set fast option in command-line", name: "set fast option in command-line",
args: []string{"--fast"}, args: []string{"--fast"},
enabledLinters: getEnabledByDefaultFastLintersWith(), enabledLinters: getEnabledByDefaultFastLintersWith(t),
noImplicitFast: true, noImplicitFast: true,
}, },
{ {
@ -86,7 +88,7 @@ func TestEnabledLinters(t *testing.T) {
fast: false fast: false
`, `,
args: []string{"--fast"}, args: []string{"--fast"},
enabledLinters: getEnabledByDefaultFastLintersWith(), enabledLinters: getEnabledByDefaultFastLintersWith(t),
noImplicitFast: true, noImplicitFast: true,
}, },
{ {
@ -96,13 +98,13 @@ func TestEnabledLinters(t *testing.T) {
fast: true fast: true
`, `,
args: []string{"--fast=false"}, args: []string{"--fast=false"},
enabledLinters: getEnabledByDefaultLinters(), enabledLinters: getEnabledByDefaultLinters(t),
noImplicitFast: true, noImplicitFast: true,
}, },
{ {
name: "fast option combined with enable and enable-all", name: "fast option combined with enable and enable-all",
args: []string{"--enable-all", "--fast", "--enable=unused"}, args: []string{"--enable-all", "--fast", "--enable=unused"},
enabledLinters: getAllFastLintersWith("unused"), enabledLinters: getAllFastLintersWith(t, "unused"),
noImplicitFast: true, noImplicitFast: true,
}, },
} }
@ -127,7 +129,7 @@ func TestEnabledLinters(t *testing.T) {
Runner(). Runner().
Run() Run()
sort.StringSlice(c.enabledLinters).Sort() sort.Strings(c.enabledLinters)
r.ExpectOutputContains(fmt.Sprintf("Active %d linters: [%s]", r.ExpectOutputContains(fmt.Sprintf("Active %d linters: [%s]",
len(c.enabledLinters), strings.Join(c.enabledLinters, " "))) len(c.enabledLinters), strings.Join(c.enabledLinters, " ")))
@ -135,8 +137,12 @@ func TestEnabledLinters(t *testing.T) {
} }
} }
func getEnabledByDefaultFastLintersExcept(except ...string) []string { func getEnabledByDefaultFastLintersExcept(t *testing.T, except ...string) []string {
m := lintersdb.NewManager(nil, nil) t.Helper()
m, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
require.NoError(t, err)
ebdl := m.GetAllEnabledByDefaultLinters() ebdl := m.GetAllEnabledByDefaultLinters()
var ret []string var ret []string
for _, lc := range ebdl { for _, lc := range ebdl {
@ -152,8 +158,13 @@ func getEnabledByDefaultFastLintersExcept(except ...string) []string {
return ret return ret
} }
func getAllFastLintersWith(with ...string) []string { func getAllFastLintersWith(t *testing.T, with ...string) []string {
linters := lintersdb.NewManager(nil, nil).GetAllSupportedLinterConfigs() t.Helper()
dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
require.NoError(t, err)
linters := dbManager.GetAllSupportedLinterConfigs()
ret := append([]string{}, with...) ret := append([]string{}, with...)
for _, lc := range linters { for _, lc := range linters {
if lc.IsSlowLinter() { if lc.IsSlowLinter() {
@ -165,8 +176,13 @@ func getAllFastLintersWith(with ...string) []string {
return ret return ret
} }
func getEnabledByDefaultLinters() []string { func getEnabledByDefaultLinters(t *testing.T) []string {
ebdl := lintersdb.NewManager(nil, nil).GetAllEnabledByDefaultLinters() t.Helper()
dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
require.NoError(t, err)
ebdl := dbManager.GetAllEnabledByDefaultLinters()
var ret []string var ret []string
for _, lc := range ebdl { for _, lc := range ebdl {
if lc.Internal { if lc.Internal {
@ -179,8 +195,13 @@ func getEnabledByDefaultLinters() []string {
return ret return ret
} }
func getEnabledByDefaultFastLintersWith(with ...string) []string { func getEnabledByDefaultFastLintersWith(t *testing.T, with ...string) []string {
ebdl := lintersdb.NewManager(nil, nil).GetAllEnabledByDefaultLinters() t.Helper()
dbManager, err := lintersdb.NewManager(nil, nil, lintersdb.NewLinterBuilder())
require.NoError(t, err)
ebdl := dbManager.GetAllEnabledByDefaultLinters()
ret := append([]string{}, with...) ret := append([]string{}, with...)
for _, lc := range ebdl { for _, lc := range ebdl {
if lc.IsSlowLinter() { if lc.IsSlowLinter() {