Skip to content

Commit b77bb1a

Browse files
fix: cognitive complexity nesting with if-else chains (#1268)
* test: prove bug in cognitive complexity rule * fix: cognitive complexity nesting with if-else chains Currently, an if-else chain will increase the nesting level and add the nesting increment for every addition `else if` statement in an if-else chain. This is incorrect behaviour; an `else if` statement should increment complexity by 1 (regardless of current nesting level) and leave the nesting level as-is. For example, the following should yield a total complexity of 5: ``` for { // +1 if a { // +2 (nesting = 1) foo() } else if b { // +1 bar() } else if c { // +1 baz() } } ``` but the current implementation incorrectly increments the nesting level with each `else if` and adds the nesting increment where it shouldn't: ``` for { // +1 if a { // +2 (nesting = 1) foo() } else if b { // +3 (nesting = 2) bar() } else if c { // +4 (nesting = 3) baz() } } ```
1 parent 6d0498c commit b77bb1a

File tree

2 files changed

+37
-2
lines changed

2 files changed

+37
-2
lines changed

rule/cognitive_complexity.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ func (v *cognitiveComplexityVisitor) subTreeComplexity(n ast.Node) int {
9898
func (v *cognitiveComplexityVisitor) Visit(n ast.Node) ast.Visitor {
9999
switch n := n.(type) {
100100
case *ast.IfStmt:
101-
targets := []ast.Node{n.Cond, n.Body, n.Else}
102-
v.walk(1, targets...)
101+
v.walkIfElse(n)
103102
return nil
104103
case *ast.ForStmt:
105104
targets := []ast.Node{n.Cond, n.Body}
@@ -156,6 +155,29 @@ func (v *cognitiveComplexityVisitor) walk(complexityIncrement int, targets ...as
156155
v.nestingLevel = nesting
157156
}
158157

158+
func (v *cognitiveComplexityVisitor) walkIfElse(n *ast.IfStmt) {
159+
var w func(n *ast.IfStmt)
160+
w = func(n *ast.IfStmt) {
161+
ast.Walk(v, n.Cond)
162+
ast.Walk(v, n.Body)
163+
if n.Else != nil {
164+
if elif, ok := n.Else.(*ast.IfStmt); ok {
165+
v.complexity++
166+
w(elif)
167+
} else {
168+
ast.Walk(v, n.Else)
169+
}
170+
}
171+
}
172+
173+
// Nesting level is incremented in 'if' and 'else' blocks, but only the first 'if' in an 'if-else-if' chain sees its
174+
// complexity increased by the nesting level.
175+
v.complexity += 1 + v.nestingLevel
176+
v.nestingLevel++
177+
w(n)
178+
v.nestingLevel--
179+
}
180+
159181
func (*cognitiveComplexityVisitor) binExpComplexity(n *ast.BinaryExpr) int {
160182
calculator := binExprComplexityCalculator{opsStack: []token.Token{}}
161183

testdata/cognitive_complexity.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,3 +291,16 @@ func Walk(t *Tree, ch chan int) { // MATCH /function Walk has cognitive complexi
291291
ch <- t.Value
292292
Walk(t.Right, ch) // +1
293293
}
294+
295+
// Test if-else if chains
296+
func chainedIfElse(a, b, c, d bool) { // MATCH /function chainedIfElse has cognitive complexity 4 (> max enabled 0)/
297+
if a { // +1
298+
foo()
299+
} else if b && c { // +2
300+
bar()
301+
} else if d { // +1
302+
baz()
303+
} else {
304+
qux()
305+
}
306+
}

0 commit comments

Comments
 (0)