Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find dead code in Golang monorepo

Tags:

My team has all our Golang code in a monorepo.

  • Various package subdirectories with library code.
  • Binaries/services/tools under cmd

We've had it for a while and are doing some cleanup. Are there any tools or techniques that can find functions not used by the binaries under cmd?

I know go vet can find private functions that are unused in a package. However I suspect we also have exported library functions that aren't used either.

like image 227
Michael McLoughlin Avatar asked Oct 07 '16 18:10

Michael McLoughlin


2 Answers

UPD 2020: The unused tool has been incorporated into staticcheck. Unfortunately, v0.0.1-2020.1.4 will probably be the last to support this feature. Dominik explains that it is because the check consumes a lot of resources and is hard to get right.

To get that version:

env GO111MODULE=on go get honnef.co/go/tools/cmd/[email protected] 

To use it:

$ staticcheck --unused.whole-program=true -- ./... ./internal/pkg/a.go:5:6: type A is unused (U1001) 

Original answer below.


Dominik Honnef's unused tool might be what you're looking for:

Optionally via the -exported flag, unused can analyse all arguments as a single program and report unused exported identifiers. This can be useful for checking "internal" packages, or large software projects that do not export an API to the public, but use exported methods between components.

like image 50
Ainar-G Avatar answered Sep 25 '22 01:09

Ainar-G


Try running go build -gcflags -live. This passes the -live flag to the compiler (go tool compile), instructing it to output debugging messages about liveness analysis. Unfortunately, it only prints when it's found live code, not dead code, but you could in theory look to see what doesn't show up in the output.

Here's an example from compiling the following program stored in dead.go:

package main  import "fmt"  func main() {     if true {         fmt.Println(true)     } else {         fmt.Println(false)     } } 

Output of go build -gcflags -live:

# _/tmp/dead ./dead.go:7: live at call to convT2E: autotmp_5 ./dead.go:7: live at call to Println: autotmp_5 

If I'm reading this correctly, the second line states that the implicit call to convT2E (which converts non-interface types to interface types, since fmt.Println takes arguments of type interface{}) is live, and the third line states that the call to fmt.Println is live. Note that it doesn't say that the fmt.Println(false) call is live, so we can deduce that it must be dead.

I know that's not a perfect answer, but I hope it helps.

like image 28
joshlf Avatar answered Sep 22 '22 01:09

joshlf