From c7d7f10bdff703e4a3c0414e8a33d4e45c91eb35 Mon Sep 17 00:00:00 2001 From: Dmitry Vyukov Date: Sat, 4 Jul 2020 11:12:55 +0200 Subject: go.mod: vendor golangci-lint --- vendor/github.com/uudashr/gocognit/LICENSE | 21 ++ vendor/github.com/uudashr/gocognit/README.md | 185 +++++++++++++++ vendor/github.com/uudashr/gocognit/go.mod | 3 + vendor/github.com/uudashr/gocognit/go.sum | 0 vendor/github.com/uudashr/gocognit/gocognit.go | 313 +++++++++++++++++++++++++ 5 files changed, 522 insertions(+) create mode 100644 vendor/github.com/uudashr/gocognit/LICENSE create mode 100644 vendor/github.com/uudashr/gocognit/README.md create mode 100644 vendor/github.com/uudashr/gocognit/go.mod create mode 100644 vendor/github.com/uudashr/gocognit/go.sum create mode 100644 vendor/github.com/uudashr/gocognit/gocognit.go (limited to 'vendor/github.com/uudashr') diff --git a/vendor/github.com/uudashr/gocognit/LICENSE b/vendor/github.com/uudashr/gocognit/LICENSE new file mode 100644 index 000000000..75d4b9c98 --- /dev/null +++ b/vendor/github.com/uudashr/gocognit/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Nuruddin Ashr + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/uudashr/gocognit/README.md b/vendor/github.com/uudashr/gocognit/README.md new file mode 100644 index 000000000..4a8846907 --- /dev/null +++ b/vendor/github.com/uudashr/gocognit/README.md @@ -0,0 +1,185 @@ +[![GoDoc](https://godoc.org/github.com/uudashr/gocognit?status.svg)](https://godoc.org/github.com/uudashr/gocognit) +# Gocognit +Gocognit calculates cognitive complexities of functions in Go source code. A measurement of how hard does the code is intuitively to understand. + +## Understanding the complexity + +Given code using `if` statement, +```go +func GetWords(number int) string { + 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" + } +} // Cognitive complexity = 4 +``` + +Above code can be refactored using `switch` statement, +```go +func GetWords(number int) string { + switch number { // +1 + case 1: + return "one" + case 2: + return "a couple" + case 3: + return "a few" + default: + return "lots" + } +} // Cognitive complexity = 1 +``` + +As you see above codes are the same, but the second code are easier to understand, that is why the cognitive complexity score are lower compare to the first one. + +## Comparison with cyclometic complexity + +### Example 1 +#### Cyclometic complexity +```go +func GetWords(number int) string { // +1 + switch number { + case 1: // +1 + return "one" + case 2: // +1 + return "a couple" + case 3: // +1 + return "a few" + default: + return "lots" + } +} // Cyclomatic complexity = 4 +``` + +#### Cognitive complexity +```go +func GetWords(number int) string { + switch number { // +1 + case 1: + return "one" + case 2: + return "a couple" + case 3: + return "a few" + default: + return "lots" + } +} // Cognitive complexity = 1 +``` + +Cognitive complexity give lower score compare to cyclomatic complexity. + +### Example 2 +#### Cyclomatic complexity +```go +func SumOfPrimes(max int) int { // +1 + var total int + +OUT: + for i := 1; i < max; i++ { // +1 + for j := 2; j < i; j++ { // +1 + if i%j == 0 { // +1 + continue OUT + } + } + total += i + } + + return total +} // Cyclomatic complexity = 4 +``` + +#### Cognitive complexity +```go +func SumOfPrimes(max int) int { + var total int + +OUT: + for i := 1; i < max; i++ { // +1 + for j := 2; j < i; j++ { // +2 (nesting = 1) + if i%j == 0 { // +3 (nesting = 2) + continue OUT // +1 + } + } + total += i + } + + return total +} // Cognitive complexity = 7 +``` + +Cognitive complexity give higher score compare to cyclomatic complexity. + +## Rules + +The cognitive complexity of a function is calculated according to the +following rules: +> Note: these rules are specific for Go, please see the [original whitepaper](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) for more complete reference. + +### Increments +There is an increment for each of the following: +1. `if`, `else if`, `else` +2. `switch`, `select` +3. `for` +4. `goto` LABEL, `break` LABEL, `continue` LABEL +5. sequence of binary logical operators +6. each method in a recursion cycle + +### Nesting level +The following structures increment the nesting level: +1. `if`, `else if`, `else` +2. `switch`, `select` +3. `for` +4. function literal or lambda + +### Nesting increments +The following structures receive a nesting increment commensurate with their nested depth inside nesting structures: +1. `if` +2. `switch`, `select` +3. `for` + +## Installation +``` +$ go get github.com/uudashr/gocognit/cmd/gocognit +``` + +## Usage + +``` +$ gocognit +Calculate cognitive complexities of Go functions. +Usage: + gocognit [flags] ... +Flags: + -over N show functions with complexity > N only and + return exit code 1 if the set is non-empty + -top N show the top N most complex functions only + -avg show the average complexity over all functions, + not depending on whether -over or -top are set +The output fields for each line are: + +``` + +Examples: + +``` +$ gocognit . +$ gocognit main.go +$ gocognit -top 10 src/ +$ gocognit -over 25 docker +$ gocognit -avg . +``` + +The output fields for each line are: +``` + +``` + +## Related project +- [Gocyclo](https://github.com/fzipp/gocyclo) where the code are based on. +- [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) white paper by G. Ann Campbell. \ No newline at end of file diff --git a/vendor/github.com/uudashr/gocognit/go.mod b/vendor/github.com/uudashr/gocognit/go.mod new file mode 100644 index 000000000..1a68d3880 --- /dev/null +++ b/vendor/github.com/uudashr/gocognit/go.mod @@ -0,0 +1,3 @@ +module github.com/uudashr/gocognit + +go 1.13 diff --git a/vendor/github.com/uudashr/gocognit/go.sum b/vendor/github.com/uudashr/gocognit/go.sum new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/github.com/uudashr/gocognit/gocognit.go b/vendor/github.com/uudashr/gocognit/gocognit.go new file mode 100644 index 000000000..11d5ee527 --- /dev/null +++ b/vendor/github.com/uudashr/gocognit/gocognit.go @@ -0,0 +1,313 @@ +package gocognit + +import ( + "fmt" + "go/ast" + "go/token" +) + +// Stat is statistic of the complexity. +type Stat struct { + PkgName string + FuncName string + Complexity int + Pos token.Position +} + +func (s Stat) String() string { + return fmt.Sprintf("%d %s %s %s", s.Complexity, s.PkgName, s.FuncName, s.Pos) +} + +// ComplexityStats builds the complexity statistics. +func ComplexityStats(f *ast.File, fset *token.FileSet, stats []Stat) []Stat { + for _, decl := range f.Decls { + if fn, ok := decl.(*ast.FuncDecl); ok { + stats = append(stats, Stat{ + PkgName: f.Name.Name, + FuncName: funcName(fn), + Complexity: Complexity(fn), + Pos: fset.Position(fn.Pos()), + }) + } + } + return stats +} + +// funcName returns the name representation of a function or method: +// "(Type).Name" for methods or simply "Name" for functions. +func funcName(fn *ast.FuncDecl) string { + if fn.Recv != nil { + if fn.Recv.NumFields() > 0 { + typ := fn.Recv.List[0].Type + return fmt.Sprintf("(%s).%s", recvString(typ), fn.Name) + } + } + return fn.Name.Name +} + +// recvString returns a string representation of recv of the +// form "T", "*T", or "BADRECV" (if not a proper receiver type). +func recvString(recv ast.Expr) string { + switch t := recv.(type) { + case *ast.Ident: + return t.Name + case *ast.StarExpr: + return "*" + recvString(t.X) + } + return "BADRECV" +} + +// Complexity calculates the cognitive complexity of a function. +func Complexity(fn *ast.FuncDecl) int { + v := complexityVisitor{ + name: fn.Name, + } + ast.Walk(&v, fn) + return v.complexity +} + +type complexityVisitor struct { + name *ast.Ident + complexity int + nesting int + elseNodes map[ast.Node]bool + calculatedExprs map[ast.Expr]bool +} + +func (v *complexityVisitor) incNesting() { + v.nesting++ +} + +func (v *complexityVisitor) decNesting() { + v.nesting-- +} + +func (v *complexityVisitor) incComplexity() { + v.complexity++ +} + +func (v *complexityVisitor) nestIncComplexity() { + v.complexity += (v.nesting + 1) +} + +func (v *complexityVisitor) markAsElseNode(n ast.Node) { + if v.elseNodes == nil { + v.elseNodes = make(map[ast.Node]bool) + } + + v.elseNodes[n] = true +} + +func (v *complexityVisitor) markedAsElseNode(n ast.Node) bool { + if v.elseNodes == nil { + return false + } + + return v.elseNodes[n] +} + +func (v *complexityVisitor) markCalculated(e ast.Expr) { + if v.calculatedExprs == nil { + v.calculatedExprs = make(map[ast.Expr]bool) + } + + v.calculatedExprs[e] = true +} + +func (v *complexityVisitor) isCalculated(e ast.Expr) bool { + if v.calculatedExprs == nil { + return false + } + + return v.calculatedExprs[e] +} + +// Visit implements the ast.Visitor interface. +func (v *complexityVisitor) Visit(n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.IfStmt: + return v.visitIfStmt(n) + case *ast.SwitchStmt: + return v.visitSwitchStmt(n) + case *ast.SelectStmt: + return v.visitSelectStmt(n) + case *ast.ForStmt: + return v.visitForStmt(n) + case *ast.RangeStmt: + return v.visitRangeStmt(n) + case *ast.FuncLit: + return v.visitFuncLit(n) + case *ast.BranchStmt: + return v.visitBranchStmt(n) + case *ast.BinaryExpr: + return v.visitBinaryExpr(n) + case *ast.CallExpr: + return v.visitCallExpr(n) + } + return v +} + +func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor { + v.incIfComplexity(n) + + if n.Init != nil { + ast.Walk(v, n.Init) + } + + ast.Walk(v, n.Cond) + + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() + + if _, ok := n.Else.(*ast.BlockStmt); ok { + v.incComplexity() + + v.incNesting() + ast.Walk(v, n.Else) + v.decNesting() + } else if _, ok := n.Else.(*ast.IfStmt); ok { + v.markAsElseNode(n.Else) + ast.Walk(v, n.Else) + } + return nil +} + +func (v *complexityVisitor) visitSwitchStmt(n *ast.SwitchStmt) ast.Visitor { + v.nestIncComplexity() + + if n.Init != nil { + ast.Walk(v, n.Init) + } + + if n.Tag != nil { + ast.Walk(v, n.Tag) + } + + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() + return nil +} + +func (v *complexityVisitor) visitSelectStmt(n *ast.SelectStmt) ast.Visitor { + v.nestIncComplexity() + + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() + return nil +} + +func (v *complexityVisitor) visitForStmt(n *ast.ForStmt) ast.Visitor { + v.nestIncComplexity() + + if n.Init != nil { + ast.Walk(v, n.Init) + } + + if n.Cond != nil { + ast.Walk(v, n.Cond) + } + + if n.Post != nil { + ast.Walk(v, n.Post) + } + + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() + return nil +} + +func (v *complexityVisitor) visitRangeStmt(n *ast.RangeStmt) ast.Visitor { + v.nestIncComplexity() + + if n.Key != nil { + ast.Walk(v, n.Key) + } + + if n.Value != nil { + ast.Walk(v, n.Value) + } + + ast.Walk(v, n.X) + + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() + return nil +} + +func (v *complexityVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor { + ast.Walk(v, n.Type) + + v.incNesting() + ast.Walk(v, n.Body) + v.decNesting() + return nil +} + +func (v *complexityVisitor) visitBranchStmt(n *ast.BranchStmt) ast.Visitor { + if n.Label != nil { + v.incComplexity() + } + return v +} + +func (v *complexityVisitor) visitBinaryExpr(n *ast.BinaryExpr) ast.Visitor { + if (n.Op == token.LAND || n.Op == token.LOR) && !v.isCalculated(n) { + ops := v.collectBinaryOps(n) + + var lastOp token.Token + for _, op := range ops { + if lastOp != op { + v.incComplexity() + lastOp = op + } + } + } + return v +} + +func (v *complexityVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor { + if name, ok := n.Fun.(*ast.Ident); ok { + if name.Obj == v.name.Obj && name.Name == v.name.Name { + v.incComplexity() + } + } + return v +} + +func (v *complexityVisitor) collectBinaryOps(exp ast.Expr) []token.Token { + v.markCalculated(exp) + switch exp := exp.(type) { + case *ast.BinaryExpr: + return mergeBinaryOps(v.collectBinaryOps(exp.X), exp.Op, v.collectBinaryOps(exp.Y)) + case *ast.ParenExpr: + // interest only on what inside paranthese + return v.collectBinaryOps(exp.X) + default: + return []token.Token{} + } +} + +func (v *complexityVisitor) incIfComplexity(n *ast.IfStmt) { + if v.markedAsElseNode(n) { + v.incComplexity() + } else { + v.nestIncComplexity() + } +} + +func mergeBinaryOps(x []token.Token, op token.Token, y []token.Token) []token.Token { + var out []token.Token + if len(x) != 0 { + out = append(out, x...) + } + out = append(out, op) + if len(y) != 0 { + out = append(out, y...) + } + return out +} -- cgit mrf-deployment