@@ -15,6 +15,7 @@ type Stat struct {
15
15
PkgName string
16
16
FuncName string
17
17
Complexity int
18
+ Traces []Trace `json:",omitempty"`
18
19
Pos token.Position
19
20
}
20
21
@@ -23,18 +24,21 @@ func (s Stat) String() string {
23
24
}
24
25
25
26
// ComplexityStats builds the complexity statistics.
26
- func ComplexityStats (f * ast.File , fset * token.FileSet , stats []Stat ) []Stat {
27
+ func ComplexityStats (f * ast.File , fset * token.FileSet , stats []Stat , includeTrace bool ) []Stat {
27
28
for _ , decl := range f .Decls {
28
29
if fn , ok := decl .(* ast.FuncDecl ); ok {
29
30
d := parseDirective (fn .Doc )
30
31
if d .Ignore {
31
32
continue
32
33
}
33
34
35
+ res := Scan (fn , includeTrace )
36
+
34
37
stats = append (stats , Stat {
35
38
PkgName : f .Name .Name ,
36
39
FuncName : funcName (fn ),
37
- Complexity : Complexity (fn ),
40
+ Complexity : res .Complexity ,
41
+ Traces : res .Traces ,
38
42
Pos : fset .Position (fn .Pos ()),
39
43
})
40
44
}
@@ -77,13 +81,44 @@ func funcName(fn *ast.FuncDecl) string {
77
81
78
82
// Complexity calculates the cognitive complexity of a function.
79
83
func Complexity (fn * ast.FuncDecl ) int {
84
+ res := Scan (fn , false )
85
+
86
+ return res .Complexity
87
+ }
88
+
89
+ // Scan scans the function declaration.
90
+ func Scan (fn * ast.FuncDecl , trace bool ) Result {
80
91
v := complexityVisitor {
81
- name : fn .Name ,
92
+ name : fn .Name ,
93
+ trace : trace ,
82
94
}
83
95
84
96
ast .Walk (& v , fn )
85
97
86
- return v .complexity
98
+ return Result {
99
+ Traces : v .traces ,
100
+ Complexity : v .complexity ,
101
+ }
102
+ }
103
+
104
+ type Result struct {
105
+ Traces []Trace
106
+ Complexity int
107
+ }
108
+
109
+ type Trace struct {
110
+ Inc int
111
+ Nesting int `json:",omitempty"`
112
+ Text string
113
+ Pos token.Pos
114
+ }
115
+
116
+ func (t Trace ) String () string {
117
+ if t .Nesting == 0 {
118
+ return fmt .Sprintf ("+%d" , t .Inc )
119
+ }
120
+
121
+ return fmt .Sprintf ("+%d (nesting=%d)" , t .Inc , t .Nesting )
87
122
}
88
123
89
124
type complexityVisitor struct {
@@ -92,6 +127,11 @@ type complexityVisitor struct {
92
127
nesting int
93
128
elseNodes map [ast.Node ]bool
94
129
calculatedExprs map [ast.Expr ]bool
130
+
131
+ fset * token.FileSet
132
+
133
+ trace bool
134
+ traces []Trace
95
135
}
96
136
97
137
func (v * complexityVisitor ) incNesting () {
@@ -102,12 +142,33 @@ func (v *complexityVisitor) decNesting() {
102
142
v .nesting --
103
143
}
104
144
105
- func (v * complexityVisitor ) incComplexity () {
145
+ func (v * complexityVisitor ) incComplexity (text string , pos token. Pos ) {
106
146
v .complexity ++
147
+
148
+ if ! v .trace {
149
+ return
150
+ }
151
+
152
+ v .traces = append (v .traces , Trace {
153
+ Inc : 1 ,
154
+ Text : text ,
155
+ Pos : pos ,
156
+ })
107
157
}
108
158
109
- func (v * complexityVisitor ) nestIncComplexity () {
159
+ func (v * complexityVisitor ) nestIncComplexity (text string , pos token. Pos ) {
110
160
v .complexity += (v .nesting + 1 )
161
+
162
+ if ! v .trace {
163
+ return
164
+ }
165
+
166
+ v .traces = append (v .traces , Trace {
167
+ Inc : v .nesting + 1 ,
168
+ Nesting : v .nesting ,
169
+ Text : text ,
170
+ Pos : pos ,
171
+ })
111
172
}
112
173
113
174
func (v * complexityVisitor ) markAsElseNode (n ast.Node ) {
@@ -171,7 +232,7 @@ func (v *complexityVisitor) Visit(n ast.Node) ast.Visitor {
171
232
}
172
233
173
234
func (v * complexityVisitor ) visitIfStmt (n * ast.IfStmt ) ast.Visitor {
174
- v .incIfComplexity (n )
235
+ v .incIfComplexity (n , "if" , n . Pos () )
175
236
176
237
if n := n .Init ; n != nil {
177
238
ast .Walk (v , n )
@@ -184,7 +245,7 @@ func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
184
245
v .decNesting ()
185
246
186
247
if _ , ok := n .Else .(* ast.BlockStmt ); ok {
187
- v .incComplexity ()
248
+ v .incComplexity ("else" , n . Else . Pos () )
188
249
189
250
ast .Walk (v , n .Else )
190
251
} else if _ , ok := n .Else .(* ast.IfStmt ); ok {
@@ -196,7 +257,7 @@ func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
196
257
}
197
258
198
259
func (v * complexityVisitor ) visitSwitchStmt (n * ast.SwitchStmt ) ast.Visitor {
199
- v .nestIncComplexity ()
260
+ v .nestIncComplexity ("switch" , n . Pos () )
200
261
201
262
if n := n .Init ; n != nil {
202
263
ast .Walk (v , n )
@@ -214,7 +275,7 @@ func (v *complexityVisitor) visitSwitchStmt(n *ast.SwitchStmt) ast.Visitor {
214
275
}
215
276
216
277
func (v * complexityVisitor ) visitTypeSwitchStmt (n * ast.TypeSwitchStmt ) ast.Visitor {
217
- v .nestIncComplexity ()
278
+ v .nestIncComplexity ("switch" , n . Pos () )
218
279
219
280
if n := n .Init ; n != nil {
220
281
ast .Walk (v , n )
@@ -232,7 +293,7 @@ func (v *complexityVisitor) visitTypeSwitchStmt(n *ast.TypeSwitchStmt) ast.Visit
232
293
}
233
294
234
295
func (v * complexityVisitor ) visitSelectStmt (n * ast.SelectStmt ) ast.Visitor {
235
- v .nestIncComplexity ()
296
+ v .nestIncComplexity ("select" , n . Pos () )
236
297
237
298
v .incNesting ()
238
299
ast .Walk (v , n .Body )
@@ -242,7 +303,7 @@ func (v *complexityVisitor) visitSelectStmt(n *ast.SelectStmt) ast.Visitor {
242
303
}
243
304
244
305
func (v * complexityVisitor ) visitForStmt (n * ast.ForStmt ) ast.Visitor {
245
- v .nestIncComplexity ()
306
+ v .nestIncComplexity ("for" , n . Pos () )
246
307
247
308
if n := n .Init ; n != nil {
248
309
ast .Walk (v , n )
@@ -264,7 +325,7 @@ func (v *complexityVisitor) visitForStmt(n *ast.ForStmt) ast.Visitor {
264
325
}
265
326
266
327
func (v * complexityVisitor ) visitRangeStmt (n * ast.RangeStmt ) ast.Visitor {
267
- v .nestIncComplexity ()
328
+ v .nestIncComplexity ("for" , n . Pos () )
268
329
269
330
if n := n .Key ; n != nil {
270
331
ast .Walk (v , n )
@@ -294,7 +355,7 @@ func (v *complexityVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
294
355
295
356
func (v * complexityVisitor ) visitBranchStmt (n * ast.BranchStmt ) ast.Visitor {
296
357
if n .Label != nil {
297
- v .incComplexity ()
358
+ v .incComplexity (n . Tok . String (), n . Pos () )
298
359
}
299
360
return v
300
361
}
@@ -306,8 +367,7 @@ func (v *complexityVisitor) visitBinaryExpr(n *ast.BinaryExpr) ast.Visitor {
306
367
var lastOp token.Token
307
368
for _ , op := range ops {
308
369
if lastOp != op {
309
- v .incComplexity ()
310
-
370
+ v .incComplexity (op .String (), n .OpPos )
311
371
lastOp = op
312
372
}
313
373
}
@@ -321,7 +381,7 @@ func (v *complexityVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
321
381
obj , name := callIdent .Obj , callIdent .Name
322
382
if obj == v .name .Obj && name == v .name .Name {
323
383
// called by same function directly (direct recursion)
324
- v .incComplexity ()
384
+ v .incComplexity (name , n . Pos () )
325
385
}
326
386
}
327
387
@@ -337,11 +397,11 @@ func (v *complexityVisitor) collectBinaryOps(exp ast.Expr) []token.Token {
337
397
return nil
338
398
}
339
399
340
- func (v * complexityVisitor ) incIfComplexity (n * ast.IfStmt ) {
400
+ func (v * complexityVisitor ) incIfComplexity (n * ast.IfStmt , text string , pos token. Pos ) {
341
401
if v .markedAsElseNode (n ) {
342
- v .incComplexity ()
402
+ v .incComplexity (text , pos )
343
403
} else {
344
- v .nestIncComplexity ()
404
+ v .nestIncComplexity (text , pos )
345
405
}
346
406
}
347
407
0 commit comments