Skip to content

Commit d5b98c8

Browse files
committed
Add tracing capability
1 parent 8b1a11f commit d5b98c8

File tree

2 files changed

+93
-30
lines changed

2 files changed

+93
-30
lines changed

cmd/gocognit/main.go

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ func main() {
110110
format string
111111
jsonEncode bool
112112
ignoreExpr string
113+
trace bool
113114
)
114115

115116
flag.IntVar(&over, "over", defaultOverFlagVal, "show functions with complexity > N only")
@@ -119,6 +120,7 @@ func main() {
119120
flag.StringVar(&format, "f", defaultFormat, "the format to use")
120121
flag.BoolVar(&jsonEncode, "json", false, "encode the output as JSON")
121122
flag.StringVar(&ignoreExpr, "ignore", "", "ignore files matching the given regexp")
123+
flag.BoolVar(&trace, "trace", false, "include trace information of the complexity")
122124

123125
log.SetFlags(0)
124126
log.SetPrefix("gocognit: ")
@@ -136,7 +138,8 @@ func main() {
136138
log.Fatal(err)
137139
}
138140

139-
stats, err := analyze(args, includeTests)
141+
traceEnabled := trace && jsonEncode
142+
stats, err := analyze(args, includeTests, traceEnabled)
140143
if err != nil {
141144
log.Fatal(err)
142145
}
@@ -170,19 +173,19 @@ func main() {
170173
}
171174
}
172175

173-
func analyzePath(path string, includeTests bool) ([]gocognit.Stat, error) {
176+
func analyzePath(path string, includeTests bool, trace bool) ([]gocognit.Stat, error) {
174177
if isDir(path) {
175-
return analyzeDir(path, includeTests, nil)
178+
return analyzeDir(path, includeTests, nil, trace)
176179
}
177180

178-
return analyzeFile(path, nil)
181+
return analyzeFile(path, nil, trace)
179182
}
180183

181-
func analyze(paths []string, includeTests bool) (stats []gocognit.Stat, err error) {
184+
func analyze(paths []string, includeTests bool, trace bool) (stats []gocognit.Stat, err error) {
182185
var out []gocognit.Stat
183186

184187
for _, path := range paths {
185-
stats, err := analyzePath(path, includeTests)
188+
stats, err := analyzePath(path, includeTests, trace)
186189
if err != nil {
187190
return nil, err
188191
}
@@ -199,18 +202,18 @@ func isDir(filename string) bool {
199202
return err == nil && fi.IsDir()
200203
}
201204

202-
func analyzeFile(fname string, stats []gocognit.Stat) ([]gocognit.Stat, error) {
205+
func analyzeFile(fname string, stats []gocognit.Stat, trace bool) ([]gocognit.Stat, error) {
203206
fset := token.NewFileSet()
204207

205208
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
206209
if err != nil {
207210
return nil, err
208211
}
209212

210-
return gocognit.ComplexityStats(f, fset, stats), nil
213+
return gocognit.ComplexityStats(f, fset, stats, trace), nil
211214
}
212215

213-
func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat) ([]gocognit.Stat, error) {
216+
func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat, trace bool) ([]gocognit.Stat, error) {
214217
err := filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error {
215218
if err != nil {
216219
return err
@@ -228,7 +231,7 @@ func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat) ([]goc
228231
return nil
229232
}
230233

231-
stats, err = analyzeFile(path, stats)
234+
stats, err = analyzeFile(path, stats, trace)
232235
if err != nil {
233236
return err
234237
}

gocognit.go

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type Stat struct {
1515
PkgName string
1616
FuncName string
1717
Complexity int
18+
Traces []Trace `json:",omitempty"`
1819
Pos token.Position
1920
}
2021

@@ -23,18 +24,21 @@ func (s Stat) String() string {
2324
}
2425

2526
// 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 {
2728
for _, decl := range f.Decls {
2829
if fn, ok := decl.(*ast.FuncDecl); ok {
2930
d := parseDirective(fn.Doc)
3031
if d.Ignore {
3132
continue
3233
}
3334

35+
res := Scan(fn, includeTrace)
36+
3437
stats = append(stats, Stat{
3538
PkgName: f.Name.Name,
3639
FuncName: funcName(fn),
37-
Complexity: Complexity(fn),
40+
Complexity: res.Complexity,
41+
Traces: res.Traces,
3842
Pos: fset.Position(fn.Pos()),
3943
})
4044
}
@@ -77,13 +81,44 @@ func funcName(fn *ast.FuncDecl) string {
7781

7882
// Complexity calculates the cognitive complexity of a function.
7983
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 {
8091
v := complexityVisitor{
81-
name: fn.Name,
92+
name: fn.Name,
93+
trace: trace,
8294
}
8395

8496
ast.Walk(&v, fn)
8597

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)
87122
}
88123

89124
type complexityVisitor struct {
@@ -92,6 +127,11 @@ type complexityVisitor struct {
92127
nesting int
93128
elseNodes map[ast.Node]bool
94129
calculatedExprs map[ast.Expr]bool
130+
131+
fset *token.FileSet
132+
133+
trace bool
134+
traces []Trace
95135
}
96136

97137
func (v *complexityVisitor) incNesting() {
@@ -102,12 +142,33 @@ func (v *complexityVisitor) decNesting() {
102142
v.nesting--
103143
}
104144

105-
func (v *complexityVisitor) incComplexity() {
145+
func (v *complexityVisitor) incComplexity(text string, pos token.Pos) {
106146
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+
})
107157
}
108158

109-
func (v *complexityVisitor) nestIncComplexity() {
159+
func (v *complexityVisitor) nestIncComplexity(text string, pos token.Pos) {
110160
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+
})
111172
}
112173

113174
func (v *complexityVisitor) markAsElseNode(n ast.Node) {
@@ -171,7 +232,7 @@ func (v *complexityVisitor) Visit(n ast.Node) ast.Visitor {
171232
}
172233

173234
func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
174-
v.incIfComplexity(n)
235+
v.incIfComplexity(n, "if", n.Pos())
175236

176237
if n := n.Init; n != nil {
177238
ast.Walk(v, n)
@@ -184,7 +245,7 @@ func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
184245
v.decNesting()
185246

186247
if _, ok := n.Else.(*ast.BlockStmt); ok {
187-
v.incComplexity()
248+
v.incComplexity("else", n.Else.Pos())
188249

189250
ast.Walk(v, n.Else)
190251
} else if _, ok := n.Else.(*ast.IfStmt); ok {
@@ -196,7 +257,7 @@ func (v *complexityVisitor) visitIfStmt(n *ast.IfStmt) ast.Visitor {
196257
}
197258

198259
func (v *complexityVisitor) visitSwitchStmt(n *ast.SwitchStmt) ast.Visitor {
199-
v.nestIncComplexity()
260+
v.nestIncComplexity("switch", n.Pos())
200261

201262
if n := n.Init; n != nil {
202263
ast.Walk(v, n)
@@ -214,7 +275,7 @@ func (v *complexityVisitor) visitSwitchStmt(n *ast.SwitchStmt) ast.Visitor {
214275
}
215276

216277
func (v *complexityVisitor) visitTypeSwitchStmt(n *ast.TypeSwitchStmt) ast.Visitor {
217-
v.nestIncComplexity()
278+
v.nestIncComplexity("switch", n.Pos())
218279

219280
if n := n.Init; n != nil {
220281
ast.Walk(v, n)
@@ -232,7 +293,7 @@ func (v *complexityVisitor) visitTypeSwitchStmt(n *ast.TypeSwitchStmt) ast.Visit
232293
}
233294

234295
func (v *complexityVisitor) visitSelectStmt(n *ast.SelectStmt) ast.Visitor {
235-
v.nestIncComplexity()
296+
v.nestIncComplexity("select", n.Pos())
236297

237298
v.incNesting()
238299
ast.Walk(v, n.Body)
@@ -242,7 +303,7 @@ func (v *complexityVisitor) visitSelectStmt(n *ast.SelectStmt) ast.Visitor {
242303
}
243304

244305
func (v *complexityVisitor) visitForStmt(n *ast.ForStmt) ast.Visitor {
245-
v.nestIncComplexity()
306+
v.nestIncComplexity("for", n.Pos())
246307

247308
if n := n.Init; n != nil {
248309
ast.Walk(v, n)
@@ -264,7 +325,7 @@ func (v *complexityVisitor) visitForStmt(n *ast.ForStmt) ast.Visitor {
264325
}
265326

266327
func (v *complexityVisitor) visitRangeStmt(n *ast.RangeStmt) ast.Visitor {
267-
v.nestIncComplexity()
328+
v.nestIncComplexity("for", n.Pos())
268329

269330
if n := n.Key; n != nil {
270331
ast.Walk(v, n)
@@ -294,7 +355,7 @@ func (v *complexityVisitor) visitFuncLit(n *ast.FuncLit) ast.Visitor {
294355

295356
func (v *complexityVisitor) visitBranchStmt(n *ast.BranchStmt) ast.Visitor {
296357
if n.Label != nil {
297-
v.incComplexity()
358+
v.incComplexity(n.Tok.String(), n.Pos())
298359
}
299360
return v
300361
}
@@ -306,8 +367,7 @@ func (v *complexityVisitor) visitBinaryExpr(n *ast.BinaryExpr) ast.Visitor {
306367
var lastOp token.Token
307368
for _, op := range ops {
308369
if lastOp != op {
309-
v.incComplexity()
310-
370+
v.incComplexity(op.String(), n.OpPos)
311371
lastOp = op
312372
}
313373
}
@@ -321,7 +381,7 @@ func (v *complexityVisitor) visitCallExpr(n *ast.CallExpr) ast.Visitor {
321381
obj, name := callIdent.Obj, callIdent.Name
322382
if obj == v.name.Obj && name == v.name.Name {
323383
// called by same function directly (direct recursion)
324-
v.incComplexity()
384+
v.incComplexity(name, n.Pos())
325385
}
326386
}
327387

@@ -337,11 +397,11 @@ func (v *complexityVisitor) collectBinaryOps(exp ast.Expr) []token.Token {
337397
return nil
338398
}
339399

340-
func (v *complexityVisitor) incIfComplexity(n *ast.IfStmt) {
400+
func (v *complexityVisitor) incIfComplexity(n *ast.IfStmt, text string, pos token.Pos) {
341401
if v.markedAsElseNode(n) {
342-
v.incComplexity()
402+
v.incComplexity(text, pos)
343403
} else {
344-
v.nestIncComplexity()
404+
v.nestIncComplexity(text, pos)
345405
}
346406
}
347407

0 commit comments

Comments
 (0)