147 lines
6.2 KiB
Go
147 lines
6.2 KiB
Go
// Copyright 2023 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
/*
|
|
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 matches the listed packages and any other
|
|
packages belonging to the same modules. 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.
|
|
|
|
The tool also does not report marker interface methods by default.
|
|
Marker interface methods are typically used to create compile-time constraints
|
|
to ensure that only specific types can implement a particular interface.
|
|
These methods have no other functionality (empty function body) and are never invoked.
|
|
Although marker interface methods are technically unreachable, removing them would break
|
|
the interface implementation. Hence, the tool excludes them from the report.
|
|
|
|
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
|
|
Marker bool // function is a marker interface method
|
|
}
|
|
|
|
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
|
|
}
|
|
*/
|
|
package main
|