Static analysis that reports per-function cyclomatic-style metrics using Tree-sitter.
Supported languages:
- Go (
.go) - JavaScript (
.js) - Rust (
.rs) - and more to come
A lot of agentic-created code results in large functions with business logic pushed inside a single function, which becomes god-function.
This is a tool to detect such functions and act accordingly.
For now only cyclomatic complexity metric is measured using Tree-sitter analysis, but more metrics may be added later.
Let's run the analysis on the examples:
go run main.go -sort complexity examples/example.jsWe'll receive output:
function | code place | decision points | exit points | cyclomatic complexity
loop | examples/example.js:15:1 | 3 | 1 | 4
main | examples/example.js:1:1 | 0 | 0 | 2
startProcessing | examples/example.js:7:1 | 1 | 2 | 1
The main metric here is cyclomatic complexity. It combines both decision points and exit points in a single metric.
decision point is any branching happening in the function:
if (cond) {} <- single decision point
if (cond1 && cond2) {} <- two decision points exit point is any return from the function:
if (cond) { <- single decision point
return val; <- single exit point
}And from these metrics cyclomatic complexity is calculated as:
cyclomaticComplexity = decisionPoints - exitPoints + 2Norms for the cyclomatic complexity from wikipedia:
- 1–10: Simple procedure, little risk
- 11–20: More complex, moderate risk
- 21–50: Complex, high risk
-
50: Untestable code, very high risk
This metric should be analyzed case by case, as some functions must be
complex and need to have a lot of branching, but it should signal that maybe
there are some problem with the code or a better approach must be searched.
Cyclomatic complexity is correlated with how hard it is to test and
support the created code, as the bigger complexity signalizes that more
branching is happening in the function.
To perform analysis tree-sitter is used, so the tool is language-independent.
To add a new language Lang:
- check if a definition for the
Langexist in the tree-sitter org repo - install bindings for the
Lang, e.g. forrust:
go get github.com/tree-sitter/tree-sitter-rust/bindings/go- create new
Langpackage in language - implement the
SourceFileProcessorin the created package and export it - test the implementation
- register new lang in the main.go, process may be updated later (or not lol)
make test # CGO-enabled; requires a C toolchain (e.g. Xcode CLI tools on macOS, build-essential on Linux)
make build # writes bin/despagettifierUse a version tag of this repository:
- uses: <owner>/despagettifier@v1
with:
paths: "*.go"
sort: complexityInputs
| Input | Required | Description |
|---|---|---|
paths |
yes | Space- or newline-separated files or glob patterns (see tool help for glob limits). |
sort |
no | complexity, line, name, or empty for default ordering. |
The job must checkout the code you want to analyze; the container runs with the workspace mounted at the job’s working directory.