build(deps): bump github.com/go-critic/go-critic from 0.6.3 to 0.6.4 ()

Co-authored-by: Fernandez Ludovic <ldez@users.noreply.github.com>
This commit is contained in:
dependabot[bot] 2022-08-14 18:21:32 +02:00 committed by GitHub
parent edeaa17fd5
commit 9da04f5070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 486 additions and 423 deletions

@ -1113,7 +1113,7 @@ linters-settings:
makezero:
# Allow only slices initialized with a length of zero.
# Default: false
always: false
always: true
maligned:
# Print struct with more effective memory layout or not.

@ -27,7 +27,6 @@ linters-settings:
- ifElseChain
- octalLiteral
- whyNoLint
- wrapperFunc
gocyclo:
min-complexity: 15
goimports:

8
go.mod

@ -27,7 +27,7 @@ require (
github.com/fatih/color v1.13.0
github.com/firefart/nonamedreturns v1.0.4
github.com/fzipp/gocyclo v0.6.0
github.com/go-critic/go-critic v0.6.3
github.com/go-critic/go-critic v0.6.4
github.com/go-xmlfmt/xmlfmt v0.0.0-20191208150333-d5b6f63a941b
github.com/gofrs/flock v0.8.1
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2
@ -121,8 +121,8 @@ require (
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-toolsmith/astcast v1.0.0 // indirect
github.com/go-toolsmith/astcopy v1.0.0 // indirect
github.com/go-toolsmith/astequal v1.0.1 // indirect
github.com/go-toolsmith/astcopy v1.0.1 // indirect
github.com/go-toolsmith/astequal v1.0.2 // indirect
github.com/go-toolsmith/astfmt v1.0.0 // indirect
github.com/go-toolsmith/astp v1.0.0 // indirect
github.com/go-toolsmith/strparse v1.0.0 // indirect
@ -153,7 +153,7 @@ require (
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a // indirect
github.com/quasilyte/go-ruleguard v0.3.17 // indirect
github.com/quasilyte/gogrep v0.0.0-20220120141003-628d8b3623b5 // indirect
github.com/quasilyte/regex/syntax v0.0.0-20200407221936-30656e2c4a95 // indirect
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect

17
go.sum generated

@ -131,6 +131,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsr
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cristalhq/acmd v0.7.0/go.mod h1:LG5oa43pE/BbxtfMoImHCQN++0Su7dzipdgBjMCBVDQ=
github.com/daixiang0/gci v0.6.3 h1:wUAqXChk8HbwXn8AfxD9DYSCp9Bpz1L3e6Q4Roe+q9E=
github.com/daixiang0/gci v0.6.3/go.mod h1:EpVfrztufwVgQRXjnX4zuNinEpLj5OmMjtu/+MB0V0c=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -170,8 +171,8 @@ github.com/fullstorydev/grpcurl v1.6.0/go.mod h1:ZQ+ayqbKMJNhzLmbpCiurTVlaK2M/3n
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-critic/go-critic v0.6.3 h1:abibh5XYBTASawfTQ0rA7dVtQT+6KzpGqb/J+DxRDaw=
github.com/go-critic/go-critic v0.6.3/go.mod h1:c6b3ZP1MQ7o6lPR7Rv3lEf7pYQUmAcx8ABHgdZCQt/k=
github.com/go-critic/go-critic v0.6.4 h1:tucuG1pvOyYgpBIrVxw0R6gwO42lNa92Aq3VaDoIs+E=
github.com/go-critic/go-critic v0.6.4/go.mod h1:qL5SOlk7NtY6sJPoVCTKDIgzNOxHkkkOCVDyi9wJe1U=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -190,11 +191,13 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-toolsmith/astcast v1.0.0 h1:JojxlmI6STnFVG9yOImLeGREv8W2ocNUM+iOhR6jE7g=
github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4=
github.com/go-toolsmith/astcopy v1.0.0 h1:OMgl1b1MEpjFQ1m5ztEO06rz5CUd3oBv9RF7+DyvdG8=
github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ=
github.com/go-toolsmith/astcopy v1.0.1 h1:l09oBhAPyV74kLJ3ZO31iBU8htZGTwr9LTjuMCyL8go=
github.com/go-toolsmith/astcopy v1.0.1/go.mod h1:4TcEdbElGc9twQEYpVo/aieIXfHhiuLh4aLAck6dO7Y=
github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY=
github.com/go-toolsmith/astequal v1.0.1 h1:JbSszi42Jiqu36Gnf363HWS9MTEAz67vTQLponh3Moc=
github.com/go-toolsmith/astequal v1.0.1/go.mod h1:4oGA3EZXTVItV/ipGiOx7NWkY5veFfcsOJVS2YxltLw=
github.com/go-toolsmith/astequal v1.0.2 h1:+XvaV8zNxua+9+Oa4AHmgmpo4RYAbwr/qjNppLfX2yM=
github.com/go-toolsmith/astequal v1.0.2/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
github.com/go-toolsmith/astfmt v1.0.0 h1:A0vDDXt+vsvLEdbMFJAUBI/uTbRw1ffOPnxsILnFL6k=
github.com/go-toolsmith/astfmt v1.0.0/go.mod h1:cnWmsOAuq4jJY6Ct5YWlVLmcmLMn1JUPuQIHCY7CJDw=
github.com/go-toolsmith/astp v1.0.0 h1:alXE75TXgcmupDsMK1fRAy0YUzLzqPVvBKoyWV+KPXg=
@ -570,10 +573,9 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/pseudomuto/protoc-gen-doc v1.3.2/go.mod h1:y5+P6n3iGrbKG+9O04V5ld71in3v/bX88wUwgt+U8EA=
github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q=
github.com/quasilyte/go-ruleguard v0.3.1-0.20210203134552-1b5a410e1cc8/go.mod h1:KsAh3x0e7Fkpgs+Q9pNLS5XpFSvYCEVl5gP9Pp1xp30=
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a h1:sWFavxtIctGrVs5SYZ5Ml1CvrDAs8Kf5kx2PI3C41dA=
github.com/quasilyte/go-ruleguard v0.3.16-0.20220213074421-6aa060fab41a/go.mod h1:VMX+OnnSw4LicdiEGtRSD/1X8kW7GuEscjYNr4cOIT4=
github.com/quasilyte/go-ruleguard v0.3.17 h1:cDdoaSbQg11LXPDQqiCK54QmQXsEQQCTIgdcpeULGSI=
github.com/quasilyte/go-ruleguard v0.3.17/go.mod h1:sST5PvaR7yb/Az5ksX8oc88usJ4EGjmJv7cK7y3jyig=
github.com/quasilyte/go-ruleguard/dsl v0.3.0/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/dsl v0.3.16/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/dsl v0.3.21 h1:vNkC6fC6qMLzCOGbnIHOd5ixUGgTbp3Z4fGnUgULlDA=
github.com/quasilyte/go-ruleguard/dsl v0.3.21/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/rules v0.0.0-20201231183845-9e62ed36efe1/go.mod h1:7JTjp89EGyU1d6XfBiXihJNG37wB2VRkd125Q1u7Plc=
@ -780,6 +782,7 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d h1:+W8Qf4iJtMGKkyAygcKohjxTk4JPsL9DpzApJ22m5Ic=
golang.org/x/exp/typeparams v0.0.0-20220613132600-b0d781184e0d/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=

@ -117,11 +117,6 @@ func NewExecutor(version, commit, date string) *Executor {
// recreate after getting config
e.DBManager = lintersdb.NewManager(e.cfg, e.log).WithCustomLinters()
e.cfg.LintersSettings.Gocritic.InferEnabledChecks(e.log)
if err = e.cfg.LintersSettings.Gocritic.Validate(e.log); err != nil {
e.log.Fatalf("Invalid gocritic settings: %s", err)
}
// Slice options must be explicitly set for proper merging of config and command-line options.
fixSlicesFlags(e.runCmd.Flags())
fixSlicesFlags(e.lintersCmd.Flags())

@ -40,8 +40,8 @@ var defaultLintersSettings = LintersSettings{
Gocognit: GocognitSettings{
MinComplexity: 30,
},
Gocritic: GocriticSettings{
SettingsPerCheck: map[string]GocriticCheckSettings{},
Gocritic: GoCriticSettings{
SettingsPerCheck: map[string]GoCriticCheckSettings{},
},
Godox: GodoxSettings{
Keywords: []string{},
@ -133,7 +133,7 @@ type LintersSettings struct {
Gci GciSettings
Gocognit GocognitSettings
Goconst GoConstSettings
Gocritic GocriticSettings
Gocritic GoCriticSettings
Gocyclo GoCycloSettings
Godot GodotSettings
Godox GodoxSettings
@ -306,6 +306,16 @@ type GoConstSettings struct {
IgnoreCalls bool `mapstructure:"ignore-calls"`
}
type GoCriticSettings struct {
EnabledChecks []string `mapstructure:"enabled-checks"`
DisabledChecks []string `mapstructure:"disabled-checks"`
EnabledTags []string `mapstructure:"enabled-tags"`
DisabledTags []string `mapstructure:"disabled-tags"`
SettingsPerCheck map[string]GoCriticCheckSettings `mapstructure:"settings"`
}
type GoCriticCheckSettings map[string]interface{}
type GoCycloSettings struct {
MinComplexity int `mapstructure:"min-complexity"`
}

@ -1,366 +0,0 @@
package config
import (
"fmt"
"sort"
"strings"
_ "github.com/go-critic/go-critic/checkers" // this import register checkers
"github.com/go-critic/go-critic/framework/linter"
"github.com/pkg/errors"
"github.com/golangci/golangci-lint/pkg/logutils"
)
const gocriticDebugKey = "gocritic"
var (
gocriticDebugf = logutils.Debug(gocriticDebugKey)
isGocriticDebug = logutils.HaveDebugTag(gocriticDebugKey)
allGocriticCheckers = linter.GetCheckersInfo()
allGocriticCheckerMap = func() map[string]*linter.CheckerInfo {
checkInfoMap := make(map[string]*linter.CheckerInfo)
for _, checkInfo := range allGocriticCheckers {
checkInfoMap[checkInfo.Name] = checkInfo
}
return checkInfoMap
}()
)
type GocriticCheckSettings map[string]interface{}
type GocriticSettings struct {
EnabledChecks []string `mapstructure:"enabled-checks"`
DisabledChecks []string `mapstructure:"disabled-checks"`
EnabledTags []string `mapstructure:"enabled-tags"`
DisabledTags []string `mapstructure:"disabled-tags"`
SettingsPerCheck map[string]GocriticCheckSettings `mapstructure:"settings"`
inferredEnabledChecks map[string]bool
}
func debugChecksListf(checks []string, format string, args ...interface{}) {
if isGocriticDebug {
prefix := fmt.Sprintf(format, args...)
gocriticDebugf(prefix+" checks (%d): %s", len(checks), sprintStrings(checks))
}
}
func stringsSliceToSet(ss []string) map[string]bool {
ret := make(map[string]bool, len(ss))
for _, s := range ss {
ret[s] = true
}
return ret
}
func buildGocriticTagToCheckersMap() map[string][]string {
tagToCheckers := map[string][]string{}
for _, checker := range allGocriticCheckers {
for _, tag := range checker.Tags {
tagToCheckers[tag] = append(tagToCheckers[tag], checker.Name)
}
}
return tagToCheckers
}
func gocriticCheckerTagsDebugf() {
if !isGocriticDebug {
return
}
tagToCheckers := buildGocriticTagToCheckersMap()
allTags := make([]string, 0, len(tagToCheckers))
for tag := range tagToCheckers {
allTags = append(allTags, tag)
}
sort.Strings(allTags)
gocriticDebugf("All gocritic existing tags and checks:")
for _, tag := range allTags {
debugChecksListf(tagToCheckers[tag], " tag %q", tag)
}
}
func (s *GocriticSettings) gocriticDisabledCheckersDebugf() {
if !isGocriticDebug {
return
}
var disabledCheckers []string
for _, checker := range allGocriticCheckers {
if s.inferredEnabledChecks[strings.ToLower(checker.Name)] {
continue
}
disabledCheckers = append(disabledCheckers, checker.Name)
}
if len(disabledCheckers) == 0 {
gocriticDebugf("All checks are enabled")
} else {
debugChecksListf(disabledCheckers, "Final not used")
}
}
func (s *GocriticSettings) InferEnabledChecks(log logutils.Log) {
gocriticCheckerTagsDebugf()
enabledByDefaultChecks := getDefaultEnabledGocriticCheckersNames()
debugChecksListf(enabledByDefaultChecks, "Enabled by default")
disabledByDefaultChecks := getDefaultDisabledGocriticCheckersNames()
debugChecksListf(disabledByDefaultChecks, "Disabled by default")
enabledChecks := make([]string, 0, len(s.EnabledTags)+len(enabledByDefaultChecks))
// EnabledTags
if len(s.EnabledTags) != 0 {
tagToCheckers := buildGocriticTagToCheckersMap()
for _, tag := range s.EnabledTags {
enabledChecks = append(enabledChecks, tagToCheckers[tag]...)
}
debugChecksListf(enabledChecks, "Enabled by config tags %s", sprintStrings(s.EnabledTags))
}
if !(len(s.EnabledTags) == 0 && len(s.EnabledChecks) != 0) {
// don't use default checks only if we have no enabled tags and enable some checks manually
enabledChecks = append(enabledChecks, enabledByDefaultChecks...)
}
// DisabledTags
if len(s.DisabledTags) != 0 {
enabledChecks = filterByDisableTags(enabledChecks, s.DisabledTags, log)
}
// EnabledChecks
if len(s.EnabledChecks) != 0 {
debugChecksListf(s.EnabledChecks, "Enabled by config")
alreadyEnabledChecksSet := stringsSliceToSet(enabledChecks)
for _, enabledCheck := range s.EnabledChecks {
if alreadyEnabledChecksSet[enabledCheck] {
log.Warnf("No need to enable check %q: it's already enabled", enabledCheck)
continue
}
enabledChecks = append(enabledChecks, enabledCheck)
}
}
// DisabledChecks
if len(s.DisabledChecks) != 0 {
debugChecksListf(s.DisabledChecks, "Disabled by config")
enabledChecksSet := stringsSliceToSet(enabledChecks)
for _, disabledCheck := range s.DisabledChecks {
if !enabledChecksSet[disabledCheck] {
log.Warnf("Gocritic check %q was explicitly disabled via config. However, as this check "+
"is disabled by default, there is no need to explicitly disable it via config.", disabledCheck)
continue
}
delete(enabledChecksSet, disabledCheck)
}
enabledChecks = nil
for enabledCheck := range enabledChecksSet {
enabledChecks = append(enabledChecks, enabledCheck)
}
}
s.inferredEnabledChecks = map[string]bool{}
for _, check := range enabledChecks {
s.inferredEnabledChecks[strings.ToLower(check)] = true
}
debugChecksListf(enabledChecks, "Final used")
s.gocriticDisabledCheckersDebugf()
}
func validateStringsUniq(ss []string) error {
set := map[string]bool{}
for _, s := range ss {
_, ok := set[s]
if ok {
return fmt.Errorf("%q occurs multiple times in list", s)
}
set[s] = true
}
return nil
}
func intersectStringSlice(s1, s2 []string) []string {
s1Map := make(map[string]struct{}, len(s1))
for _, s := range s1 {
s1Map[s] = struct{}{}
}
result := make([]string, 0)
for _, s := range s2 {
if _, exists := s1Map[s]; exists {
result = append(result, s)
}
}
return result
}
func (s *GocriticSettings) Validate(log logutils.Log) error {
if len(s.EnabledTags) == 0 {
if len(s.EnabledChecks) != 0 && len(s.DisabledChecks) != 0 {
return errors.New("both enabled and disabled check aren't allowed for gocritic")
}
} else {
if err := validateStringsUniq(s.EnabledTags); err != nil {
return errors.Wrap(err, "validate enabled tags")
}
tagToCheckers := buildGocriticTagToCheckersMap()
for _, tag := range s.EnabledTags {
if _, ok := tagToCheckers[tag]; !ok {
return fmt.Errorf("gocritic [enabled]tag %q doesn't exist", tag)
}
}
}
if len(s.DisabledTags) > 0 {
tagToCheckers := buildGocriticTagToCheckersMap()
for _, tag := range s.EnabledTags {
if _, ok := tagToCheckers[tag]; !ok {
return fmt.Errorf("gocritic [disabled]tag %q doesn't exist", tag)
}
}
}
if err := validateStringsUniq(s.EnabledChecks); err != nil {
return errors.Wrap(err, "validate enabled checks")
}
if err := validateStringsUniq(s.DisabledChecks); err != nil {
return errors.Wrap(err, "validate disabled checks")
}
if err := s.validateCheckerNames(log); err != nil {
return errors.Wrap(err, "validation failed")
}
return nil
}
func (s *GocriticSettings) IsCheckEnabled(name string) bool {
return s.inferredEnabledChecks[strings.ToLower(name)]
}
func sprintAllowedCheckerNames(allowedNames map[string]bool) string {
namesSlice := make([]string, 0, len(allowedNames))
for name := range allowedNames {
namesSlice = append(namesSlice, name)
}
return sprintStrings(namesSlice)
}
func sprintStrings(ss []string) string {
sort.Strings(ss)
return fmt.Sprint(ss)
}
// getAllCheckerNames returns a map containing all checker names supported by gocritic.
func getAllCheckerNames() map[string]bool {
allCheckerNames := make(map[string]bool, len(allGocriticCheckers))
for _, checker := range allGocriticCheckers {
allCheckerNames[strings.ToLower(checker.Name)] = true
}
return allCheckerNames
}
func isEnabledByDefaultGocriticCheck(info *linter.CheckerInfo) bool {
return !info.HasTag("experimental") &&
!info.HasTag("opinionated") &&
!info.HasTag("performance")
}
func getDefaultEnabledGocriticCheckersNames() []string {
var enabled []string
for _, info := range allGocriticCheckers {
enable := isEnabledByDefaultGocriticCheck(info)
if enable {
enabled = append(enabled, info.Name)
}
}
return enabled
}
func getDefaultDisabledGocriticCheckersNames() []string {
var disabled []string
for _, info := range allGocriticCheckers {
enable := isEnabledByDefaultGocriticCheck(info)
if !enable {
disabled = append(disabled, info.Name)
}
}
return disabled
}
func (s *GocriticSettings) validateCheckerNames(log logutils.Log) error {
allowedNames := getAllCheckerNames()
for _, name := range s.EnabledChecks {
if !allowedNames[strings.ToLower(name)] {
return fmt.Errorf("enabled checker %s doesn't exist, all existing checkers: %s",
name, sprintAllowedCheckerNames(allowedNames))
}
}
for _, name := range s.DisabledChecks {
if !allowedNames[strings.ToLower(name)] {
return fmt.Errorf("disabled checker %s doesn't exist, all existing checkers: %s",
name, sprintAllowedCheckerNames(allowedNames))
}
}
for checkName := range s.SettingsPerCheck {
if _, ok := allowedNames[checkName]; !ok {
return fmt.Errorf("invalid setting, checker %s doesn't exist, all existing checkers: %s",
checkName, sprintAllowedCheckerNames(allowedNames))
}
if !s.IsCheckEnabled(checkName) {
log.Warnf("Gocritic settings were provided for not enabled check %q", checkName)
}
}
return nil
}
func (s *GocriticSettings) GetLowercasedParams() map[string]GocriticCheckSettings {
ret := make(map[string]GocriticCheckSettings, len(s.SettingsPerCheck))
for checker, params := range s.SettingsPerCheck {
ret[strings.ToLower(checker)] = params
}
return ret
}
func filterByDisableTags(enabledChecks, disableTags []string, log logutils.Log) []string {
enabledChecksSet := stringsSliceToSet(enabledChecks)
for _, enabledCheck := range enabledChecks {
checkInfo, checkInfoExists := allGocriticCheckerMap[enabledCheck]
if !checkInfoExists {
log.Warnf("Gocritic check %q was not exists via filtering disabled tags", enabledCheck)
continue
}
hitTags := intersectStringSlice(checkInfo.Tags, disableTags)
if len(hitTags) != 0 {
delete(enabledChecksSet, enabledCheck)
}
}
debugChecksListf(enabledChecks, "Disabled by config tags %s", sprintStrings(disableTags))
enabledChecks = nil
for enabledCheck := range enabledChecksSet {
enabledChecks = append(enabledChecks, enabledCheck)
}
return enabledChecks
}

@ -11,31 +11,39 @@ import (
"strings"
"sync"
"github.com/go-critic/go-critic/checkers"
gocriticlinter "github.com/go-critic/go-critic/framework/linter"
"github.com/pkg/errors"
"golang.org/x/tools/go/analysis"
"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"
"github.com/golangci/golangci-lint/pkg/result"
)
const gocriticName = "gocritic"
const goCriticName = "gocritic"
func NewGocritic(settings *config.GocriticSettings, cfg *config.Config) *goanalysis.Linter {
const goCriticDebugKey = "gocritic"
var (
goCriticDebugf = logutils.Debug(goCriticDebugKey)
isGoCriticDebug = logutils.HaveDebugTag(goCriticDebugKey)
)
func NewGoCritic(settings *config.GoCriticSettings, cfg *config.Config) *goanalysis.Linter {
var mu sync.Mutex
var resIssues []goanalysis.Issue
sizes := types.SizesFor("gc", runtime.GOARCH)
wrapper := goCriticWrapper{
wrapper := &goCriticWrapper{
settings: settings,
cfg: cfg,
sizes: sizes,
sizes: types.SizesFor("gc", runtime.GOARCH),
}
analyzer := &analysis.Analyzer{
Name: gocriticName,
Name: goCriticName,
Doc: goanalysis.TheOnlyanalyzerDoc,
Run: func(pass *analysis.Pass) (interface{}, error) {
issues, err := wrapper.run(pass)
@ -56,24 +64,56 @@ func NewGocritic(settings *config.GocriticSettings, cfg *config.Config) *goanaly
}
return goanalysis.NewLinter(
gocriticName,
goCriticName,
`Provides diagnostics that check for bugs, performance and style issues.
Extensible without recompilation through dynamic rules.
Dynamic rules are written declaratively with AST patterns, filters, report message and optional suggestion.`,
[]*analysis.Analyzer{analyzer},
nil,
).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
return resIssues
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
).
WithContextSetter(func(context *linter.Context) {
wrapper.init()
}).
WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
return resIssues
}).WithLoadMode(goanalysis.LoadModeTypesInfo)
}
type goCriticWrapper struct {
settings *config.GocriticSettings
cfg *config.Config
sizes types.Sizes
settings *config.GoCriticSettings
settingsWrapper *goCriticSettingsWrapper
cfg *config.Config
sizes types.Sizes
once sync.Once
}
func (w goCriticWrapper) run(pass *analysis.Pass) ([]goanalysis.Issue, error) {
func (w *goCriticWrapper) init() {
if w.settings != nil {
// the 'defer' is here to catch the panic from checkers.InitEmbeddedRules()
defer func() {
if err := recover(); err != nil {
linterLogger.Fatalf("%s: %v: setting an explicit GOROOT can fix this problem.", goCriticName, err)
return
}
}()
w.once.Do(checkers.InitEmbeddedRules)
settingsWrapper := newGoCriticSettingsWrapper(w.settings)
settingsWrapper.inferEnabledChecks(linterLogger)
if err := settingsWrapper.validate(linterLogger); err != nil {
linterLogger.Fatalf("%s: invalid settings: %s", goCriticName, err)
}
w.settingsWrapper = settingsWrapper
}
}
func (w *goCriticWrapper) run(pass *analysis.Pass) ([]goanalysis.Issue, error) {
if w.settingsWrapper == nil {
return nil, fmt.Errorf("the settings wrapper is nil")
}
linterCtx := gocriticlinter.NewContext(pass.Fset, w.sizes)
enabledCheckers, err := w.buildEnabledCheckers(linterCtx)
@ -93,12 +133,12 @@ func (w goCriticWrapper) run(pass *analysis.Pass) ([]goanalysis.Issue, error) {
return issues, nil
}
func (w goCriticWrapper) buildEnabledCheckers(linterCtx *gocriticlinter.Context) ([]*gocriticlinter.Checker, error) {
allParams := w.settings.GetLowercasedParams()
func (w *goCriticWrapper) buildEnabledCheckers(linterCtx *gocriticlinter.Context) ([]*gocriticlinter.Checker, error) {
allParams := w.settingsWrapper.getLowerCasedParams()
var enabledCheckers []*gocriticlinter.Checker
for _, info := range gocriticlinter.GetCheckersInfo() {
if !w.settings.IsCheckEnabled(info.Name) {
if !w.settingsWrapper.isCheckEnabled(info.Name) {
continue
}
@ -116,23 +156,23 @@ func (w goCriticWrapper) buildEnabledCheckers(linterCtx *gocriticlinter.Context)
return enabledCheckers, nil
}
func runGocriticOnPackage(linterCtx *gocriticlinter.Context, checkers []*gocriticlinter.Checker,
func runGocriticOnPackage(linterCtx *gocriticlinter.Context, checks []*gocriticlinter.Checker,
files []*ast.File) []result.Issue {
var res []result.Issue
for _, f := range files {
filename := filepath.Base(linterCtx.FileSet.Position(f.Pos()).Filename)
linterCtx.SetFileInfo(filename, f)
issues := runGocriticOnFile(linterCtx, f, checkers)
issues := runGocriticOnFile(linterCtx, f, checks)
res = append(res, issues...)
}
return res
}
func runGocriticOnFile(linterCtx *gocriticlinter.Context, f *ast.File, checkers []*gocriticlinter.Checker) []result.Issue {
func runGocriticOnFile(linterCtx *gocriticlinter.Context, f *ast.File, checks []*gocriticlinter.Checker) []result.Issue {
var res []result.Issue
for _, c := range checkers {
for _, c := range checks {
// All checkers are expected to use *lint.Context
// as read-only structure, so no copying is required.
for _, warn := range c.Check(f) {
@ -140,7 +180,7 @@ func runGocriticOnFile(linterCtx *gocriticlinter.Context, f *ast.File, checkers
issue := result.Issue{
Pos: pos,
Text: fmt.Sprintf("%s: %s", c.Info.Name, warn.Text),
FromLinter: gocriticName,
FromLinter: goCriticName,
}
if warn.HasQuickFix() {
@ -160,7 +200,7 @@ func runGocriticOnFile(linterCtx *gocriticlinter.Context, f *ast.File, checkers
return res
}
func (w goCriticWrapper) configureCheckerInfo(info *gocriticlinter.CheckerInfo, allParams map[string]config.GocriticCheckSettings) error {
func (w *goCriticWrapper) configureCheckerInfo(info *gocriticlinter.CheckerInfo, allParams map[string]config.GoCriticCheckSettings) error {
params := allParams[strings.ToLower(info.Name)]
if params == nil { // no config for this checker
return nil
@ -208,7 +248,7 @@ func normalizeCheckerInfoParams(info *gocriticlinter.CheckerInfo) gocriticlinter
// but the file parsers (TOML, YAML, JSON) don't create the same representation for raw type.
// then we have to convert value types into the expected value types.
// Maybe in the future, this kind of conversion will be done in go-critic itself.
func (w goCriticWrapper) normalizeCheckerParamsValue(p interface{}) interface{} {
func (w *goCriticWrapper) normalizeCheckerParamsValue(p interface{}) interface{} {
rv := reflect.ValueOf(p)
switch rv.Type().Kind() {
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
@ -222,3 +262,378 @@ func (w goCriticWrapper) normalizeCheckerParamsValue(p interface{}) interface{}
return p
}
}
// TODO(ldez): rewrite and simplify goCriticSettingsWrapper.
type goCriticSettingsWrapper struct {
*config.GoCriticSettings
allCheckers []*gocriticlinter.CheckerInfo
allCheckerMap map[string]*gocriticlinter.CheckerInfo
inferredEnabledChecks map[string]bool
}
func newGoCriticSettingsWrapper(settings *config.GoCriticSettings) *goCriticSettingsWrapper {
allCheckers := gocriticlinter.GetCheckersInfo()
allCheckerMap := make(map[string]*gocriticlinter.CheckerInfo)
for _, checkInfo := range allCheckers {
allCheckerMap[checkInfo.Name] = checkInfo
}
return &goCriticSettingsWrapper{
GoCriticSettings: settings,
allCheckers: allCheckers,
allCheckerMap: allCheckerMap,
inferredEnabledChecks: map[string]bool{},
}
}
func (s *goCriticSettingsWrapper) buildTagToCheckersMap() map[string][]string {
tagToCheckers := map[string][]string{}
for _, checker := range s.allCheckers {
for _, tag := range checker.Tags {
tagToCheckers[tag] = append(tagToCheckers[tag], checker.Name)
}
}
return tagToCheckers
}
func (s *goCriticSettingsWrapper) checkerTagsDebugf() {
if !isGoCriticDebug {
return
}
tagToCheckers := s.buildTagToCheckersMap()
allTags := make([]string, 0, len(tagToCheckers))
for tag := range tagToCheckers {
allTags = append(allTags, tag)
}
sort.Strings(allTags)
goCriticDebugf("All gocritic existing tags and checks:")
for _, tag := range allTags {
debugChecksListf(tagToCheckers[tag], " tag %q", tag)
}
}
func (s *goCriticSettingsWrapper) disabledCheckersDebugf() {
if !isGoCriticDebug {
return
}
var disabledCheckers []string
for _, checker := range s.allCheckers {
if s.inferredEnabledChecks[strings.ToLower(checker.Name)] {
continue
}
disabledCheckers = append(disabledCheckers, checker.Name)
}
if len(disabledCheckers) == 0 {
goCriticDebugf("All checks are enabled")
} else {
debugChecksListf(disabledCheckers, "Final not used")
}
}
func (s *goCriticSettingsWrapper) inferEnabledChecks(log logutils.Log) {
s.checkerTagsDebugf()
enabledByDefaultChecks := s.getDefaultEnabledCheckersNames()
debugChecksListf(enabledByDefaultChecks, "Enabled by default")
disabledByDefaultChecks := s.getDefaultDisabledCheckersNames()
debugChecksListf(disabledByDefaultChecks, "Disabled by default")
enabledChecks := make([]string, 0, len(s.EnabledTags)+len(enabledByDefaultChecks))
// EnabledTags
if len(s.EnabledTags) != 0 {
tagToCheckers := s.buildTagToCheckersMap()
for _, tag := range s.EnabledTags {
enabledChecks = append(enabledChecks, tagToCheckers[tag]...)
}
debugChecksListf(enabledChecks, "Enabled by config tags %s", sprintStrings(s.EnabledTags))
}
if !(len(s.EnabledTags) == 0 && len(s.EnabledChecks) != 0) {
// don't use default checks only if we have no enabled tags and enable some checks manually
enabledChecks = append(enabledChecks, enabledByDefaultChecks...)
}
// DisabledTags
if len(s.DisabledTags) != 0 {
enabledChecks = s.filterByDisableTags(enabledChecks, s.DisabledTags, log)
}
// EnabledChecks
if len(s.EnabledChecks) != 0 {
debugChecksListf(s.EnabledChecks, "Enabled by config")
alreadyEnabledChecksSet := stringsSliceToSet(enabledChecks)
for _, enabledCheck := range s.EnabledChecks {
if alreadyEnabledChecksSet[enabledCheck] {
log.Warnf("No need to enable check %q: it's already enabled", enabledCheck)
continue
}
enabledChecks = append(enabledChecks, enabledCheck)
}
}
// DisabledChecks
if len(s.DisabledChecks) != 0 {
debugChecksListf(s.DisabledChecks, "Disabled by config")
enabledChecksSet := stringsSliceToSet(enabledChecks)
for _, disabledCheck := range s.DisabledChecks {
if !enabledChecksSet[disabledCheck] {
log.Warnf("Gocritic check %q was explicitly disabled via config. However, as this check "+
"is disabled by default, there is no need to explicitly disable it via config.", disabledCheck)
continue
}
delete(enabledChecksSet, disabledCheck)
}
enabledChecks = nil
for enabledCheck := range enabledChecksSet {
enabledChecks = append(enabledChecks, enabledCheck)
}
}
s.inferredEnabledChecks = map[string]bool{}
for _, check := range enabledChecks {
s.inferredEnabledChecks[strings.ToLower(check)] = true
}
debugChecksListf(enabledChecks, "Final used")
s.disabledCheckersDebugf()
}
func (s *goCriticSettingsWrapper) validate(log logutils.Log) error {
if len(s.EnabledTags) == 0 {
if len(s.EnabledChecks) != 0 && len(s.DisabledChecks) != 0 {
return errors.New("both enabled and disabled check aren't allowed for gocritic")
}
} else {
if err := validateStringsUniq(s.EnabledTags); err != nil {
return errors.Wrap(err, "validate enabled tags")
}
tagToCheckers := s.buildTagToCheckersMap()
for _, tag := range s.EnabledTags {
if _, ok := tagToCheckers[tag]; !ok {
return fmt.Errorf("gocritic [enabled]tag %q doesn't exist", tag)
}
}
}
if len(s.DisabledTags) > 0 {
tagToCheckers := s.buildTagToCheckersMap()
for _, tag := range s.EnabledTags {
if _, ok := tagToCheckers[tag]; !ok {
return fmt.Errorf("gocritic [disabled]tag %q doesn't exist", tag)
}
}
}
if err := validateStringsUniq(s.EnabledChecks); err != nil {
return errors.Wrap(err, "validate enabled checks")
}
if err := validateStringsUniq(s.DisabledChecks); err != nil {
return errors.Wrap(err, "validate disabled checks")
}
if err := s.validateCheckerNames(log); err != nil {
return errors.Wrap(err, "validation failed")
}
return nil
}
func (s *goCriticSettingsWrapper) isCheckEnabled(name string) bool {
return s.inferredEnabledChecks[strings.ToLower(name)]
}
// getAllCheckerNames returns a map containing all checker names supported by gocritic.
func (s *goCriticSettingsWrapper) getAllCheckerNames() map[string]bool {
allCheckerNames := make(map[string]bool, len(s.allCheckers))
for _, checker := range s.allCheckers {
allCheckerNames[strings.ToLower(checker.Name)] = true
}
return allCheckerNames
}
func (s *goCriticSettingsWrapper) getDefaultEnabledCheckersNames() []string {
var enabled []string
for _, info := range s.allCheckers {
enable := s.isEnabledByDefaultCheck(info)
if enable {
enabled = append(enabled, info.Name)
}
}
return enabled
}
func (s *goCriticSettingsWrapper) getDefaultDisabledCheckersNames() []string {
var disabled []string
for _, info := range s.allCheckers {
enable := s.isEnabledByDefaultCheck(info)
if !enable {
disabled = append(disabled, info.Name)
}
}
return disabled
}
func (s *goCriticSettingsWrapper) validateCheckerNames(log logutils.Log) error {
allowedNames := s.getAllCheckerNames()
for _, name := range s.EnabledChecks {
if !allowedNames[strings.ToLower(name)] {
return fmt.Errorf("enabled checker %s doesn't exist, all existing checkers: %s",
name, sprintAllowedCheckerNames(allowedNames))
}
}
for _, name := range s.DisabledChecks {
if !allowedNames[strings.ToLower(name)] {
return fmt.Errorf("disabled checker %s doesn't exist, all existing checkers: %s",
name, sprintAllowedCheckerNames(allowedNames))
}
}
for checkName := range s.SettingsPerCheck {
if _, ok := allowedNames[checkName]; !ok {
return fmt.Errorf("invalid setting, checker %s doesn't exist, all existing checkers: %s",
checkName, sprintAllowedCheckerNames(allowedNames))
}
if !s.isCheckEnabled(checkName) {
log.Warnf("%s: settings were provided for not enabled check %q", goCriticName, checkName)
}
}
return nil
}
func (s *goCriticSettingsWrapper) getLowerCasedParams() map[string]config.GoCriticCheckSettings {
ret := make(map[string]config.GoCriticCheckSettings, len(s.SettingsPerCheck))
for checker, params := range s.SettingsPerCheck {
ret[strings.ToLower(checker)] = params
}
return ret
}
func (s *goCriticSettingsWrapper) filterByDisableTags(enabledChecks, disableTags []string, log logutils.Log) []string {
enabledChecksSet := stringsSliceToSet(enabledChecks)
for _, enabledCheck := range enabledChecks {
checkInfo, checkInfoExists := s.allCheckerMap[enabledCheck]
if !checkInfoExists {
log.Warnf("%s: check %q was not exists via filtering disabled tags", goCriticName, enabledCheck)
continue
}
hitTags := intersectStringSlice(checkInfo.Tags, disableTags)
if len(hitTags) != 0 {
delete(enabledChecksSet, enabledCheck)
}
}
debugChecksListf(enabledChecks, "Disabled by config tags %s", sprintStrings(disableTags))
enabledChecks = nil
for enabledCheck := range enabledChecksSet {
enabledChecks = append(enabledChecks, enabledCheck)
}
return enabledChecks
}
func (s *goCriticSettingsWrapper) isEnabledByDefaultCheck(info *gocriticlinter.CheckerInfo) bool {
return !info.HasTag("experimental") &&
!info.HasTag("opinionated") &&
!info.HasTag("performance")
}
func validateStringsUniq(ss []string) error {
set := map[string]bool{}
for _, s := range ss {
_, ok := set[s]
if ok {
return fmt.Errorf("%q occurs multiple times in list", s)
}
set[s] = true
}
return nil
}
func intersectStringSlice(s1, s2 []string) []string {
s1Map := make(map[string]struct{}, len(s1))
for _, s := range s1 {
s1Map[s] = struct{}{}
}
results := make([]string, 0)
for _, s := range s2 {
if _, exists := s1Map[s]; exists {
results = append(results, s)
}
}
return results
}
func sprintAllowedCheckerNames(allowedNames map[string]bool) string {
namesSlice := make([]string, 0, len(allowedNames))
for name := range allowedNames {
namesSlice = append(namesSlice, name)
}
return sprintStrings(namesSlice)
}
func sprintStrings(ss []string) string {
sort.Strings(ss)
return fmt.Sprint(ss)
}
func debugChecksListf(checks []string, format string, args ...interface{}) {
if !isGoCriticDebug {
return
}
goCriticDebugf("%s checks (%d): %s", fmt.Sprintf(format, args...), len(checks), sprintStrings(checks))
}
func stringsSliceToSet(ss []string) map[string]bool {
ret := make(map[string]bool, len(ss))
for _, s := range ss {
ret[s] = true
}
return ret
}

@ -1,4 +1,4 @@
package config
package golinters
import (
"log"
@ -25,7 +25,9 @@ func Test_filterByDisableTags(t *testing.T) {
disabledTags := []string{"experimental", "opinionated"}
enabledChecks := []string{"appendAssign", "sortSlice", "caseOrder", "dupImport"}
filterEnabledChecks := filterByDisableTags(enabledChecks, disabledTags, &tLog{})
settingsWrapper := newGoCriticSettingsWrapper(nil)
filterEnabledChecks := settingsWrapper.filterByDisableTags(enabledChecks, disabledTags, &tLog{})
sort.Strings(filterEnabledChecks)

@ -88,7 +88,7 @@ func getLLLIssuesForFile(filename string, maxLineLen int, tabSpaces string) ([]r
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
line = strings.Replace(line, "\t", tabSpaces, -1)
line = strings.ReplaceAll(line, "\t", tabSpaces)
lineLen := utf8.RuneCountInString(line)
if lineLen > maxLineLen {
res = append(res, result.Issue{

@ -119,7 +119,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
gciCfg *config.GciSettings
gocognitCfg *config.GocognitSettings
goconstCfg *config.GoConstSettings
gocriticCfg *config.GocriticSettings
gocriticCfg *config.GoCriticSettings
gocycloCfg *config.GoCycloSettings
godotCfg *config.GodotSettings
godoxCfg *config.GodoxSettings
@ -436,7 +436,7 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
WithPresets(linter.PresetStyle).
WithURL("https://github.com/jgautheron/goconst"),
linter.NewConfig(golinters.NewGocritic(gocriticCfg, m.cfg)).
linter.NewConfig(golinters.NewGoCritic(gocriticCfg, m.cfg)).
WithSince("v1.12.0").
WithPresets(linter.PresetStyle, linter.PresetMetaLinter).
WithLoadForGoAnalysis().

@ -31,8 +31,8 @@ func (p PathShortener) Name() string {
func (p PathShortener) Process(issues []result.Issue) ([]result.Issue, error) {
return transformIssues(issues, func(i *result.Issue) *result.Issue {
newI := i
newI.Text = strings.Replace(newI.Text, p.wd+"/", "", -1)
newI.Text = strings.Replace(newI.Text, p.wd, "", -1)
newI.Text = strings.ReplaceAll(newI.Text, p.wd+"/", "")
newI.Text = strings.ReplaceAll(newI.Text, p.wd, "")
return newI
}), nil
}

@ -33,7 +33,7 @@ func errorCheck(outStr string, wantAuto bool, defaultWantedLinter string, fullsh
for i := range out {
for j := 0; j < len(fullshort); j += 2 {
full, short := fullshort[j], fullshort[j+1]
out[i] = strings.Replace(out[i], full, short, -1)
out[i] = strings.ReplaceAll(out[i], full, short)
}
}

@ -3,10 +3,11 @@ linters-settings:
enabled-checks:
- rangeValCopy
- flagDeref
- wrapperFunc
- ruleguard
settings:
rangevalcopy:
sizethreshold: 2
rangeValCopy:
sizeThreshold: 2
ruleguard:
debug: dupSubExpr
failOn: dsl,import

@ -42,3 +42,7 @@ func gocriticDup(x bool) {
log.Print("x is true")
}
}
func gocriticRuleWrapperFunc() {
strings.Replace("abcabc", "a", "d", -1) // ERROR "ruleguard: this Replace call can be simplified.*"
}

@ -154,7 +154,7 @@ func (r *LintRunner) RunCommandWithYamlConfig(cfg, command string, args ...strin
}
cfg = strings.TrimSpace(cfg)
cfg = strings.Replace(cfg, "\t", " ", -1)
cfg = strings.ReplaceAll(cfg, "\t", " ")
err = os.WriteFile(cfgPath, []byte(cfg), os.ModePerm)
assert.NoError(r.t, err)