fix: speed up "fast" linters (#4653)

This commit is contained in:
Ludovic Fernandez 2024-04-18 21:45:50 +02:00 committed by GitHub
parent a6601827b3
commit 15c57c176e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 112 additions and 36 deletions

View File

@ -20,5 +20,5 @@ func NewTypecheck() *goanalysis.Linter {
"Like the front-end of a Go compiler, parses and type-checks Go code", "Like the front-end of a Go compiler, parses and type-checks Go code",
[]*analysis.Analyzer{analyzer}, []*analysis.Analyzer{analyzer},
nil, nil,
).WithLoadMode(goanalysis.LoadModeTypesInfo) ).WithLoadMode(goanalysis.LoadModeNone)
} }

View File

@ -752,10 +752,7 @@ func (LinterBuilder) Build(cfg *config.Config) ([]*linter.Config, error) {
linter.NewConfig(golinters.NewTypecheck()). linter.NewConfig(golinters.NewTypecheck()).
WithInternal(). WithInternal().
WithEnabledByDefault(). WithEnabledByDefault().
WithSince("v1.3.0"). WithSince("v1.3.0"),
WithLoadForGoAnalysis().
WithPresets(linter.PresetBugs).
WithURL(""),
linter.NewConfig(unconvert.New(&cfg.LintersSettings.Unconvert)). linter.NewConfig(unconvert.New(&cfg.LintersSettings.Unconvert)).
WithSince("v1.0.0"). WithSince("v1.0.0").

View File

@ -204,21 +204,30 @@ func (m *Manager) build(enabledByDefaultLinters []*linter.Config) map[string]*li
} }
func (m *Manager) combineGoAnalysisLinters(linters map[string]*linter.Config) { func (m *Manager) combineGoAnalysisLinters(linters map[string]*linter.Config) {
mlConfig := &linter.Config{}
var goanalysisLinters []*goanalysis.Linter var goanalysisLinters []*goanalysis.Linter
goanalysisPresets := map[string]bool{}
for _, lc := range linters { for _, lc := range linters {
lnt, ok := lc.Linter.(*goanalysis.Linter) lnt, ok := lc.Linter.(*goanalysis.Linter)
if !ok { if !ok {
continue continue
} }
if lnt.LoadMode() == goanalysis.LoadModeWholeProgram { if lnt.LoadMode() == goanalysis.LoadModeWholeProgram {
// It's ineffective by CPU and memory to run whole-program and incremental analyzers at once. // It's ineffective by CPU and memory to run whole-program and incremental analyzers at once.
continue continue
} }
goanalysisLinters = append(goanalysisLinters, lnt)
for _, p := range lc.InPresets { mlConfig.LoadMode |= lc.LoadMode
goanalysisPresets[p] = true
if lc.IsSlowLinter() {
mlConfig.ConsiderSlow()
} }
mlConfig.InPresets = append(mlConfig.InPresets, lc.InPresets...)
goanalysisLinters = append(goanalysisLinters, lnt)
} }
if len(goanalysisLinters) <= 1 { if len(goanalysisLinters) <= 1 {
@ -245,22 +254,13 @@ func (m *Manager) combineGoAnalysisLinters(linters map[string]*linter.Config) {
return a.Name() <= b.Name() return a.Name() <= b.Name()
}) })
ml := goanalysis.NewMetaLinter(goanalysisLinters) mlConfig.Linter = goanalysis.NewMetaLinter(goanalysisLinters)
presets := maps.Keys(goanalysisPresets) sort.Strings(mlConfig.InPresets)
sort.Strings(presets) mlConfig.InPresets = slices.Compact(mlConfig.InPresets)
mlConfig := &linter.Config{ linters[mlConfig.Linter.Name()] = mlConfig
Linter: ml,
EnabledByDefault: false,
InPresets: presets,
AlternativeNames: nil,
OriginalURL: "",
}
mlConfig = mlConfig.WithLoadForGoAnalysis()
linters[ml.Name()] = mlConfig
m.debugf("Combined %d go/analysis linters into one metalinter", len(goanalysisLinters)) m.debugf("Combined %d go/analysis linters into one metalinter", len(goanalysisLinters))
} }

View File

@ -5,6 +5,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/tools/go/packages"
"github.com/golangci/golangci-lint/pkg/config" "github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/goanalysis" "github.com/golangci/golangci-lint/pkg/goanalysis"
@ -55,10 +56,10 @@ func TestManager_GetOptimizedLinters(t *testing.T) {
mlConfig := &linter.Config{ mlConfig := &linter.Config{
Linter: goanalysis.NewMetaLinter(gaLinters), Linter: goanalysis.NewMetaLinter(gaLinters),
InPresets: []string{"bugs", "format"}, InPresets: []string{"format"},
} }
expected := []*linter.Config{mlConfig.WithLoadForGoAnalysis()} expected := []*linter.Config{mlConfig.WithLoadFiles()}
assert.Equal(t, expected, optimizedLinters) assert.Equal(t, expected, optimizedLinters)
} }
@ -176,8 +177,11 @@ func TestManager_combineGoAnalysisLinters(t *testing.T) {
m, err := NewManager(nil, nil) m, err := NewManager(nil, nil)
require.NoError(t, err) require.NoError(t, err)
foo := goanalysis.NewLinter("foo", "example foo", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo) fooTyped := goanalysis.NewLinter("foo", "example foo", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo)
bar := goanalysis.NewLinter("bar", "example bar", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo) barTyped := goanalysis.NewLinter("bar", "example bar", nil, nil).WithLoadMode(goanalysis.LoadModeTypesInfo)
fooSyntax := goanalysis.NewLinter("foo", "example foo", nil, nil).WithLoadMode(goanalysis.LoadModeSyntax)
barSyntax := goanalysis.NewLinter("bar", "example bar", nil, nil).WithLoadMode(goanalysis.LoadModeSyntax)
testCases := []struct { testCases := []struct {
desc string desc string
@ -188,37 +192,112 @@ func TestManager_combineGoAnalysisLinters(t *testing.T) {
desc: "no combined, one linter", desc: "no combined, one linter",
linters: map[string]*linter.Config{ linters: map[string]*linter.Config{
"foo": { "foo": {
Linter: foo, Linter: fooTyped,
InPresets: []string{"A"}, InPresets: []string{"A"},
}, },
}, },
expected: map[string]*linter.Config{ expected: map[string]*linter.Config{
"foo": { "foo": {
Linter: foo, Linter: fooTyped,
InPresets: []string{"A"}, InPresets: []string{"A"},
}, },
}, },
}, },
{ {
desc: "combined, several linters", desc: "combined, several linters (typed)",
linters: map[string]*linter.Config{ linters: map[string]*linter.Config{
"foo": { "foo": {
Linter: foo, Linter: fooTyped,
InPresets: []string{"A"}, InPresets: []string{"A"},
}, },
"bar": { "bar": {
Linter: bar, Linter: barTyped,
InPresets: []string{"B"}, InPresets: []string{"B"},
}, },
}, },
expected: func() map[string]*linter.Config { expected: func() map[string]*linter.Config {
mlConfig := &linter.Config{ mlConfig := &linter.Config{
Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{bar, foo}), Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{barTyped, fooTyped}),
InPresets: []string{"A", "B"}, InPresets: []string{"A", "B"},
} }
return map[string]*linter.Config{ return map[string]*linter.Config{
"goanalysis_metalinter": mlConfig.WithLoadForGoAnalysis(), "goanalysis_metalinter": mlConfig,
}
}(),
},
{
desc: "combined, several linters (different LoadMode)",
linters: map[string]*linter.Config{
"foo": {
Linter: fooTyped,
InPresets: []string{"A"},
LoadMode: packages.NeedName,
},
"bar": {
Linter: barTyped,
InPresets: []string{"B"},
LoadMode: packages.NeedTypesSizes,
},
},
expected: func() map[string]*linter.Config {
mlConfig := &linter.Config{
Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{barTyped, fooTyped}),
InPresets: []string{"A", "B"},
LoadMode: packages.NeedName | packages.NeedTypesSizes,
}
return map[string]*linter.Config{
"goanalysis_metalinter": mlConfig,
}
}(),
},
{
desc: "combined, several linters (same LoadMode)",
linters: map[string]*linter.Config{
"foo": {
Linter: fooTyped,
InPresets: []string{"A"},
LoadMode: packages.NeedName,
},
"bar": {
Linter: barTyped,
InPresets: []string{"B"},
LoadMode: packages.NeedName,
},
},
expected: func() map[string]*linter.Config {
mlConfig := &linter.Config{
Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{barTyped, fooTyped}),
InPresets: []string{"A", "B"},
LoadMode: packages.NeedName,
}
return map[string]*linter.Config{
"goanalysis_metalinter": mlConfig,
}
}(),
},
{
desc: "combined, several linters (syntax)",
linters: map[string]*linter.Config{
"foo": {
Linter: fooSyntax,
InPresets: []string{"A"},
},
"bar": {
Linter: barSyntax,
InPresets: []string{"B"},
},
},
expected: func() map[string]*linter.Config {
mlConfig := &linter.Config{
Linter: goanalysis.NewMetaLinter([]*goanalysis.Linter{barSyntax, fooSyntax}),
InPresets: []string{"A", "B"},
}
return map[string]*linter.Config{
"goanalysis_metalinter": mlConfig,
} }
}(), }(),
}, },

View File

@ -148,7 +148,7 @@ func getEnabledByDefaultFastLintersExcept(t *testing.T, except ...string) []stri
ebdl := m.GetAllEnabledByDefaultLinters() ebdl := m.GetAllEnabledByDefaultLinters()
var ret []string var ret []string
for _, lc := range ebdl { for _, lc := range ebdl {
if lc.IsSlowLinter() { if lc.IsSlowLinter() || lc.Internal {
continue continue
} }
@ -169,7 +169,7 @@ func getAllFastLintersWith(t *testing.T, with ...string) []string {
linters := dbManager.GetAllSupportedLinterConfigs() 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() || lc.Internal {
continue continue
} }
ret = append(ret, lc.Name()) ret = append(ret, lc.Name())
@ -206,7 +206,7 @@ func getEnabledByDefaultFastLintersWith(t *testing.T, with ...string) []string {
ebdl := dbManager.GetAllEnabledByDefaultLinters() 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() || lc.Internal {
continue continue
} }