deadset-go

module
v1.4.0 Latest Latest
Warning

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

Go to latest
Published: Sep 18, 2026 License: GPL-3.0

README

deadset-go

Go Reference Go version Mutation

Deterministic whole-program dead-code analysis for a Go module and the consumers it declares.

⚠️ Pre-release software

deadset-go implements version 0.1.0 of the deadset contract and has not shipped its first analysis. Today the binary answers version and refuses a --fix flag; the other verbs below are the contract's and arrive with the analysis. The report shape, the exit codes and the configuration keys can change until 1.0.

What it does

deadset-go loads a Go module with its tests, type-checks it and reports the declarations nothing uses: a function nobody calls, a struct field nothing reads, an exported symbol no consumer imports, an interface no value is converted to. Every verdict comes from type information and the reference graph, never from a text search, so two runs over one tree produce one report.

Three properties separate it from a per-package linter:

  • Consumers are part of the program. A library's exported API is dead only when no declared consumer uses it. Name the repositories that import the module and the analysis loads them into one whole-program graph, so an export a downstream repository calls is never reported.
  • Exemptions are computed, not configured. A method that satisfies an interface a value of its type is converted to, a main, an init, a test function, a symbol a go:linkname directive names: each is held live by a documented exemption class, and print-retained lists every symbol an exemption held back and the class that held it.
  • A cascade is one finding. When a dead function is the only caller of three more, the report names the root and counts what falls with it, so the deletion total is known before the edit.

It also reports over-visibility: an exported symbol only its own package uses, which can be unexported without a behavior change.

The output is a report, never an edit: a JSON document, one path:line:col text line per finding, or SARIF 2.1.0 for code-scanning upload. Every verb refuses a --fix flag with exit 2.

Quick start

go install github.com/cplieger/deadset-go/cmd/deadset-go@latest
deadset-go version

Installing needs Go 1.27 or later, and running needs the go toolchain on PATH, which loads the packages. deadset-go runs no language server and builds with cgo disabled, so a host with no C toolchain runs it.

Commands

Verb What it does
analyze Loads the module and its declared consumers, writes the report, exits with the verdict
explain Says why a symbol is dead, why it is live, or why it was not reported
print-config Prints the resolved configuration with the source of every setting
print-roots Prints the resolved root set
print-retained Prints every symbol an exemption held back, with the classes that held it
describe Prints the analyzer's name, version, contract version and conformance record as JSON
version Prints the analyzer version and the contract version

Exit codes follow the contract: 0 clean, 1 findings, 2 a usage error or a requested source edit, 3 a load or type-check failure, 4 a finding whose cross-language reference is unresolved.

How it relates to deadcode

golang.org/x/tools/cmd/deadcode answers a different question, and running both is worth it. deadcode builds a call graph from each main with rapid type analysis, which resolves calls through interfaces by tracking the types that reach run time, and reports the functions no call path reaches. deadset-go covers the symbol kinds a call graph has no node for (types, fields, constants, variables, interface methods, type parameters), treats a library's declared consumers as part of the program, and records on each finding which relation produced it: a reference count of zero, or unreachability from the roots. Run deadcode for the functions and deadset-go for everything else.

Security

deadset-go reads source and writes a report. It edits no file, and every verb refuses a --fix flag, or any flag whose name contains fix, with exit 2. It spawns no analysis process other than the Go toolchain's own go list, which resolves the module's dependencies as go build does, and it opens no network connection of its own: a module download happens only where go list would download, so a run against a populated module cache stays offline.

  • deadset-spec: the contract and the conformance corpus deadset-go implements and passes before each release.
  • deadset-ts: the same analysis for TypeScript and JavaScript. Neither analyzer can analyze the other's language: the TypeScript 7 compiler module github.com/microsoft/TypeScript/tsc keeps every analysis package under internal/, its programmatic surface is a TypeScript client over a private protocol, and no port of Go's type checker to JavaScript exists.
  • deadset: runs both analyzers over a repository holding both languages, resolves the references that cross the language boundary and merges the reports into one.

Dependencies

deadset-go allows itself two dependencies: the Go standard library and golang.org/x/tools, which supplies the package loader and the type information no reimplementation can. go version -m on a released binary lists nothing else. Renovate keeps the pins in go.mod current.

Contributing

Issues and pull requests are welcome. The general guidelines live in cplieger/.github.

Disclaimer

This project is built with care and follows security best practices, but it is intended for personal / self-hosted use. No guarantees of fitness for production environments. Use at your own risk.

This project was built with AI-assisted tooling using Claude, GPT, and Kiro. The human maintainer defines architecture, supervises implementation, and makes all final decisions.

License

GPL-3.0-or-later. See LICENSE.

Directories

Path Synopsis
cmd
deadset-go command
Command deadset-go reports unused symbols in a Go module and its declared consumers.
Command deadset-go reports unused symbols in a Go module and its declared consumers.
internal
config
Package config resolves the deadset configuration.
Package config resolves the deadset configuration.
exempt
Package exempt computes the exemptions of one loaded build configuration: the classes of reason for which a symbol the reference graph alone would report is live all the same.
Package exempt computes the exemptions of one loaded build configuration: the classes of reason for which a symbol the reference graph alone would report is live all the same.
graph
Package graph builds the graph of one loaded build configuration: the symbols a target declares, the references between them, and what the roots keep alive.
Package graph builds the graph of one loaded build configuration: the symbols a target declares, the references between them, and what the roots keep alive.
load
Package load resolves one build configuration of a target into type-checked packages, test variants included.
Package load resolves one build configuration of a target into type-checked packages, test variants included.
scope
Package scope reads the scope document: the module a run analyzes and the consumers whose references count against it.
Package scope reads the scope document: the module a run analyzes and the consumers whose references count against it.

Jump to

Keyboard shortcuts

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