github.com/pankona/gometalinter@v2.0.11+incompatible/_linters/src/honnef.co/go/tools/unused/unused.go (about)

     1  package unused // import "honnef.co/go/tools/unused"
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/token"
     7  	"go/types"
     8  	"io"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"honnef.co/go/tools/lint"
    13  
    14  	"golang.org/x/tools/go/loader"
    15  	"golang.org/x/tools/go/types/typeutil"
    16  )
    17  
    18  func NewLintChecker(c *Checker) *LintChecker {
    19  	l := &LintChecker{
    20  		c: c,
    21  	}
    22  	return l
    23  }
    24  
    25  type LintChecker struct {
    26  	c *Checker
    27  }
    28  
    29  func (*LintChecker) Name() string   { return "unused" }
    30  func (*LintChecker) Prefix() string { return "U" }
    31  
    32  func (l *LintChecker) Init(*lint.Program) {}
    33  func (l *LintChecker) Funcs() map[string]lint.Func {
    34  	return map[string]lint.Func{
    35  		"U1000": l.Lint,
    36  	}
    37  }
    38  
    39  func typString(obj types.Object) string {
    40  	switch obj := obj.(type) {
    41  	case *types.Func:
    42  		return "func"
    43  	case *types.Var:
    44  		if obj.IsField() {
    45  			return "field"
    46  		}
    47  		return "var"
    48  	case *types.Const:
    49  		return "const"
    50  	case *types.TypeName:
    51  		return "type"
    52  	default:
    53  		// log.Printf("%T", obj)
    54  		return "identifier"
    55  	}
    56  }
    57  
    58  func (l *LintChecker) Lint(j *lint.Job) {
    59  	unused := l.c.Check(j.Program.Prog)
    60  	for _, u := range unused {
    61  		name := u.Obj.Name()
    62  		if sig, ok := u.Obj.Type().(*types.Signature); ok && sig.Recv() != nil {
    63  			switch sig.Recv().Type().(type) {
    64  			case *types.Named, *types.Pointer:
    65  				typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" })
    66  				if len(typ) > 0 && typ[0] == '*' {
    67  					name = fmt.Sprintf("(%s).%s", typ, u.Obj.Name())
    68  				} else if len(typ) > 0 {
    69  					name = fmt.Sprintf("%s.%s", typ, u.Obj.Name())
    70  				}
    71  			}
    72  		}
    73  		j.Errorf(u.Obj, "%s %s is unused", typString(u.Obj), name)
    74  	}
    75  }
    76  
    77  type graph struct {
    78  	roots []*graphNode
    79  	nodes map[interface{}]*graphNode
    80  }
    81  
    82  func (g *graph) markUsedBy(obj, usedBy interface{}) {
    83  	objNode := g.getNode(obj)
    84  	usedByNode := g.getNode(usedBy)
    85  	if objNode.obj == usedByNode.obj {
    86  		return
    87  	}
    88  	usedByNode.uses[objNode] = struct{}{}
    89  }
    90  
    91  var labelCounter = 1
    92  
    93  func (g *graph) getNode(obj interface{}) *graphNode {
    94  	for {
    95  		if pt, ok := obj.(*types.Pointer); ok {
    96  			obj = pt.Elem()
    97  		} else {
    98  			break
    99  		}
   100  	}
   101  	_, ok := g.nodes[obj]
   102  	if !ok {
   103  		g.addObj(obj)
   104  	}
   105  
   106  	return g.nodes[obj]
   107  }
   108  
   109  func (g *graph) addObj(obj interface{}) {
   110  	if pt, ok := obj.(*types.Pointer); ok {
   111  		obj = pt.Elem()
   112  	}
   113  	node := &graphNode{obj: obj, uses: make(map[*graphNode]struct{}), n: labelCounter}
   114  	g.nodes[obj] = node
   115  	labelCounter++
   116  
   117  	if obj, ok := obj.(*types.Struct); ok {
   118  		n := obj.NumFields()
   119  		for i := 0; i < n; i++ {
   120  			field := obj.Field(i)
   121  			g.markUsedBy(obj, field)
   122  		}
   123  	}
   124  }
   125  
   126  type graphNode struct {
   127  	obj   interface{}
   128  	uses  map[*graphNode]struct{}
   129  	used  bool
   130  	quiet bool
   131  	n     int
   132  }
   133  
   134  type CheckMode int
   135  
   136  const (
   137  	CheckConstants CheckMode = 1 << iota
   138  	CheckFields
   139  	CheckFunctions
   140  	CheckTypes
   141  	CheckVariables
   142  
   143  	CheckAll = CheckConstants | CheckFields | CheckFunctions | CheckTypes | CheckVariables
   144  )
   145  
   146  type Unused struct {
   147  	Obj      types.Object
   148  	Position token.Position
   149  }
   150  
   151  type Checker struct {
   152  	Mode               CheckMode
   153  	WholeProgram       bool
   154  	ConsiderReflection bool
   155  	Debug              io.Writer
   156  
   157  	graph *graph
   158  
   159  	msCache      typeutil.MethodSetCache
   160  	lprog        *loader.Program
   161  	topmostCache map[*types.Scope]*types.Scope
   162  	interfaces   []*types.Interface
   163  }
   164  
   165  func NewChecker(mode CheckMode) *Checker {
   166  	return &Checker{
   167  		Mode: mode,
   168  		graph: &graph{
   169  			nodes: make(map[interface{}]*graphNode),
   170  		},
   171  		topmostCache: make(map[*types.Scope]*types.Scope),
   172  	}
   173  }
   174  
   175  func (c *Checker) checkConstants() bool { return (c.Mode & CheckConstants) > 0 }
   176  func (c *Checker) checkFields() bool    { return (c.Mode & CheckFields) > 0 }
   177  func (c *Checker) checkFunctions() bool { return (c.Mode & CheckFunctions) > 0 }
   178  func (c *Checker) checkTypes() bool     { return (c.Mode & CheckTypes) > 0 }
   179  func (c *Checker) checkVariables() bool { return (c.Mode & CheckVariables) > 0 }
   180  
   181  func (c *Checker) markFields(typ types.Type) {
   182  	structType, ok := typ.Underlying().(*types.Struct)
   183  	if !ok {
   184  		return
   185  	}
   186  	n := structType.NumFields()
   187  	for i := 0; i < n; i++ {
   188  		field := structType.Field(i)
   189  		c.graph.markUsedBy(field, typ)
   190  	}
   191  }
   192  
   193  type Error struct {
   194  	Errors map[string][]error
   195  }
   196  
   197  func (e Error) Error() string {
   198  	return fmt.Sprintf("errors in %d packages", len(e.Errors))
   199  }
   200  
   201  func (c *Checker) Check(lprog *loader.Program) []Unused {
   202  	var unused []Unused
   203  	c.lprog = lprog
   204  	if c.WholeProgram {
   205  		c.findExportedInterfaces()
   206  	}
   207  	for _, pkg := range c.lprog.InitialPackages() {
   208  		c.processDefs(pkg)
   209  		c.processUses(pkg)
   210  		c.processTypes(pkg)
   211  		c.processSelections(pkg)
   212  		c.processAST(pkg)
   213  	}
   214  
   215  	for _, node := range c.graph.nodes {
   216  		obj, ok := node.obj.(types.Object)
   217  		if !ok {
   218  			continue
   219  		}
   220  		typNode, ok := c.graph.nodes[obj.Type()]
   221  		if !ok {
   222  			continue
   223  		}
   224  		node.uses[typNode] = struct{}{}
   225  	}
   226  
   227  	roots := map[*graphNode]struct{}{}
   228  	for _, root := range c.graph.roots {
   229  		roots[root] = struct{}{}
   230  	}
   231  	markNodesUsed(roots)
   232  	c.markNodesQuiet()
   233  
   234  	if c.Debug != nil {
   235  		c.printDebugGraph(c.Debug)
   236  	}
   237  
   238  	for _, node := range c.graph.nodes {
   239  		if node.used || node.quiet {
   240  			continue
   241  		}
   242  		obj, ok := node.obj.(types.Object)
   243  		if !ok {
   244  			continue
   245  		}
   246  		found := false
   247  		if !false {
   248  			for _, pkg := range c.lprog.InitialPackages() {
   249  				if pkg.Pkg == obj.Pkg() {
   250  					found = true
   251  					break
   252  				}
   253  			}
   254  		}
   255  		if !found {
   256  			continue
   257  		}
   258  
   259  		pos := c.lprog.Fset.Position(obj.Pos())
   260  		if pos.Filename == "" || filepath.Base(pos.Filename) == "C" {
   261  			continue
   262  		}
   263  		generated := false
   264  		for _, file := range c.lprog.Package(obj.Pkg().Path()).Files {
   265  			if c.lprog.Fset.Position(file.Pos()).Filename != pos.Filename {
   266  				continue
   267  			}
   268  			if len(file.Comments) > 0 {
   269  				generated = isGenerated(file.Comments[0].Text())
   270  			}
   271  			break
   272  		}
   273  		if generated {
   274  			continue
   275  		}
   276  		unused = append(unused, Unused{Obj: obj, Position: pos})
   277  	}
   278  	return unused
   279  }
   280  
   281  // isNoCopyType reports whether a type represents the NoCopy sentinel
   282  // type. The NoCopy type is a named struct with no fields and exactly
   283  // one method `func Lock()` that is empty.
   284  //
   285  // FIXME(dh): currently we're not checking that the function body is
   286  // empty.
   287  func isNoCopyType(typ types.Type) bool {
   288  	st, ok := typ.Underlying().(*types.Struct)
   289  	if !ok {
   290  		return false
   291  	}
   292  	if st.NumFields() != 0 {
   293  		return false
   294  	}
   295  
   296  	named, ok := typ.(*types.Named)
   297  	if !ok {
   298  		return false
   299  	}
   300  	if named.NumMethods() != 1 {
   301  		return false
   302  	}
   303  	meth := named.Method(0)
   304  	if meth.Name() != "Lock" {
   305  		return false
   306  	}
   307  	sig := meth.Type().(*types.Signature)
   308  	if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
   309  		return false
   310  	}
   311  	return true
   312  }
   313  
   314  func (c *Checker) useNoCopyFields(typ types.Type) {
   315  	if st, ok := typ.Underlying().(*types.Struct); ok {
   316  		n := st.NumFields()
   317  		for i := 0; i < n; i++ {
   318  			field := st.Field(i)
   319  			if isNoCopyType(field.Type()) {
   320  				c.graph.markUsedBy(field, typ)
   321  			}
   322  		}
   323  	}
   324  }
   325  
   326  func (c *Checker) useExportedFields(typ types.Type) {
   327  	if st, ok := typ.Underlying().(*types.Struct); ok {
   328  		n := st.NumFields()
   329  		for i := 0; i < n; i++ {
   330  			field := st.Field(i)
   331  			if field.Exported() {
   332  				c.graph.markUsedBy(field, typ)
   333  			}
   334  		}
   335  	}
   336  }
   337  
   338  func (c *Checker) useExportedMethods(typ types.Type) {
   339  	named, ok := typ.(*types.Named)
   340  	if !ok {
   341  		return
   342  	}
   343  	ms := typeutil.IntuitiveMethodSet(named, &c.msCache)
   344  	for i := 0; i < len(ms); i++ {
   345  		meth := ms[i].Obj()
   346  		if meth.Exported() {
   347  			c.graph.markUsedBy(meth, typ)
   348  		}
   349  	}
   350  
   351  	st, ok := named.Underlying().(*types.Struct)
   352  	if !ok {
   353  		return
   354  	}
   355  	n := st.NumFields()
   356  	for i := 0; i < n; i++ {
   357  		field := st.Field(i)
   358  		if !field.Anonymous() {
   359  			continue
   360  		}
   361  		ms := typeutil.IntuitiveMethodSet(field.Type(), &c.msCache)
   362  		for j := 0; j < len(ms); j++ {
   363  			if ms[j].Obj().Exported() {
   364  				c.graph.markUsedBy(field, typ)
   365  				break
   366  			}
   367  		}
   368  	}
   369  }
   370  
   371  func (c *Checker) processDefs(pkg *loader.PackageInfo) {
   372  	for _, obj := range pkg.Defs {
   373  		if obj == nil {
   374  			continue
   375  		}
   376  		c.graph.getNode(obj)
   377  
   378  		if obj, ok := obj.(*types.TypeName); ok {
   379  			c.graph.markUsedBy(obj.Type().Underlying(), obj.Type())
   380  			c.graph.markUsedBy(obj.Type(), obj) // TODO is this needed?
   381  			c.graph.markUsedBy(obj, obj.Type())
   382  
   383  			// We mark all exported fields as used. For normal
   384  			// operation, we have to. The user may use these fields
   385  			// without us knowing.
   386  			//
   387  			// TODO(dh): In whole-program mode, however, we mark them
   388  			// as used because of reflection (such as JSON
   389  			// marshaling). Strictly speaking, we would only need to
   390  			// mark them used if an instance of the type was
   391  			// accessible via an interface value.
   392  			if !c.WholeProgram || c.ConsiderReflection {
   393  				c.useExportedFields(obj.Type())
   394  			}
   395  
   396  			// TODO(dh): Traditionally we have not marked all exported
   397  			// methods as exported, even though they're strictly
   398  			// speaking accessible through reflection. We've done that
   399  			// because using methods just via reflection is rare, and
   400  			// not worth the false negatives. With the new -reflect
   401  			// flag, however, we should reconsider that choice.
   402  			if !c.WholeProgram {
   403  				c.useExportedMethods(obj.Type())
   404  			}
   405  		}
   406  
   407  		switch obj := obj.(type) {
   408  		case *types.Var, *types.Const, *types.Func, *types.TypeName:
   409  			if obj.Exported() {
   410  				// Exported variables and constants use their types,
   411  				// even if there's no expression using them in the
   412  				// checked program.
   413  				//
   414  				// Also operates on funcs and type names, but that's
   415  				// irrelevant/redundant.
   416  				c.graph.markUsedBy(obj.Type(), obj)
   417  			}
   418  			if obj.Name() == "_" {
   419  				node := c.graph.getNode(obj)
   420  				node.quiet = true
   421  				scope := c.topmostScope(pkg.Pkg.Scope().Innermost(obj.Pos()), pkg.Pkg)
   422  				if scope == pkg.Pkg.Scope() {
   423  					c.graph.roots = append(c.graph.roots, node)
   424  				} else {
   425  					c.graph.markUsedBy(obj, scope)
   426  				}
   427  			} else {
   428  				// Variables declared in functions are used. This is
   429  				// done so that arguments and return parameters are
   430  				// always marked as used.
   431  				if _, ok := obj.(*types.Var); ok {
   432  					if obj.Parent() != obj.Pkg().Scope() && obj.Parent() != nil {
   433  						c.graph.markUsedBy(obj, c.topmostScope(obj.Parent(), obj.Pkg()))
   434  						c.graph.markUsedBy(obj.Type(), obj)
   435  					}
   436  				}
   437  			}
   438  		}
   439  
   440  		if fn, ok := obj.(*types.Func); ok {
   441  			// A function uses its signature
   442  			c.graph.markUsedBy(fn, fn.Type())
   443  
   444  			// A function uses its return types
   445  			sig := fn.Type().(*types.Signature)
   446  			res := sig.Results()
   447  			n := res.Len()
   448  			for i := 0; i < n; i++ {
   449  				c.graph.markUsedBy(res.At(i).Type(), fn)
   450  			}
   451  		}
   452  
   453  		if obj, ok := obj.(interface {
   454  			Scope() *types.Scope
   455  			Pkg() *types.Package
   456  		}); ok {
   457  			scope := obj.Scope()
   458  			c.graph.markUsedBy(c.topmostScope(scope, obj.Pkg()), obj)
   459  		}
   460  
   461  		if c.isRoot(obj) {
   462  			node := c.graph.getNode(obj)
   463  			c.graph.roots = append(c.graph.roots, node)
   464  			if obj, ok := obj.(*types.PkgName); ok {
   465  				scope := obj.Pkg().Scope()
   466  				c.graph.markUsedBy(scope, obj)
   467  			}
   468  		}
   469  	}
   470  }
   471  
   472  func (c *Checker) processUses(pkg *loader.PackageInfo) {
   473  	for ident, usedObj := range pkg.Uses {
   474  		if _, ok := usedObj.(*types.PkgName); ok {
   475  			continue
   476  		}
   477  		pos := ident.Pos()
   478  		scope := pkg.Pkg.Scope().Innermost(pos)
   479  		scope = c.topmostScope(scope, pkg.Pkg)
   480  		if scope != pkg.Pkg.Scope() {
   481  			c.graph.markUsedBy(usedObj, scope)
   482  		}
   483  
   484  		switch usedObj.(type) {
   485  		case *types.Var, *types.Const:
   486  			c.graph.markUsedBy(usedObj.Type(), usedObj)
   487  		}
   488  	}
   489  }
   490  
   491  func (c *Checker) findExportedInterfaces() {
   492  	c.interfaces = []*types.Interface{types.Universe.Lookup("error").Type().(*types.Named).Underlying().(*types.Interface)}
   493  	var pkgs []*loader.PackageInfo
   494  	if c.WholeProgram {
   495  		for _, pkg := range c.lprog.AllPackages {
   496  			pkgs = append(pkgs, pkg)
   497  		}
   498  	} else {
   499  		pkgs = c.lprog.InitialPackages()
   500  	}
   501  
   502  	for _, pkg := range pkgs {
   503  		for _, tv := range pkg.Types {
   504  			iface, ok := tv.Type.(*types.Interface)
   505  			if !ok {
   506  				continue
   507  			}
   508  			if iface.NumMethods() == 0 {
   509  				continue
   510  			}
   511  			c.interfaces = append(c.interfaces, iface)
   512  		}
   513  	}
   514  }
   515  
   516  func (c *Checker) processTypes(pkg *loader.PackageInfo) {
   517  	named := map[*types.Named]*types.Pointer{}
   518  	var interfaces []*types.Interface
   519  	for _, tv := range pkg.Types {
   520  		if typ, ok := tv.Type.(interface {
   521  			Elem() types.Type
   522  		}); ok {
   523  			c.graph.markUsedBy(typ.Elem(), typ)
   524  		}
   525  
   526  		switch obj := tv.Type.(type) {
   527  		case *types.Named:
   528  			named[obj] = types.NewPointer(obj)
   529  			c.graph.markUsedBy(obj, obj.Underlying())
   530  			c.graph.markUsedBy(obj.Underlying(), obj)
   531  		case *types.Interface:
   532  			if obj.NumMethods() > 0 {
   533  				interfaces = append(interfaces, obj)
   534  			}
   535  		case *types.Struct:
   536  			c.useNoCopyFields(obj)
   537  			if pkg.Pkg.Name() != "main" && !c.WholeProgram {
   538  				c.useExportedFields(obj)
   539  			}
   540  		}
   541  	}
   542  
   543  	// Pretend that all types are meant to implement as many
   544  	// interfaces as possible.
   545  	//
   546  	// TODO(dh): For normal operations, that's the best we can do, as
   547  	// we have no idea what external users will do with our types. In
   548  	// whole-program mode, we could be more conservative, in two ways:
   549  	// 1) Only consider interfaces if a type has been assigned to one
   550  	// 2) Use SSA and flow analysis and determine the exact set of
   551  	// interfaces that is relevant.
   552  	fn := func(iface *types.Interface) {
   553  		for obj, objPtr := range named {
   554  			if !types.Implements(obj, iface) && !types.Implements(objPtr, iface) {
   555  				continue
   556  			}
   557  			ifaceMethods := make(map[string]struct{}, iface.NumMethods())
   558  			n := iface.NumMethods()
   559  			for i := 0; i < n; i++ {
   560  				meth := iface.Method(i)
   561  				ifaceMethods[meth.Name()] = struct{}{}
   562  			}
   563  			for _, obj := range []types.Type{obj, objPtr} {
   564  				ms := c.msCache.MethodSet(obj)
   565  				n := ms.Len()
   566  				for i := 0; i < n; i++ {
   567  					sel := ms.At(i)
   568  					meth := sel.Obj().(*types.Func)
   569  					_, found := ifaceMethods[meth.Name()]
   570  					if !found {
   571  						continue
   572  					}
   573  					c.graph.markUsedBy(meth.Type().(*types.Signature).Recv().Type(), obj) // embedded receiver
   574  					if len(sel.Index()) > 1 {
   575  						f := getField(obj, sel.Index()[0])
   576  						c.graph.markUsedBy(f, obj) // embedded receiver
   577  					}
   578  					c.graph.markUsedBy(meth, obj)
   579  				}
   580  			}
   581  		}
   582  	}
   583  
   584  	for _, iface := range interfaces {
   585  		fn(iface)
   586  	}
   587  	for _, iface := range c.interfaces {
   588  		fn(iface)
   589  	}
   590  }
   591  
   592  func (c *Checker) processSelections(pkg *loader.PackageInfo) {
   593  	fn := func(expr *ast.SelectorExpr, sel *types.Selection, offset int) {
   594  		scope := pkg.Pkg.Scope().Innermost(expr.Pos())
   595  		c.graph.markUsedBy(expr.X, c.topmostScope(scope, pkg.Pkg))
   596  		c.graph.markUsedBy(sel.Obj(), expr.X)
   597  		if len(sel.Index()) > 1 {
   598  			typ := sel.Recv()
   599  			indices := sel.Index()
   600  			for _, idx := range indices[:len(indices)-offset] {
   601  				obj := getField(typ, idx)
   602  				typ = obj.Type()
   603  				c.graph.markUsedBy(obj, expr.X)
   604  			}
   605  		}
   606  	}
   607  
   608  	for expr, sel := range pkg.Selections {
   609  		switch sel.Kind() {
   610  		case types.FieldVal:
   611  			fn(expr, sel, 0)
   612  		case types.MethodVal:
   613  			fn(expr, sel, 1)
   614  		}
   615  	}
   616  }
   617  
   618  func dereferenceType(typ types.Type) types.Type {
   619  	if typ, ok := typ.(*types.Pointer); ok {
   620  		return typ.Elem()
   621  	}
   622  	return typ
   623  }
   624  
   625  // processConversion marks fields as used if they're part of a type conversion.
   626  func (c *Checker) processConversion(pkg *loader.PackageInfo, node ast.Node) {
   627  	if node, ok := node.(*ast.CallExpr); ok {
   628  		callTyp := pkg.TypeOf(node.Fun)
   629  		var typDst *types.Struct
   630  		var ok bool
   631  		switch typ := callTyp.(type) {
   632  		case *types.Named:
   633  			typDst, ok = typ.Underlying().(*types.Struct)
   634  		case *types.Pointer:
   635  			typDst, ok = typ.Elem().Underlying().(*types.Struct)
   636  		default:
   637  			return
   638  		}
   639  		if !ok {
   640  			return
   641  		}
   642  
   643  		if typ, ok := pkg.TypeOf(node.Args[0]).(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
   644  			// This is an unsafe conversion. Assume that all the
   645  			// fields are relevant (they are, because of memory
   646  			// layout)
   647  			n := typDst.NumFields()
   648  			for i := 0; i < n; i++ {
   649  				c.graph.markUsedBy(typDst.Field(i), typDst)
   650  			}
   651  			return
   652  		}
   653  
   654  		typSrc, ok := dereferenceType(pkg.TypeOf(node.Args[0])).Underlying().(*types.Struct)
   655  		if !ok {
   656  			return
   657  		}
   658  
   659  		// When we convert from type t1 to t2, were t1 and t2 are
   660  		// structs, all fields are relevant, as otherwise the
   661  		// conversion would fail.
   662  		//
   663  		// We mark t2's fields as used by t1's fields, and vice
   664  		// versa. That way, if no code actually refers to a field
   665  		// in either type, it's still correctly marked as unused.
   666  		// If a field is used in either struct, it's implicitly
   667  		// relevant in the other one, too.
   668  		//
   669  		// It works in a similar way for conversions between types
   670  		// of two packages, only that the extra information in the
   671  		// graph is redundant unless we're in whole program mode.
   672  		n := typDst.NumFields()
   673  		for i := 0; i < n; i++ {
   674  			fDst := typDst.Field(i)
   675  			fSrc := typSrc.Field(i)
   676  			c.graph.markUsedBy(fDst, fSrc)
   677  			c.graph.markUsedBy(fSrc, fDst)
   678  		}
   679  	}
   680  }
   681  
   682  // processCompositeLiteral marks fields as used if the struct is used
   683  // in a composite literal.
   684  func (c *Checker) processCompositeLiteral(pkg *loader.PackageInfo, node ast.Node) {
   685  	// XXX how does this actually work? wouldn't it match t{}?
   686  	if node, ok := node.(*ast.CompositeLit); ok {
   687  		typ := pkg.TypeOf(node)
   688  		if _, ok := typ.(*types.Named); ok {
   689  			typ = typ.Underlying()
   690  		}
   691  		if _, ok := typ.(*types.Struct); !ok {
   692  			return
   693  		}
   694  
   695  		if isBasicStruct(node.Elts) {
   696  			c.markFields(typ)
   697  		}
   698  	}
   699  }
   700  
   701  // processCgoExported marks functions as used if they're being
   702  // exported to cgo.
   703  func (c *Checker) processCgoExported(pkg *loader.PackageInfo, node ast.Node) {
   704  	if node, ok := node.(*ast.FuncDecl); ok {
   705  		if node.Doc == nil {
   706  			return
   707  		}
   708  		for _, cmt := range node.Doc.List {
   709  			if !strings.HasPrefix(cmt.Text, "//go:cgo_export_") {
   710  				return
   711  			}
   712  			obj := pkg.ObjectOf(node.Name)
   713  			c.graph.roots = append(c.graph.roots, c.graph.getNode(obj))
   714  		}
   715  	}
   716  }
   717  
   718  func (c *Checker) processVariableDeclaration(pkg *loader.PackageInfo, node ast.Node) {
   719  	if decl, ok := node.(*ast.GenDecl); ok {
   720  		for _, spec := range decl.Specs {
   721  			spec, ok := spec.(*ast.ValueSpec)
   722  			if !ok {
   723  				continue
   724  			}
   725  			for i, name := range spec.Names {
   726  				if i >= len(spec.Values) {
   727  					break
   728  				}
   729  				value := spec.Values[i]
   730  				fn := func(node ast.Node) bool {
   731  					if node3, ok := node.(*ast.Ident); ok {
   732  						obj := pkg.ObjectOf(node3)
   733  						if _, ok := obj.(*types.PkgName); ok {
   734  							return true
   735  						}
   736  						c.graph.markUsedBy(obj, pkg.ObjectOf(name))
   737  					}
   738  					return true
   739  				}
   740  				ast.Inspect(value, fn)
   741  			}
   742  		}
   743  	}
   744  }
   745  
   746  func (c *Checker) processArrayConstants(pkg *loader.PackageInfo, node ast.Node) {
   747  	if decl, ok := node.(*ast.ArrayType); ok {
   748  		ident, ok := decl.Len.(*ast.Ident)
   749  		if !ok {
   750  			return
   751  		}
   752  		c.graph.markUsedBy(pkg.ObjectOf(ident), pkg.TypeOf(decl))
   753  	}
   754  }
   755  
   756  func (c *Checker) processKnownReflectMethodCallers(pkg *loader.PackageInfo, node ast.Node) {
   757  	call, ok := node.(*ast.CallExpr)
   758  	if !ok {
   759  		return
   760  	}
   761  	sel, ok := call.Fun.(*ast.SelectorExpr)
   762  	if !ok {
   763  		return
   764  	}
   765  	if types.TypeString(pkg.TypeOf(sel.X), nil) != "*net/rpc.Server" {
   766  		x, ok := sel.X.(*ast.Ident)
   767  		if !ok {
   768  			return
   769  		}
   770  		pkgname, ok := pkg.ObjectOf(x).(*types.PkgName)
   771  		if !ok {
   772  			return
   773  		}
   774  		if pkgname.Imported().Path() != "net/rpc" {
   775  			return
   776  		}
   777  	}
   778  
   779  	var arg ast.Expr
   780  	switch sel.Sel.Name {
   781  	case "Register":
   782  		if len(call.Args) != 1 {
   783  			return
   784  		}
   785  		arg = call.Args[0]
   786  	case "RegisterName":
   787  		if len(call.Args) != 2 {
   788  			return
   789  		}
   790  		arg = call.Args[1]
   791  	}
   792  	typ := pkg.TypeOf(arg)
   793  	ms := types.NewMethodSet(typ)
   794  	for i := 0; i < ms.Len(); i++ {
   795  		c.graph.markUsedBy(ms.At(i).Obj(), typ)
   796  	}
   797  }
   798  
   799  func (c *Checker) processAST(pkg *loader.PackageInfo) {
   800  	fn := func(node ast.Node) bool {
   801  		c.processConversion(pkg, node)
   802  		c.processKnownReflectMethodCallers(pkg, node)
   803  		c.processCompositeLiteral(pkg, node)
   804  		c.processCgoExported(pkg, node)
   805  		c.processVariableDeclaration(pkg, node)
   806  		c.processArrayConstants(pkg, node)
   807  		return true
   808  	}
   809  	for _, file := range pkg.Files {
   810  		ast.Inspect(file, fn)
   811  	}
   812  }
   813  
   814  func isBasicStruct(elts []ast.Expr) bool {
   815  	for _, elt := range elts {
   816  		if _, ok := elt.(*ast.KeyValueExpr); !ok {
   817  			return true
   818  		}
   819  	}
   820  	return false
   821  }
   822  
   823  func isPkgScope(obj types.Object) bool {
   824  	return obj.Parent() == obj.Pkg().Scope()
   825  }
   826  
   827  func isMain(obj types.Object) bool {
   828  	if obj.Pkg().Name() != "main" {
   829  		return false
   830  	}
   831  	if obj.Name() != "main" {
   832  		return false
   833  	}
   834  	if !isPkgScope(obj) {
   835  		return false
   836  	}
   837  	if !isFunction(obj) {
   838  		return false
   839  	}
   840  	if isMethod(obj) {
   841  		return false
   842  	}
   843  	return true
   844  }
   845  
   846  func isFunction(obj types.Object) bool {
   847  	_, ok := obj.(*types.Func)
   848  	return ok
   849  }
   850  
   851  func isMethod(obj types.Object) bool {
   852  	if !isFunction(obj) {
   853  		return false
   854  	}
   855  	return obj.(*types.Func).Type().(*types.Signature).Recv() != nil
   856  }
   857  
   858  func isVariable(obj types.Object) bool {
   859  	_, ok := obj.(*types.Var)
   860  	return ok
   861  }
   862  
   863  func isConstant(obj types.Object) bool {
   864  	_, ok := obj.(*types.Const)
   865  	return ok
   866  }
   867  
   868  func isType(obj types.Object) bool {
   869  	_, ok := obj.(*types.TypeName)
   870  	return ok
   871  }
   872  
   873  func isField(obj types.Object) bool {
   874  	if obj, ok := obj.(*types.Var); ok && obj.IsField() {
   875  		return true
   876  	}
   877  	return false
   878  }
   879  
   880  func (c *Checker) checkFlags(v interface{}) bool {
   881  	obj, ok := v.(types.Object)
   882  	if !ok {
   883  		return false
   884  	}
   885  	if isFunction(obj) && !c.checkFunctions() {
   886  		return false
   887  	}
   888  	if isVariable(obj) && !c.checkVariables() {
   889  		return false
   890  	}
   891  	if isConstant(obj) && !c.checkConstants() {
   892  		return false
   893  	}
   894  	if isType(obj) && !c.checkTypes() {
   895  		return false
   896  	}
   897  	if isField(obj) && !c.checkFields() {
   898  		return false
   899  	}
   900  	return true
   901  }
   902  
   903  func (c *Checker) isRoot(obj types.Object) bool {
   904  	// - in local mode, main, init, tests, and non-test, non-main exported are roots
   905  	// - in global mode (not yet implemented), main, init and tests are roots
   906  
   907  	if _, ok := obj.(*types.PkgName); ok {
   908  		return true
   909  	}
   910  
   911  	if isMain(obj) || (isFunction(obj) && !isMethod(obj) && obj.Name() == "init") {
   912  		return true
   913  	}
   914  	if obj.Exported() {
   915  		f := c.lprog.Fset.Position(obj.Pos()).Filename
   916  		if strings.HasSuffix(f, "_test.go") {
   917  			return strings.HasPrefix(obj.Name(), "Test") ||
   918  				strings.HasPrefix(obj.Name(), "Benchmark") ||
   919  				strings.HasPrefix(obj.Name(), "Example")
   920  		}
   921  
   922  		// Package-level are used, except in package main
   923  		if isPkgScope(obj) && obj.Pkg().Name() != "main" && !c.WholeProgram {
   924  			return true
   925  		}
   926  	}
   927  	return false
   928  }
   929  
   930  func markNodesUsed(nodes map[*graphNode]struct{}) {
   931  	for node := range nodes {
   932  		wasUsed := node.used
   933  		node.used = true
   934  		if !wasUsed {
   935  			markNodesUsed(node.uses)
   936  		}
   937  	}
   938  }
   939  
   940  func (c *Checker) markNodesQuiet() {
   941  	for _, node := range c.graph.nodes {
   942  		if node.used {
   943  			continue
   944  		}
   945  		if obj, ok := node.obj.(types.Object); ok && !c.checkFlags(obj) {
   946  			node.quiet = true
   947  			continue
   948  		}
   949  		c.markObjQuiet(node.obj)
   950  	}
   951  }
   952  
   953  func (c *Checker) markObjQuiet(obj interface{}) {
   954  	switch obj := obj.(type) {
   955  	case *types.Named:
   956  		n := obj.NumMethods()
   957  		for i := 0; i < n; i++ {
   958  			meth := obj.Method(i)
   959  			node := c.graph.getNode(meth)
   960  			node.quiet = true
   961  			c.markObjQuiet(meth.Scope())
   962  		}
   963  	case *types.Struct:
   964  		n := obj.NumFields()
   965  		for i := 0; i < n; i++ {
   966  			field := obj.Field(i)
   967  			c.graph.nodes[field].quiet = true
   968  		}
   969  	case *types.Func:
   970  		c.markObjQuiet(obj.Scope())
   971  	case *types.Scope:
   972  		if obj == nil {
   973  			return
   974  		}
   975  		if obj.Parent() == types.Universe {
   976  			return
   977  		}
   978  		for _, name := range obj.Names() {
   979  			v := obj.Lookup(name)
   980  			if n, ok := c.graph.nodes[v]; ok {
   981  				n.quiet = true
   982  			}
   983  		}
   984  		n := obj.NumChildren()
   985  		for i := 0; i < n; i++ {
   986  			c.markObjQuiet(obj.Child(i))
   987  		}
   988  	}
   989  }
   990  
   991  func getField(typ types.Type, idx int) *types.Var {
   992  	switch obj := typ.(type) {
   993  	case *types.Pointer:
   994  		return getField(obj.Elem(), idx)
   995  	case *types.Named:
   996  		switch v := obj.Underlying().(type) {
   997  		case *types.Struct:
   998  			return v.Field(idx)
   999  		case *types.Pointer:
  1000  			return getField(v.Elem(), idx)
  1001  		default:
  1002  			panic(fmt.Sprintf("unexpected type %s", typ))
  1003  		}
  1004  	case *types.Struct:
  1005  		return obj.Field(idx)
  1006  	}
  1007  	return nil
  1008  }
  1009  
  1010  func (c *Checker) topmostScope(scope *types.Scope, pkg *types.Package) (ret *types.Scope) {
  1011  	if top, ok := c.topmostCache[scope]; ok {
  1012  		return top
  1013  	}
  1014  	defer func() {
  1015  		c.topmostCache[scope] = ret
  1016  	}()
  1017  	if scope == pkg.Scope() {
  1018  		return scope
  1019  	}
  1020  	if scope.Parent().Parent() == pkg.Scope() {
  1021  		return scope
  1022  	}
  1023  	return c.topmostScope(scope.Parent(), pkg)
  1024  }
  1025  
  1026  func (c *Checker) printDebugGraph(w io.Writer) {
  1027  	fmt.Fprintln(w, "digraph {")
  1028  	fmt.Fprintln(w, "n0 [label = roots]")
  1029  	for _, node := range c.graph.nodes {
  1030  		s := fmt.Sprintf("%s (%T)", node.obj, node.obj)
  1031  		s = strings.Replace(s, "\n", "", -1)
  1032  		s = strings.Replace(s, `"`, "", -1)
  1033  		fmt.Fprintf(w, `n%d [label = %q]`, node.n, s)
  1034  		color := "black"
  1035  		switch {
  1036  		case node.used:
  1037  			color = "green"
  1038  		case node.quiet:
  1039  			color = "orange"
  1040  		case !c.checkFlags(node.obj):
  1041  			color = "purple"
  1042  		default:
  1043  			color = "red"
  1044  		}
  1045  		fmt.Fprintf(w, "[color = %s]", color)
  1046  		fmt.Fprintln(w)
  1047  	}
  1048  
  1049  	for _, node1 := range c.graph.nodes {
  1050  		for node2 := range node1.uses {
  1051  			fmt.Fprintf(w, "n%d -> n%d\n", node1.n, node2.n)
  1052  		}
  1053  	}
  1054  	for _, root := range c.graph.roots {
  1055  		fmt.Fprintf(w, "n0 -> n%d\n", root.n)
  1056  	}
  1057  	fmt.Fprintln(w, "}")
  1058  }
  1059  
  1060  func isGenerated(comment string) bool {
  1061  	return strings.Contains(comment, "Code generated by") ||
  1062  		strings.Contains(comment, "DO NOT EDIT")
  1063  }