Concepts¶
go-crap computes the CRAP score by combining two independent analyses and merging them:
Pipeline Overview¶
go-crap scan
│
├── scan.Scan() — orchestrator (internal/scan)
│ ├── build exclude regex from --exclude flags
│ ├── coverage.Scan() — discover Go modules, run go test -cover
│ │ │
│ │ └── coverage data per (file, func)
│ │
│ ├── complexity.Analyze() — walk AST, compute cyclomatic complexity
│ │ │
│ │ └── complexity data per (file, func)
│ │
│ ├── merge.Merge() — join by (filepath, funcname)
│ │ │
│ │ └── merged entries with CC + Coverage
│ │
│ ├── score.Score() — apply CRAP formula + missing policy
│ │ │
│ │ └── CRAP entries
│ │
│ └── applyFilters() — sort descending, apply --min and --top
│
└── report.Format() — table / json / github / sarif / pr-comment
|
└── output
The Modules¶
Each module is independently testable:
| Module | Purpose |
|---|---|
internal/scan |
Orchestrates the pipeline: coverage, complexity, merge, score, filters |
internal/complexity |
AST walking to compute cyclomatic complexity (adapted from gocyclo, BSD-3-Clause) |
internal/coverage |
Module discovery + go test -cover profiling (adapted from test-finder, MIT) |
internal/merge |
Join coverage and complexity by (filepath, funcname) using a double index |
internal/score |
CRAP formula + missing coverage policy |
internal/report |
Output formatters (table, JSON, GitHub, SARIF, PR comment) |
pkg/logger |
Logger interface and configuration types |
pkg/slogger |
slog-backed Logger implementation |
Important: Path Matching¶
Coverage and complexity produce paths in different formats. The merge layer uses a double index to match them — never canonicalize relative paths against CWD. See internal/merge/index.go and the test TestMerge_RelativePathsNotResolvedAgainstCWD.
Function Name Matching¶
Coverage and complexity also produce function names in different formats. Coverage includes the receiver in the name (e.g. Level.String, (*JSONFormatter).Format), while complexity stores the receiver separately. The merge layer normalizes both sides to bare method names before matching, supporting pointer receivers (*Type), value receivers (Type), and plain functions.