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  }