Skip to content

Pafaul/despagettifier

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

despagettifier

Static analysis that reports per-function cyclomatic-style metrics using Tree-sitter.

Supported languages:

  • Go (.go)
  • JavaScript (.js)
  • Rust (.rs)
  • and more to come

What is it for?

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.

How to read the output

Let's run the analysis on the examples:

go run main.go -sort complexity examples/example.js

We'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 + 2

Norms 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.

Why is this needed?

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.

Adding new languages

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 Lang exist in the tree-sitter org repo
  • install bindings for the Lang, e.g. for rust:
go get github.com/tree-sitter/tree-sitter-rust/bindings/go
  • create new Lang package in language
  • implement the SourceFileProcessor in the created package and export it
  • test the implementation
  • register new lang in the main.go, process may be updated later (or not lol)

Build and test (local)

make test    # CGO-enabled; requires a C toolchain (e.g. Xcode CLI tools on macOS, build-essential on Linux)
make build   # writes bin/despagettifier

GitHub Action

Use a version tag of this repository:

- uses: <owner>/despagettifier@v1
  with:
    paths: "*.go"
    sort: complexity

Inputs

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.

About

Analyze the code for the spagetti metrics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors