├── README.md └── codesweep.go /README.md: -------------------------------------------------------------------------------- 1 | About: 2 | 3 | The codesweep program analyzes a collection of Go packages to identify 4 | objects that are never used. 5 | 6 | Install: 7 | 8 | $ go get github.com/mdempsky/codesweep 9 | 10 | Usage: 11 | 12 | $ codesweep cmd/compile/... 13 | 14 | Caveats: 15 | 16 | Codesweep's primary use case is for cleaning up the Go toolchain's 17 | machine-translated and non-idiomatic Go source code. 18 | 19 | At this time, I suspect https://github.com/dominikh/go-unused with its 20 | -exported flag does everything I want with fewer false positives. 21 | -------------------------------------------------------------------------------- /codesweep.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "go/ast" 7 | "go/types" 8 | "log" 9 | "sort" 10 | 11 | "github.com/kisielk/gotool" 12 | "golang.org/x/tools/go/loader" 13 | ) 14 | 15 | func main() { 16 | flag.Parse() 17 | 18 | importPaths := gotool.ImportPaths(flag.Args()) 19 | if len(importPaths) == 0 { 20 | return 21 | } 22 | 23 | var conf loader.Config 24 | for _, importPath := range importPaths { 25 | conf.Import(importPath) 26 | } 27 | prog, err := conf.Load() 28 | if err != nil { 29 | log.Fatal(err) 30 | } 31 | 32 | used := make(map[string]bool) 33 | for _, pkg := range prog.InitialPackages() { 34 | for sym, obj := range pkg.Uses { 35 | if !interesting(sym, obj) { 36 | continue 37 | } 38 | used[obj.Id()] = true 39 | } 40 | } 41 | 42 | var msgs []string 43 | for _, pkg := range prog.InitialPackages() { 44 | for sym, obj := range pkg.Defs { 45 | if !interesting(sym, obj) { 46 | continue 47 | } 48 | if !used[obj.Id()] { 49 | msgs = append(msgs, fmt.Sprint(conf.Fset.Position(sym.Pos()), obj)) 50 | } 51 | } 52 | } 53 | 54 | sort.Strings(msgs) 55 | for _, msg := range msgs { 56 | fmt.Println(msg) 57 | } 58 | } 59 | 60 | func interesting(sym *ast.Ident, obj types.Object) bool { 61 | // Skip blank identifiers. 62 | if sym.Name == "_" { 63 | return false 64 | } 65 | 66 | // Skip identifiers that don't actually declare objects (e.g., package names). 67 | if obj == nil { 68 | return false 69 | } 70 | 71 | // Skip universal objects. 72 | if obj.Pkg() == nil { 73 | return false 74 | } 75 | 76 | // Fields and methods are interesting. 77 | if obj, ok := obj.(*types.Var); ok && obj.IsField() { 78 | return true 79 | } 80 | if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil { 81 | return true 82 | } 83 | 84 | // Objects declared at packages scope are interesting, 85 | // except for init and main. 86 | if obj.Parent() == obj.Pkg().Scope() && !(sym.Name == "init" || sym.Name == "main" && obj.Pkg().Name() == "main") { 87 | return true 88 | } 89 | 90 | // Otherwise, ignore for now. 91 | return false 92 | } 93 | --------------------------------------------------------------------------------