“This is the 9th day of my participation in the Gwen Challenge in November. Check out the details: The Last Gwen Challenge in 2021.”
primers
In the # Githook practice using golangci-lint as an example, we mentioned using Githook + Golangci-lint to check the quality of code before committing. There is a code complexity check item, which can be used to check the complexity of the code in the project. If the code is too complex, the submission will be rejected. Use code complexity checks to avoid writing unmaintainable code.
Speaking of complexity, those who brush Leetcode frequently may immediately think of time complexity and space complexity, but in fact, code specification inspection software cannot do such a detailed inspection. As a result, code specification checking software often uses the concept of cyclomatic complexity to determine the complexity of code.
So what is cyclomatic complexity?
Simply put, if a piece of code has many branches of flow control statements, it can double the workload of the testing girl.
For example, there was a lot of discussion about the large number of if statements that were found in the early Taio E Scroll code when it was decompiled, and the most obvious problem with this was that it reduced maintainability. (The author has only studied for one month, so the requirement should not be too high, but as professionals, we should avoid this situation.)
www.zhihu.com/question/29…
The industry practice
cyclop
Golangci-lint is a plugin that iterates through all functions in your code and then iterates through the syntax tree (AST) of the function, increasing the complexity of the code by 1
ast.FuncDecl
If a function is defined within a function, it may be that the object code implements a closure or coroutines within the functionast.IfStmt
For every if, cyclomatic complexity increases by +1ast.ForStmt
.ast.RangeStmt
Cyclomatic complexity +1 for each forast.CaseClause
Cyclomatic complexity increases by 1 for each additional case statement in a switch statementast.CommClause
Switch statement each additional slavechannel
Case of receiving data, cyclomatic complexity +1ast.BinaryExpr
In each expression if there isand
oror
Logically, cyclomatic complexity +1
The core logic is shown below
type complexityVisitor struct {
Complexity int
}
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
}
Copy the code
Here’s the code
Evaluation: Although the cyclomatic complexity statistics above are simple and rough, there may be some cases of killing innocent people by mistake, but according to practical experience, it can still prevent “dirty” code in most cases. The only exception is if you need to use switch-case to implement the state machine, then you need to adjust the cyclomatic complexity upper limit. Or add //nolint to the code.
gocyclo
Gocyclo was also introduced as a plug-in in Golangci-Lint, which is a bit more nuanced about code complexity
- encounter
if
.for
Statement complexity +1 - encounter
switch-case
Statement complexity +1, encountereddefault
Branches don’t add +1 - encounter
&&
or||
Operator, complexity +1
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:
ifn.List ! =nil { // ignore default case
v.complexity++
}
case *ast.CommClause:
ifn.Comm ! =nil { // ignore default case
v.complexity++
}
case *ast.BinaryExpr:
if n.Op == token.LAND || n.Op == token.LOR {
v.complexity++
}
}
return v
}
Copy the code
Here’s the code
conclusion
The core idea of the above open-source code cyclomatic complexity detection software is to count cyclomatic complexity by traversing the nodes of the abstract syntax tree (AST) of the function. If a specific node is encountered, the cyclomatic complexity of the code is +1.
How can cyclomatic complexity be reduced
- Split large functions, which can be split if there are too many if statements /for loops and there is no correlation between the top and the bottom
- use
map
Store mappings from conditions to results, eliminating excessiveif
judge - Optimize your algorithm 😀