golangci-lint/pkg/golinters/gocritic/gocritic_test.go

457 lines
13 KiB
Go

package gocritic
import (
"strings"
"testing"
"github.com/go-critic/go-critic/checkers"
gocriticlinter "github.com/go-critic/go-critic/linter"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"github.com/golangci/golangci-lint/pkg/config"
"github.com/golangci/golangci-lint/pkg/logutils"
)
// https://go-critic.com/overview.html
func Test_settingsWrapper_InferEnabledChecks(t *testing.T) {
err := checkers.InitEmbeddedRules()
require.NoError(t, err)
allCheckersInfo := gocriticlinter.GetCheckersInfo()
allChecksByTag := make(map[string][]string)
allChecks := make([]string, 0, len(allCheckersInfo))
for _, checker := range allCheckersInfo {
allChecks = append(allChecks, checker.Name)
for _, tag := range checker.Tags {
allChecksByTag[tag] = append(allChecksByTag[tag], checker.Name)
}
}
enabledByDefaultChecks := make([]string, 0, len(allCheckersInfo))
for _, info := range allCheckersInfo {
if isEnabledByDefaultGoCriticChecker(info) {
enabledByDefaultChecks = append(enabledByDefaultChecks, info.Name)
}
}
t.Logf("enabled by default checks:\n%s", strings.Join(enabledByDefaultChecks, "\n"))
insert := func(in []string, toInsert ...string) []string {
return append(slices.Clone(in), toInsert...)
}
remove := func(in []string, toRemove ...string) []string {
result := slices.Clone(in)
for _, v := range toRemove {
if i := slices.Index(result, v); i != -1 {
result = slices.Delete(result, i, i+1)
}
}
return result
}
uniq := func(in []string) []string {
result := slices.Clone(in)
slices.Sort(result)
return slices.Compact(result)
}
cases := []struct {
name string
sett *config.GoCriticSettings
expectedEnabledChecks []string
}{
{
name: "no configuration",
sett: &config.GoCriticSettings{},
expectedEnabledChecks: enabledByDefaultChecks,
},
{
name: "enable checks",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"assignOp", "badCall", "emptyDecl"},
},
expectedEnabledChecks: insert(enabledByDefaultChecks, "emptyDecl"),
},
{
name: "disable checks",
sett: &config.GoCriticSettings{
DisabledChecks: []string{"assignOp", "emptyDecl"},
},
expectedEnabledChecks: remove(enabledByDefaultChecks, "assignOp"),
},
{
name: "enable tags",
sett: &config.GoCriticSettings{
EnabledTags: []string{"style", "experimental"},
},
expectedEnabledChecks: uniq(insert(insert(
enabledByDefaultChecks,
allChecksByTag["style"]...),
allChecksByTag["experimental"]...)),
},
{
name: "disable tags",
sett: &config.GoCriticSettings{
DisabledTags: []string{"diagnostic"},
},
expectedEnabledChecks: remove(enabledByDefaultChecks, allChecksByTag["diagnostic"]...),
},
{
name: "enable checks disable checks",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"badCall", "badLock"},
DisabledChecks: []string{"assignOp", "badSorting"},
},
expectedEnabledChecks: insert(remove(enabledByDefaultChecks, "assignOp"), "badLock"),
},
{
name: "enable checks enable tags",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"badCall", "badLock", "hugeParam"},
EnabledTags: []string{"diagnostic"},
},
expectedEnabledChecks: uniq(insert(insert(enabledByDefaultChecks,
allChecksByTag["diagnostic"]...),
"hugeParam")),
},
{
name: "enable checks disable tags",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"badCall", "badLock", "boolExprSimplify", "hugeParam"},
DisabledTags: []string{"style", "diagnostic"},
},
expectedEnabledChecks: insert(remove(remove(enabledByDefaultChecks,
allChecksByTag["style"]...),
allChecksByTag["diagnostic"]...),
"hugeParam"),
},
{
name: "enable all checks via tags",
sett: &config.GoCriticSettings{
EnabledTags: []string{"diagnostic", "experimental", "opinionated", "performance", "style"},
},
expectedEnabledChecks: allChecks,
},
{
name: "disable checks enable tags",
sett: &config.GoCriticSettings{
DisabledChecks: []string{"assignOp", "badCall", "badLock", "hugeParam"},
EnabledTags: []string{"style", "diagnostic"},
},
expectedEnabledChecks: remove(uniq(insert(insert(enabledByDefaultChecks,
allChecksByTag["style"]...),
allChecksByTag["diagnostic"]...)),
"assignOp", "badCall", "badLock"),
},
{
name: "disable checks disable tags",
sett: &config.GoCriticSettings{
DisabledChecks: []string{"badCall", "badLock", "codegenComment", "hugeParam"},
DisabledTags: []string{"style"},
},
expectedEnabledChecks: remove(remove(enabledByDefaultChecks,
allChecksByTag["style"]...),
"badCall", "codegenComment"),
},
{
name: "enable tags disable tags",
sett: &config.GoCriticSettings{
EnabledTags: []string{"experimental"},
DisabledTags: []string{"style"},
},
expectedEnabledChecks: remove(uniq(insert(enabledByDefaultChecks,
allChecksByTag["experimental"]...)),
allChecksByTag["style"]...),
},
{
name: "enable checks disable checks enable tags",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"badCall", "badLock", "boolExprSimplify", "indexAlloc", "hugeParam"},
DisabledChecks: []string{"deprecatedComment", "typeSwitchVar"},
EnabledTags: []string{"experimental"},
},
expectedEnabledChecks: remove(uniq(insert(insert(enabledByDefaultChecks,
allChecksByTag["experimental"]...),
"indexAlloc", "hugeParam")),
"deprecatedComment", "typeSwitchVar"),
},
{
name: "enable checks disable checks enable tags disable tags",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"badCall", "badCond", "badLock", "indexAlloc", "hugeParam"},
DisabledChecks: []string{"deprecatedComment", "typeSwitchVar"},
EnabledTags: []string{"experimental"},
DisabledTags: []string{"performance"},
},
expectedEnabledChecks: remove(remove(uniq(insert(insert(enabledByDefaultChecks,
allChecksByTag["experimental"]...),
"badCond")),
allChecksByTag["performance"]...),
"deprecatedComment", "typeSwitchVar"),
},
{
name: "enable single tag only",
sett: &config.GoCriticSettings{
DisableAll: true,
EnabledTags: []string{"experimental"},
},
expectedEnabledChecks: allChecksByTag["experimental"],
},
{
name: "enable two tags only",
sett: &config.GoCriticSettings{
DisableAll: true,
EnabledTags: []string{"experimental", "performance"},
},
expectedEnabledChecks: uniq(insert(allChecksByTag["experimental"], allChecksByTag["performance"]...)),
},
{
name: "disable single tag only",
sett: &config.GoCriticSettings{
EnableAll: true,
DisabledTags: []string{"style"},
},
expectedEnabledChecks: remove(allChecks, allChecksByTag["style"]...),
},
{
name: "disable two tags only",
sett: &config.GoCriticSettings{
EnableAll: true,
DisabledTags: []string{"style", "diagnostic"},
},
expectedEnabledChecks: remove(remove(allChecks, allChecksByTag["style"]...), allChecksByTag["diagnostic"]...),
},
{
name: "enable some checks only",
sett: &config.GoCriticSettings{
DisableAll: true,
EnabledChecks: []string{"deferInLoop", "dupImport", "ifElseChain", "mapKey"},
},
expectedEnabledChecks: []string{"deferInLoop", "dupImport", "ifElseChain", "mapKey"},
},
{
name: "disable some checks only",
sett: &config.GoCriticSettings{
EnableAll: true,
DisabledChecks: []string{"deferInLoop", "dupImport", "ifElseChain", "mapKey"},
},
expectedEnabledChecks: remove(allChecks, "deferInLoop", "dupImport", "ifElseChain", "mapKey"),
},
{
name: "enable single tag and some checks from another tag only",
sett: &config.GoCriticSettings{
DisableAll: true,
EnabledTags: []string{"experimental"},
EnabledChecks: []string{"importShadow"},
},
expectedEnabledChecks: insert(allChecksByTag["experimental"], "importShadow"),
},
{
name: "disable single tag and some checks from another tag only",
sett: &config.GoCriticSettings{
EnableAll: true,
DisabledTags: []string{"experimental"},
DisabledChecks: []string{"importShadow"},
},
expectedEnabledChecks: remove(remove(allChecks, allChecksByTag["experimental"]...), "importShadow"),
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
lg := logutils.NewStderrLog("Test_goCriticSettingsWrapper_InferEnabledChecks")
wr := newSettingsWrapper(tt.sett, lg)
wr.InferEnabledChecks()
assert.ElementsMatch(t, tt.expectedEnabledChecks, maps.Keys(wr.inferredEnabledChecks))
assert.NoError(t, wr.Validate())
})
}
}
func Test_settingsWrapper_Validate(t *testing.T) {
cases := []struct {
name string
sett *config.GoCriticSettings
expectedErr bool
}{
{
name: "combine enable-all and disable-all",
sett: &config.GoCriticSettings{
EnableAll: true,
DisableAll: true,
},
expectedErr: true,
},
{
name: "combine enable-all and enabled-tags",
sett: &config.GoCriticSettings{
EnableAll: true,
EnabledTags: []string{"experimental"},
},
expectedErr: true,
},
{
name: "combine enable-all and enabled-checks",
sett: &config.GoCriticSettings{
EnableAll: true,
EnabledChecks: []string{"dupImport"},
},
expectedErr: true,
},
{
name: "combine disable-all and disabled-tags",
sett: &config.GoCriticSettings{
DisableAll: true,
DisabledTags: []string{"style"},
},
expectedErr: true,
},
{
name: "combine disable-all and disable-checks",
sett: &config.GoCriticSettings{
DisableAll: true,
DisabledChecks: []string{"appendAssign"},
},
expectedErr: true,
},
{
name: "disable-all and no one check enabled",
sett: &config.GoCriticSettings{
DisableAll: true,
},
expectedErr: true,
},
{
name: "unknown enabled tag",
sett: &config.GoCriticSettings{
EnabledTags: []string{"diagnostic", "go-proverbs"},
},
expectedErr: true,
},
{
name: "unknown disabled tag",
sett: &config.GoCriticSettings{
DisabledTags: []string{"style", "go-proverbs"},
},
expectedErr: true,
},
{
name: "unknown enabled check",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"appendAssign", "noExitAfterDefer", "underef"},
},
expectedErr: true,
},
{
name: "unknown disabled check",
sett: &config.GoCriticSettings{
DisabledChecks: []string{"dupSubExpr", "noExitAfterDefer", "returnAfterHttpError"},
},
expectedErr: true,
},
{
name: "settings for unknown check",
sett: &config.GoCriticSettings{
SettingsPerCheck: map[string]config.GoCriticCheckSettings{
"captLocall": {"paramsOnly": false},
"unnamedResult": {"checkExported": true},
},
},
expectedErr: true,
},
{
name: "settings for disabled check",
sett: &config.GoCriticSettings{
DisabledChecks: []string{"elseif"},
SettingsPerCheck: map[string]config.GoCriticCheckSettings{
"elseif": {"skipBalanced": true},
},
},
expectedErr: false, // Just logging.
},
{
name: "settings by lower-cased checker name",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"tooManyResultsChecker"},
SettingsPerCheck: map[string]config.GoCriticCheckSettings{
"toomanyresultschecker": {"maxResults": 3},
"unnamedResult": {"checkExported": true},
},
},
expectedErr: false,
},
{
name: "enabled and disabled at one moment check",
sett: &config.GoCriticSettings{
EnabledChecks: []string{"appendAssign", "codegenComment", "underef"},
DisabledChecks: []string{"elseif", "underef"},
},
expectedErr: true,
},
{
name: "enabled and disabled at one moment tag",
sett: &config.GoCriticSettings{
EnabledTags: []string{"performance", "style"},
DisabledTags: []string{"style", "diagnostic"},
},
expectedErr: true,
},
{
name: "disable all checks via tags",
sett: &config.GoCriticSettings{
DisabledTags: []string{"diagnostic", "experimental", "opinionated", "performance", "style"},
},
expectedErr: true,
},
{
name: "enable-all and disable all checks via tags",
sett: &config.GoCriticSettings{
EnableAll: true,
DisabledTags: []string{"diagnostic", "experimental", "opinionated", "performance", "style"},
},
expectedErr: true,
},
{
name: "valid configuration",
sett: &config.GoCriticSettings{
EnabledTags: []string{"performance"},
DisabledChecks: []string{"dupImport", "ifElseChain", "octalLiteral", "whyNoLint"},
SettingsPerCheck: map[string]config.GoCriticCheckSettings{
"hugeParam": {"sizeThreshold": 100},
"rangeValCopy": {"skipTestFuncs": true},
},
},
expectedErr: false,
},
}
for _, tt := range cases {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
lg := logutils.NewStderrLog("Test_goCriticSettingsWrapper_Validate")
wr := newSettingsWrapper(tt.sett, lg)
wr.InferEnabledChecks()
err := wr.Validate()
if tt.expectedErr {
if assert.Error(t, err) {
t.Log(err)
}
} else {
assert.NoError(t, err)
}
})
}
}