diff --git a/internal/cmd/deadcode/deadcode.go b/internal/cmd/deadcode/deadcode.go index ecb2a055254..da1c2049538 100644 --- a/internal/cmd/deadcode/deadcode.go +++ b/internal/cmd/deadcode/deadcode.go @@ -18,6 +18,7 @@ import ( "io" "log" "os" + "path/filepath" "regexp" "runtime" "runtime/pprof" @@ -336,14 +337,15 @@ func main() { } if len(functions) > 0 { packages = append(packages, jsonPackage{ + Name: fns[0].Pkg.Pkg.Name(), Path: pkgpath, Funcs: functions, }) } } - // Default format: functions grouped by package. - format := `{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}` + // Default line-oriented format: "a/b/c.go:1:2: unreachable func: T.f" + format := `{{range .Funcs}}{{printf "%s: unreachable func: %s\n" .Position .Name}}{{end}}` if *formatFlag != "" { format = *formatFlag } @@ -553,8 +555,16 @@ func isStaticCall(edge *callgraph.Edge) bool { return edge.Site != nil && edge.Site.Common().StaticCallee() != nil } +var cwd, _ = os.Getwd() + func toJSONPosition(posn token.Position) jsonPosition { - return jsonPosition{posn.Filename, posn.Line, posn.Column} + // Use cwd-relative filename if possible. + filename := posn.Filename + if rel, err := filepath.Rel(cwd, filename); err == nil && !strings.HasPrefix(rel, "..") { + filename = rel + } + + return jsonPosition{filename, posn.Line, posn.Column} } func cond[T any](cond bool, t, f T) T { @@ -578,8 +588,9 @@ type jsonFunction struct { func (f jsonFunction) String() string { return f.Name } type jsonPackage struct { - Path string - Funcs []jsonFunction + Name string // declared name + Path string // full import path + Funcs []jsonFunction // non-empty list of package's dead functions } func (p jsonPackage) String() string { return p.Path } diff --git a/internal/cmd/deadcode/doc.go b/internal/cmd/deadcode/doc.go index 098b2391346..44d47ad198a 100644 --- a/internal/cmd/deadcode/doc.go +++ b/internal/cmd/deadcode/doc.go @@ -59,20 +59,28 @@ easier to compute the intersection of results across all runs. The command supports three output formats. -With no flags, the command prints dead functions grouped by package. +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/lsp/command.go:1206:6: unreachable func: openClientEditor + gopls/internal/lsp/template/parse.go:414:18: unreachable func: Parsed.WriteNode + gopls/internal/lsp/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 produces a result similar to the -default format: - - -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' +on each Package record. So, this template shows dead functions grouped +by package: -And this template shows only the list of source positions of dead functions: + $ deadcode -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test ./gopls/... + golang.org/x/tools/gopls/internal/lsp + openClientEditor - -f='{{range .Funcs}}{{println .Position}}{{end}}' + golang.org/x/tools/gopls/internal/lsp/template + Parsed.WriteNode + wrNode.writeNode # Why is a function not dead? @@ -104,7 +112,8 @@ is static or dynamic, and its source line number. For example: # JSON schema type Package struct { - Path string // import path of package + Name string // declared name + Path string // full import path Funcs []Function // list of dead functions within it } diff --git a/internal/cmd/deadcode/testdata/generated.txtar b/internal/cmd/deadcode/testdata/generated.txtar index 4a50a6eb543..a2a29497cbe 100644 --- a/internal/cmd/deadcode/testdata/generated.txtar +++ b/internal/cmd/deadcode/testdata/generated.txtar @@ -1,14 +1,14 @@ # Test of -generated flag output. - deadcode example.com -!want "main" - want "Dead1" -!want "Dead2" - - deadcode -generated example.com -!want "main" - want "Dead1" - want "Dead2" + deadcode "-f={{range .Funcs}}{{$.Name}}.{{.Name}}{{end}}" example.com +!want "main.main" + want "main.Dead1" +!want "main.Dead2" + + deadcode "-f={{range .Funcs}}{{$.Name}}.{{.Name}}{{end}}" -generated example.com +!want "main.main" + want "main.Dead1" + want "main.Dead2" -- go.mod -- module example.com diff --git a/internal/cmd/deadcode/testdata/lineflag.txtar b/internal/cmd/deadcode/testdata/lineflag.txtar index f8e5fac8b38..6ba006d6aa6 100644 --- a/internal/cmd/deadcode/testdata/lineflag.txtar +++ b/internal/cmd/deadcode/testdata/lineflag.txtar @@ -1,10 +1,10 @@ # Test of line-oriented output. - deadcode `-f={{range .Funcs}}{{printf "%s.%s\n" $.Path .Name}}{{end}}` -filter= example.com + deadcode `-f={{range .Funcs}}{{printf "%s: %s.%s\n" .Position $.Path .Name}}{{end}}` -filter= example.com - want "example.com.T.Goodbye" + want "main.go:13:10: example.com.T.Goodbye" !want "example.com.T.Hello" - want "example.com.unreferenced" + want "main.go:15:6: example.com.unreferenced" want "fmt.Scanf" want "fmt.Printf"