codemetrics

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 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.

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.

Roadmap

The API is shaped for multi-language support: Parse(language, src) and the nil-able Cognitive field exist so additional languages can be added without a breaking change. A tree-sitter backend covering Python / JavaScript / TypeScript / Java / Rust / C / C++ / C# / Kotlin / PHP / Ruby / Scala and more is planned.

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

Jump to

Keyboard shortcuts

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