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