Skip to content
Permalink
Browse files

Add gocognit linter (#756)

* Add gocognit linter

* Remove gocognit to the golangci config

* Make changes on README.md

* Remove gocognit from megacheck benchtest

* Remove command line flags

* Comply with new style
  • Loading branch information
uudashr authored and jirfag committed Oct 3, 2019
1 parent dbf0231 commit 92ec1a1f4c82c8dee350261cc8a965530a883cbd
@@ -116,6 +116,9 @@ linters-settings:
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
@@ -201,6 +201,7 @@ dupl: Tool for code clone detection [fast: true, auto-fix: false]
funlen: Tool for detection of long functions [fast: true, auto-fix: false]
gochecknoglobals: Checks that no globals are present in Go code [fast: true, auto-fix: false]
gochecknoinits: Checks that no init functions are present in Go code [fast: true, auto-fix: false]
gocognit: Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
goconst: Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
gocritic: The most opinionated Go source code linter [fast: true, auto-fix: false]
gocyclo: Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
@@ -449,6 +450,7 @@ golangci-lint help linters
- [dupl](https://github.com/mibk/dupl) - Tool for code clone detection
- [goconst](https://github.com/jgautheron/goconst) - Finds repeated strings that could be replaced by a constant
- [gocyclo](https://github.com/alecthomas/gocyclo) - Computes and checks the cyclomatic complexity of functions
- [gocognit](https://github.com/uudashr/gocognit) - Computes and checks the cognitive complexity of functions
- [gofmt](https://golang.org/cmd/gofmt/) - Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
- [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) - Goimports does everything that gofmt does. Additionally it checks unused imports
- [maligned](https://github.com/mdempsky/maligned) - Tool to detect Go structs that would take less memory if their fields were sorted
@@ -703,6 +705,9 @@ linters-settings:
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
gocognit:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 10
maligned:
# print struct with more effective memory layout or not, false by default
suggest-new: true
@@ -1108,6 +1113,7 @@ Thanks to developers and authors of used linters:
- [jgautheron](https://github.com/jgautheron)
- [remyoudompheng](https://github.com/remyoudompheng)
- [alecthomas](https://github.com/alecthomas)
- [uudashr](https://github.com/uudashr)
- [OpenPeeDeeP](https://github.com/OpenPeeDeeP)
- [client9](https://github.com/client9)
- [walle](https://github.com/walle)
1 go.mod
@@ -37,6 +37,7 @@ require (
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e
github.com/ultraware/funlen v0.0.2
github.com/ultraware/whitespace v0.0.3
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517
github.com/valyala/quicktemplate v1.2.0
golang.org/x/tools v0.0.0-20190912215617-3720d1ec3678
gopkg.in/yaml.v2 v2.2.2
2 go.sum
@@ -234,6 +234,8 @@ github.com/ultraware/funlen v0.0.2 h1:Av96YVBwwNSe4MLR7iI/BIa3VyI7/djnto/pK3Uxbd
github.com/ultraware/funlen v0.0.2/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA=
github.com/ultraware/whitespace v0.0.3 h1:S5BCRRB5sttNy0bSOhbpw+0mb+cHiCmWfrvxpEzuUk0=
github.com/ultraware/whitespace v0.0.3/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA=
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517 h1:ChMKTho2hWKpks/nD/FL2KqM1wuVt62oJeiE8+eFpGs=
github.com/uudashr/gocognit v0.0.0-20190926065955-1655d0de0517/go.mod h1:j44Ayx2KW4+oB6SWMv8KsmHzZrOInQav7D3cQMJ5JUM=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s=
@@ -138,6 +138,9 @@ type LintersSettings struct {
Gocyclo struct {
MinComplexity int `mapstructure:"min-complexity"`
}
Gocognit struct {
MinComplexity int `mapstructure:"min-complexity"`
}
Varcheck struct {
CheckExportedFields bool `mapstructure:"exported-fields"`
}
@@ -0,0 +1,69 @@
// nolint:dupl
package golinters

import (
"fmt"
"sort"
"sync"

"github.com/uudashr/gocognit"
"golang.org/x/tools/go/analysis"

"github.com/golangci/golangci-lint/pkg/golinters/goanalysis"
"github.com/golangci/golangci-lint/pkg/lint/linter"
"github.com/golangci/golangci-lint/pkg/result"
)

const gocognitName = "gocognit"

func NewGocognit() *goanalysis.Linter {
var mu sync.Mutex
var resIssues []result.Issue

analyzer := &analysis.Analyzer{
Name: goanalysis.TheOnlyAnalyzerName,
Doc: goanalysis.TheOnlyanalyzerDoc,
}
return goanalysis.NewLinter(
gocognitName,
"Computes and checks the cognitive complexity of functions",
[]*analysis.Analyzer{analyzer},
nil,
).WithContextSetter(func(lintCtx *linter.Context) {
analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
var stats []gocognit.Stat
for _, f := range pass.Files {
stats = gocognit.ComplexityStats(f, pass.Fset, stats)
}
if len(stats) == 0 {
return nil, nil
}

sort.Slice(stats, func(i, j int) bool {
return stats[i].Complexity > stats[j].Complexity
})

res := make([]result.Issue, 0, len(stats))
for _, s := range stats {
if s.Complexity <= lintCtx.Settings().Gocognit.MinComplexity {
break // Break as the stats is already sorted from greatest to least
}

res = append(res, result.Issue{
Pos: s.Pos,
Text: fmt.Sprintf("cognitive complexity %d of func %s is high (> %d)",
s.Complexity, formatCode(s.FuncName, lintCtx.Cfg), lintCtx.Settings().Gocognit.MinComplexity),
FromLinter: gocognitName,
})
}

mu.Lock()
resIssues = append(resIssues, res...)
mu.Unlock()

return nil, nil
}
}).WithIssuesReporter(func(*linter.Context) []result.Issue {
return resIssues
}).WithLoadMode(goanalysis.LoadModeSyntax)
}
@@ -1,3 +1,4 @@
// nolint:dupl
package golinters

import (
@@ -137,6 +137,9 @@ func (m Manager) GetAllSupportedLinterConfigs() []*linter.Config {
linter.NewConfig(golinters.NewGocyclo()).
WithPresets(linter.PresetComplexity).
WithURL("https://github.com/alecthomas/gocyclo"),
linter.NewConfig(golinters.NewGocognit()).
WithPresets(linter.PresetComplexity).
WithURL("https://github.com/uudashr/gocognit"),
linter.NewConfig(golinters.NewTypecheck()).
WithLoadForGoAnalysis().
WithPresets(linter.PresetBugs).
@@ -0,0 +1,23 @@
//args: -Egocognit
//config: linters-settings.gocognit.min-complexity=2
package testdata

func GocognitGetWords(number int) string { // ERROR "cognitive complexity 4 of func .* is high .*"
if number == 1 { // +1
return "one"
} else if number == 2 { // +1
return "a couple"
} else if number == 3 { // +1
return "a few"
} else { // +1
return "lots"
}
} // total complexity = 4

func GoCognitFact(n int) int { // ERROR "cognitive complexity 3 of func .* is high .*"
if n <= 1 { // +1
return 1
} else { // +1
return n + GoCognitFact(n-1) // +1
}
} // total complexity = 3

Some generated files are not rendered by default. Learn more.

Some generated files are not rendered by default. Learn more.

Some generated files are not rendered by default. Learn more.

0 comments on commit 92ec1a1

Please sign in to comment.
You can’t perform that action at this time.