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>
## Init
The configuration is loaded from file and flags by `config.Loader` inside `PersistentPreRun` (or `PreRun`) of the commands that require configuration.
The linter database (`linterdb.Manager`) is fill based on the configuration:
- The linters ("internals" and plugins) are built by `linterdb.LinterBuilder` and `linterdb.PluginBuilder` builders.
- The configuration is validated by `linterdb.Validator`.
## Load Packages
Loading packages is listing all packages and their recursive dependencies for analysis.
Also, depending on the enabled linters set some parsing of the source code can be performed
at this step.
Also, depending on the enabled linters set some parsing of the source code can be performed at this step.
Packages loading starts here:
@ -79,10 +86,10 @@ and outputs list of packages and requested information about them: filenames, ty
First, we need to find all enabled linters. All linters are registered here:
```go title=pkg/lint/lintersdb/manager.go
func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
```go title=pkg/lint/lintersdb/builder_linter.go
func (b LinterBuilder) Build(cfg *config.Config) []*linter.Config {
// ...
linters = append(linters,
return []*linter.Config{
// ...
linter.NewConfig(golinters.NewBodyclose()).
WithSince("v1.18.0").
@ -97,26 +104,25 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithPresets(linter.PresetBugs, linter.PresetMetaLinter).
WithAlternativeNames("vet", "vetshadow").
WithURL("https://pkg.go.dev/cmd/vet"),
// ...
}
// ...
}
```
We filter requested in config and command-line linters in `EnabledSet`:
```go title=pkg/lint/lintersdb/enabled_set.go
func (es EnabledSet) GetEnabledLintersMap() (map[string]*linter.Config, error)
```go title=pkg/lint/lintersdb/manager.go
func (m *Manager) GetEnabledLintersMap() (map[string]*linter.Config, error)
```
We merge enabled linters into one `MetaLinter` to improve execution time if we can:
```go title=pkg/lint/lintersdb/enabled_set.go
// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters
// into a fewer number of linters. E.g. some go/analysis linters can be optimized into
// one metalinter for data reuse and speed up.
func (es EnabledSet) GetOptimizedLinters() ([]*linter.Config, error) {
```go titlepkg/lint/lintersdb/manager.go
// GetOptimizedLinters returns enabled linters after optimization (merging) of multiple linters into a fewer number of linters.
// E.g. some go/analysis linters can be optimized into one metalinter for data reuse and speed up.
func (m *Manager) GetOptimizedLinters() ([]*linter.Config, error) {
// ...
es.combineGoAnalysisLinters(resultLintersSet)
m.combineGoAnalysisLinters(resultLintersSet)
// ...
}
```
@ -133,9 +139,11 @@ type MetaLinter struct {
Currently, all linters except `unused` can be merged into this meta linter.
The `unused` isn't merged because it has high memory usage.
Linters execution starts in `runAnalyzers`. It's the most complex part of the `golangci-lint`.
We use custom [go/analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis) runner there. It runs as much as it can in parallel. It lazy-loads as much as it can
to reduce memory usage. Also, it sets all heavyweight data to `nil` as becomes unneeded to save memory.
Linters execution starts in `runAnalyzers`.
It's the most complex part of the `golangci-lint`.
We use custom [go/analysis](https://pkg.go.dev/golang.org/x/tools/go/analysis) runner there.
It runs as much as it can in parallel. It lazy-loads as much as it can to reduce memory usage.
Also, it sets all heavyweight data to `nil` as becomes unneeded to save memory.
We don't use existing [multichecker](https://pkg.go.dev/golang.org/x/tools/go/analysis/multichecker) because
it doesn't use caching and doesn't have some important performance optimizations.

View file

@ -28,8 +28,8 @@ After that:
Look at other linters in this directory.
Implement linter integration and check that test passes.
3. Add the new struct for the linter (which you've implemented in `pkg/golinters/{yourlintername}.go`) to the
list of all supported linters in [`pkg/lint/lintersdb/manager.go`](https://github.com/golangci/golangci-lint/blob/master/pkg/lint/lintersdb/manager.go)
to the function `GetAllSupportedLinterConfigs`.
list of all supported linters in [`pkg/lint/lintersdb/builder_linter.go`](https://github.com/golangci/golangci-lint/blob/master/pkg/lint/lintersdb/builder_linter.go)
to the method `LinterBuilder.Build`.
- Add `WithSince("next_version")`, where `next_version` must be replaced by the next minor version. (ex: v1.2.0 if the current version is v1.1.0)
4. Find out what options do you need to configure for the linter.
For example, `nakedret` has only 1 option: [`max-func-lines`](https://github.com/golangci/golangci-lint/blob/master/.golangci.reference.yml).

View file

@ -41,7 +41,7 @@ func newHelpCommand(logger logutils.Log) *helpCommand {
Args: cobra.NoArgs,
ValidArgsFunction: cobra.NoFileCompletions,
Run: c.execute,
PreRun: c.preRun,
PreRunE: c.preRunE,
},
)
@ -50,10 +50,18 @@ func newHelpCommand(logger logutils.Log) *helpCommand {
return c
}
func (c *helpCommand) preRun(_ *cobra.Command, _ []string) {
func (c *helpCommand) preRunE(_ *cobra.Command, _ []string) error {
// The command doesn't depend on the real configuration.
// It just needs the list of all plugins and all presets.
c.dbManager = lintersdb.NewManager(config.NewDefault(), c.log)
dbManager, err := lintersdb.NewManager(c.log.Child(logutils.DebugKeyLintersDB), config.NewDefault(),
lintersdb.NewPluginBuilder(c.log), lintersdb.NewLinterBuilder())
if err != nil {
return err
}
c.dbManager = dbManager
return nil
}
func (c *helpCommand) execute(_ *cobra.Command, _ []string) {

View file

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

View file

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

View file

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

View file

@ -1,9 +1,8 @@
package config
import (
"errors"
"fmt"
"os"
"regexp"
"strings"
hcversion "github.com/hashicorp/go-version"
@ -33,18 +32,16 @@ func (c *Config) GetConfigDir() string {
}
func (c *Config) Validate() error {
for i, rule := range c.Issues.ExcludeRules {
if err := rule.Validate(); err != nil {
return fmt.Errorf("error in exclude rule #%d: %w", i, err)
}
validators := []func() error{
c.Issues.Validate,
c.Severity.Validate,
c.LintersSettings.Validate,
c.Linters.Validate,
}
if len(c.Severity.Rules) > 0 && c.Severity.Default == "" {
return errors.New("can't set severity rule option: no default severity defined")
}
for i, rule := range c.Severity.Rules {
if err := rule.Validate(); err != nil {
return fmt.Errorf("error in severity rule #%d: %w", i, err)
for _, v := range validators {
if err := v(); err != nil {
return err
}
}
@ -90,3 +87,22 @@ func detectGoVersion() string {
return "1.17"
}
// Trims the Go version to keep only M.m.
// Since Go 1.21 the version inside the go.mod can be a patched version (ex: 1.21.0).
// The version can also include information which we want to remove (ex: 1.21alpha1)
// https://go.dev/doc/toolchain#versions
// This a problem with staticcheck and gocritic.
func trimGoVersion(v string) string {
if v == "" {
return ""
}
exp := regexp.MustCompile(`(\d\.\d+)(?:\.\d+|[a-z]+\d)`)
if exp.MatchString(v) {
return exp.FindStringSubmatch(v)[1]
}
return v
}

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"`
}
func (i *Issues) Validate() error {
for i, rule := range i.ExcludeRules {
if err := rule.Validate(); err != nil {
return fmt.Errorf("error in exclude rule #%d: %w", i, err)
}
}
return nil
}
type ExcludeRule struct {
BaseRule `mapstructure:",squash"`
}

View file

@ -1,5 +1,10 @@
package config
import (
"errors"
"fmt"
)
type Linters struct {
Enable []string
Disable []string
@ -9,3 +14,52 @@ type Linters struct {
Presets []string
}
func (l *Linters) Validate() error {
if err := l.validateAllDisableEnableOptions(); err != nil {
return err
}
if err := l.validateDisabledAndEnabledAtOneMoment(); err != nil {
return err
}
return nil
}
func (l *Linters) validateAllDisableEnableOptions() error {
if l.EnableAll && l.DisableAll {
return errors.New("--enable-all and --disable-all options must not be combined")
}
if l.DisableAll {
if len(l.Enable) == 0 && len(l.Presets) == 0 {
return errors.New("all linters were disabled, but no one linter was enabled: must enable at least one")
}
if len(l.Disable) != 0 {
return errors.New("can't combine options --disable-all and --disable")
}
}
if l.EnableAll && len(l.Enable) != 0 && !l.Fast {
return errors.New("can't combine options --enable-all and --enable")
}
return nil
}
func (l *Linters) validateDisabledAndEnabledAtOneMoment() error {
enabledLintersSet := map[string]bool{}
for _, name := range l.Enable {
enabledLintersSet[name] = true
}
for _, name := range l.Disable {
if enabledLintersSet[name] {
return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name)
}
}
return nil
}

View file

@ -291,6 +291,10 @@ type LintersSettings struct {
Custom map[string]CustomLinterSettings
}
func (s *LintersSettings) Validate() error {
return s.Govet.Validate()
}
type AsasalintSettings struct {
Exclude []string `mapstructure:"exclude"`
UseBuiltinExclusions bool `mapstructure:"use-builtin-exclusions"`
@ -606,15 +610,14 @@ type GovetSettings struct {
}
func (cfg *GovetSettings) Validate() error {
// TODO(ldez) need to be move into the linter file.
if cfg.EnableAll && cfg.DisableAll {
return errors.New("enable-all and disable-all can't be combined")
return errors.New("govet: enable-all and disable-all can't be combined")
}
if cfg.EnableAll && len(cfg.Enable) != 0 {
return errors.New("enable-all and enable can't be combined")
return errors.New("govet: enable-all and enable can't be combined")
}
if cfg.DisableAll && len(cfg.Disable) != 0 {
return errors.New("disable-all and disable can't be combined")
return errors.New("govet: disable-all and disable can't be combined")
}
return nil
}

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

View file

@ -1,5 +1,10 @@
package config
import (
"errors"
"fmt"
)
const severityRuleMinConditionsCount = 1
type Severity struct {
@ -8,6 +13,20 @@ type Severity struct {
Rules []SeverityRule `mapstructure:"rules"`
}
func (s *Severity) Validate() error {
if len(s.Rules) > 0 && s.Default == "" {
return errors.New("can't set severity rule option: no default severity defined")
}
for i, rule := range s.Rules {
if err := rule.Validate(); err != nil {
return fmt.Errorf("error in severity rule #%d: %w", i, err)
}
}
return nil
}
type SeverityRule struct {
BaseRule `mapstructure:",squash"`
Severity string

View file

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

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

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"
"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 {
desc string
version string
expected string
linters map[string]*linter.Config
expected map[string]*linter.Config
}{
{
desc: "patched version",
version: "1.22.0",
expected: "1.22",
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: "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",
desc: "combined, several linters",
linters: map[string]*linter.Config{
"foo": {
Linter: foo,
InPresets: []string{"A"},
},
"bar": {
Linter: bar,
InPresets: []string{"B"},
},
},
expected: func() map[string]*linter.Config {
mlConfig := &linter.Config{
Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{bar, foo}),
InPresets: []string{"A", "B"},
}
return map[string]*linter.Config{
"goanalysis_metalinter": mlConfig.WithLoadForGoAnalysis(),
}
}(),
},
}
@ -49,8 +229,9 @@ func Test_trimGoVersion(t *testing.T) {
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
version := trimGoVersion(test.version)
assert.Equal(t, test.expected, version)
m.combineGoAnalysisLinters(test.linters)
assert.Equal(t, test.expected, test.linters)
})
}
}

View file

@ -14,9 +14,29 @@ type Validator struct {
}
func NewValidator(m *Manager) *Validator {
return &Validator{
m: m,
return &Validator{m: m}
}
// Validate validates the configuration by calling all other validators for different
// sections in the configuration and then some additional linter validation functions.
func (v Validator) Validate(cfg *config.Config) error {
err := cfg.Validate()
if err != nil {
return err
}
validators := []func(cfg *config.Linters) error{
v.validateLintersNames,
v.validatePresets,
}
for _, v := range validators {
if err := v(&cfg.Linters); err != nil {
return err
}
}
return nil
}
func (v Validator) validateLintersNames(cfg *config.Linters) error {
@ -55,56 +75,3 @@ func (v Validator) validatePresets(cfg *config.Linters) error {
return nil
}
func (v Validator) validateAllDisableEnableOptions(cfg *config.Linters) error {
if cfg.EnableAll && cfg.DisableAll {
return errors.New("--enable-all and --disable-all options must not be combined")
}
if cfg.DisableAll {
if len(cfg.Enable) == 0 && len(cfg.Presets) == 0 {
return errors.New("all linters were disabled, but no one linter was enabled: must enable at least one")
}
if len(cfg.Disable) != 0 {
return fmt.Errorf("can't combine options --disable-all and --disable %s", cfg.Disable[0])
}
}
if cfg.EnableAll && len(cfg.Enable) != 0 && !cfg.Fast {
return fmt.Errorf("can't combine options --enable-all and --enable %s", cfg.Enable[0])
}
return nil
}
func (v Validator) validateDisabledAndEnabledAtOneMoment(cfg *config.Linters) error {
enabledLintersSet := map[string]bool{}
for _, name := range cfg.Enable {
enabledLintersSet[name] = true
}
for _, name := range cfg.Disable {
if enabledLintersSet[name] {
return fmt.Errorf("linter %q can't be disabled and enabled at one moment", name)
}
}
return nil
}
func (v Validator) validateEnabledDisabledLintersConfig(cfg *config.Linters) error {
validators := []func(cfg *config.Linters) error{
v.validateLintersNames,
v.validatePresets,
v.validateAllDisableEnableOptions,
v.validateDisabledAndEnabledAtOneMoment,
}
for _, v := range validators {
if err := v(cfg); err != nil {
return err
}
}
return nil
}

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 {
desc string
cfg *config.Linters
@ -172,116 +106,43 @@ var validatePresetsTestCases = []validatorTestCase{
},
}
var validateDisabledAndEnabledAtOneMomentTestCases = []validatorTestCase{
{
desc: "2 different sets",
cfg: &config.Linters{
Enable: []string{"dupl", "gofmt", "misspell"},
Disable: []string{"goimports", "gosec", "nolintlint"},
},
},
{
desc: "only enable",
cfg: &config.Linters{
Enable: []string{"goimports", "gosec", "nolintlint"},
Disable: nil,
},
},
{
desc: "only disable",
cfg: &config.Linters{
Enable: nil,
Disable: []string{"dupl", "gofmt", "misspell"},
},
},
{
desc: "no sets",
cfg: &config.Linters{
Enable: nil,
Disable: nil,
},
},
}
func TestValidator_Validate(t *testing.T) {
m, err := NewManager(nil, nil, NewLinterBuilder())
require.NoError(t, err)
var validateAllDisableEnableOptionsTestCases = []validatorTestCase{
{
desc: "nothing",
cfg: &config.Linters{},
},
{
desc: "enable and disable",
cfg: &config.Linters{
Enable: []string{"goimports", "gosec", "nolintlint"},
EnableAll: false,
Disable: []string{"dupl", "gofmt", "misspell"},
DisableAll: false,
},
},
{
desc: "disable-all and enable",
cfg: &config.Linters{
Enable: []string{"goimports", "gosec", "nolintlint"},
EnableAll: false,
Disable: nil,
DisableAll: true,
},
},
{
desc: "enable-all and disable",
cfg: &config.Linters{
Enable: nil,
EnableAll: true,
Disable: []string{"goimports", "gosec", "nolintlint"},
DisableAll: false,
},
},
{
desc: "enable-all and enable and fast",
cfg: &config.Linters{
Enable: []string{"dupl", "gofmt", "misspell"},
EnableAll: true,
Disable: nil,
DisableAll: false,
Fast: true,
},
},
}
func TestValidator_validateEnabledDisabledLintersConfig(t *testing.T) {
v := NewValidator(NewManager(nil, nil))
v := NewValidator(m)
var testCases []validatorTestCase
testCases = append(testCases, validateLintersNamesTestCases...)
testCases = append(testCases, validatePresetsTestCases...)
testCases = append(testCases, validateDisabledAndEnabledAtOneMomentTestCases...)
testCases = append(testCases, validateAllDisableEnableOptionsTestCases...)
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := v.validateEnabledDisabledLintersConfig(test.cfg)
err := v.Validate(&config.Config{Linters: *test.cfg})
require.NoError(t, err)
})
}
}
func TestValidator_validateEnabledDisabledLintersConfig_error(t *testing.T) {
v := NewValidator(NewManager(nil, nil))
func TestValidator_Validate_error(t *testing.T) {
m, err := NewManager(nil, nil, NewLinterBuilder())
require.NoError(t, err)
v := NewValidator(m)
var testCases []validateErrorTestCase
testCases = append(testCases, validateLintersNamesErrorTestCases...)
testCases = append(testCases, validatePresetsErrorTestCases...)
testCases = append(testCases, validateDisabledAndEnabledAtOneMomentErrorTestCases...)
testCases = append(testCases, validateAllDisableEnableOptionsErrorTestCases...)
for _, test := range testCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := v.validateEnabledDisabledLintersConfig(test.cfg)
err := v.Validate(&config.Config{Linters: *test.cfg})
require.Error(t, err)
require.EqualError(t, err, test.expected)
@ -290,7 +151,10 @@ func TestValidator_validateEnabledDisabledLintersConfig_error(t *testing.T) {
}
func TestValidator_validateLintersNames(t *testing.T) {
v := NewValidator(NewManager(nil, nil))
m, err := NewManager(nil, nil, NewLinterBuilder())
require.NoError(t, err)
v := NewValidator(m)
for _, test := range validateLintersNamesTestCases {
test := test
@ -304,7 +168,10 @@ func TestValidator_validateLintersNames(t *testing.T) {
}
func TestValidator_validateLintersNames_error(t *testing.T) {
v := NewValidator(NewManager(nil, nil))
m, err := NewManager(nil, nil, NewLinterBuilder())
require.NoError(t, err)
v := NewValidator(m)
for _, test := range validateLintersNamesErrorTestCases {
test := test
@ -348,63 +215,3 @@ func TestValidator_validatePresets_error(t *testing.T) {
})
}
}
func TestValidator_validateDisabledAndEnabledAtOneMoment(t *testing.T) {
v := NewValidator(nil)
for _, test := range validateDisabledAndEnabledAtOneMomentTestCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := v.validateDisabledAndEnabledAtOneMoment(test.cfg)
require.NoError(t, err)
})
}
}
func TestValidator_validateDisabledAndEnabledAtOneMoment_error(t *testing.T) {
v := NewValidator(nil)
for _, test := range validateDisabledAndEnabledAtOneMomentErrorTestCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := v.validateDisabledAndEnabledAtOneMoment(test.cfg)
require.Error(t, err)
require.EqualError(t, err, test.expected)
})
}
}
func TestValidator_validateAllDisableEnableOptions(t *testing.T) {
v := NewValidator(nil)
for _, test := range validateAllDisableEnableOptionsTestCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := v.validateAllDisableEnableOptions(test.cfg)
require.NoError(t, err)
})
}
}
func TestValidator_validateAllDisableEnableOptions_error(t *testing.T) {
v := NewValidator(nil)
for _, test := range validateAllDisableEnableOptionsErrorTestCases {
test := test
t.Run(test.desc, func(t *testing.T) {
t.Parallel()
err := v.validateAllDisableEnableOptions(test.cfg)
require.Error(t, err)
require.EqualError(t, err, test.expected)
})
}
}

View file

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

View file

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

View file

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

View file

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