Skip to content

Commit 1faed92

Browse files
committed
Report diagnostic position
1 parent 8fb2033 commit 1faed92

File tree

2 files changed

+115
-56
lines changed

2 files changed

+115
-56
lines changed

cmd/gocognit/main.go

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,9 @@ Flags:
6666
not depending on whether -over or -top are set
6767
-test indicates whether test files should be included
6868
-json encode the output as JSON
69+
-d enable diagnostic output
6970
-f format string the format to use
70-
(default "{{.PkgName}}.{{.FuncName}}:{{.Complexity}}:{{.Pos}}")
71+
(default "{{.Complexity}} {{.PkgName}} {{.FuncName}} {{.Pos}}")
7172
7273
The (default) output fields for each line are:
7374
@@ -82,10 +83,24 @@ or equal to <complexity> <package> <function> <file:row:column>
8283
The struct being passed to the template is:
8384
8485
type Stat struct {
85-
PkgName string
86-
FuncName string
87-
Complexity int
88-
Pos token.Position
86+
PkgName string
87+
FuncName string
88+
Complexity int
89+
Diagnostics []Diagnostics
90+
Pos token.Position
91+
}
92+
93+
type Diagnostic struct {
94+
Inc string
95+
Nesting int
96+
Text string
97+
Pos DiagnosticPosition
98+
}
99+
100+
type DiagnosticPosition struct {
101+
Offset int
102+
Line int
103+
Column int
89104
}
90105
`
91106

@@ -110,7 +125,7 @@ func main() {
110125
format string
111126
jsonEncode bool
112127
ignoreExpr string
113-
trace bool
128+
diagnostic bool
114129
)
115130

116131
flag.IntVar(&over, "over", defaultOverFlagVal, "show functions with complexity > N only")
@@ -120,7 +135,7 @@ func main() {
120135
flag.StringVar(&format, "f", defaultFormat, "the format to use")
121136
flag.BoolVar(&jsonEncode, "json", false, "encode the output as JSON")
122137
flag.StringVar(&ignoreExpr, "ignore", "", "ignore files matching the given regexp")
123-
flag.BoolVar(&trace, "trace", false, "include trace information of the complexity")
138+
flag.BoolVar(&diagnostic, "d", false, "enable diagnostic output")
124139

125140
log.SetFlags(0)
126141
log.SetPrefix("gocognit: ")
@@ -138,8 +153,7 @@ func main() {
138153
log.Fatal(err)
139154
}
140155

141-
traceEnabled := trace && jsonEncode
142-
stats, err := analyze(args, includeTests, traceEnabled)
156+
stats, err := analyze(args, includeTests, diagnostic)
143157
if err != nil {
144158
log.Fatal(err)
145159
}
@@ -173,19 +187,19 @@ func main() {
173187
}
174188
}
175189

176-
func analyzePath(path string, includeTests bool, trace bool) ([]gocognit.Stat, error) {
190+
func analyzePath(path string, includeTests bool, includeDiagnostic bool) ([]gocognit.Stat, error) {
177191
if isDir(path) {
178-
return analyzeDir(path, includeTests, nil, trace)
192+
return analyzeDir(path, includeTests, nil, includeDiagnostic)
179193
}
180194

181-
return analyzeFile(path, nil, trace)
195+
return analyzeFile(path, nil, includeDiagnostic)
182196
}
183197

184-
func analyze(paths []string, includeTests bool, trace bool) (stats []gocognit.Stat, err error) {
198+
func analyze(paths []string, includeTests bool, includeDiagnostic bool) (stats []gocognit.Stat, err error) {
185199
var out []gocognit.Stat
186200

187201
for _, path := range paths {
188-
stats, err := analyzePath(path, includeTests, trace)
202+
stats, err := analyzePath(path, includeTests, includeDiagnostic)
189203
if err != nil {
190204
return nil, err
191205
}
@@ -202,15 +216,15 @@ func isDir(filename string) bool {
202216
return err == nil && fi.IsDir()
203217
}
204218

205-
func analyzeFile(fname string, stats []gocognit.Stat, trace bool) ([]gocognit.Stat, error) {
219+
func analyzeFile(fname string, stats []gocognit.Stat, includeDiagnostic bool) ([]gocognit.Stat, error) {
206220
fset := token.NewFileSet()
207221

208222
f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
209223
if err != nil {
210224
return nil, err
211225
}
212226

213-
return gocognit.ComplexityStats(f, fset, stats, trace), nil
227+
return gocognit.ComplexityStatsWithDiagnostic(f, fset, stats, includeDiagnostic), nil
214228
}
215229

216230
func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat, trace bool) ([]gocognit.Stat, error) {

gocognit.go

Lines changed: 85 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"go/ast"
66
"go/token"
7+
"strconv"
78

89
"golang.org/x/tools/go/analysis"
910
"golang.org/x/tools/go/analysis/passes/inspect"
@@ -12,64 +13,108 @@ import (
1213

1314
// Stat is statistic of the complexity.
1415
type Stat struct {
15-
PkgName string
16-
FuncName string
17-
Complexity int
18-
Traces []Trace `json:",omitempty"`
19-
Pos token.Position
16+
PkgName string
17+
FuncName string
18+
Complexity int
19+
Diagnostics []Diagnostic `json:",omitempty"`
20+
Pos token.Position
2021
}
2122

22-
type Trace struct {
23+
// Diagnostic contrains information how the complexity increase.
24+
type Diagnostic struct {
2325
Inc int
2426
Nesting int `json:",omitempty"`
2527
Text string
26-
Pos token.Position
28+
Pos DiagnosticPosition
2729
}
2830

29-
func (t Trace) String() string {
30-
if t.Nesting == 0 {
31-
return fmt.Sprintf("+%d", t.Inc)
31+
// DiagnosticPosition is the position of the diagnostic.
32+
type DiagnosticPosition struct {
33+
Offset int // offset, starting at 0
34+
Line int // line number, starting at 1
35+
Column int // column number, starting at 1 (byte count)
36+
}
37+
38+
func (pos DiagnosticPosition) isValid() bool {
39+
return pos.Line > 0
40+
}
41+
42+
func (pos DiagnosticPosition) String() string {
43+
var s string
44+
if pos.isValid() {
45+
if s != "" {
46+
s += ":"
47+
}
48+
49+
s += strconv.Itoa(pos.Line)
50+
if pos.Column != 0 {
51+
s += fmt.Sprintf(":%d", pos.Column)
52+
}
53+
}
54+
55+
if s == "" {
56+
s = "-"
3257
}
3358

34-
return fmt.Sprintf("+%d (nesting=%d)", t.Inc, t.Nesting)
59+
return s
60+
}
61+
62+
func (d Diagnostic) String() string {
63+
if d.Nesting == 0 {
64+
return fmt.Sprintf("+%d", d.Inc)
65+
}
66+
67+
return fmt.Sprintf("+%d (nesting=%d)", d.Inc, d.Nesting)
3568
}
3669

3770
func (s Stat) String() string {
3871
return fmt.Sprintf("%d %s %s %s", s.Complexity, s.PkgName, s.FuncName, s.Pos)
3972
}
4073

4174
// ComplexityStats builds the complexity statistics.
42-
func ComplexityStats(f *ast.File, fset *token.FileSet, stats []Stat, includeTrace bool) []Stat {
75+
func ComplexityStats(f *ast.File, fset *token.FileSet, stats []Stat) []Stat {
76+
return ComplexityStatsWithDiagnostic(f, fset, stats, false)
77+
}
78+
79+
// ComplexityStatsWithDiagnostic builds the complexity statistics with diagnostic.
80+
func ComplexityStatsWithDiagnostic(f *ast.File, fset *token.FileSet, stats []Stat, enableDiagnostic bool) []Stat {
4381
for _, decl := range f.Decls {
4482
if fn, ok := decl.(*ast.FuncDecl); ok {
4583
d := parseDirective(fn.Doc)
4684
if d.Ignore {
4785
continue
4886
}
4987

50-
res := ScanComplexity(fn, includeTrace)
88+
res := ScanComplexity(fn, enableDiagnostic)
5189

5290
stats = append(stats, Stat{
53-
PkgName: f.Name.Name,
54-
FuncName: funcName(fn),
55-
Complexity: res.Complexity,
56-
Traces: traces(fset, res.Traces),
57-
Pos: fset.Position(fn.Pos()),
91+
PkgName: f.Name.Name,
92+
FuncName: funcName(fn),
93+
Complexity: res.Complexity,
94+
Diagnostics: generateDiagnostics(fset, res.Diagnostics),
95+
Pos: fset.Position(fn.Pos()),
5896
})
5997
}
6098
}
6199

62100
return stats
63101
}
64102

65-
func traces(fset *token.FileSet, traces []trace) []Trace {
66-
tags := make([]Trace, 0, len(traces))
67-
for _, t := range traces {
68-
tags = append(tags, Trace{
69-
Inc: t.Inc,
70-
Nesting: t.Nesting,
71-
Text: t.Text,
72-
Pos: fset.Position(t.Pos),
103+
func generateDiagnostics(fset *token.FileSet, diags []diagnostic) []Diagnostic {
104+
tags := make([]Diagnostic, 0, len(diags))
105+
for _, diag := range diags {
106+
pos := fset.Position(diag.Pos)
107+
tracePos := DiagnosticPosition{
108+
Offset: pos.Offset,
109+
Line: pos.Line,
110+
Column: pos.Column,
111+
}
112+
113+
tags = append(tags, Diagnostic{
114+
Inc: diag.Inc,
115+
Nesting: diag.Nesting,
116+
Text: diag.Text,
117+
Pos: tracePos,
73118
})
74119
}
75120

@@ -116,26 +161,26 @@ func Complexity(fn *ast.FuncDecl) int {
116161
}
117162

118163
// ScanComplexity scans the function declaration.
119-
func ScanComplexity(fn *ast.FuncDecl, trace bool) ScanResult {
164+
func ScanComplexity(fn *ast.FuncDecl, includeDiagnostic bool) ScanResult {
120165
v := complexityVisitor{
121-
name: fn.Name,
122-
trace: trace,
166+
name: fn.Name,
167+
diagnosticEnabled: includeDiagnostic,
123168
}
124169

125170
ast.Walk(&v, fn)
126171

127172
return ScanResult{
128-
Traces: v.traces,
129-
Complexity: v.complexity,
173+
Diagnostics: v.diagnostics,
174+
Complexity: v.complexity,
130175
}
131176
}
132177

133178
type ScanResult struct {
134-
Traces []trace
135-
Complexity int
179+
Diagnostics []diagnostic
180+
Complexity int
136181
}
137182

138-
type trace struct {
183+
type diagnostic struct {
139184
Inc int
140185
Nesting int
141186
Text string
@@ -151,8 +196,8 @@ type complexityVisitor struct {
151196

152197
fset *token.FileSet
153198

154-
trace bool
155-
traces []trace
199+
diagnosticEnabled bool
200+
diagnostics []diagnostic
156201
}
157202

158203
func (v *complexityVisitor) incNesting() {
@@ -166,11 +211,11 @@ func (v *complexityVisitor) decNesting() {
166211
func (v *complexityVisitor) incComplexity(text string, pos token.Pos) {
167212
v.complexity++
168213

169-
if !v.trace {
214+
if !v.diagnosticEnabled {
170215
return
171216
}
172217

173-
v.traces = append(v.traces, trace{
218+
v.diagnostics = append(v.diagnostics, diagnostic{
174219
Inc: 1,
175220
Text: text,
176221
Pos: pos,
@@ -180,11 +225,11 @@ func (v *complexityVisitor) incComplexity(text string, pos token.Pos) {
180225
func (v *complexityVisitor) nestIncComplexity(text string, pos token.Pos) {
181226
v.complexity += (v.nesting + 1)
182227

183-
if !v.trace {
228+
if !v.diagnosticEnabled {
184229
return
185230
}
186231

187-
v.traces = append(v.traces, trace{
232+
v.diagnostics = append(v.diagnostics, diagnostic{
188233
Inc: v.nesting + 1,
189234
Nesting: v.nesting,
190235
Text: text,

0 commit comments

Comments
 (0)