github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/unused/unused.go (about)

     1  // Package unused contains code for finding unused code.
     2  package unused
     3  
     4  import (
     5  	"fmt"
     6  	"go/ast"
     7  	"go/token"
     8  	"go/types"
     9  	"io"
    10  	"reflect"
    11  	"strings"
    12  
    13  	"github.com/amarpal/go-tools/analysis/facts/directives"
    14  	"github.com/amarpal/go-tools/analysis/facts/generated"
    15  	"github.com/amarpal/go-tools/analysis/lint"
    16  	"github.com/amarpal/go-tools/analysis/report"
    17  	"github.com/amarpal/go-tools/go/ast/astutil"
    18  	"github.com/amarpal/go-tools/go/types/typeutil"
    19  
    20  	"golang.org/x/tools/go/analysis"
    21  	"golang.org/x/tools/go/types/objectpath"
    22  )
    23  
    24  // OPT(dh): don't track local variables that can't have any interesting outgoing edges. For example, using a local
    25  // variable of type int is meaningless; we don't care if `int` is used or not.
    26  //
    27  // Note that we do have to track variables with for example array types, because the array type could have involved a
    28  // named constant.
    29  //
    30  // We probably have different culling needs depending on the mode of operation, too. If we analyze multiple packages in
    31  // one graph (unused's "whole program" mode), we could remove further useless edges (e.g. into nodes that themselves
    32  // have no outgoing edges and aren't meaningful objects on their own) after having analyzed a package, to keep the
    33  // in-memory representation small on average. If we only analyze a single package, that step would just waste cycles, as
    34  // we're about to throw the entire graph away, anyway.
    35  
    36  // TODO(dh): currently, types use methods that implement interfaces. However, this makes a method used even if the
    37  // relevant interface is never used. What if instead interfaces used those methods? Right now we cannot do that, because
    38  // methods use their receivers, so using a method uses the type. But do we need that edge? Is there a way to refer to a
    39  // method without explicitly mentioning the type somewhere? If not, the edge from method to receiver is superfluous.
    40  
    41  // XXX vet all code for proper use of core types
    42  
    43  // TODO(dh): we cannot observe function calls in assembly files.
    44  
    45  /*
    46  
    47  This overview is true when using the default options. Different options may change individual behaviors.
    48  
    49  - packages use:
    50    - (1.1) exported named types
    51    - (1.2) exported functions (but not methods!)
    52    - (1.3) exported variables
    53    - (1.4) exported constants
    54    - (1.5) init functions
    55    - (1.6) functions exported to cgo
    56    - (1.7) the main function iff in the main package
    57    - (1.8) symbols linked via go:linkname
    58    - (1.9) objects in generated files
    59  
    60  - named types use:
    61    - (2.1) exported methods
    62    - (2.2) the type they're based on
    63    - (2.5) all their type parameters. Unused type parameters are probably useless, but they're a brand new feature and we
    64      don't want to introduce false positives because we couldn't anticipate some novel use-case.
    65    - (2.6) all their type arguments
    66  
    67  - functions use:
    68    - (4.1) all their arguments, return parameters and receivers
    69    - (4.2) anonymous functions defined beneath them
    70    - (4.3) closures and bound methods.
    71      this implements a simplified model where a function is used merely by being referenced, even if it is never called.
    72      that way we don't have to keep track of closures escaping functions.
    73    - (4.4) functions they return. we assume that someone else will call the returned function
    74    - (4.5) functions/interface methods they call
    75    - (4.6) types they instantiate or convert to
    76    - (4.7) fields they access
    77    - (4.9) package-level variables they assign to iff in tests (sinks for benchmarks)
    78    - (4.10) all their type parameters. See 2.5 for reasoning.
    79    - (4.11) local variables
    80    - Note that the majority of this is handled implicitly by seeing idents be used. In particular, unlike the old
    81      IR-based implementation, the AST-based one doesn't care about closures, bound methods or anonymous functions.
    82      They're all just additional nodes in the AST.
    83  
    84  - conversions use:
    85    - (5.1) when converting between two equivalent structs, the fields in
    86      either struct use each other. the fields are relevant for the
    87      conversion, but only if the fields are also accessed outside the
    88      conversion.
    89    - (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
    90  
    91  - structs use:
    92    - (6.1) fields of type NoCopy sentinel
    93    - (6.2) exported fields
    94    - (6.3) embedded fields that help implement interfaces (either fully implements it, or contributes required methods) (recursively)
    95    - (6.4) embedded fields that have exported methods (recursively)
    96    - (6.5) embedded structs that have exported fields (recursively)
    97  
    98  - (7.1) field accesses use fields
    99  - (7.2) fields use their types
   100  
   101  - (8.0) How we handle interfaces:
   102    - (8.1) We do not technically care about interfaces that only consist of
   103      exported methods. Exported methods on concrete types are always
   104      marked as used.
   105    - (8.2) Any concrete type implements all known interfaces. Even if it isn't
   106      assigned to any interfaces in our code, the user may receive a value
   107      of the type and expect to pass it back to us through an interface.
   108  
   109      Concrete types use their methods that implement interfaces. If the
   110      type is used, it uses those methods. Otherwise, it doesn't. This
   111      way, types aren't incorrectly marked reachable through the edge
   112      from method to type.
   113  
   114    - (8.3) All interface methods are marked as used, even if they never get
   115      called. This is to accommodate sum types (unexported interface
   116      method that must exist but never gets called.)
   117  
   118    - (8.4) All embedded interfaces are marked as used. This is an
   119      extension of 8.3, but we have to explicitly track embedded
   120      interfaces because in a chain C->B->A, B wouldn't be marked as
   121      used by 8.3 just because it contributes A's methods to C.
   122  
   123  - Inherent uses:
   124    - (9.2) variables use their types
   125    - (9.3) types use their underlying and element types
   126    - (9.4) conversions use the type they convert to
   127    - (9.7) variable _reads_ use variables, writes do not, except in tests
   128    - (9.8) runtime functions that may be called from user code via the compiler
   129    - (9.9) objects named the blank identifier are used. They cannot be referred to and are usually used explicitly to
   130       use something that would otherwise be unused.
   131    - The majority of idents get marked as read by virtue of being in the AST.
   132  
   133  - const groups:
   134    - (10.1) if one constant out of a block of constants is used, mark all
   135      of them used. a lot of the time, unused constants exist for the sake
   136      of completeness. See also
   137      https://github.com/dominikh/go-tools/issues/365
   138  
   139      Do not, however, include constants named _ in constant groups.
   140  
   141  
   142  - (11.1) anonymous struct types use all their fields. we cannot
   143    deduplicate struct types, as that leads to order-dependent
   144    reports. we can't not deduplicate struct types while still
   145    tracking fields, because then each instance of the unnamed type in
   146    the data flow chain will get its own fields, causing false
   147    positives. Thus, we only accurately track fields of named struct
   148    types, and assume that unnamed struct types use all their fields.
   149  
   150  - type parameters use:
   151    - (12.1) their constraint type
   152  
   153  */
   154  
   155  var Debug io.Writer
   156  
   157  func assert(b bool) {
   158  	if !b {
   159  		panic("failed assertion")
   160  	}
   161  }
   162  
   163  // TODO(dh): should we return a map instead of two slices?
   164  type Result struct {
   165  	Used   []Object
   166  	Unused []Object
   167  	Quiet  []Object
   168  }
   169  
   170  var Analyzer = &lint.Analyzer{
   171  	Doc: &lint.Documentation{
   172  		Title: "Unused code",
   173  	},
   174  	Analyzer: &analysis.Analyzer{
   175  		Name:       "U1000",
   176  		Doc:        "Unused code",
   177  		Run:        run,
   178  		Requires:   []*analysis.Analyzer{generated.Analyzer, directives.Analyzer},
   179  		ResultType: reflect.TypeOf(Result{}),
   180  	},
   181  }
   182  
   183  func newGraph(
   184  	fset *token.FileSet,
   185  	files []*ast.File,
   186  	pkg *types.Package,
   187  	info *types.Info,
   188  	directives []lint.Directive,
   189  	generated map[string]generated.Generator,
   190  	opts Options,
   191  ) *graph {
   192  	g := graph{
   193  		pkg:        pkg,
   194  		info:       info,
   195  		files:      files,
   196  		directives: directives,
   197  		generated:  generated,
   198  		fset:       fset,
   199  		nodes:      []Node{{}},
   200  		edges:      map[edge]struct{}{},
   201  		objects:    map[types.Object]NodeID{},
   202  		opts:       opts,
   203  	}
   204  
   205  	return &g
   206  }
   207  
   208  func run(pass *analysis.Pass) (interface{}, error) {
   209  	g := newGraph(
   210  		pass.Fset,
   211  		pass.Files,
   212  		pass.Pkg,
   213  		pass.TypesInfo,
   214  		pass.ResultOf[directives.Analyzer].([]lint.Directive),
   215  		pass.ResultOf[generated.Analyzer].(map[string]generated.Generator),
   216  		DefaultOptions,
   217  	)
   218  	g.entry()
   219  
   220  	sg := &SerializedGraph{
   221  		nodes: g.nodes,
   222  	}
   223  
   224  	if Debug != nil {
   225  		Debug.Write([]byte(sg.Dot()))
   226  	}
   227  
   228  	return sg.Results(), nil
   229  }
   230  
   231  type Options struct {
   232  	FieldWritesAreUses     bool
   233  	PostStatementsAreReads bool
   234  	ExportedIsUsed         bool
   235  	ExportedFieldsAreUsed  bool
   236  	ParametersAreUsed      bool
   237  	LocalVariablesAreUsed  bool
   238  	GeneratedIsUsed        bool
   239  }
   240  
   241  var DefaultOptions = Options{
   242  	FieldWritesAreUses:     true,
   243  	PostStatementsAreReads: false,
   244  	ExportedIsUsed:         true,
   245  	ExportedFieldsAreUsed:  true,
   246  	ParametersAreUsed:      true,
   247  	LocalVariablesAreUsed:  true,
   248  	GeneratedIsUsed:        true,
   249  }
   250  
   251  type edgeKind uint8
   252  
   253  const (
   254  	edgeKindUse = iota + 1
   255  	edgeKindOwn
   256  )
   257  
   258  type edge struct {
   259  	from, to NodeID
   260  	kind     edgeKind
   261  }
   262  
   263  type graph struct {
   264  	pkg        *types.Package
   265  	info       *types.Info
   266  	files      []*ast.File
   267  	fset       *token.FileSet
   268  	directives []lint.Directive
   269  	generated  map[string]generated.Generator
   270  
   271  	opts Options
   272  
   273  	// edges tracks all edges between nodes (uses and owns relationships). This data is also present in the Node struct,
   274  	// but there it can't be accessed in O(1) time. edges is used to deduplicate edges.
   275  	edges   map[edge]struct{}
   276  	nodes   []Node
   277  	objects map[types.Object]NodeID
   278  
   279  	// package-level named types
   280  	namedTypes     []*types.TypeName
   281  	interfaceTypes []*types.Interface
   282  }
   283  
   284  type nodeState uint8
   285  
   286  //gcassert:inline
   287  func (ns nodeState) seen() bool { return ns&nodeStateSeen != 0 }
   288  
   289  //gcassert:inline
   290  func (ns nodeState) quiet() bool { return ns&nodeStateQuiet != 0 }
   291  
   292  const (
   293  	nodeStateSeen nodeState = 1 << iota
   294  	nodeStateQuiet
   295  )
   296  
   297  // OPT(dh): 32 bits would be plenty, but the Node struct would end up with padding, anyway.
   298  type NodeID uint64
   299  
   300  type Node struct {
   301  	id  NodeID
   302  	obj Object
   303  
   304  	// using slices instead of maps here helps make merging of graphs simpler and more efficient, because we can rewrite
   305  	// IDs in place instead of having to build new maps.
   306  	uses []NodeID
   307  	owns []NodeID
   308  }
   309  
   310  func (g *graph) objectToObject(obj types.Object) Object {
   311  	// OPT(dh): I think we only need object paths in whole-program mode. In other cases, position-based node merging
   312  	// should suffice.
   313  
   314  	// objectpath.For is an expensive function and we'd like to avoid calling it when we know that there cannot be a
   315  	// path, or when the path doesn't matter.
   316  	//
   317  	// Unexported global objects don't have paths. Local variables may have paths when they're parameters or return
   318  	// parameters, but we do not care about those, because they're not API that other packages can refer to directly. We
   319  	// do have to track fields, because they may be part of an anonymous type declared in a parameter or return
   320  	// parameter. We cannot categorically ignore unexported identifiers, because an exported field might have been
   321  	// embedded via an unexported field, which will be referred to.
   322  
   323  	var relevant bool
   324  	switch obj := obj.(type) {
   325  	case *types.Var:
   326  		// If it's a field or it's an exported top-level variable, we care about it. Otherwise, we don't.
   327  		// OPT(dh): same question as posed in the default branch
   328  		relevant = obj.IsField() || token.IsExported(obj.Name())
   329  	default:
   330  		// OPT(dh): See if it's worth checking that the object is actually in package scope, and doesn't just have a
   331  		// capitalized name.
   332  		relevant = token.IsExported(obj.Name())
   333  	}
   334  
   335  	var path ObjectPath
   336  	if relevant {
   337  		objPath, _ := objectpath.For(obj)
   338  		if objPath != "" {
   339  			path = ObjectPath{
   340  				PkgPath: obj.Pkg().Path(),
   341  				ObjPath: objPath,
   342  			}
   343  		}
   344  	}
   345  	name := obj.Name()
   346  	if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil {
   347  		switch sig.Recv().Type().(type) {
   348  		case *types.Named, *types.Pointer:
   349  			typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" })
   350  			if len(typ) > 0 && typ[0] == '*' {
   351  				name = fmt.Sprintf("(%s).%s", typ, obj.Name())
   352  			} else if len(typ) > 0 {
   353  				name = fmt.Sprintf("%s.%s", typ, obj.Name())
   354  			}
   355  		}
   356  	}
   357  	return Object{
   358  		Name:            name,
   359  		ShortName:       obj.Name(),
   360  		Kind:            typString(obj),
   361  		Path:            path,
   362  		Position:        g.fset.PositionFor(obj.Pos(), false),
   363  		DisplayPosition: report.DisplayPosition(g.fset, obj.Pos()),
   364  	}
   365  }
   366  
   367  func typString(obj types.Object) string {
   368  	switch obj := obj.(type) {
   369  	case *types.Func:
   370  		return "func"
   371  	case *types.Var:
   372  		if obj.IsField() {
   373  			return "field"
   374  		}
   375  		return "var"
   376  	case *types.Const:
   377  		return "const"
   378  	case *types.TypeName:
   379  		if _, ok := obj.Type().(*types.TypeParam); ok {
   380  			return "type param"
   381  		} else {
   382  			return "type"
   383  		}
   384  	default:
   385  		return "identifier"
   386  	}
   387  }
   388  
   389  func (g *graph) newNode(obj types.Object) NodeID {
   390  	id := NodeID(len(g.nodes))
   391  	n := Node{
   392  		id:  id,
   393  		obj: g.objectToObject(obj),
   394  	}
   395  	g.nodes = append(g.nodes, n)
   396  	if _, ok := g.objects[obj]; ok {
   397  		panic(fmt.Sprintf("already had a node for %s", obj))
   398  	}
   399  	g.objects[obj] = id
   400  	return id
   401  }
   402  
   403  func (g *graph) node(obj types.Object) NodeID {
   404  	if obj == nil {
   405  		return 0
   406  	}
   407  	obj = origin(obj)
   408  	if n, ok := g.objects[obj]; ok {
   409  		return n
   410  	}
   411  	n := g.newNode(obj)
   412  	return n
   413  }
   414  
   415  func origin(obj types.Object) types.Object {
   416  	switch obj := obj.(type) {
   417  	case *types.Var:
   418  		return obj.Origin()
   419  	case *types.Func:
   420  		return obj.Origin()
   421  	default:
   422  		return obj
   423  	}
   424  }
   425  
   426  func (g *graph) addEdge(e edge) bool {
   427  	if _, ok := g.edges[e]; ok {
   428  		return false
   429  	}
   430  	g.edges[e] = struct{}{}
   431  	return true
   432  }
   433  
   434  func (g *graph) addOwned(owner, owned NodeID) {
   435  	e := edge{owner, owned, edgeKindOwn}
   436  	if !g.addEdge(e) {
   437  		return
   438  	}
   439  	n := &g.nodes[owner]
   440  	n.owns = append(n.owns, owned)
   441  }
   442  
   443  func (g *graph) addUse(by, used NodeID) {
   444  	e := edge{by, used, edgeKindUse}
   445  	if !g.addEdge(e) {
   446  		return
   447  	}
   448  	nBy := &g.nodes[by]
   449  	nBy.uses = append(nBy.uses, used)
   450  }
   451  
   452  func (g *graph) see(obj, owner types.Object) {
   453  	if obj == nil {
   454  		panic("saw nil object")
   455  	}
   456  
   457  	if g.opts.ExportedIsUsed && obj.Pkg() != g.pkg || obj.Pkg() == nil {
   458  		return
   459  	}
   460  
   461  	nObj := g.node(obj)
   462  	if owner != nil {
   463  		nOwner := g.node(owner)
   464  		g.addOwned(nOwner, nObj)
   465  	}
   466  }
   467  
   468  func isIrrelevant(obj types.Object) bool {
   469  	switch obj.(type) {
   470  	case *types.PkgName:
   471  		return true
   472  	default:
   473  		return false
   474  	}
   475  }
   476  
   477  func (g *graph) use(used, by types.Object) {
   478  	if g.opts.ExportedIsUsed {
   479  		if used.Pkg() != g.pkg || used.Pkg() == nil {
   480  			return
   481  		}
   482  		if by != nil && by.Pkg() != g.pkg {
   483  			return
   484  		}
   485  	}
   486  
   487  	if isIrrelevant(used) {
   488  		return
   489  	}
   490  
   491  	nUsed := g.node(used)
   492  	nBy := g.node(by)
   493  	g.addUse(nBy, nUsed)
   494  }
   495  
   496  func (g *graph) entry() {
   497  	for _, f := range g.files {
   498  		for _, cg := range f.Comments {
   499  			for _, c := range cg.List {
   500  				if strings.HasPrefix(c.Text, "//go:linkname ") {
   501  					// FIXME(dh): we're looking at all comments. The
   502  					// compiler only looks at comments in the
   503  					// left-most column. The intention probably is to
   504  					// only look at top-level comments.
   505  
   506  					// (1.8) packages use symbols linked via go:linkname
   507  					fields := strings.Fields(c.Text)
   508  					if len(fields) == 3 {
   509  						obj := g.pkg.Scope().Lookup(fields[1])
   510  						if obj == nil {
   511  							continue
   512  						}
   513  						g.use(obj, nil)
   514  					}
   515  				}
   516  			}
   517  		}
   518  	}
   519  
   520  	for _, f := range g.files {
   521  		for _, decl := range f.Decls {
   522  			g.decl(decl, nil)
   523  		}
   524  	}
   525  
   526  	if g.opts.GeneratedIsUsed {
   527  		// OPT(dh): depending on the options used, we do not need to track all objects. For example, if local variables
   528  		// are always used, then it is enough to use their surrounding function.
   529  		for obj := range g.objects {
   530  			path := g.fset.PositionFor(obj.Pos(), false).Filename
   531  			if _, ok := g.generated[path]; ok {
   532  				g.use(obj, nil)
   533  			}
   534  		}
   535  	}
   536  
   537  	processMethodSet := func(named *types.TypeName, ms *types.MethodSet) {
   538  		if g.opts.ExportedIsUsed {
   539  			for i := 0; i < ms.Len(); i++ {
   540  				m := ms.At(i)
   541  				if token.IsExported(m.Obj().Name()) {
   542  					// (2.1) named types use exported methods
   543  					// (6.4) structs use embedded fields that have exported methods
   544  					//
   545  					// By reading the selection, we read all embedded fields that are part of the path
   546  					g.readSelection(m, named)
   547  				}
   548  			}
   549  		}
   550  
   551  		if _, ok := named.Type().Underlying().(*types.Interface); !ok {
   552  			// (8.0) handle interfaces
   553  			//
   554  			// We don't care about interfaces implementing interfaces; all their methods are already used, anyway
   555  			for _, iface := range g.interfaceTypes {
   556  				if sels, ok := implements(named.Type(), iface, ms); ok {
   557  					for _, sel := range sels {
   558  						// (8.2) any concrete type implements all known interfaces
   559  						// (6.3) structs use embedded fields that help implement interfaces
   560  						g.readSelection(sel, named)
   561  					}
   562  				}
   563  			}
   564  		}
   565  	}
   566  
   567  	for _, named := range g.namedTypes {
   568  		// OPT(dh): do we already have the method set available?
   569  		processMethodSet(named, types.NewMethodSet(named.Type()))
   570  		processMethodSet(named, types.NewMethodSet(types.NewPointer(named.Type())))
   571  
   572  	}
   573  
   574  	type ignoredKey struct {
   575  		file string
   576  		line int
   577  	}
   578  	ignores := map[ignoredKey]struct{}{}
   579  	for _, dir := range g.directives {
   580  		if dir.Command != "ignore" && dir.Command != "file-ignore" {
   581  			continue
   582  		}
   583  		if len(dir.Arguments) == 0 {
   584  			continue
   585  		}
   586  		for _, check := range strings.Split(dir.Arguments[0], ",") {
   587  			if check == "U1000" {
   588  				pos := g.fset.PositionFor(dir.Node.Pos(), false)
   589  				var key ignoredKey
   590  				switch dir.Command {
   591  				case "ignore":
   592  					key = ignoredKey{
   593  						pos.Filename,
   594  						pos.Line,
   595  					}
   596  				case "file-ignore":
   597  					key = ignoredKey{
   598  						pos.Filename,
   599  						-1,
   600  					}
   601  				}
   602  
   603  				ignores[key] = struct{}{}
   604  				break
   605  			}
   606  		}
   607  	}
   608  
   609  	if len(ignores) > 0 {
   610  		// all objects annotated with a //lint:ignore U1000 are considered used
   611  		for obj := range g.objects {
   612  			pos := g.fset.PositionFor(obj.Pos(), false)
   613  			key1 := ignoredKey{
   614  				pos.Filename,
   615  				pos.Line,
   616  			}
   617  			key2 := ignoredKey{
   618  				pos.Filename,
   619  				-1,
   620  			}
   621  			_, ok := ignores[key1]
   622  			if !ok {
   623  				_, ok = ignores[key2]
   624  			}
   625  			if ok {
   626  				g.use(obj, nil)
   627  
   628  				// use methods and fields of ignored types
   629  				if obj, ok := obj.(*types.TypeName); ok {
   630  					if obj.IsAlias() {
   631  						if typ, ok := obj.Type().(*types.Named); ok && (g.opts.ExportedIsUsed && typ.Obj().Pkg() != obj.Pkg() || typ.Obj().Pkg() == nil) {
   632  							// This is an alias of a named type in another package.
   633  							// Don't walk its fields or methods; we don't have to.
   634  							//
   635  							// For aliases to types in the same package, we do want to ignore the fields and methods,
   636  							// because ignoring the alias should ignore the aliased type.
   637  							continue
   638  						}
   639  					}
   640  					if typ, ok := obj.Type().(*types.Named); ok {
   641  						for i := 0; i < typ.NumMethods(); i++ {
   642  							g.use(typ.Method(i), nil)
   643  						}
   644  					}
   645  					if typ, ok := obj.Type().Underlying().(*types.Struct); ok {
   646  						for i := 0; i < typ.NumFields(); i++ {
   647  							g.use(typ.Field(i), nil)
   648  						}
   649  					}
   650  				}
   651  			}
   652  		}
   653  	}
   654  }
   655  
   656  func isOfType[T any](x any) bool {
   657  	_, ok := x.(T)
   658  	return ok
   659  }
   660  
   661  func (g *graph) read(node ast.Node, by types.Object) {
   662  	if node == nil {
   663  		return
   664  	}
   665  
   666  	switch node := node.(type) {
   667  	case *ast.Ident:
   668  		// Among many other things, this handles
   669  		// (7.1) field accesses use fields
   670  
   671  		obj := g.info.ObjectOf(node)
   672  		g.use(obj, by)
   673  
   674  	case *ast.BasicLit:
   675  		// Nothing to do
   676  
   677  	case *ast.SliceExpr:
   678  		g.read(node.X, by)
   679  		g.read(node.Low, by)
   680  		g.read(node.High, by)
   681  		g.read(node.Max, by)
   682  
   683  	case *ast.UnaryExpr:
   684  		g.read(node.X, by)
   685  
   686  	case *ast.ParenExpr:
   687  		g.read(node.X, by)
   688  
   689  	case *ast.ArrayType:
   690  		g.read(node.Len, by)
   691  		g.read(node.Elt, by)
   692  
   693  	case *ast.SelectorExpr:
   694  		g.readSelectorExpr(node, by)
   695  
   696  	case *ast.IndexExpr:
   697  		// Among many other things, this handles
   698  		// (2.6) named types use all their type arguments
   699  		g.read(node.X, by)
   700  		g.read(node.Index, by)
   701  
   702  	case *ast.IndexListExpr:
   703  		// Among many other things, this handles
   704  		// (2.6) named types use all their type arguments
   705  		g.read(node.X, by)
   706  		for _, index := range node.Indices {
   707  			g.read(index, by)
   708  		}
   709  
   710  	case *ast.BinaryExpr:
   711  		g.read(node.X, by)
   712  		g.read(node.Y, by)
   713  
   714  	case *ast.CompositeLit:
   715  		g.read(node.Type, by)
   716  		// We get the type of the node itself, not of node.Type, to handle nested composite literals of the kind
   717  		// T{{...}}
   718  		typ, isStruct := typeutil.CoreType(g.info.TypeOf(node)).(*types.Struct)
   719  
   720  		if isStruct {
   721  			unkeyed := len(node.Elts) != 0 && !isOfType[*ast.KeyValueExpr](node.Elts[0])
   722  			if g.opts.FieldWritesAreUses && unkeyed {
   723  				// Untagged struct literal that specifies all fields. We have to manually use the fields in the type,
   724  				// because the unkeyd literal doesn't contain any nodes referring to the fields.
   725  				for i := 0; i < typ.NumFields(); i++ {
   726  					g.use(typ.Field(i), by)
   727  				}
   728  			}
   729  			if g.opts.FieldWritesAreUses || unkeyed {
   730  				for _, elt := range node.Elts {
   731  					g.read(elt, by)
   732  				}
   733  			} else {
   734  				for _, elt := range node.Elts {
   735  					kv := elt.(*ast.KeyValueExpr)
   736  					g.write(kv.Key, by)
   737  					g.read(kv.Value, by)
   738  				}
   739  			}
   740  		} else {
   741  			for _, elt := range node.Elts {
   742  				g.read(elt, by)
   743  			}
   744  		}
   745  
   746  	case *ast.KeyValueExpr:
   747  		g.read(node.Key, by)
   748  		g.read(node.Value, by)
   749  
   750  	case *ast.StarExpr:
   751  		g.read(node.X, by)
   752  
   753  	case *ast.MapType:
   754  		g.read(node.Key, by)
   755  		g.read(node.Value, by)
   756  
   757  	case *ast.FuncLit:
   758  		g.read(node.Type, by)
   759  
   760  		// See graph.decl's handling of ast.FuncDecl for why this bit of code is necessary.
   761  		fn := g.info.TypeOf(node).(*types.Signature)
   762  		for params, i := fn.Params(), 0; i < params.Len(); i++ {
   763  			g.see(params.At(i), by)
   764  			if params.At(i).Name() == "" {
   765  				g.use(params.At(i), by)
   766  			}
   767  		}
   768  
   769  		g.block(node.Body, by)
   770  
   771  	case *ast.FuncType:
   772  		m := map[*types.Var]struct{}{}
   773  		if !g.opts.ParametersAreUsed {
   774  			m = map[*types.Var]struct{}{}
   775  			// seeScope marks all local variables in the scope as used, but we don't want to unconditionally use
   776  			// parameters, as this is controlled by Options.ParametersAreUsed. Pass seeScope a list of variables it
   777  			// should skip.
   778  			for _, f := range node.Params.List {
   779  				for _, name := range f.Names {
   780  					m[g.info.ObjectOf(name).(*types.Var)] = struct{}{}
   781  				}
   782  			}
   783  		}
   784  		g.seeScope(node, by, m)
   785  
   786  		// (4.1) functions use all their arguments, return parameters and receivers
   787  		// (12.1) type parameters use their constraint type
   788  		g.read(node.TypeParams, by)
   789  		if g.opts.ParametersAreUsed {
   790  			g.read(node.Params, by)
   791  		}
   792  		g.read(node.Results, by)
   793  
   794  	case *ast.FieldList:
   795  		if node == nil {
   796  			return
   797  		}
   798  
   799  		// This branch is only hit for field lists enclosed by parentheses or square brackets, i.e. parameters. Fields
   800  		// (for structs) and method lists (for interfaces) are handled elsewhere.
   801  
   802  		for _, field := range node.List {
   803  			if len(field.Names) == 0 {
   804  				g.read(field.Type, by)
   805  			} else {
   806  				for _, name := range field.Names {
   807  					// OPT(dh): instead of by -> name -> type, we could just emit by -> type. We don't care about the
   808  					// (un)usedness of parameters of any kind.
   809  					obj := g.info.ObjectOf(name)
   810  					g.use(obj, by)
   811  					g.read(field.Type, obj)
   812  				}
   813  			}
   814  		}
   815  
   816  	case *ast.ChanType:
   817  		g.read(node.Value, by)
   818  
   819  	case *ast.StructType:
   820  		// This is only used for anonymous struct types, not named ones.
   821  
   822  		for _, field := range node.Fields.List {
   823  			if len(field.Names) == 0 {
   824  				// embedded field
   825  
   826  				f := g.embeddedField(field.Type, by)
   827  				g.use(f, by)
   828  			} else {
   829  				for _, name := range field.Names {
   830  					// (11.1) anonymous struct types use all their fields
   831  					// OPT(dh): instead of by -> name -> type, we could just emit by -> type. If the type is used, then the fields are used.
   832  					obj := g.info.ObjectOf(name)
   833  					g.see(obj, by)
   834  					g.use(obj, by)
   835  					g.read(field.Type, g.info.ObjectOf(name))
   836  				}
   837  			}
   838  		}
   839  
   840  	case *ast.TypeAssertExpr:
   841  		g.read(node.X, by)
   842  		g.read(node.Type, by)
   843  
   844  	case *ast.InterfaceType:
   845  		if len(node.Methods.List) != 0 {
   846  			g.interfaceTypes = append(g.interfaceTypes, g.info.TypeOf(node).(*types.Interface))
   847  		}
   848  		for _, meth := range node.Methods.List {
   849  			switch len(meth.Names) {
   850  			case 0:
   851  				// Embedded type or type union
   852  				// (8.4) all embedded interfaces are marked as used
   853  				// (this also covers type sets)
   854  
   855  				g.read(meth.Type, by)
   856  			case 1:
   857  				// Method
   858  				// (8.3) all interface methods are marked as used
   859  				obj := g.info.ObjectOf(meth.Names[0])
   860  				g.see(obj, by)
   861  				g.use(obj, by)
   862  				g.read(meth.Type, obj)
   863  			default:
   864  				panic(fmt.Sprintf("unexpected number of names: %d", len(meth.Names)))
   865  			}
   866  		}
   867  
   868  	case *ast.Ellipsis:
   869  		g.read(node.Elt, by)
   870  
   871  	case *ast.CallExpr:
   872  		g.read(node.Fun, by)
   873  		for _, arg := range node.Args {
   874  			g.read(arg, by)
   875  		}
   876  
   877  		// Handle conversiosn
   878  		conv := node
   879  		if len(conv.Args) != 1 || conv.Ellipsis.IsValid() {
   880  			return
   881  		}
   882  
   883  		dst := g.info.TypeOf(conv.Fun)
   884  		src := g.info.TypeOf(conv.Args[0])
   885  
   886  		// XXX use DereferenceR instead
   887  		// XXX guard against infinite recursion in DereferenceR
   888  		tSrc := typeutil.CoreType(typeutil.Dereference(src))
   889  		tDst := typeutil.CoreType(typeutil.Dereference(dst))
   890  		stSrc, okSrc := tSrc.(*types.Struct)
   891  		stDst, okDst := tDst.(*types.Struct)
   892  		if okDst && okSrc {
   893  			// Converting between two structs. The fields are
   894  			// relevant for the conversion, but only if the
   895  			// fields are also used outside of the conversion.
   896  			// Mark fields as used by each other.
   897  
   898  			assert(stDst.NumFields() == stSrc.NumFields())
   899  			for i := 0; i < stDst.NumFields(); i++ {
   900  				// (5.1) when converting between two equivalent structs, the fields in
   901  				// either struct use each other. the fields are relevant for the
   902  				// conversion, but only if the fields are also accessed outside the
   903  				// conversion.
   904  				g.use(stDst.Field(i), stSrc.Field(i))
   905  				g.use(stSrc.Field(i), stDst.Field(i))
   906  			}
   907  		} else if okSrc && tDst == types.Typ[types.UnsafePointer] {
   908  			// (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
   909  			g.useAllFieldsRecursively(stSrc, by)
   910  		} else if okDst && tSrc == types.Typ[types.UnsafePointer] {
   911  			// (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
   912  			g.useAllFieldsRecursively(stDst, by)
   913  		}
   914  
   915  	default:
   916  		lint.ExhaustiveTypeSwitch(node)
   917  	}
   918  }
   919  
   920  func (g *graph) useAllFieldsRecursively(typ types.Type, by types.Object) {
   921  	switch typ := typ.Underlying().(type) {
   922  	case *types.Struct:
   923  		for i := 0; i < typ.NumFields(); i++ {
   924  			field := typ.Field(i)
   925  			g.use(field, by)
   926  			g.useAllFieldsRecursively(field.Type(), by)
   927  		}
   928  	case *types.Array:
   929  		g.useAllFieldsRecursively(typ.Elem(), by)
   930  	default:
   931  		return
   932  	}
   933  }
   934  
   935  func (g *graph) write(node ast.Node, by types.Object) {
   936  	if node == nil {
   937  		return
   938  	}
   939  
   940  	switch node := node.(type) {
   941  	case *ast.Ident:
   942  		obj := g.info.ObjectOf(node)
   943  		if obj == nil {
   944  			// This can happen for `switch x := v.(type)`, where that x doesn't have an object
   945  			return
   946  		}
   947  
   948  		// (4.9) functions use package-level variables they assign to iff in tests (sinks for benchmarks)
   949  		// (9.7) variable _reads_ use variables, writes do not, except in tests
   950  		path := g.fset.File(obj.Pos()).Name()
   951  		if strings.HasSuffix(path, "_test.go") {
   952  			if isGlobal(obj) {
   953  				g.use(obj, by)
   954  			}
   955  		}
   956  
   957  	case *ast.IndexExpr:
   958  		g.read(node.X, by)
   959  		g.read(node.Index, by)
   960  
   961  	case *ast.SelectorExpr:
   962  		if g.opts.FieldWritesAreUses {
   963  			// Writing to a field constitutes a use. See https://staticcheck.dev/issues/288 for some discussion on that.
   964  			//
   965  			// This code can also get triggered by qualified package variables, in which case it doesn't matter what we do,
   966  			// because the object is in another package.
   967  			//
   968  			// FIXME(dh): ^ isn't true if we track usedness of exported identifiers
   969  			g.readSelectorExpr(node, by)
   970  		} else {
   971  			g.read(node.X, by)
   972  			g.write(node.Sel, by)
   973  		}
   974  
   975  	case *ast.StarExpr:
   976  		g.read(node.X, by)
   977  
   978  	case *ast.ParenExpr:
   979  		g.write(node.X, by)
   980  
   981  	default:
   982  		lint.ExhaustiveTypeSwitch(node)
   983  	}
   984  }
   985  
   986  // readSelectorExpr reads all elements of a selector expression, including implicit fields.
   987  func (g *graph) readSelectorExpr(sel *ast.SelectorExpr, by types.Object) {
   988  	// cover AST-based accesses
   989  	g.read(sel.X, by)
   990  	g.read(sel.Sel, by)
   991  
   992  	tsel, ok := g.info.Selections[sel]
   993  	if !ok {
   994  		return
   995  	}
   996  	g.readSelection(tsel, by)
   997  }
   998  
   999  func (g *graph) readSelection(sel *types.Selection, by types.Object) {
  1000  	indices := sel.Index()
  1001  	base := sel.Recv()
  1002  	for _, idx := range indices[:len(indices)-1] {
  1003  		// XXX do we need core types here?
  1004  		field := typeutil.Dereference(base.Underlying()).Underlying().(*types.Struct).Field(idx)
  1005  		g.use(field, by)
  1006  		base = field.Type()
  1007  	}
  1008  
  1009  	g.use(sel.Obj(), by)
  1010  }
  1011  
  1012  func (g *graph) block(block *ast.BlockStmt, by types.Object) {
  1013  	if block == nil {
  1014  		return
  1015  	}
  1016  
  1017  	g.seeScope(block, by, nil)
  1018  	for _, stmt := range block.List {
  1019  		g.stmt(stmt, by)
  1020  	}
  1021  }
  1022  
  1023  func isGlobal(obj types.Object) bool {
  1024  	return obj.Parent() == obj.Pkg().Scope()
  1025  }
  1026  
  1027  func (g *graph) decl(decl ast.Decl, by types.Object) {
  1028  	switch decl := decl.(type) {
  1029  	case *ast.GenDecl:
  1030  		switch decl.Tok {
  1031  		case token.IMPORT:
  1032  			// Nothing to do
  1033  
  1034  		case token.CONST:
  1035  			for _, spec := range decl.Specs {
  1036  				vspec := spec.(*ast.ValueSpec)
  1037  				assert(len(vspec.Values) == 0 || len(vspec.Values) == len(vspec.Names))
  1038  				for i, name := range vspec.Names {
  1039  					obj := g.info.ObjectOf(name)
  1040  					g.see(obj, by)
  1041  					g.read(vspec.Type, obj)
  1042  
  1043  					if len(vspec.Values) != 0 {
  1044  						g.read(vspec.Values[i], obj)
  1045  					}
  1046  
  1047  					if name.Name == "_" {
  1048  						// (9.9) objects named the blank identifier are used
  1049  						g.use(obj, by)
  1050  					} else if token.IsExported(name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed {
  1051  						g.use(obj, nil)
  1052  					}
  1053  				}
  1054  			}
  1055  
  1056  			groups := astutil.GroupSpecs(g.fset, decl.Specs)
  1057  			for _, group := range groups {
  1058  				// (10.1) if one constant out of a block of constants is used, mark all of them used
  1059  				//
  1060  				// We encode this as a ring. If we have a constant group 'const ( a; b; c )', then we'll produce the
  1061  				// following graph: a -> b -> c -> a.
  1062  
  1063  				var first, prev, last types.Object
  1064  				for _, spec := range group {
  1065  					for _, name := range spec.(*ast.ValueSpec).Names {
  1066  						if name.Name == "_" {
  1067  							// Having a blank constant in a group doesn't mark the whole group as used
  1068  							continue
  1069  						}
  1070  
  1071  						obj := g.info.ObjectOf(name)
  1072  						if first == nil {
  1073  							first = obj
  1074  						} else {
  1075  							g.use(obj, prev)
  1076  						}
  1077  						prev = obj
  1078  						last = obj
  1079  					}
  1080  				}
  1081  				if first != nil && first != last {
  1082  					g.use(first, last)
  1083  				}
  1084  			}
  1085  
  1086  		case token.TYPE:
  1087  			for _, spec := range decl.Specs {
  1088  				tspec := spec.(*ast.TypeSpec)
  1089  				obj := g.info.ObjectOf(tspec.Name).(*types.TypeName)
  1090  				g.see(obj, by)
  1091  				g.seeScope(tspec, obj, nil)
  1092  				if !tspec.Assign.IsValid() {
  1093  					g.namedTypes = append(g.namedTypes, obj)
  1094  				}
  1095  				if token.IsExported(tspec.Name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed {
  1096  					// (1.1) packages use exported named types
  1097  					g.use(g.info.ObjectOf(tspec.Name), nil)
  1098  				}
  1099  
  1100  				// (2.5) named types use all their type parameters
  1101  				g.read(tspec.TypeParams, obj)
  1102  
  1103  				g.namedType(obj, tspec.Type)
  1104  
  1105  				if tspec.Name.Name == "_" {
  1106  					// (9.9) objects named the blank identifier are used
  1107  					g.use(obj, by)
  1108  				}
  1109  			}
  1110  
  1111  		case token.VAR:
  1112  			// We cannot rely on types.Initializer for package-level variables because
  1113  			// - initializers are only tracked for variables that are actually initialized
  1114  			// - we want to see the AST of the type, if specified, not just the rhs
  1115  
  1116  			for _, spec := range decl.Specs {
  1117  				vspec := spec.(*ast.ValueSpec)
  1118  				for i, name := range vspec.Names {
  1119  					obj := g.info.ObjectOf(name)
  1120  					g.see(obj, by)
  1121  					// variables and constants use their types
  1122  					g.read(vspec.Type, obj)
  1123  
  1124  					if len(vspec.Names) == len(vspec.Values) {
  1125  						// One value per variable
  1126  						g.read(vspec.Values[i], obj)
  1127  					} else if len(vspec.Values) != 0 {
  1128  						// Multiple variables initialized with a single rhs
  1129  						// assert(len(vspec.Values) == 1)
  1130  						if len(vspec.Values) != 1 {
  1131  							panic(g.fset.PositionFor(vspec.Pos(), false))
  1132  						}
  1133  						g.read(vspec.Values[0], obj)
  1134  					}
  1135  
  1136  					if token.IsExported(name.Name) && isGlobal(obj) && g.opts.ExportedIsUsed {
  1137  						// (1.3) packages use exported variables
  1138  						g.use(obj, nil)
  1139  					}
  1140  
  1141  					if name.Name == "_" {
  1142  						// (9.9) objects named the blank identifier are used
  1143  						g.use(obj, by)
  1144  					}
  1145  				}
  1146  			}
  1147  
  1148  		default:
  1149  			panic(fmt.Sprintf("unexpected token %s", decl.Tok))
  1150  		}
  1151  
  1152  	case *ast.FuncDecl:
  1153  		obj := g.info.ObjectOf(decl.Name).(*types.Func).Origin()
  1154  		g.see(obj, nil)
  1155  
  1156  		if token.IsExported(decl.Name.Name) && g.opts.ExportedIsUsed {
  1157  			if decl.Recv == nil {
  1158  				// (1.2) packages use exported functions
  1159  				g.use(obj, nil)
  1160  			}
  1161  		} else if decl.Name.Name == "init" {
  1162  			// (1.5) packages use init functions
  1163  			g.use(obj, nil)
  1164  		} else if decl.Name.Name == "main" && g.pkg.Name() == "main" {
  1165  			// (1.7) packages use the main function iff in the main package
  1166  			g.use(obj, nil)
  1167  		} else if g.pkg.Path() == "runtime" && runtimeFuncs[decl.Name.Name] {
  1168  			// (9.8) runtime functions that may be called from user code via the compiler
  1169  			g.use(obj, nil)
  1170  		} else if g.pkg.Path() == "runtime/coverage" && runtimeCoverageFuncs[decl.Name.Name] {
  1171  			// (9.8) runtime functions that may be called from user code via the compiler
  1172  			g.use(obj, nil)
  1173  		}
  1174  
  1175  		// (4.1) functions use their receivers
  1176  		g.read(decl.Recv, obj)
  1177  		g.read(decl.Type, obj)
  1178  		g.block(decl.Body, obj)
  1179  
  1180  		// g.read(decl.Type) will ultimately call g.seeScopes and see parameters that way. But because it relies
  1181  		// entirely on the AST, it cannot resolve unnamed parameters to types.Object. For that reason we explicitly
  1182  		// handle arguments here, as well as for FuncLits elsewhere.
  1183  		//
  1184  		// g.seeScopes can't get to the types.Signature for this function because there is no mapping from ast.FuncType to
  1185  		// types.Signature, only from ast.Ident to types.Signature.
  1186  		//
  1187  		// This code is only really relevant when Options.ParametersAreUsed is false. Otherwise, all parameters are
  1188  		// considered used, and if we never see a parameter then no harm done (we still see its type separately).
  1189  		fn := g.info.TypeOf(decl.Name).(*types.Signature)
  1190  		for params, i := fn.Params(), 0; i < params.Len(); i++ {
  1191  			g.see(params.At(i), obj)
  1192  			if params.At(i).Name() == "" {
  1193  				g.use(params.At(i), obj)
  1194  			}
  1195  		}
  1196  
  1197  		if decl.Name.Name == "_" {
  1198  			// (9.9) objects named the blank identifier are used
  1199  			g.use(obj, nil)
  1200  		}
  1201  
  1202  		if decl.Doc != nil {
  1203  			for _, cmt := range decl.Doc.List {
  1204  				if strings.HasPrefix(cmt.Text, "//go:cgo_export_") {
  1205  					// (1.6) packages use functions exported to cgo
  1206  					g.use(obj, nil)
  1207  				}
  1208  			}
  1209  		}
  1210  
  1211  	default:
  1212  		// We do not cover BadDecl, but we shouldn't ever see one of those
  1213  		lint.ExhaustiveTypeSwitch(decl)
  1214  	}
  1215  }
  1216  
  1217  // seeScope sees all objects in node's scope. If Options.LocalVariablesAreUsed is true, all objects that aren't fields
  1218  // are marked as used. Variables set in skipLvars will not be marked as used.
  1219  func (g *graph) seeScope(node ast.Node, by types.Object, skipLvars map[*types.Var]struct{}) {
  1220  	// A note on functions and scopes: for a function declaration, the body's BlockStmt can't be found in
  1221  	// types.Info.Scopes. Instead, the FuncType can, and that scope will contain receivers, parameters, return
  1222  	// parameters and immediate local variables.
  1223  
  1224  	scope := g.info.Scopes[node]
  1225  	if scope == nil {
  1226  		return
  1227  	}
  1228  	for _, name := range scope.Names() {
  1229  		obj := scope.Lookup(name)
  1230  		g.see(obj, by)
  1231  
  1232  		if g.opts.LocalVariablesAreUsed {
  1233  			if obj, ok := obj.(*types.Var); ok && !obj.IsField() {
  1234  				if _, ok := skipLvars[obj]; !ok {
  1235  					g.use(obj, by)
  1236  				}
  1237  			}
  1238  		}
  1239  	}
  1240  }
  1241  
  1242  func (g *graph) stmt(stmt ast.Stmt, by types.Object) {
  1243  	if stmt == nil {
  1244  		return
  1245  	}
  1246  
  1247  	for {
  1248  		// We don't care about labels, so unwrap LabeledStmts. Note that a label can itself be labeled.
  1249  		if labeled, ok := stmt.(*ast.LabeledStmt); ok {
  1250  			stmt = labeled.Stmt
  1251  		} else {
  1252  			break
  1253  		}
  1254  	}
  1255  
  1256  	switch stmt := stmt.(type) {
  1257  	case *ast.AssignStmt:
  1258  		for _, lhs := range stmt.Lhs {
  1259  			g.write(lhs, by)
  1260  		}
  1261  		for _, rhs := range stmt.Rhs {
  1262  			// Note: it would be more accurate to have the rhs used by the lhs, but it ultimately doesn't matter,
  1263  			// because local variables always end up used, anyway.
  1264  			//
  1265  			// TODO(dh): we'll have to change that once we allow tracking the usedness of parameters
  1266  			g.read(rhs, by)
  1267  		}
  1268  
  1269  	case *ast.BlockStmt:
  1270  		g.block(stmt, by)
  1271  
  1272  	case *ast.BranchStmt:
  1273  		// Nothing to do
  1274  
  1275  	case *ast.DeclStmt:
  1276  		g.decl(stmt.Decl, by)
  1277  
  1278  	case *ast.DeferStmt:
  1279  		g.read(stmt.Call, by)
  1280  
  1281  	case *ast.ExprStmt:
  1282  		g.read(stmt.X, by)
  1283  
  1284  	case *ast.ForStmt:
  1285  		g.seeScope(stmt, by, nil)
  1286  		g.stmt(stmt.Init, by)
  1287  		g.read(stmt.Cond, by)
  1288  		g.stmt(stmt.Post, by)
  1289  		g.block(stmt.Body, by)
  1290  
  1291  	case *ast.GoStmt:
  1292  		g.read(stmt.Call, by)
  1293  
  1294  	case *ast.IfStmt:
  1295  		g.seeScope(stmt, by, nil)
  1296  		g.stmt(stmt.Init, by)
  1297  		g.read(stmt.Cond, by)
  1298  		g.block(stmt.Body, by)
  1299  		g.stmt(stmt.Else, by)
  1300  
  1301  	case *ast.IncDecStmt:
  1302  		if g.opts.PostStatementsAreReads {
  1303  			g.read(stmt.X, by)
  1304  			g.write(stmt.X, by)
  1305  		} else {
  1306  			// We treat post-increment as a write only. This ends up using fields, and sinks in tests, but not other
  1307  			// variables.
  1308  			g.write(stmt.X, by)
  1309  		}
  1310  
  1311  	case *ast.RangeStmt:
  1312  		g.seeScope(stmt, by, nil)
  1313  
  1314  		g.write(stmt.Key, by)
  1315  		g.write(stmt.Value, by)
  1316  		g.read(stmt.X, by)
  1317  		g.block(stmt.Body, by)
  1318  
  1319  	case *ast.ReturnStmt:
  1320  		for _, ret := range stmt.Results {
  1321  			g.read(ret, by)
  1322  		}
  1323  
  1324  	case *ast.SelectStmt:
  1325  		for _, clause_ := range stmt.Body.List {
  1326  			clause := clause_.(*ast.CommClause)
  1327  			g.seeScope(clause, by, nil)
  1328  			switch comm := clause.Comm.(type) {
  1329  			case *ast.SendStmt:
  1330  				g.read(comm.Chan, by)
  1331  				g.read(comm.Value, by)
  1332  			case *ast.ExprStmt:
  1333  				g.read(astutil.Unparen(comm.X).(*ast.UnaryExpr).X, by)
  1334  			case *ast.AssignStmt:
  1335  				for _, lhs := range comm.Lhs {
  1336  					g.write(lhs, by)
  1337  				}
  1338  				for _, rhs := range comm.Rhs {
  1339  					g.read(rhs, by)
  1340  				}
  1341  			case nil:
  1342  			default:
  1343  				lint.ExhaustiveTypeSwitch(comm)
  1344  			}
  1345  			for _, body := range clause.Body {
  1346  				g.stmt(body, by)
  1347  			}
  1348  		}
  1349  
  1350  	case *ast.SendStmt:
  1351  		g.read(stmt.Chan, by)
  1352  		g.read(stmt.Value, by)
  1353  
  1354  	case *ast.SwitchStmt:
  1355  		g.seeScope(stmt, by, nil)
  1356  		g.stmt(stmt.Init, by)
  1357  		g.read(stmt.Tag, by)
  1358  		for _, clause_ := range stmt.Body.List {
  1359  			clause := clause_.(*ast.CaseClause)
  1360  			g.seeScope(clause, by, nil)
  1361  			for _, expr := range clause.List {
  1362  				g.read(expr, by)
  1363  			}
  1364  			for _, body := range clause.Body {
  1365  				g.stmt(body, by)
  1366  			}
  1367  		}
  1368  
  1369  	case *ast.TypeSwitchStmt:
  1370  		g.seeScope(stmt, by, nil)
  1371  		g.stmt(stmt.Init, by)
  1372  		g.stmt(stmt.Assign, by)
  1373  		for _, clause_ := range stmt.Body.List {
  1374  			clause := clause_.(*ast.CaseClause)
  1375  			g.seeScope(clause, by, nil)
  1376  			for _, expr := range clause.List {
  1377  				g.read(expr, by)
  1378  			}
  1379  			for _, body := range clause.Body {
  1380  				g.stmt(body, by)
  1381  			}
  1382  		}
  1383  
  1384  	case *ast.EmptyStmt:
  1385  		// Nothing to do
  1386  
  1387  	default:
  1388  		lint.ExhaustiveTypeSwitch(stmt)
  1389  	}
  1390  }
  1391  
  1392  // embeddedField sees the field declared by the embedded field node, and marks the type as used by the field.
  1393  //
  1394  // Embedded fields are special in two ways: they don't have names, so we don't have immediate access to an ast.Ident to
  1395  // resolve to the field's types.Var and need to instead walk the AST, and we cannot use g.read on the type because
  1396  // eventually we do get to an ast.Ident, and ObjectOf resolves embedded fields to the field they declare, not the type.
  1397  // That's why we have code specially for handling embedded fields.
  1398  func (g *graph) embeddedField(node ast.Node, by types.Object) *types.Var {
  1399  	// We need to traverse the tree to find the ast.Ident, but all the nodes we traverse should be used by the object we
  1400  	// get once we resolve the ident. Collect the nodes and process them once we've found the ident.
  1401  	nodes := make([]ast.Node, 0, 4)
  1402  	for {
  1403  		switch node_ := node.(type) {
  1404  		case *ast.Ident:
  1405  			// obj is the field
  1406  			obj := g.info.ObjectOf(node_).(*types.Var)
  1407  			// the field is declared by the enclosing type
  1408  			g.see(obj, by)
  1409  			for _, n := range nodes {
  1410  				g.read(n, obj)
  1411  			}
  1412  
  1413  			if tname, ok := g.info.Uses[node_].(*types.TypeName); ok && tname.IsAlias() {
  1414  				// When embedding an alias we want to use the alias, not what the alias points to.
  1415  				g.use(tname, obj)
  1416  			} else {
  1417  				switch typ := typeutil.Dereference(g.info.TypeOf(node_)).(type) {
  1418  				case *types.Named:
  1419  					// (7.2) fields use their types
  1420  					g.use(typ.Obj(), obj)
  1421  				case *types.Basic:
  1422  					// Nothing to do
  1423  				default:
  1424  					// Other types are only possible for aliases, which we've already handled
  1425  					lint.ExhaustiveTypeSwitch(typ)
  1426  				}
  1427  			}
  1428  			return obj
  1429  		case *ast.StarExpr:
  1430  			node = node_.X
  1431  		case *ast.SelectorExpr:
  1432  			node = node_.Sel
  1433  			nodes = append(nodes, node_.X)
  1434  		case *ast.IndexExpr:
  1435  			node = node_.X
  1436  			nodes = append(nodes, node_.Index)
  1437  		case *ast.IndexListExpr:
  1438  			node = node_.X
  1439  		default:
  1440  			lint.ExhaustiveTypeSwitch(node_)
  1441  		}
  1442  	}
  1443  }
  1444  
  1445  // isNoCopyType reports whether a type represents the NoCopy sentinel
  1446  // type. The NoCopy type is a named struct with no fields and exactly
  1447  // one method `func Lock()` that is empty.
  1448  //
  1449  // FIXME(dh): currently we're not checking that the function body is
  1450  // empty.
  1451  func isNoCopyType(typ types.Type) bool {
  1452  	st, ok := typ.Underlying().(*types.Struct)
  1453  	if !ok {
  1454  		return false
  1455  	}
  1456  	if st.NumFields() != 0 {
  1457  		return false
  1458  	}
  1459  
  1460  	named, ok := typ.(*types.Named)
  1461  	if !ok {
  1462  		return false
  1463  	}
  1464  	switch num := named.NumMethods(); num {
  1465  	case 1, 2:
  1466  		for i := 0; i < num; i++ {
  1467  			meth := named.Method(i)
  1468  			if meth.Name() != "Lock" && meth.Name() != "Unlock" {
  1469  				return false
  1470  			}
  1471  			sig := meth.Type().(*types.Signature)
  1472  			if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
  1473  				return false
  1474  			}
  1475  		}
  1476  	default:
  1477  		return false
  1478  	}
  1479  	return true
  1480  }
  1481  
  1482  func (g *graph) namedType(typ *types.TypeName, spec ast.Expr) {
  1483  	// (2.2) named types use the type they're based on
  1484  
  1485  	if st, ok := spec.(*ast.StructType); ok {
  1486  		// Named structs are special in that its unexported fields are only used if they're being written to. That is,
  1487  		// the fields are not used by the named type itself, nor are the types of the fields.
  1488  		for _, field := range st.Fields.List {
  1489  			seen := map[*types.Struct]struct{}{}
  1490  			// For `type x struct { *x; F int }`, don't visit the embedded x
  1491  			seen[g.info.TypeOf(st).(*types.Struct)] = struct{}{}
  1492  			var hasExportedField func(t types.Type) bool
  1493  			hasExportedField = func(T types.Type) bool {
  1494  				t, ok := typeutil.Dereference(T).Underlying().(*types.Struct)
  1495  				if !ok {
  1496  					return false
  1497  				}
  1498  				if _, ok := seen[t]; ok {
  1499  					return false
  1500  				}
  1501  				seen[t] = struct{}{}
  1502  				for i := 0; i < t.NumFields(); i++ {
  1503  					field := t.Field(i)
  1504  					if field.Exported() {
  1505  						return true
  1506  					}
  1507  					if field.Embedded() && hasExportedField(field.Type()) {
  1508  						return true
  1509  					}
  1510  				}
  1511  				return false
  1512  			}
  1513  
  1514  			if len(field.Names) == 0 {
  1515  				fieldVar := g.embeddedField(field.Type, typ)
  1516  				if token.IsExported(fieldVar.Name()) && g.opts.ExportedIsUsed {
  1517  					// (6.2) structs use exported fields
  1518  					g.use(fieldVar, typ)
  1519  				}
  1520  				if g.opts.ExportedIsUsed && g.opts.ExportedFieldsAreUsed && hasExportedField(fieldVar.Type()) {
  1521  					// (6.5) structs use embedded structs that have exported fields (recursively)
  1522  					g.use(fieldVar, typ)
  1523  				}
  1524  			} else {
  1525  				for _, name := range field.Names {
  1526  					obj := g.info.ObjectOf(name)
  1527  					g.see(obj, typ)
  1528  					// (7.2) fields use their types
  1529  					//
  1530  					// This handles aliases correctly because ObjectOf(alias) returns the TypeName of the alias, not
  1531  					// what the alias points to.
  1532  					g.read(field.Type, obj)
  1533  					if name.Name == "_" {
  1534  						// (9.9) objects named the blank identifier are used
  1535  						g.use(obj, typ)
  1536  					} else if token.IsExported(name.Name) && g.opts.ExportedIsUsed {
  1537  						// (6.2) structs use exported fields
  1538  						g.use(obj, typ)
  1539  					}
  1540  
  1541  					if isNoCopyType(obj.Type()) {
  1542  						// (6.1) structs use fields of type NoCopy sentinel
  1543  						g.use(obj, typ)
  1544  					}
  1545  				}
  1546  			}
  1547  
  1548  		}
  1549  	} else {
  1550  		g.read(spec, typ)
  1551  	}
  1552  }
  1553  
  1554  func (g *SerializedGraph) color(rootID NodeID, states []nodeState) {
  1555  	root := g.nodes[rootID]
  1556  	if states[rootID].seen() {
  1557  		return
  1558  	}
  1559  	states[rootID] |= nodeStateSeen
  1560  	for _, n := range root.uses {
  1561  		g.color(n, states)
  1562  	}
  1563  }
  1564  
  1565  type Object struct {
  1566  	Name      string
  1567  	ShortName string
  1568  	// OPT(dh): use an enum for the kind
  1569  	Kind            string
  1570  	Path            ObjectPath
  1571  	Position        token.Position
  1572  	DisplayPosition token.Position
  1573  }
  1574  
  1575  func (g *SerializedGraph) Results() Result {
  1576  	// XXX objectpath does not return paths for unexported objects, which means that if we analyze the same code twice
  1577  	// (e.g. normal and test variant), then some objects will appear multiple times, but may not be used identically. we
  1578  	// have to deduplicate based on the token.Position. Actually we have to do that, anyway, because we may flag types
  1579  	// local to functions. Those are probably always both used or both unused, but we don't want to flag them twice,
  1580  	// either.
  1581  	//
  1582  	// Note, however, that we still need objectpaths to deduplicate exported identifiers when analyzing independent
  1583  	// packages in whole-program mode, because if package A uses an object from package B, B will have been imported
  1584  	// from export data, and we will not have column information.
  1585  	//
  1586  	// XXX ^ document that design requirement.
  1587  
  1588  	states := g.colorAndQuieten()
  1589  
  1590  	var res Result
  1591  	// OPT(dh): can we find meaningful initial capacities for the used and unused slices?
  1592  	for _, n := range g.nodes[1:] {
  1593  		state := states[n.id]
  1594  		if state.seen() {
  1595  			res.Used = append(res.Used, n.obj)
  1596  		} else if state.quiet() {
  1597  			res.Quiet = append(res.Quiet, n.obj)
  1598  		} else {
  1599  			res.Unused = append(res.Unused, n.obj)
  1600  		}
  1601  	}
  1602  
  1603  	return res
  1604  }
  1605  
  1606  func (g *SerializedGraph) colorAndQuieten() []nodeState {
  1607  	states := make([]nodeState, len(g.nodes)+1)
  1608  	g.color(0, states)
  1609  
  1610  	var quieten func(id NodeID)
  1611  	quieten = func(id NodeID) {
  1612  		states[id] |= nodeStateQuiet
  1613  		for _, owned := range g.nodes[id].owns {
  1614  			quieten(owned)
  1615  		}
  1616  	}
  1617  
  1618  	for _, n := range g.nodes {
  1619  		if states[n.id].seen() {
  1620  			continue
  1621  		}
  1622  		for _, owned := range n.owns {
  1623  			quieten(owned)
  1624  		}
  1625  	}
  1626  
  1627  	return states
  1628  }
  1629  
  1630  // Dot formats a graph in Graphviz dot format.
  1631  func (g *SerializedGraph) Dot() string {
  1632  	b := &strings.Builder{}
  1633  	states := g.colorAndQuieten()
  1634  	// Note: We use addresses in our node names. This only works as long as Go's garbage collector doesn't move
  1635  	// memory around in the middle of our debug printing.
  1636  	debugNode := func(n Node) {
  1637  		if n.id == 0 {
  1638  			fmt.Fprintf(b, "n%d [label=\"Root\"];\n", n.id)
  1639  		} else {
  1640  			color := "red"
  1641  			if states[n.id].seen() {
  1642  				color = "green"
  1643  			} else if states[n.id].quiet() {
  1644  				color = "grey"
  1645  			}
  1646  			label := fmt.Sprintf("%s %s\n%s", n.obj.Kind, n.obj.Name, n.obj.Position)
  1647  			fmt.Fprintf(b, "n%d [label=%q, color=%q];\n", n.id, label, color)
  1648  		}
  1649  		for _, e := range n.uses {
  1650  			fmt.Fprintf(b, "n%d -> n%d;\n", n.id, e)
  1651  		}
  1652  
  1653  		for _, owned := range n.owns {
  1654  			fmt.Fprintf(b, "n%d -> n%d [style=dashed];\n", n.id, owned)
  1655  		}
  1656  	}
  1657  
  1658  	fmt.Fprintf(b, "digraph{\n")
  1659  	for _, v := range g.nodes {
  1660  		debugNode(v)
  1661  	}
  1662  
  1663  	fmt.Fprintf(b, "}\n")
  1664  
  1665  	return b.String()
  1666  }
  1667  
  1668  func Graph(fset *token.FileSet,
  1669  	files []*ast.File,
  1670  	pkg *types.Package,
  1671  	info *types.Info,
  1672  	directives []lint.Directive,
  1673  	generated map[string]generated.Generator,
  1674  	opts Options,
  1675  ) []Node {
  1676  	g := newGraph(fset, files, pkg, info, directives, generated, opts)
  1677  	g.entry()
  1678  	return g.nodes
  1679  }