diff --git a/check/utils.go b/check/utils.go index fe6434a..337d28b 100644 --- a/check/utils.go +++ b/check/utils.go @@ -334,7 +334,12 @@ func GoTool(dir string, filenames, command []string) (float64, []FileSummary, er params := command[1:] params = addSkipDirs(params) - params = append(params, dir+"/...") + + if strings.Contains(command[len(command)-1], "cyclo") { + params = append(params, dir) + } else { + params = append(params, dir+"/...") + } cmd := exec.Command(command[0], params...) stdout, err := cmd.StdoutPipe() diff --git a/go.mod b/go.mod index f913200..1557adb 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.16 require ( github.com/DataDog/zstd v1.4.8 // indirect - github.com/alecthomas/gocyclo v0.0.0-20150208221726-aa8f8b160214 github.com/alecthomas/gometalinter v1.0.3 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15 // indirect @@ -14,6 +13,7 @@ require ( github.com/dgraph-io/ristretto v0.0.3 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dustin/go-humanize v1.0.0 + github.com/fzipp/gocyclo v0.3.1 github.com/golang/protobuf v1.5.2 // indirect github.com/golang/snappy v0.0.3 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect diff --git a/go.sum b/go.sum index e661859..27e4ea0 100644 --- a/go.sum +++ b/go.sum @@ -5,8 +5,6 @@ github.com/DataDog/zstd v1.4.8 h1:Rpmta4xZ/MgZnriKNd24iZMhGpP5dvUcs/uqfBapKZY= github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/gocyclo v0.0.0-20150208221726-aa8f8b160214 h1:YI/8G3uLbYyowJeOPVL6BMKe2wbL54h0FdEKmncU6lU= -github.com/alecthomas/gocyclo v0.0.0-20150208221726-aa8f8b160214/go.mod h1:Ef5UOtJdJ5rVFObdOVsrNgKV/Wf4I+daTCSk8GTrHIk= github.com/alecthomas/gometalinter v1.0.3 h1:Y5sxwml3eb93scdwfdOOh5VDULOtOEuCMrOnRpRwn1w= github.com/alecthomas/gometalinter v1.0.3/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= @@ -38,6 +36,8 @@ github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUn github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fzipp/gocyclo v0.3.1 h1:A9UeX3HJSXTBzvHzhqoYVuE0eAhe+aM8XBCCwsPMZOc= +github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -50,8 +50,6 @@ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/gordonklaus/ineffassign v0.0.0-20210522101830-0589229737b2 h1:hC4RAQwLzbDbHsa+CwwGBm1uG2oX9o3Frx9G73duPi8= -github.com/gordonklaus/ineffassign v0.0.0-20210522101830-0589229737b2/go.mod h1:M9mZEtGIsR1oDaZagNPNG9iq9n2HrhZ17dsXk73V3Lw= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8 h1:PVRE9d4AQKmbelZ7emNig1+NT27DUmKZn5qXxfio54U= github.com/gordonklaus/ineffassign v0.0.0-20210914165742-4cc7213b9bc8/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= @@ -106,14 +104,12 @@ golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -131,8 +127,6 @@ golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210611083646-a4fc73990273 h1:faDu4veV+8pcThn4fewv6TVlNCezafGoC1gM/mxQLbQ= -golang.org/x/sys v0.0.0-20210611083646-a4fc73990273/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211015200801-69063c4bb744 h1:KzbpndAYEM+4oHRp9JmB2ewj0NHHxO3Z0g7Gus2O1kk= golang.org/x/sys v0.0.0-20211015200801-69063c4bb744/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -144,8 +138,6 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= diff --git a/scripts/make-install.sh b/scripts/make-install.sh index bbdb48f..96788be 100755 --- a/scripts/make-install.sh +++ b/scripts/make-install.sh @@ -3,6 +3,6 @@ go install ./vendor/github.com/alecthomas/gometalinter go install ./vendor/golang.org/x/lint/golint -go install ./vendor/github.com/alecthomas/gocyclo +go install ./vendor/github.com/fzipp/gocyclo go install ./vendor/github.com/gordonklaus/ineffassign go install ./vendor/github.com/client9/misspell diff --git a/tools/tools.go b/tools/tools.go index 8b1c717..d266c83 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -4,9 +4,9 @@ package tools import ( - _ "github.com/alecthomas/gocyclo" _ "github.com/alecthomas/gometalinter" _ "github.com/client9/misspell/cmd/misspell" + _ "github.com/fzipp/gocyclo/cmd/gocyclo" _ "github.com/gordonklaus/ineffassign" _ "golang.org/x/lint/golint" _ "honnef.co/go/tools/cmd/staticcheck" diff --git a/vendor/github.com/alecthomas/gocyclo/README.md b/vendor/github.com/alecthomas/gocyclo/README.md deleted file mode 100644 index b2ec36a..0000000 --- a/vendor/github.com/alecthomas/gocyclo/README.md +++ /dev/null @@ -1,31 +0,0 @@ -Gocyclo calculates cyclomatic complexities of functions in Go source code. - -The cyclomatic complexity of a function is calculated according to the -following rules: - - 1 is the base complexity of a function - +1 for each 'if', 'for', 'case', '&&' or '||' - -To install, run - - $ go get github.com/fzipp/gocyclo - -and put the resulting binary in one of your PATH directories if -`$GOPATH/bin` isn't already in your PATH. - -Usage: - - $ gocyclo [ ...] ... - -Examples: - - $ gocyclo . - $ gocyclo main.go - $ gocyclo -top 10 src/ - $ gocyclo -over 25 docker - $ gocyclo -avg . - -The output fields for each line are: - - - diff --git a/vendor/github.com/alecthomas/gocyclo/gocyclo.go b/vendor/github.com/alecthomas/gocyclo/gocyclo.go deleted file mode 100644 index d22b94d..0000000 --- a/vendor/github.com/alecthomas/gocyclo/gocyclo.go +++ /dev/null @@ -1,222 +0,0 @@ -// Copyright 2013 Frederik Zipp. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Gocyclo calculates the cyclomatic complexities of functions and -// methods in Go source code. -// -// Usage: -// gocyclo [ ...] ... -// -// Flags -// -over N show functions with complexity > N only and -// return exit code 1 if the output is non-empty -// -top N show the top N most complex functions only -// -avg show the average complexity -// -// The output fields for each line are: -// -package main - -import ( - "flag" - "fmt" - "go/ast" - "go/parser" - "go/token" - "io" - "os" - "path/filepath" - "sort" -) - -const usageDoc = `Calculate cyclomatic complexities of Go functions. -usage: - gocyclo [ ...] ... - -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: - -` - -func usage() { - fmt.Fprintf(os.Stderr, usageDoc) - os.Exit(2) -} - -var ( - over = flag.Int("over", 0, "show functions with complexity > N only") - top = flag.Int("top", -1, "show the top N most complex functions only") - avg = flag.Bool("avg", false, "show the average complexity") -) - -func main() { - flag.Usage = usage - flag.Parse() - args := flag.Args() - if len(args) == 0 { - usage() - } - - stats := analyze(args) - sort.Sort(byComplexity(stats)) - written := writeStats(os.Stdout, stats) - - if *avg { - showAverage(stats) - } - - if *over > 0 && written > 0 { - os.Exit(1) - } -} - -func analyze(paths []string) []stat { - stats := make([]stat, 0) - for _, path := range paths { - if isDir(path) { - stats = analyzeDir(path, stats) - } else { - stats = analyzeFile(path, stats) - } - } - return stats -} - -func isDir(filename string) bool { - fi, err := os.Stat(filename) - return err == nil && fi.IsDir() -} - -func analyzeFile(fname string, stats []stat) []stat { - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, fname, nil, 0) - if err != nil { - exitError(err) - } - return buildStats(f, fset, stats) -} - -func analyzeDir(dirname string, stats []stat) []stat { - files, _ := filepath.Glob(filepath.Join(dirname, "*.go")) - for _, file := range files { - stats = analyzeFile(file, stats) - } - return stats -} - -func exitError(err error) { - fmt.Fprintln(os.Stderr, err) - os.Exit(1) -} - -func writeStats(w io.Writer, sortedStats []stat) int { - for i, stat := range sortedStats { - if i == *top { - return i - } - if stat.Complexity <= *over { - return i - } - fmt.Fprintln(w, stat) - } - return len(sortedStats) -} - -func showAverage(stats []stat) { - fmt.Printf("Average: %.3g\n", average(stats)) -} - -func average(stats []stat) float64 { - total := 0 - for _, s := range stats { - total += s.Complexity - } - return float64(total) / float64(len(stats)) -} - -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) -} - -type byComplexity []stat - -func (s byComplexity) Len() int { return len(s) } -func (s byComplexity) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s byComplexity) Less(i, j int) bool { - return s[i].Complexity >= s[j].Complexity -} - -func buildStats(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 { - 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 cyclomatic complexity of a function. -func complexity(fn *ast.FuncDecl) int { - v := complexityVisitor{} - ast.Walk(&v, fn) - return v.Complexity -} - -type complexityVisitor struct { - // Complexity is the cyclomatic complexity - Complexity int -} - -// Visit implements the ast.Visitor interface. -func (v *complexityVisitor) Visit(n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.FuncDecl, *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.CaseClause, *ast.CommClause: - v.Complexity++ - case *ast.BinaryExpr: - if n.Op == token.LAND || n.Op == token.LOR { - v.Complexity++ - } - } - return v -} diff --git a/vendor/github.com/fzipp/gocyclo/CHANGELOG.md b/vendor/github.com/fzipp/gocyclo/CHANGELOG.md new file mode 100644 index 0000000..3959a62 --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/CHANGELOG.md @@ -0,0 +1,38 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [0.3.1] +### Added +- Test coverage + +### Fixed +- Fix cyclomatic complexity for function literals (base complexity of 1 was missing) + +## [0.3.0] - 2020-10-17 +### Added +- New `-avg-short` and `-total-short` options for printing average and total cyclomatic complexities without label +- Export the `AnalyzeASTFile` function in package API +- Doc comments for exported functions and types + +### Fixed +- Ignore `default` cases + +## [0.2.0] - 2020-10-17 +### Added +- Support for gocyclo as a package +- Support for ignoring of individual functions via a new `gocyclo:ignore` directive +- New `-total` option to compute total cyclomatic complexity +- New `-ignore` option to ignore files matching a regular expression +- Analysis of function literals at declaration level + +### Changed +- Breaking: installation changed to `go get github.com/fzipp/gocyclo/cmd/gocyclo` + +## [0.1.0] - 2020-10-17 + +### Added +- `go.mod` file; beginning of versioning + diff --git a/vendor/github.com/fzipp/gocyclo/CONTRIBUTORS b/vendor/github.com/fzipp/gocyclo/CONTRIBUTORS new file mode 100644 index 0000000..1c09f1a --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/CONTRIBUTORS @@ -0,0 +1,7 @@ +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Frederik Zipp +Harshavardhana diff --git a/vendor/github.com/alecthomas/gocyclo/LICENSE b/vendor/github.com/fzipp/gocyclo/LICENSE similarity index 100% rename from vendor/github.com/alecthomas/gocyclo/LICENSE rename to vendor/github.com/fzipp/gocyclo/LICENSE diff --git a/vendor/github.com/fzipp/gocyclo/README.md b/vendor/github.com/fzipp/gocyclo/README.md new file mode 100644 index 0000000..f105693 --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/README.md @@ -0,0 +1,107 @@ +# gocyclo + +[![PkgGoDev](https://pkg.go.dev/badge/github.com/fzipp/gocyclo)](https://pkg.go.dev/github.com/fzipp/gocyclo) +[![Go Report Card](https://goreportcard.com/badge/github.com/fzipp/gocyclo)](https://goreportcard.com/report/github.com/fzipp/gocyclo) + +Gocyclo calculates +[cyclomatic complexities](https://en.wikipedia.org/wiki/Cyclomatic_complexity) +of functions in Go source code. + +Cyclomatic complexity is a +[code quality metric](https://en.wikipedia.org/wiki/Software_metric) +which can be used to identify code that needs refactoring. +It measures the number of linearly independent paths through a function's +source code. + +The cyclomatic complexity of a function is calculated according to the +following rules: + +``` + 1 is the base complexity of a function ++1 for each 'if', 'for', 'case', '&&' or '||' +``` + +A function with a higher cyclomatic complexity requires more test cases to +cover all possible paths and is potentially harder to understand. The +complexity can be reduced by applying common refactoring techniques that lead +to smaller functions. + +## Installation + +To install the `gocyclo` command, run + +``` +$ go get github.com/fzipp/gocyclo/cmd/gocyclo +``` + +and put the resulting binary in one of your PATH directories if +`$GOPATH/bin` isn't already in your PATH. + +## Usage + +``` +Calculate cyclomatic complexities of Go functions. +Usage: + gocyclo [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, -avg-short show the average complexity over all functions; + the short option prints the value without a label + -total, -total-short show the total complexity for all functions; + the short option prints the value without a label + -ignore REGEX exclude files matching the given regular expression + +The output fields for each line are: + +``` + +## Examples + +``` +$ gocyclo . +$ gocyclo main.go +$ gocyclo -top 10 src/ +$ gocyclo -over 25 docker +$ gocyclo -avg . +$ gocyclo -top 20 -ignore "_test|Godeps|vendor/" . +$ gocyclo -over 3 -avg gocyclo/ +``` + +Example output: + +``` +9 gocyclo (*complexityVisitor).Visit complexity.go:30:1 +8 main main cmd/gocyclo/main.go:53:1 +7 gocyclo (*fileAnalyzer).analyzeDecl analyze.go:96:1 +4 gocyclo Analyze analyze.go:24:1 +4 gocyclo parseDirectives directives.go:27:1 +4 gocyclo (Stats).SortAndFilter stats.go:52:1 +Average: 2.72 +``` + +Note that the average is calculated over all analyzed functions, +not just the printed ones. + +### Ignoring individual functions + +Individual functions can be ignored with a `gocyclo:ignore` directive: + +``` +//gocyclo:ignore +func f1() { + // ... +} + +//gocyclo:ignore +var f2 = func() { + // ... +} +``` + +## License + +This project is free and open source software licensed under the +[BSD 3-Clause License](LICENSE). diff --git a/vendor/github.com/fzipp/gocyclo/analyze.go b/vendor/github.com/fzipp/gocyclo/analyze.go new file mode 100644 index 0000000..c053e83 --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/analyze.go @@ -0,0 +1,151 @@ +// Copyright 2020 Frederik Zipp. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocyclo + +import ( + "fmt" + "go/ast" + "go/parser" + "go/token" + "log" + "os" + "path/filepath" + "regexp" + "strings" +) + +// Analyze calculates the cyclomatic complexities of the functions and methods +// in the Go source code files in the given paths. If a path is a directory +// all Go files under that directory are analyzed recursively. +// Files with paths matching the 'ignore' regular expressions are skipped. +// The 'ignore' parameter can be nil, meaning that no files are skipped. +func Analyze(paths []string, ignore *regexp.Regexp) Stats { + var stats Stats + for _, path := range paths { + info, err := os.Stat(path) + if err != nil { + log.Printf("could not get file info for path %q: %s\n", path, err) + continue + } + if info.IsDir() { + stats = analyzeDir(path, ignore, stats) + } else { + stats = analyzeFile(path, ignore, stats) + } + } + return stats +} + +func analyzeDir(dirname string, ignore *regexp.Regexp, stats Stats) Stats { + filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error { + if err == nil && isGoFile(info) { + stats = analyzeFile(path, ignore, stats) + } + return err + }) + return stats +} + +func isGoFile(f os.FileInfo) bool { + return !f.IsDir() && strings.HasSuffix(f.Name(), ".go") +} + +func analyzeFile(path string, ignore *regexp.Regexp, stats Stats) Stats { + if isIgnored(path, ignore) { + return stats + } + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, path, nil, parser.ParseComments) + if err != nil { + log.Fatal(err) + } + return AnalyzeASTFile(f, fset, stats) +} + +func isIgnored(path string, ignore *regexp.Regexp) bool { + return ignore != nil && ignore.MatchString(path) +} + +// AnalyzeASTFile calculates the cyclomatic complexities of the functions +// and methods in the abstract syntax tree (AST) of a parsed Go file and +// appends the results to the given Stats slice. +func AnalyzeASTFile(f *ast.File, fs *token.FileSet, s Stats) Stats { + analyzer := &fileAnalyzer{ + file: f, + fileSet: fs, + stats: s, + } + return analyzer.analyze() +} + +type fileAnalyzer struct { + file *ast.File + fileSet *token.FileSet + stats Stats +} + +func (a *fileAnalyzer) analyze() Stats { + for _, decl := range a.file.Decls { + a.analyzeDecl(decl) + } + return a.stats +} + +func (a *fileAnalyzer) analyzeDecl(d ast.Decl) { + switch decl := d.(type) { + case *ast.FuncDecl: + a.addStatIfNotIgnored(decl, funcName(decl), decl.Doc) + case *ast.GenDecl: + for _, spec := range decl.Specs { + valueSpec, ok := spec.(*ast.ValueSpec) + if !ok { + continue + } + for _, value := range valueSpec.Values { + funcLit, ok := value.(*ast.FuncLit) + if !ok { + continue + } + a.addStatIfNotIgnored(funcLit, valueSpec.Names[0].Name, decl.Doc) + } + } + } +} + +func (a *fileAnalyzer) addStatIfNotIgnored(node ast.Node, funcName string, doc *ast.CommentGroup) { + if parseDirectives(doc).HasIgnore() { + return + } + a.stats = append(a.stats, Stat{ + PkgName: a.file.Name.Name, + FuncName: funcName, + Complexity: Complexity(node), + Pos: a.fileSet.Position(node.Pos()), + }) +} + +// 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" +} diff --git a/vendor/github.com/fzipp/gocyclo/cmd/gocyclo/main.go b/vendor/github.com/fzipp/gocyclo/cmd/gocyclo/main.go new file mode 100644 index 0000000..f282c7d --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/cmd/gocyclo/main.go @@ -0,0 +1,121 @@ +// Copyright 2013 Frederik Zipp. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Gocyclo calculates the cyclomatic complexities of functions and +// methods in Go source code. +// +// Usage: +// gocyclo [ ...] ... +// +// Flags: +// -over N show functions with complexity > N only and +// return exit code 1 if the output is non-empty +// -top N show the top N most complex functions only +// -avg, -avg-short show the average complexity; +// the short option prints the value without a label +// -total, -total-short show the total complexity; +// the short option prints the value without a label +// -ignore REGEX exclude files matching the given regular expression +// +// The output fields for each line are: +// +package main + +import ( + "flag" + "fmt" + "log" + "os" + "regexp" + + "github.com/fzipp/gocyclo" +) + +const usageDoc = `Calculate cyclomatic complexities of Go functions. +Usage: + gocyclo [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, -avg-short show the average complexity over all functions; + the short option prints the value without a label + -total, -total-short show the total complexity for all functions; + the short option prints the value without a label + -ignore REGEX exclude files matching the given regular expression + +The output fields for each line are: + +` + +func main() { + over := flag.Int("over", 0, "show functions with complexity > N only") + top := flag.Int("top", -1, "show the top N most complex functions only") + avg := flag.Bool("avg", false, "show the average complexity") + avgShort := flag.Bool("avg-short", false, "show the average complexity without a label") + total := flag.Bool("total", false, "show the total complexity") + totalShort := flag.Bool("total-short", false, "show the total complexity without a label") + ignore := flag.String("ignore", "", "exclude files matching the given regular expression") + + log.SetFlags(0) + log.SetPrefix("gocyclo: ") + flag.Usage = usage + flag.Parse() + paths := flag.Args() + if len(paths) == 0 { + usage() + } + + allStats := gocyclo.Analyze(paths, regex(*ignore)) + shownStats := allStats.SortAndFilter(*top, *over) + + printStats(shownStats) + if *avg || *avgShort { + printAverage(allStats, *avgShort) + } + if *total || *totalShort { + printTotal(allStats, *totalShort) + } + + if *over > 0 && len(shownStats) > 0 { + os.Exit(1) + } +} + +func regex(expr string) *regexp.Regexp { + if expr == "" { + return nil + } + re, err := regexp.Compile(expr) + if err != nil { + log.Fatal(err) + } + return re +} + +func printStats(s gocyclo.Stats) { + for _, stat := range s { + fmt.Println(stat) + } +} + +func printAverage(s gocyclo.Stats, short bool) { + if !short { + fmt.Print("Average: ") + } + fmt.Printf("%.3g\n", s.AverageComplexity()) +} + +func printTotal(s gocyclo.Stats, short bool) { + if !short { + fmt.Print("Total: ") + } + fmt.Printf("%d\n", s.TotalComplexity()) +} + +func usage() { + _, _ = fmt.Fprintf(os.Stderr, usageDoc) + os.Exit(2) +} diff --git a/vendor/github.com/fzipp/gocyclo/complexity.go b/vendor/github.com/fzipp/gocyclo/complexity.go new file mode 100644 index 0000000..65f5077 --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/complexity.go @@ -0,0 +1,48 @@ +// Copyright 2020 Frederik Zipp. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package gocyclo calculates the cyclomatic complexities of functions and +// methods in Go source code. +package gocyclo + +import ( + "go/ast" + "go/token" +) + +// Complexity calculates the cyclomatic complexity of a function. +// The 'fn' node is either a *ast.FuncDecl or a *ast.FuncLit. +func Complexity(fn ast.Node) int { + v := complexityVisitor{ + complexity: 1, + } + ast.Walk(&v, fn) + return v.complexity +} + +type complexityVisitor struct { + // complexity is the cyclomatic complexity + complexity int +} + +// Visit implements the ast.Visitor interface. +func (v *complexityVisitor) Visit(n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt: + v.complexity++ + case *ast.CaseClause: + if n.List != nil { // ignore default case + v.complexity++ + } + case *ast.CommClause: + if n.Comm != nil { // ignore default case + v.complexity++ + } + case *ast.BinaryExpr: + if n.Op == token.LAND || n.Op == token.LOR { + v.complexity++ + } + } + return v +} diff --git a/vendor/github.com/fzipp/gocyclo/directives.go b/vendor/github.com/fzipp/gocyclo/directives.go new file mode 100644 index 0000000..b4ee3c4 --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/directives.go @@ -0,0 +1,39 @@ +// Copyright 2020 Frederik Zipp. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocyclo + +import ( + "go/ast" + "strings" +) + +type directives []string + +func (ds directives) HasIgnore() bool { + return ds.isPresent("ignore") +} + +func (ds directives) isPresent(name string) bool { + for _, d := range ds { + if d == name { + return true + } + } + return false +} + +func parseDirectives(doc *ast.CommentGroup) directives { + if doc == nil { + return directives{} + } + const prefix = "//gocyclo:" + var ds directives + for _, comment := range doc.List { + if strings.HasPrefix(comment.Text, prefix) { + ds = append(ds, strings.TrimSpace(strings.TrimPrefix(comment.Text, prefix))) + } + } + return ds +} diff --git a/vendor/github.com/fzipp/gocyclo/go.mod b/vendor/github.com/fzipp/gocyclo/go.mod new file mode 100644 index 0000000..c809827 --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/go.mod @@ -0,0 +1,3 @@ +module github.com/fzipp/gocyclo + +go 1.15 diff --git a/vendor/github.com/fzipp/gocyclo/stats.go b/vendor/github.com/fzipp/gocyclo/stats.go new file mode 100644 index 0000000..90f5eef --- /dev/null +++ b/vendor/github.com/fzipp/gocyclo/stats.go @@ -0,0 +1,73 @@ +// Copyright 2020 Frederik Zipp. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gocyclo + +import ( + "fmt" + "go/token" + "sort" +) + +// Stat holds the cyclomatic complexity of a function, along with its package +// and and function name and its position in the source code. +type Stat struct { + PkgName string + FuncName string + Complexity int + Pos token.Position +} + +// String formats the cyclomatic complexity information of a function in +// the following format: " " +func (s Stat) String() string { + return fmt.Sprintf("%d %s %s %s", s.Complexity, s.PkgName, s.FuncName, s.Pos) +} + +// Stats hold the cyclomatic complexities of many functions. +type Stats []Stat + +// AverageComplexity calculates the average cyclomatic complexity of the +// cyclomatic complexities in s. +func (s Stats) AverageComplexity() float64 { + return float64(s.TotalComplexity()) / float64(len(s)) +} + +// TotalComplexity calculates the total sum of all cyclomatic +// complexities in s. +func (s Stats) TotalComplexity() uint64 { + total := uint64(0) + for _, stat := range s { + total += uint64(stat.Complexity) + } + return total +} + +// SortAndFilter sorts the cyclomatic complexities in s in descending order +// and returns a slice of s limited to the 'top' N entries with a cyclomatic +// complexity greater than 'over'. If 'top' is negative, i.e. -1, it does +// not limit the result. If 'over' is <= 0 it does not limit the result either, +// because a function has a base cyclomatic complexity of at least 1. +func (s Stats) SortAndFilter(top, over int) Stats { + result := make(Stats, len(s)) + copy(result, s) + sort.Sort(byComplexityDesc(result)) + for i, stat := range result { + if i == top { + return result[:i] + } + if stat.Complexity <= over { + return result[:i] + } + } + return result +} + +type byComplexityDesc Stats + +func (s byComplexityDesc) Len() int { return len(s) } +func (s byComplexityDesc) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s byComplexityDesc) Less(i, j int) bool { + return s[i].Complexity >= s[j].Complexity +} diff --git a/vendor/modules.txt b/vendor/modules.txt index ffefdb2..29be88b 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -3,9 +3,6 @@ github.com/BurntSushi/toml # github.com/DataDog/zstd v1.4.8 ## explicit github.com/DataDog/zstd -# github.com/alecthomas/gocyclo v0.0.0-20150208221726-aa8f8b160214 -## explicit -github.com/alecthomas/gocyclo # github.com/alecthomas/gometalinter v1.0.3 ## explicit github.com/alecthomas/gometalinter @@ -44,6 +41,10 @@ github.com/dgryski/go-farm # github.com/dustin/go-humanize v1.0.0 ## explicit github.com/dustin/go-humanize +# github.com/fzipp/gocyclo v0.3.1 +## explicit +github.com/fzipp/gocyclo +github.com/fzipp/gocyclo/cmd/gocyclo # github.com/golang/protobuf v1.5.2 ## explicit github.com/golang/protobuf/proto