deadcode

command
v0.20.0 Latest Latest
Warning

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

Go to latest
Published: Apr 4, 2024 License: BSD-3-Clause Imports: 25 Imported by: 1

Documentation

Overview

The deadcode command reports unreachable functions in Go programs.

Usage: deadcode [flags] package...

The deadcode command loads a Go program from source then uses Rapid Type Analysis (RTA) to build a call graph of all the functions reachable from the program's main function. Any functions that are not reachable are reported as dead code, grouped by package.

Packages are expressed in the notation of 'go list' (or other underlying build system if you are using an alternative golang.org/x/go/packages driver). Only executable (main) packages are considered starting points for the analysis.

The -test flag causes it to analyze test executables too. Tests sometimes make use of functions that would otherwise appear to be dead code, and public API functions reported as dead with -test indicate possible gaps in your test coverage. Bear in mind that an Example test function without an "Output:" comment is merely documentation: it is dead code, and does not contribute coverage.

The -filter flag restricts results to packages that match the provided regular expression; its default value is the module name of the first package. Use -filter= to display all results.

Example: show all dead code within the gopls module:

$ deadcode -test golang.org/x/tools/gopls/...

The analysis can soundly analyze dynamic calls though func values, interface methods, and reflection. However, it does not currently understand the aliasing created by //go:linkname directives, so it will fail to recognize that calls to a linkname-annotated function with no body in fact dispatch to the function named in the annotation. This may result in the latter function being spuriously reported as dead.

By default, the tool does not report dead functions in generated files, as determined by the special comment described in https://go.dev/s/generatedcode. Use the -generated flag to include them.

In any case, just because a function is reported as dead does not mean it is unconditionally safe to delete it. For example, a dead function may be referenced by another dead function, and a dead method may be required to satisfy an interface that is never called. Some judgement is required.

The analysis is valid only for a single GOOS/GOARCH/-tags configuration, so a function reported as dead may be live in a different configuration. Consider running the tool once for each configuration of interest. Consider using a line-oriented output format (see below) to make it easier to compute the intersection of results across all runs.

Output

The command supports three output formats.

With no flags, the command prints the name and location of each dead function in the form of a typical compiler diagnostic, for example:

$ deadcode -f='{{range .Funcs}}{{println .Position}}{{end}}' -test ./gopls/...
gopls/internal/protocol/command.go:1206:6: unreachable func: openClientEditor
gopls/internal/template/parse.go:414:18: unreachable func: Parsed.WriteNode
gopls/internal/template/parse.go:419:18: unreachable func: wrNode.writeNode

With the -json flag, the command prints an array of Package objects, as defined by the JSON schema (see below).

With the -f=template flag, the command executes the specified template on each Package record. So, this template shows dead functions grouped by package:

$ deadcode -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test ./gopls/...
golang.org/x/tools/gopls/internal/lsp
	openClientEditor

golang.org/x/tools/gopls/internal/template
	Parsed.WriteNode
	wrNode.writeNode

Why is a function not dead?

The -whylive=function flag explain why the named function is not dead by showing an arbitrary shortest path to it from one of the main functions. (To enumerate the functions in a program, or for more sophisticated call graph queries, use golang.org/x/tools/cmd/callgraph.)

Fully static call paths are preferred over paths involving dynamic edges, even if longer. Paths starting from a non-test package are preferred over those from tests. Paths from main functions are preferred over paths from init functions.

The result is a list of Edge objects (see JSON schema below). Again, the -json and -f=template flags may be used to control the formatting of the list of Edge objects. The default format shows, for each edge in the path, whether the call is static or dynamic, and its source line number. For example:

$ deadcode -whylive=bytes.Buffer.String -test ./cmd/deadcode/...
                 golang.org/x/tools/cmd/deadcode.main
static@L0117 --> golang.org/x/tools/go/packages.Load
static@L0262 --> golang.org/x/tools/go/packages.defaultDriver
static@L0305 --> golang.org/x/tools/go/packages.goListDriver
static@L0153 --> golang.org/x/tools/go/packages.goListDriver$1
static@L0154 --> golang.org/x/tools/go/internal/packagesdriver.GetSizesForArgsGolist
static@L0044 --> bytes.Buffer.String

JSON schema

type Package struct {
	Name  string       // declared name
	Path  string       // full import path
	Funcs []Function   // list of dead functions within it
}

type Function struct {
	Name      string   // name (sans package qualifier)
	Position  Position // file/line/column of function declaration
	Generated bool     // function is declared in a generated .go file
}

type Edge struct {
	Initial  string    // initial entrypoint (main or init); first edge only
	Kind     string    // = static | dynamic
	Position Position  // file/line/column of call site
	Callee   string    // target of the call
}

type Position struct {
	File      string   // name of file
	Line, Col int      // line and byte index, both 1-based
}

Jump to

Keyboard shortcuts

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