GithubHelp home page GithubHelp logo

fzipp / gocyclo Goto Github PK

View Code? Open in Web Editor NEW
1.3K 21.0 81.0 60 KB

Calculate cyclomatic complexities of functions in Go source code.

License: BSD 3-Clause "New" or "Revised" License

Go 100.00%
cyclomatic-complexity code-metrics go golang mccabe-metric mccabe software-metrics

gocyclo's Introduction

gocyclo

PkgGoDev Build Status Go Report Card

Gocyclo calculates cyclomatic complexities of functions in Go source code.

Cyclomatic complexity is a code quality 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 install github.com/fzipp/gocyclo/cmd/gocyclo@latest

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] <Go file or directory> ...

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
    -ignore REGEX         exclude files matching the given regular expression

The output fields for each line are:
<complexity> <package> <function> <file:line:column>

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.

gocyclo's People

Contributors

adamkasztenny avatar cove avatar fzipp avatar harshavardhana avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

gocyclo's Issues

Index out of range on fn.Recv.List[]

panic: runtime error: index out of range

goroutine 1 [running]:
main.funcName(0xc208107920, 0x0, 0x0)
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:182 +0x208
main.buildStats(0xc2080ed000, 0xc20812cc80, 0xc20810a000, 0x154, 0x266, 0x0, 0x0, 0x0)
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:169 +0x113
main.analyzeFile(0xc20812cc00, 0x3f, 0xc20810a000, 0x154, 0x266, 0x0, 0x0, 0x0)
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:107 +0x1d4
main.func·001(0xc20812cc00, 0x3f, 0x2ac4ff2eaac8, 0xc208134140, 0x0, 0x0, 0x0, 0x0)
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:113 +0x155
path/filepath.walk(0xc20812cc00, 0x3f, 0x2ac4ff2eaac8, 0xc208134140, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:347 +0x91
path/filepath.walk(0xc20811f980, 0x35, 0x2ac4ff2eaac8, 0xc20811cfa0, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0xc2080a72f0, 0x2c, 0x2ac4ff2eaac8, 0xc208068280, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0xc2080a71d0, 0x27, 0x2ac4ff2eaac8, 0xc208068230, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0xc208020220, 0x20, 0x2ac4ff2eaac8, 0xc2080689b0, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0xc208020180, 0x15, 0x2ac4ff2eaac8, 0xc208068960, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0xc208020620, 0x11, 0x2ac4ff2eaac8, 0xc208068c80, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0xc208034830, 0x6, 0x2ac4ff2eaac8, 0xc208068b90, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.walk(0x7fffbd0dd1bb, 0x1, 0x2ac4ff2eaac8, 0xc2080681e0, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:372 +0x51d
path/filepath.Walk(0x7fffbd0dd1bb, 0x1, 0xc2080dfe20, 0x0, 0x0)
/usr/local/go/src/path/filepath/path.go:394 +0xf2
main.analyzeDir(0x7fffbd0dd1bb, 0x1, 0xc20810a000, 0x154, 0x266, 0x0, 0x0, 0x0)
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:116 +0x6d
main.analyze(0xc20800a030, 0x1, 0x1, 0x0, 0x0, 0x0)
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:88 +0x131
main.main()
/home/travis/gopath/src/github.com/minio/mc/Godeps/_workspace/src/github.com/fzipp/gocyclo/gocyclo.go:71 +0xe8

Is this tool abandonware?

There are unmerged PR's open for nearly half a year. Is this still being supported or should it be forked to someone who is able to maintain it?

Improve Readme

Hello.

I've been seeing gocyclo appear in more places. Congrats. But, I don't understand what it actually does. I think the readme could include more information for noobs.

Gocyclo calculates cyclomatic complexities of functions in Go source code.

That statement leaves me with some questions:

  • What are "cyclomatic complexities of functions"?
  • What is considered good/bad?
  • Why is it considered good/bad?
  • If code is flagged, what can I do to fix it?

How to ignore local function?

I usually use local functions to split my main function to multiple part and group related functions together. However, gocyclo always report the complexity of my main function is high (e.g: 20) while it looks lower if we remove the local functions.

Can we have an option to exclude the local functions from calculation?

Change rules of cyclomatic complexity calculation

Hello, @fzipp.

I think highly nested construction should not give the same amount of points as the first-level one. Flat code is less complex than nested, isn't it? For illustration, here is a code fragment that has complexity of 11:

package zzz

func smth(obj interface{}) {
    switch obj.(type) {
    case int:
        ProcessInt(obj)
    case int8:
        // ..
    case int16:
    case int32:
    case int64:
    case uint:
    case uint8:
    case uint16:
    case uint32:
    case uint64:
    }
}

Pretty simple and straight-forward function, isn't it? Now here is another one that is "less complex" (level is 10):

package xxx

func smth(i int) {
    if i < 1 {
        if i < 2 {
            if i < 3 {
                if i < 4 {
                    if i < 5 {
                        if i < 6 {
                        } else {
                        }
                    } else {
                    }
                } else {
                    if i < 5 {
                    } else {
                    }
                }
            } else {
                if i < 4 {
                    if i < 5 {
                    } else {
                    }
                } else {
                }
            }
        } else {
        }
    } else {
    }
}

For me, personally, this doesn't look like a less complex function. What do you think?

My opinion is complexity should grow geometrically with every level of nesting. But requirements for flat code may be loosed up.

Provide Changelog

Hey!

My build failed today because of the change to the installation path of the project in 0.2.0 release.

I cannot create a branch to propose a pull request, so here's a template of changelog for you

# 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).

## [Unreleased]

## [0.3.0] - 2020-10-17
### Added

### Changed

### Removed

## [0.2.0] - 2020-10-17
### Added

### Changed
- Breaking: installation changed to `go get github.com/fzipp/gocyclo/cmd/gocyclo`

### Removed

Best regards and thanks for your lib.

Improve documentation

It is a nice to have to provide a wiki with academic definitions (as described in #10) and provide some examples of usage (with code and expected results)

Add flag to skip tests

Add a flag which will exclude testing code from the generated report.

I think it is useful to be able to optionally exclude tests from the report. I am suggesting this be a flag and optional since the exclusion or inclusion of test source code is dependant on the developer(s) or project maintainer(s) and this would give flexibility to whatever they decide.

Include variable functions

I noticed that functions declared as variables are not included. It would be nice if they were. We use variable functions a lot for testability and mocking.

Skip individual functions

Background: alecthomas/gometalinter#238

In short, when gocyclo is executed automatically (for ex. by gometalinter in CI) and it output is used to decide to pass or to fail automatic build we need more control than just single -over N value applied to all packages in current project.

Please add a feature to either completely skip gocyclo on per-function basis, or overwrite value of -over N argument on per-function basis. For example, check how another tool use annotations: https://github.com/GoASTScanner/gas#annotating-code

Add a flag to mark exit status

Can we have a CLI parameter to let gocyclo exit with non-zero value in case there are files meet -over criteria?

I'm thinking of using gocyclo in CI job, instead of pipe output to a file, then check if the file is empty or not to determine offending files, it will be great to have an option mentioned above.

Provide a switch to better fit golang

Not just an issue, but it would be nice to have a switch to avoid counting constructs clearly related to mere error checking and returning, for example IF block with less that 3/4 lines testing just err != nil could be not counted in complexity.
ES:
if err != nil {
log.......
return nil,err // or return fmt.Errorf("sfsdfsdf %w", err)
}

Proposal: Median option

Hey, thank you for your providing nice tool !
I would like you to add new option to show median of cyclomatic complexity.

I often meet occasions that a distribution of cyclomatic complexity in my code is highly skewed.
For example, a complexity of almost all functions in my repo is around 1.
But, a little functions marks 10 to 100 complexity.
In such case, average complexity tend to be higher than median or mode, so cannot capture whole trend.

I propose to inplement median option to this command like this,

    -med, -med-short      show the median complexity over all functions;
                          the short option prints the value without a label

This should be helpful for those who meet same issue with me.

Thanks,

gocycle with -over flag throws exist status code that causes automated build to fail.

Calling gocyclo from a build chain: TeamCity -> sonar-golang -> gometalinter -> gosimple.

Gocycle throws an exit code of 1 for -over flag. This causes downstream confusion. ie, the build "fails" when it catches the exit as "1". How can I further debug this?

root@1b8705ad04c3:~/go/src/private-domain/repo/app.git# pwd
/root/go/src/private-domain/repo/app.git

root@1b8705ad04c3:~/go/src/private-domain/repo/app.git# go version
go version go1.8 linux/amd64

root@1b8705ad04c3:~/go/src/private-domain/repo/app.git# go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/root/go"
GORACE=""
GOROOT="/usr/go"
GOTOOLDIR="/usr/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build008283540=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
PKG_CONFIG="pkg-config"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"

root@1b8705ad04c3:/go/src/private-domain/repo/app.git# gocyclo .
20 main main app.go:42:1
2 main TestReturnTrueOnValidInt app_test.go:14:1
2 main TestReturnTrueOnValidString app_test.go:7:1
2 main validateIntFlag app.go:166:1
2 main validateStringFlag app.go:157:1
1 main usage app.go:14:1
root@1b8705ad04c3:
/go/src/private-domain/repo/app.git# echo $?
0

root@1b8705ad04c3:/go/src/private-domain/repo/app.git# gocyclo -over 20 .
root@1b8705ad04c3:
/go/src/private-domain/repo/app.git# echo $?
0

root@1b8705ad04c3:/go/src/private-domain/repo/app.git# gocyclo -over 19 .
20 main main app.go:42:1
root@1b8705ad04c3:
/go/src/private-domain/repo/app.git# echo $?
1

root@1b8705ad04c3:/go/src/private-domain/repo/app.git# gocyclo -over 2 .
20 main main app.go:42:1
root@1b8705ad04c3:
/go/src/private-domain/repo/app.git# echo $?
1

root@1b8705ad04c3:/go/src/private-domain/repo/app.git# gocyclo -over 1 .
20 main main app.go:42:1
2 main TestReturnTrueOnValidInt app_test.go:14:1
2 main TestReturnTrueOnValidString app_test.go:7:1
2 main validateIntFlag app.go:166:1
2 main validateStringFlag app.go:157:1
root@1b8705ad04c3:
/go/src/private-domain/repo/app.git# echo $?
1

function literals not separately counted

It would be nice if function literals were assigned a complexity separate from the function declaration they were nested inside. This will require giving the nested function literals pseudo-names. E.g., the second function literal nested inside func Foo, might given pseudo name like "Foo.funcLit#2". It might also require using a map[string]stat rather than a []stat and indexing the map with a concatenation of package path with the function name.

ignore generated code

in CockroachDB we have a large amount of generated code, mostly from https://github.com/gogo/protobuf and yacc. Running gocyclo gives this:

$ gocyclo -top 10 .
645 parser (*sqlParserImpl).Parse sql/parser/yaccpar:156:1
233 roachpb (*RequestUnion).Unmarshal roachpb/api.pb.go:8930:1
233 roachpb (*ResponseUnion).Unmarshal roachpb/api.pb.go:9706:1
163 roachpb (*ErrorDetail).Unmarshal roachpb/errors.pb.go:2972:1
128 roachpb (*Transaction).Unmarshal roachpb/data.pb.go:2618:1
114 sql (*TableDescriptor).Unmarshal sql/structured.pb.go:1868:1
102 log (*LogEntry).Unmarshal util/log/log.pb.go:280:1
101 sql simplifyOneAndExpr sql/analyze.go:257:1
94 gossip (*Response).Unmarshal gossip/gossip.pb.go:796:1
93 gossip (*Request).Unmarshal gossip/gossip.pb.go:460:1

Of these, sql simplifyOneAndExpr sql/analyze.go:257 is the only hand-written function. To be useful, we need a way to teach gocyclo to ignore certain files, packages, or functions.

proper counting of switch/case/default control structures

gocyclo simply counts ast.CaseClause nodes to compute the cyclomatic complexity of a switch/case with optional default. The the default case should be counted even if it is absent (in which case we can ignore it if it is present and count the ast.SwitchStmt node instead) or maybe it should be ignored when it is present. I suppose ignoring it would be equivalent to not counting the else in an if/else.

The way the code is now is simple and elegant and I doubt the occasional off-by-one is a big deal.

There may be a similar issue with select statement and ast.CommClause nodes, but I haven't looked at that as closely.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.