Skip to content

Commit

Permalink
Merge pull request #39 from uudashr/tracing
Browse files Browse the repository at this point in the history
Add diagnostic capability
  • Loading branch information
uudashr authored Dec 7, 2024
2 parents 8b1a11f + 1f9335e commit 2881c26
Show file tree
Hide file tree
Showing 4 changed files with 325 additions and 71 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
name: Checks

on:
[pull_request]
push:
branches:
- master
pull_request:
branches:
- master

jobs:
test:
Expand All @@ -11,7 +17,7 @@ jobs:
matrix:
os: [ubuntu-latest]
os-version: ['stable']
go-version: ['1.19', '1.20', '1.21', '1.22']
go-version: ['1.19', '1.20', '1.21', '1.22', '1.23']

steps:
- name: Checkout
Expand Down
111 changes: 99 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,17 @@ Usage:
Flags:
-over N show functions with complexity > N only
and return exit code 1 if the output is non-empty
-top N show the top N most complex functions only
-avg show the average complexity over all functions,
not depending on whether -over or -top are set
-json encode the output as JSON
-f format string the format to use
(default "{{.PkgName}}.{{.FuncName}}:{{.Complexity}}:{{.Pos}}")
-over N show functions with complexity > N only
and return exit code 1 if the output is non-empty
-top N show the top N most complex functions only
-avg show the average complexity over all functions,
not depending on whether -over or -top are set
-test indicates whether test files should be included
-json encode the output as JSON
-d enable diagnostic output
-f format string the format to use
(default "{{.Complexity}} {{.PkgName}} {{.FuncName}} {{.Pos}}")
-ignore expr ignore files matching the given regexp
The (default) output fields for each line are:
Expand All @@ -191,10 +194,24 @@ or equal to <complexity> <package> <function> <file:row:column>
The struct being passed to the template is:
type Stat struct {
PkgName string
FuncName string
Complexity int
Pos token.Position
PkgName string
FuncName string
Complexity int
Pos token.Position
Diagnostics []Diagnostics
}
type Diagnostic struct {
Inc string
Nesting int
Text string
Pos DiagnosticPosition
}
type DiagnosticPosition struct {
Offset int
Line int
Column int
}
```

Expand Down Expand Up @@ -223,6 +240,76 @@ func IgnoreMe() {
}
```

## Diagnostic
To understand how the complexity are calculated, we can enable the diagnostic by using `-d` flag.

Example:
```shell
$ gocognit -json -d .
```

It will show the diagnostic output in JSON format
<details>

<summary>JSON Output</summary>

```json
[
{
"PkgName": "prime",
"FuncName": "SumOfPrimes",
"Complexity": 7,
"Pos": {
"Filename": "prime.go",
"Offset": 15,
"Line": 3,
"Column": 1
},
"Diagnostics": [
{
"Inc": 1,
"Text": "for",
"Pos": {
"Offset": 69,
"Line": 7,
"Column": 2
}
},
{
"Inc": 2,
"Nesting": 1,
"Text": "for",
"Pos": {
"Offset": 104,
"Line": 8,
"Column": 3
}
},
{
"Inc": 3,
"Nesting": 2,
"Text": "if",
"Pos": {
"Offset": 152,
"Line": 9,
"Column": 4
}
},
{
"Inc": 1,
"Text": "continue",
"Pos": {
"Offset": 190,
"Line": 10,
"Column": 5
}
}
]
}
]
```
</details>

## Related project
- [Gocyclo](https://github.com/fzipp/gocyclo) where the code are based on.
- [Cognitive Complexity: A new way of measuring understandability](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) white paper by G. Ann Campbell.
96 changes: 65 additions & 31 deletions cmd/gocognit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
// -over N show functions with complexity > N only and return exit code 1 if the output is non-empty
// -top N show the top N most complex functions only
// -avg show the average complexity over all functions, not depending on whether -over or -top are set
// -test indicates whether test files should be included
// -json encode the output as JSON
// -f format string the format to use (default "{{.PkgName}}.{{.FuncName}}:{{.Complexity}}:{{.Pos}}")
// -d enable diagnostic output
// -f format string the format to use (default "{{.Complexity}} {{.PkgName}} {{.FuncName}} {{.Pos}}")
//
// The (default) output fields for each line are:
//
Expand All @@ -29,8 +31,22 @@
// PkgName string
// FuncName string
// Complexity int
// Diagnostics []Diagnostic
// Pos token.Position
// }
//
// type Diagnostic struct {
// Inc string
// Nesting int
// Text string
// Pos DiagnosticPosition
// }
//
// type DiagnosticPosition struct {
// Offset int
// Line int
// Column int
// }
package main

import (
Expand Down Expand Up @@ -59,15 +75,17 @@ Usage:
Flags:
-over N show functions with complexity > N only
and return exit code 1 if the output is non-empty
-top N show the top N most complex functions only
-avg show the average complexity over all functions,
not depending on whether -over or -top are set
-test indicates whether test files should be included
-json encode the output as JSON
-f format string the format to use
(default "{{.PkgName}}.{{.FuncName}}:{{.Complexity}}:{{.Pos}}")
-over N show functions with complexity > N only
and return exit code 1 if the output is non-empty
-top N show the top N most complex functions only
-avg show the average complexity over all functions,
not depending on whether -over or -top are set
-test indicates whether test files should be included
-json encode the output as JSON
-d enable diagnostic output
-f format string the format to use
(default "{{.Complexity}} {{.PkgName}} {{.FuncName}} {{.Pos}}")
-ignore expr ignore files matching the given regexp
The (default) output fields for each line are:
Expand All @@ -82,10 +100,24 @@ or equal to <complexity> <package> <function> <file:row:column>
The struct being passed to the template is:
type Stat struct {
PkgName string
FuncName string
Complexity int
Pos token.Position
PkgName string
FuncName string
Complexity int
Pos token.Position
Diagnostics []Diagnostics
}
type Diagnostic struct {
Inc string
Nesting int
Text string
Pos DiagnosticPosition
}
type DiagnosticPosition struct {
Offset int
Line int
Column int
}
`

Expand All @@ -103,13 +135,14 @@ func usage() {

func main() {
var (
over int
top int
avg bool
includeTests bool
format string
jsonEncode bool
ignoreExpr string
over int
top int
avg bool
includeTests bool
format string
jsonEncode bool
enableDiagnostics bool
ignoreExpr string
)

flag.IntVar(&over, "over", defaultOverFlagVal, "show functions with complexity > N only")
Expand All @@ -118,6 +151,7 @@ func main() {
flag.BoolVar(&includeTests, "test", true, "indicates whether test files should be included")
flag.StringVar(&format, "f", defaultFormat, "the format to use")
flag.BoolVar(&jsonEncode, "json", false, "encode the output as JSON")
flag.BoolVar(&enableDiagnostics, "d", false, "enable diagnostic output")
flag.StringVar(&ignoreExpr, "ignore", "", "ignore files matching the given regexp")

log.SetFlags(0)
Expand All @@ -136,7 +170,7 @@ func main() {
log.Fatal(err)
}

stats, err := analyze(args, includeTests)
stats, err := analyze(args, includeTests, enableDiagnostics)
if err != nil {
log.Fatal(err)
}
Expand Down Expand Up @@ -170,19 +204,19 @@ func main() {
}
}

func analyzePath(path string, includeTests bool) ([]gocognit.Stat, error) {
func analyzePath(path string, includeTests bool, includeDiagnostic bool) ([]gocognit.Stat, error) {
if isDir(path) {
return analyzeDir(path, includeTests, nil)
return analyzeDir(path, includeTests, nil, includeDiagnostic)
}

return analyzeFile(path, nil)
return analyzeFile(path, nil, includeDiagnostic)
}

func analyze(paths []string, includeTests bool) (stats []gocognit.Stat, err error) {
func analyze(paths []string, includeTests bool, includeDiagnostic bool) (stats []gocognit.Stat, err error) {
var out []gocognit.Stat

for _, path := range paths {
stats, err := analyzePath(path, includeTests)
stats, err := analyzePath(path, includeTests, includeDiagnostic)
if err != nil {
return nil, err
}
Expand All @@ -199,18 +233,18 @@ func isDir(filename string) bool {
return err == nil && fi.IsDir()
}

func analyzeFile(fname string, stats []gocognit.Stat) ([]gocognit.Stat, error) {
func analyzeFile(fname string, stats []gocognit.Stat, includeDiagnostic bool) ([]gocognit.Stat, error) {
fset := token.NewFileSet()

f, err := parser.ParseFile(fset, fname, nil, parser.ParseComments)
if err != nil {
return nil, err
}

return gocognit.ComplexityStats(f, fset, stats), nil
return gocognit.ComplexityStatsWithDiagnostic(f, fset, stats, includeDiagnostic), nil
}

func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat) ([]gocognit.Stat, error) {
func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat, trace bool) ([]gocognit.Stat, error) {
err := filepath.Walk(dirname, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
Expand All @@ -228,7 +262,7 @@ func analyzeDir(dirname string, includeTests bool, stats []gocognit.Stat) ([]goc
return nil
}

stats, err = analyzeFile(path, stats)
stats, err = analyzeFile(path, stats, trace)
if err != nil {
return err
}
Expand Down
Loading

0 comments on commit 2881c26

Please sign in to comment.