github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/internal/cmd/unused/unused.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "log" 7 "os" 8 9 "github.com/amarpal/go-tools/go/loader" 10 "github.com/amarpal/go-tools/lintcmd/cache" 11 "github.com/amarpal/go-tools/unused" 12 "golang.org/x/tools/go/packages" 13 ) 14 15 // OPT(dh): we don't need full graph merging if we're not flagging exported objects. In that case, we can reuse the old 16 // list-based merging approach. 17 18 // OPT(dh): we can either merge graphs as we process packages, or we can merge them all in one go afterwards (then 19 // reloading them from cache). The first approach will likely lead to higher peak memory usage, but the latter may take 20 // more wall time to finish if we had spare CPU resources while processing packages. 21 22 func main() { 23 opts := unused.DefaultOptions 24 flag.BoolVar(&opts.FieldWritesAreUses, "field-writes-are-uses", opts.FieldWritesAreUses, "") 25 flag.BoolVar(&opts.PostStatementsAreReads, "post-statements-are-reads", opts.PostStatementsAreReads, "") 26 flag.BoolVar(&opts.ExportedIsUsed, "exported-is-used", opts.ExportedIsUsed, "") 27 flag.BoolVar(&opts.ExportedFieldsAreUsed, "exported-fields-are-used", opts.ExportedFieldsAreUsed, "") 28 flag.BoolVar(&opts.ParametersAreUsed, "parameters-are-used", opts.ParametersAreUsed, "") 29 flag.BoolVar(&opts.LocalVariablesAreUsed, "local-variables-are-used", opts.LocalVariablesAreUsed, "") 30 flag.BoolVar(&opts.GeneratedIsUsed, "generated-is-used", opts.GeneratedIsUsed, "") 31 flag.Parse() 32 33 // pprof.StartCPUProfile(os.Stdout) 34 // defer pprof.StopCPUProfile() 35 36 // XXX set cache key for this tool 37 38 c, err := cache.Default() 39 if err != nil { 40 log.Fatal(err) 41 } 42 cfg := &packages.Config{ 43 Tests: true, 44 } 45 specs, err := loader.Graph(c, cfg, flag.Args()...) 46 if err != nil { 47 log.Fatal(err) 48 } 49 50 var sg unused.SerializedGraph 51 52 ourPkgs := map[string]struct{}{} 53 54 for _, spec := range specs { 55 if len(spec.Errors) != 0 { 56 // XXX priunt errors 57 continue 58 } 59 lpkg, _, err := loader.Load(spec) 60 if err != nil { 61 continue 62 } 63 if len(lpkg.Errors) != 0 { 64 continue 65 } 66 67 // XXX get directives and generated 68 g := unused.Graph(lpkg.Fset, lpkg.Syntax, lpkg.Types, lpkg.TypesInfo, nil, nil, opts) 69 sg.Merge(g) 70 ourPkgs[spec.PkgPath] = struct{}{} 71 } 72 73 res := sg.Results() 74 for _, obj := range res.Unused { 75 // XXX format paths like staticcheck does 76 if _, ok := ourPkgs[obj.Path.PkgPath]; !ok { 77 continue 78 } 79 fmt.Printf("%s: %s %s is unused\n", obj.DisplayPosition, obj.Kind, obj.Name) 80 } 81 82 fmt.Fprintln(os.Stderr, sg.Dot()) 83 }