Defer inside loop; deferred calls accumulate until the function returns, not until the loop iteration ends. This can cause resource leaks
defer func() { Cognitive = false }()
1// SPDX-License-Identifier: MIT23package processor45import "testing"67// These tests pin the core promise of cognitive complexity: the SAME number of8// branch points (identical cyclomatic Complexity) must score HIGHER the more9// deeply they are nested. Cyclomatic counts branches flatly; cognitive weights10// each branch by 1+nesting, so shape — not just count — drives the number.11//12// All fixtures below contain exactly four `if`s (Complexity == 4) arranged from13// maximally nested to maximally flat, and assert both the exact cognitive values14// (derived from the 1+nesting rule) and the strict ordering between them.1516// deepChain: four ifs each nested one level deeper than the last.17//18// if nesting 1 -> +219// if nesting 2 -> +320// if nesting 3 -> +421// if nesting 4 -> +5 total 1422const deepChain = "func main() {\n" +23 " if a {\n" +24 " if b {\n" +25 " if c {\n" +26 " if d {\n" +27 " }\n" +28 " }\n" +29 " }\n" +30 " }\n" +31 "}\n"3233// deNested: two independent 2-deep pairs (the user's "de-nested" example).34//35// if nesting 1 -> +236// if nesting 2 -> +337// if nesting 1 -> +238// if nesting 2 -> +3 total 1039const deNested = "func main() {\n" +40 " if a {\n" +41 " if b {\n" +42 " }\n" +43 " }\n" +44 " if c {\n" +45 " if d {\n" +46 " }\n" +47 " }\n" +48 "}\n"4950// flat: all four ifs as siblings at the same level.51//52// if if if if each nesting 1 -> +2 each total 853const flat = "func main() {\n" +54 " if a {\n }\n" +55 " if b {\n }\n" +56 " if c {\n }\n" +57 " if d {\n }\n" +58 "}\n"5960// TestCognitiveDeepVsDeNestedVsFlat is the headline test: same four branches,61// three shapes, strictly decreasing cognitive as they flatten — while the flat62// cyclomatic Complexity stays 4 throughout.63func TestCognitiveDeepVsDeNestedVsFlat(t *testing.T) {64 Cognitive = true65 defer func() { Cognitive = false }()6667 deep := countCognitive(t, "Go", deepChain)68 mid := countCognitive(t, "Go", deNested)69 shallow := countCognitive(t, "Go", flat)7071 // Same number of branch points: cyclomatic cannot tell these apart.72 for name, job := range map[string]FileJob{"deep": deep, "deNested": mid, "flat": shallow} {73 if job.Complexity != 4 {74 t.Errorf("%s: expected Complexity 4 (four ifs), got %d", name, job.Complexity)75 }76 }7778 // Exact cognitive values from the 1+nesting rule.79 if deep.Cognitive != 14 {80 t.Errorf("deepChain Cognitive: expected 14, got %d", deep.Cognitive)81 }82 if mid.Cognitive != 10 {83 t.Errorf("deNested Cognitive: expected 10, got %d", mid.Cognitive)84 }85 if shallow.Cognitive != 8 {86 t.Errorf("flat Cognitive: expected 8, got %d", shallow.Cognitive)87 }8889 // The relationship the metric exists to express: deeper nesting scores90 // strictly higher, even at identical cyclomatic complexity.91 if !(deep.Cognitive > mid.Cognitive && mid.Cognitive > shallow.Cognitive) {92 t.Errorf("expected deep > deNested > flat, got %d, %d, %d",93 deep.Cognitive, mid.Cognitive, shallow.Cognitive)94 }95}9697// TestCognitiveNestedHigherThanDeNested is the user's literal example, written98// with top-level (column 0) ifs and 2-space indentation to show the ranking is99// independent of the wrapping function and the indent unit.100//101// nested: de-nested:102// if if103// if if104// if if105// if if106func TestCognitiveNestedHigherThanDeNested(t *testing.T) {107 Cognitive = true108 defer func() { Cognitive = false }()109110 nested := "if a {\n" +111 " if b {\n" +112 " if c {\n" +113 " if d {\n" +114 " }\n" +115 " }\n" +116 " }\n" +117 "}\n"118119 splitNested := "if a {\n" +120 " if b {\n" +121 " }\n" +122 "}\n" +123 "if c {\n" +124 " if d {\n" +125 " }\n" +126 "}\n"127128 nestedJob := countCognitive(t, "Go", nested)129 splitJob := countCognitive(t, "Go", splitNested)130131 if nestedJob.Complexity != splitJob.Complexity {132 t.Fatalf("fixtures must have equal cyclomatic Complexity, got nested=%d split=%d",133 nestedJob.Complexity, splitJob.Complexity)134 }135 // nested: 1+2+3+4 = 10 ; de-nested: 1+2+1+2 = 6136 if nestedJob.Cognitive != 10 {137 t.Errorf("nested Cognitive: expected 10, got %d", nestedJob.Cognitive)138 }139 if splitJob.Cognitive != 6 {140 t.Errorf("de-nested Cognitive: expected 6, got %d", splitJob.Cognitive)141 }142 if nestedJob.Cognitive <= splitJob.Cognitive {143 t.Errorf("nested (%d) should score strictly higher than de-nested (%d)",144 nestedJob.Cognitive, splitJob.Cognitive)145 }146}147148// TestCognitiveMonotonicWithDepth: a single chain of N nested ifs. Each added149// level of depth adds strictly more cognitive weight than the previous level did150// (the increments grow 2,3,4,... as nesting deepens), unlike cyclomatic which151// would add a flat 1 per branch.152func TestCognitiveMonotonicWithDepth(t *testing.T) {153 Cognitive = true154 defer func() { Cognitive = false }()155156 // Build chains of depth 1..5 and record cognitive at each depth.157 chain := func(depth int) string {158 var b string159 b = "func main() {\n"160 indent := " "161 pad := ""162 for i := 0; i < depth; i++ {163 pad += indent164 b += pad + "if x {\n"165 }166 for i := 0; i < depth; i++ {167 b += pad + "}\n"168 pad = pad[:len(pad)-len(indent)]169 }170 b += "}\n"171 return b172 }173174 prev := int64(-1)175 prevDelta := int64(-1)176 for depth := 1; depth <= 5; depth++ {177 job := countCognitive(t, "Go", chain(depth))178 if int64(job.Complexity) != int64(depth) {179 t.Errorf("depth %d: expected Complexity %d, got %d", depth, depth, job.Complexity)180 }181 if prev >= 0 {182 delta := job.Cognitive - prev183 // Each deeper level contributes more than the one before it.184 if delta <= prevDelta {185 t.Errorf("depth %d: cognitive delta %d should exceed previous delta %d (values grow super-linearly with depth)", depth, delta, prevDelta)186 }187 prevDelta = delta188 }189 if job.Cognitive <= prev {190 t.Errorf("depth %d: cognitive %d should exceed shallower depth's %d", depth, job.Cognitive, prev)191 }192 prev = job.Cognitive193 }194}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.