codemetrics

package module
v0.4.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 6 Imported by: 0

README

go-codemetrics

Go Reference

Per-function cyclomatic and cognitive complexity for source code, as a small Go library and CLI.

Most Go tools give you one metric or the other: fzipp/gocyclo does cyclomatic, uudashr/gocognit does cognitive. go-codemetrics computes both in one pass behind a single API, and is structured so more languages can be added behind Parse without breaking callers.

The Go analyzer is built on the standard library's go/astzero external dependencies.

The two metrics

  • Cyclomatic complexity (McCabe) counts decision points flatly: 1 + one per branch (if / for / range / case / comm-clause / && / ||). It measures the number of independent paths through a function.
  • Cognitive complexity (SonarSource) weights nested control flow more heavily, so deeply-nested logic scores higher than a flat sequence with the same number of branches. It tracks how hard code is to understand rather than how many paths it has. The implementation follows the SonarSource specification; results match uudashr/gocognit.

Install

go get github.com/richardwooding/go-codemetrics

CLI:

go install github.com/richardwooding/go-codemetrics/cmd/codemetrics@latest

Library usage

package main

import (
	"fmt"

	codemetrics "github.com/richardwooding/go-codemetrics"
)

func main() {
	src := []byte(`package p
func classify(n int) string {
	if n < 0 {
		return "neg"
	} else if n == 0 {
		return "zero"
	}
	return "pos"
}`)

	fns, err := codemetrics.ParseGo(src)
	if err != nil {
		panic(err)
	}
	for _, f := range fns {
		fmt.Printf("%-12s cyclomatic=%d cognitive=%d lines=%d\n",
			f.QualifiedName(), f.Cyclomatic, *f.Cognitive, f.Lines())
	}
	// classify     cyclomatic=3 cognitive=2 lines=8
}

Parse(language, src) dispatches by language identifier ("go" / "golang" today) and returns a wrapped ErrUnsupportedLanguage for anything else. SupportedLanguages() lists what's available.

type FunctionMetrics struct {
	Name       string // bare name, e.g. "Write"
	Receiver   string // receiver type for methods, e.g. "*Buffer"; "" otherwise
	Cyclomatic int
	Cognitive  *int   // nil if unavailable for the language; always set for Go
	StartLine  int    // 1-based, inclusive
	EndLine    int
}

Cognitive is a pointer so a language without cognitive support is distinguishable (nil) from a genuine zero. For Go it is always populated.

Already have an AST?

If you've already parsed Go source with go/parser, compute either metric for a single declaration without re-parsing:

func Cyclomatic(body *ast.BlockStmt) int // 1 + branch points
func Cognitive(fn *ast.FuncDecl) int     // SonarSource cognitive complexity

Both are nil-safe (return 0). This mirrors the AST-level entry points of gocyclo and gocognit.

Parsing is best-effort: input that still yields a partial syntax tree is tolerated and metrics are computed for every recovered function; only a total parse failure returns an error.

CLI usage

# Top 10 functions by cognitive complexity across a tree
codemetrics -top 10 ./...            # (pass directories or files)
codemetrics -top 10 internal/

# Sort by cyclomatic instead, only show the gnarly ones
codemetrics -sort cyclomatic -min 15 .

# JSON for tooling / CI
codemetrics -json ./mypkg | jq '.[] | select(.cognitive > 20)'

# Read from stdin
cat foo.go | codemetrics
COGNITIVE  CYCLOMATIC  LINES  FUNCTION            LOCATION
83         35          148    BuildCodeGraph      codegraph.go:172
74         39          175    UnusedExports       unused_exports.go:179
...

Directories are walked recursively for .go files, skipping vendor, testdata, and dot-directories.

Flags: -sort cognitive|cyclomatic, -top N, -min N, -json.

Other languages (tree-sitter)

The subpackage github.com/richardwooding/go-codemetrics/treesitter computes the same metrics for 16 more languages using the pure-Go tree-sitter runtime gotreesitter:

Python · JavaScript · TypeScript · Java · Rust · C · C++ · C# · Kotlin · PHP · Ruby · Scala · R · MATLAB · Perl · Swift

import "github.com/richardwooding/go-codemetrics/treesitter"

fns, err := treesitter.Parse("python", src) // -> []codemetrics.FunctionMetrics

It returns the same FunctionMetrics type, so the two backends are interchangeable. Cognitive complexity is computed for every language except Swift (whose grammar lacks a stable cognitive spec) — there Cognitive is nil while Cyclomatic is still reported.

Already parsed the tree? If you've parsed the source with gotreesitter yourself (e.g. while extracting symbols), compute metrics over that existing tree — no second parse:

func MetricsFromTree(language string, tree *ts.Tree, lang *ts.Language, spans []Span) []codemetrics.FunctionMetrics

This is how treesitter-symbols returns symbols and complexity from a single parse.

Dependencies stay opt-in. The module root is go/ast-only and pulls in nothing; gotreesitter and its embedded grammars are compiled in only when you import the treesitter subpackage. A plain build of that subpackage embeds every bundled grammar (~22 MB); to embed only the languages you use, build with the gotreesitter subset tags:

go build -tags 'grammar_subset grammar_subset_python grammar_subset_rust' ./...

License

MIT — see LICENSE.

Documentation

Overview

Package codemetrics computes per-function complexity metrics from source code: McCabe cyclomatic complexity and SonarSource cognitive complexity.

Cyclomatic complexity counts decision points flatly (1 + one per branch: if / for / range / case / comm-clause / && / ||). Cognitive complexity weights *nested* control flow more heavily, so deeply-nested logic scores higher than a flat sequence of the same number of branches — it tracks how hard code is to understand rather than how many paths it has.

The Go analyzer is built on the standard library's go/ast and has no external dependencies. The cognitive-complexity algorithm follows the SonarSource specification (reference implementation: github.com/uudashr/gocognit).

The public API is shaped so additional languages can be added behind Parse without breaking callers; today only Go is supported and FunctionMetrics.Cognitive is always populated. For languages that do not (yet) support cognitive complexity, Cognitive is nil — distinct from a genuine zero.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnsupportedLanguage = errors.New("codemetrics: unsupported language")

ErrUnsupportedLanguage is returned by Parse for a language with no analyzer. Test for it with errors.Is.

Functions

func Cognitive added in v0.2.0

func Cognitive(fn *ast.FuncDecl) int

Cognitive returns the SonarSource cognitive complexity of a Go function declaration. Use this when you already hold a parsed AST; ParseGo derives the same value from source. A nil declaration (or one without a name) returns 0.

See the package documentation for the increment rules.

func Cyclomatic added in v0.2.0

func Cyclomatic(body *ast.BlockStmt) int

Cyclomatic returns the McCabe cyclomatic complexity of a function body — 1 + one per branch point (if / for / range / case / comm-clause / && / ||).

Use this when you already hold a parsed AST (e.g. from your own go/parser pass); ParseGo derives the same value from source. A nil body returns 0.

func SupportedLanguages

func SupportedLanguages() []string

SupportedLanguages returns the language identifiers accepted by Parse.

Types

type FunctionMetrics

type FunctionMetrics struct {
	// Name is the bare function or method name (e.g. "Write"), without the
	// receiver. For methods, Receiver carries the receiver type.
	Name string
	// Receiver is the receiver type of a method (e.g. "*Buffer" or "Buffer"),
	// or "" for a plain function.
	Receiver string
	// Cyclomatic is the McCabe cyclomatic complexity (1 + branch points).
	Cyclomatic int
	// Cognitive is the SonarSource cognitive complexity, or nil when the
	// language's analyzer does not compute it. For Go it is always set.
	Cognitive *int
	// StartLine and EndLine are the 1-based inclusive line span of the
	// function declaration.
	StartLine int
	EndLine   int
}

FunctionMetrics holds the complexity metrics for a single function or method, together with its 1-based inclusive line span.

func Parse

func Parse(language string, src []byte) ([]FunctionMetrics, error)

Parse computes complexity metrics for every function in src, dispatching on language. Recognised identifiers: "go" (alias "golang"). Unknown languages return a wrapped ErrUnsupportedLanguage.

Parsing is best-effort: input that still yields a partial syntax tree is tolerated and metrics are computed for every recovered function. A hard parse failure returns a non-nil error and no metrics.

func ParseGo

func ParseGo(src []byte) ([]FunctionMetrics, error)

ParseGo computes cyclomatic and cognitive complexity for every function and method declaration with a body in a Go source file. Functions without a body (e.g. external or interface declarations) are skipped.

Parsing is best-effort: if the parser recovers a partial syntax tree from malformed input, metrics are still computed for every function it found and the returned error is nil. A total parse failure (no tree) returns the parse error and no metrics.

func (FunctionMetrics) Lines

func (m FunctionMetrics) Lines() int

Lines returns the number of source lines the function spans (inclusive).

func (FunctionMetrics) QualifiedName

func (m FunctionMetrics) QualifiedName() string

QualifiedName returns the receiver-qualified name for a method (e.g. "(*Buffer).Write") or the bare name for a plain function.

Directories

Path Synopsis
cmd
codemetrics command
Command codemetrics reports per-function cyclomatic and cognitive complexity for Go source files.
Command codemetrics reports per-function cyclomatic and cognitive complexity for Go source files.
Package treesitter computes cyclomatic and cognitive complexity for the non-Go languages that go-codemetrics supports, using the pure-Go tree-sitter runtime github.com/odvcencio/gotreesitter and its bundled grammars.
Package treesitter computes cyclomatic and cognitive complexity for the non-Go languages that go-codemetrics supports, using the pure-Go tree-sitter runtime github.com/odvcencio/gotreesitter and its bundled grammars.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL