some deletions
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.0.1-2020.1.5 / unused / unused.go
diff --git a/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.0.1-2020.1.5/unused/unused.go b/.config/coc/extensions/coc-go-data/tools/pkg/mod/honnef.co/go/tools@v0.0.1-2020.1.5/unused/unused.go
deleted file mode 100644 (file)
index 0df5fc8..0000000
+++ /dev/null
@@ -1,1978 +0,0 @@
-package unused
-
-import (
-       "fmt"
-       "go/ast"
-       "go/token"
-       "go/types"
-       "io"
-       "strings"
-       "sync"
-       "sync/atomic"
-
-       "golang.org/x/tools/go/analysis"
-       "honnef.co/go/tools/code"
-       "honnef.co/go/tools/go/types/typeutil"
-       "honnef.co/go/tools/internal/passes/buildir"
-       "honnef.co/go/tools/ir"
-       "honnef.co/go/tools/lint"
-)
-
-// The graph we construct omits nodes along a path that do not
-// contribute any new information to the solution. For example, the
-// full graph for a function with a receiver would be Func ->
-// Signature -> Var -> Type. However, since signatures cannot be
-// unused, and receivers are always considered used, we can compact
-// the graph down to Func -> Type. This makes the graph smaller, but
-// harder to debug.
-
-// TODO(dh): conversions between structs mark fields as used, but the
-// conversion itself isn't part of that subgraph. even if the function
-// containing the conversion is unused, the fields will be marked as
-// used.
-
-// TODO(dh): we cannot observe function calls in assembly files.
-
-/*
-
-- packages use:
-  - (1.1) exported named types (unless in package main)
-  - (1.2) exported functions (unless in package main)
-  - (1.3) exported variables (unless in package main)
-  - (1.4) exported constants (unless in package main)
-  - (1.5) init functions
-  - (1.6) functions exported to cgo
-  - (1.7) the main function iff in the main package
-  - (1.8) symbols linked via go:linkname
-
-- named types use:
-  - (2.1) exported methods
-  - (2.2) the type they're based on
-  - (2.3) all their aliases. we can't easily track uses of aliases
-    because go/types turns them into uses of the aliased types. assume
-    that if a type is used, so are all of its aliases.
-  - (2.4) the pointer type. this aids with eagerly implementing
-    interfaces. if a method that implements an interface is defined on
-    a pointer receiver, and the pointer type is never used, but the
-    named type is, then we still want to mark the method as used.
-
-- variables and constants use:
-  - their types
-
-- functions use:
-  - (4.1) all their arguments, return parameters and receivers
-  - (4.2) anonymous functions defined beneath them
-  - (4.3) closures and bound methods.
-    this implements a simplified model where a function is used merely by being referenced, even if it is never called.
-    that way we don't have to keep track of closures escaping functions.
-  - (4.4) functions they return. we assume that someone else will call the returned function
-  - (4.5) functions/interface methods they call
-  - types they instantiate or convert to
-  - (4.7) fields they access
-  - (4.8) types of all instructions
-  - (4.9) package-level variables they assign to iff in tests (sinks for benchmarks)
-
-- conversions use:
-  - (5.1) when converting between two equivalent structs, the fields in
-    either struct use each other. the fields are relevant for the
-    conversion, but only if the fields are also accessed outside the
-    conversion.
-  - (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
-
-- structs use:
-  - (6.1) fields of type NoCopy sentinel
-  - (6.2) exported fields
-  - (6.3) embedded fields that help implement interfaces (either fully implements it, or contributes required methods) (recursively)
-  - (6.4) embedded fields that have exported methods (recursively)
-  - (6.5) embedded structs that have exported fields (recursively)
-
-- (7.1) field accesses use fields
-- (7.2) fields use their types
-
-- (8.0) How we handle interfaces:
-  - (8.1) We do not technically care about interfaces that only consist of
-    exported methods. Exported methods on concrete types are always
-    marked as used.
-  - Any concrete type implements all known interfaces. Even if it isn't
-    assigned to any interfaces in our code, the user may receive a value
-    of the type and expect to pass it back to us through an interface.
-
-    Concrete types use their methods that implement interfaces. If the
-    type is used, it uses those methods. Otherwise, it doesn't. This
-    way, types aren't incorrectly marked reachable through the edge
-    from method to type.
-
-  - (8.3) All interface methods are marked as used, even if they never get
-    called. This is to accommodate sum types (unexported interface
-    method that must exist but never gets called.)
-
-  - (8.4) All embedded interfaces are marked as used. This is an
-    extension of 8.3, but we have to explicitly track embedded
-    interfaces because in a chain C->B->A, B wouldn't be marked as
-    used by 8.3 just because it contributes A's methods to C.
-
-- Inherent uses:
-  - thunks and other generated wrappers call the real function
-  - (9.2) variables use their types
-  - (9.3) types use their underlying and element types
-  - (9.4) conversions use the type they convert to
-  - (9.5) instructions use their operands
-  - (9.6) instructions use their operands' types
-  - (9.7) variable _reads_ use variables, writes do not, except in tests
-  - (9.8) runtime functions that may be called from user code via the compiler
-
-
-- const groups:
-  (10.1) if one constant out of a block of constants is used, mark all
-  of them used. a lot of the time, unused constants exist for the sake
-  of completeness. See also
-  https://github.com/dominikh/go-tools/issues/365
-
-
-- (11.1) anonymous struct types use all their fields. we cannot
-  deduplicate struct types, as that leads to order-dependent
-  reportings. we can't not deduplicate struct types while still
-  tracking fields, because then each instance of the unnamed type in
-  the data flow chain will get its own fields, causing false
-  positives. Thus, we only accurately track fields of named struct
-  types, and assume that unnamed struct types use all their fields.
-
-
-- Differences in whole program mode:
-  - (e2) types aim to implement all exported interfaces from all packages
-  - (e3) exported identifiers aren't automatically used. for fields and
-    methods this poses extra issues due to reflection. We assume
-    that all exported fields are used. We also maintain a list of
-    known reflection-based method callers.
-
-*/
-
-func assert(b bool) {
-       if !b {
-               panic("failed assertion")
-       }
-}
-
-func typString(obj types.Object) string {
-       switch obj := obj.(type) {
-       case *types.Func:
-               return "func"
-       case *types.Var:
-               if obj.IsField() {
-                       return "field"
-               }
-               return "var"
-       case *types.Const:
-               return "const"
-       case *types.TypeName:
-               return "type"
-       default:
-               return "identifier"
-       }
-}
-
-// /usr/lib/go/src/runtime/proc.go:433:6: func badmorestackg0 is unused (U1000)
-
-// Functions defined in the Go runtime that may be called through
-// compiler magic or via assembly.
-var runtimeFuncs = map[string]bool{
-       // The first part of the list is copied from
-       // cmd/compile/internal/gc/builtin.go, var runtimeDecls
-       "newobject":            true,
-       "panicindex":           true,
-       "panicslice":           true,
-       "panicdivide":          true,
-       "panicmakeslicelen":    true,
-       "throwinit":            true,
-       "panicwrap":            true,
-       "gopanic":              true,
-       "gorecover":            true,
-       "goschedguarded":       true,
-       "printbool":            true,
-       "printfloat":           true,
-       "printint":             true,
-       "printhex":             true,
-       "printuint":            true,
-       "printcomplex":         true,
-       "printstring":          true,
-       "printpointer":         true,
-       "printiface":           true,
-       "printeface":           true,
-       "printslice":           true,
-       "printnl":              true,
-       "printsp":              true,
-       "printlock":            true,
-       "printunlock":          true,
-       "concatstring2":        true,
-       "concatstring3":        true,
-       "concatstring4":        true,
-       "concatstring5":        true,
-       "concatstrings":        true,
-       "cmpstring":            true,
-       "intstring":            true,
-       "slicebytetostring":    true,
-       "slicebytetostringtmp": true,
-       "slicerunetostring":    true,
-       "stringtoslicebyte":    true,
-       "stringtoslicerune":    true,
-       "slicecopy":            true,
-       "slicestringcopy":      true,
-       "decoderune":           true,
-       "countrunes":           true,
-       "convI2I":              true,
-       "convT16":              true,
-       "convT32":              true,
-       "convT64":              true,
-       "convTstring":          true,
-       "convTslice":           true,
-       "convT2E":              true,
-       "convT2Enoptr":         true,
-       "convT2I":              true,
-       "convT2Inoptr":         true,
-       "assertE2I":            true,
-       "assertE2I2":           true,
-       "assertI2I":            true,
-       "assertI2I2":           true,
-       "panicdottypeE":        true,
-       "panicdottypeI":        true,
-       "panicnildottype":      true,
-       "ifaceeq":              true,
-       "efaceeq":              true,
-       "fastrand":             true,
-       "makemap64":            true,
-       "makemap":              true,
-       "makemap_small":        true,
-       "mapaccess1":           true,
-       "mapaccess1_fast32":    true,
-       "mapaccess1_fast64":    true,
-       "mapaccess1_faststr":   true,
-       "mapaccess1_fat":       true,
-       "mapaccess2":           true,
-       "mapaccess2_fast32":    true,
-       "mapaccess2_fast64":    true,
-       "mapaccess2_faststr":   true,
-       "mapaccess2_fat":       true,
-       "mapassign":            true,
-       "mapassign_fast32":     true,
-       "mapassign_fast32ptr":  true,
-       "mapassign_fast64":     true,
-       "mapassign_fast64ptr":  true,
-       "mapassign_faststr":    true,
-       "mapiterinit":          true,
-       "mapdelete":            true,
-       "mapdelete_fast32":     true,
-       "mapdelete_fast64":     true,
-       "mapdelete_faststr":    true,
-       "mapiternext":          true,
-       "mapclear":             true,
-       "makechan64":           true,
-       "makechan":             true,
-       "chanrecv1":            true,
-       "chanrecv2":            true,
-       "chansend1":            true,
-       "closechan":            true,
-       "writeBarrier":         true,
-       "typedmemmove":         true,
-       "typedmemclr":          true,
-       "typedslicecopy":       true,
-       "selectnbsend":         true,
-       "selectnbrecv":         true,
-       "selectnbrecv2":        true,
-       "selectsetpc":          true,
-       "selectgo":             true,
-       "block":                true,
-       "makeslice":            true,
-       "makeslice64":          true,
-       "growslice":            true,
-       "memmove":              true,
-       "memclrNoHeapPointers": true,
-       "memclrHasPointers":    true,
-       "memequal":             true,
-       "memequal8":            true,
-       "memequal16":           true,
-       "memequal32":           true,
-       "memequal64":           true,
-       "memequal128":          true,
-       "int64div":             true,
-       "uint64div":            true,
-       "int64mod":             true,
-       "uint64mod":            true,
-       "float64toint64":       true,
-       "float64touint64":      true,
-       "float64touint32":      true,
-       "int64tofloat64":       true,
-       "uint64tofloat64":      true,
-       "uint32tofloat64":      true,
-       "complex128div":        true,
-       "racefuncenter":        true,
-       "racefuncenterfp":      true,
-       "racefuncexit":         true,
-       "raceread":             true,
-       "racewrite":            true,
-       "racereadrange":        true,
-       "racewriterange":       true,
-       "msanread":             true,
-       "msanwrite":            true,
-       "x86HasPOPCNT":         true,
-       "x86HasSSE41":          true,
-       "arm64HasATOMICS":      true,
-
-       // The second part of the list is extracted from assembly code in
-       // the standard library, with the exception of the runtime package itself
-       "abort":                 true,
-       "aeshashbody":           true,
-       "args":                  true,
-       "asminit":               true,
-       "badctxt":               true,
-       "badmcall2":             true,
-       "badmcall":              true,
-       "badmorestackg0":        true,
-       "badmorestackgsignal":   true,
-       "badsignal2":            true,
-       "callbackasm1":          true,
-       "callCfunction":         true,
-       "cgocallback_gofunc":    true,
-       "cgocallbackg":          true,
-       "checkgoarm":            true,
-       "check":                 true,
-       "debugCallCheck":        true,
-       "debugCallWrap":         true,
-       "emptyfunc":             true,
-       "entersyscall":          true,
-       "exit":                  true,
-       "exits":                 true,
-       "exitsyscall":           true,
-       "externalthreadhandler": true,
-       "findnull":              true,
-       "goexit1":               true,
-       "gostring":              true,
-       "i386_set_ldt":          true,
-       "_initcgo":              true,
-       "init_thread_tls":       true,
-       "ldt0setup":             true,
-       "libpreinit":            true,
-       "load_g":                true,
-       "morestack":             true,
-       "mstart":                true,
-       "nacl_sysinfo":          true,
-       "nanotimeQPC":           true,
-       "nanotime":              true,
-       "newosproc0":            true,
-       "newproc":               true,
-       "newstack":              true,
-       "noted":                 true,
-       "nowQPC":                true,
-       "osinit":                true,
-       "printf":                true,
-       "racecallback":          true,
-       "reflectcallmove":       true,
-       "reginit":               true,
-       "rt0_go":                true,
-       "save_g":                true,
-       "schedinit":             true,
-       "setldt":                true,
-       "settls":                true,
-       "sighandler":            true,
-       "sigprofNonGo":          true,
-       "sigtrampgo":            true,
-       "_sigtramp":             true,
-       "sigtramp":              true,
-       "stackcheck":            true,
-       "syscall_chdir":         true,
-       "syscall_chroot":        true,
-       "syscall_close":         true,
-       "syscall_dup2":          true,
-       "syscall_execve":        true,
-       "syscall_exit":          true,
-       "syscall_fcntl":         true,
-       "syscall_forkx":         true,
-       "syscall_gethostname":   true,
-       "syscall_getpid":        true,
-       "syscall_ioctl":         true,
-       "syscall_pipe":          true,
-       "syscall_rawsyscall6":   true,
-       "syscall_rawSyscall6":   true,
-       "syscall_rawsyscall":    true,
-       "syscall_RawSyscall":    true,
-       "syscall_rawsysvicall6": true,
-       "syscall_setgid":        true,
-       "syscall_setgroups":     true,
-       "syscall_setpgid":       true,
-       "syscall_setsid":        true,
-       "syscall_setuid":        true,
-       "syscall_syscall6":      true,
-       "syscall_syscall":       true,
-       "syscall_Syscall":       true,
-       "syscall_sysvicall6":    true,
-       "syscall_wait4":         true,
-       "syscall_write":         true,
-       "traceback":             true,
-       "tstart":                true,
-       "usplitR0":              true,
-       "wbBufFlush":            true,
-       "write":                 true,
-}
-
-type pkg struct {
-       Fset       *token.FileSet
-       Files      []*ast.File
-       Pkg        *types.Package
-       TypesInfo  *types.Info
-       TypesSizes types.Sizes
-       IR         *ir.Package
-       SrcFuncs   []*ir.Function
-}
-
-type Checker struct {
-       WholeProgram bool
-       Debug        io.Writer
-
-       mu              sync.Mutex
-       initialPackages map[*types.Package]struct{}
-       allPackages     map[*types.Package]struct{}
-       graph           *Graph
-}
-
-func NewChecker(wholeProgram bool) *Checker {
-       return &Checker{
-               initialPackages: map[*types.Package]struct{}{},
-               allPackages:     map[*types.Package]struct{}{},
-               WholeProgram:    wholeProgram,
-       }
-}
-
-func (c *Checker) Analyzer() *analysis.Analyzer {
-       name := "U1000"
-       if c.WholeProgram {
-               name = "U1001"
-       }
-       return &analysis.Analyzer{
-               Name:     name,
-               Doc:      "Unused code",
-               Run:      c.Run,
-               Requires: []*analysis.Analyzer{buildir.Analyzer},
-       }
-}
-
-func (c *Checker) Run(pass *analysis.Pass) (interface{}, error) {
-       c.mu.Lock()
-       if c.graph == nil {
-               c.graph = NewGraph()
-               c.graph.wholeProgram = c.WholeProgram
-               c.graph.fset = pass.Fset
-       }
-
-       var visit func(pkg *types.Package)
-       visit = func(pkg *types.Package) {
-               if _, ok := c.allPackages[pkg]; ok {
-                       return
-               }
-               c.allPackages[pkg] = struct{}{}
-               for _, imp := range pkg.Imports() {
-                       visit(imp)
-               }
-       }
-       visit(pass.Pkg)
-
-       c.initialPackages[pass.Pkg] = struct{}{}
-       c.mu.Unlock()
-
-       irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR)
-       pkg := &pkg{
-               Fset:       pass.Fset,
-               Files:      pass.Files,
-               Pkg:        pass.Pkg,
-               TypesInfo:  pass.TypesInfo,
-               TypesSizes: pass.TypesSizes,
-               IR:         irpkg.Pkg,
-               SrcFuncs:   irpkg.SrcFuncs,
-       }
-
-       c.processPkg(c.graph, pkg)
-
-       return nil, nil
-}
-
-func (c *Checker) ProblemObject(fset *token.FileSet, obj types.Object) lint.Problem {
-       name := obj.Name()
-       if sig, ok := obj.Type().(*types.Signature); ok && sig.Recv() != nil {
-               switch sig.Recv().Type().(type) {
-               case *types.Named, *types.Pointer:
-                       typ := types.TypeString(sig.Recv().Type(), func(*types.Package) string { return "" })
-                       if len(typ) > 0 && typ[0] == '*' {
-                               name = fmt.Sprintf("(%s).%s", typ, obj.Name())
-                       } else if len(typ) > 0 {
-                               name = fmt.Sprintf("%s.%s", typ, obj.Name())
-                       }
-               }
-       }
-
-       checkName := "U1000"
-       if c.WholeProgram {
-               checkName = "U1001"
-       }
-       return lint.Problem{
-               Pos:     lint.DisplayPosition(fset, obj.Pos()),
-               Message: fmt.Sprintf("%s %s is unused", typString(obj), name),
-               Check:   checkName,
-       }
-}
-
-func (c *Checker) Result() []types.Object {
-       out := c.results()
-
-       out2 := make([]types.Object, 0, len(out))
-       for _, v := range out {
-               if _, ok := c.initialPackages[v.Pkg()]; !ok {
-                       continue
-               }
-               out2 = append(out2, v)
-       }
-
-       return out2
-}
-
-func (c *Checker) debugf(f string, v ...interface{}) {
-       if c.Debug != nil {
-               fmt.Fprintf(c.Debug, f, v...)
-       }
-}
-
-func (graph *Graph) quieten(node *Node) {
-       if node.seen {
-               return
-       }
-       switch obj := node.obj.(type) {
-       case *types.Named:
-               for i := 0; i < obj.NumMethods(); i++ {
-                       m := obj.Method(i)
-                       if node, ok := graph.nodeMaybe(m); ok {
-                               node.quiet = true
-                       }
-               }
-       case *types.Struct:
-               for i := 0; i < obj.NumFields(); i++ {
-                       if node, ok := graph.nodeMaybe(obj.Field(i)); ok {
-                               node.quiet = true
-                       }
-               }
-       case *types.Interface:
-               for i := 0; i < obj.NumExplicitMethods(); i++ {
-                       m := obj.ExplicitMethod(i)
-                       if node, ok := graph.nodeMaybe(m); ok {
-                               node.quiet = true
-                       }
-               }
-       }
-}
-
-func (c *Checker) results() []types.Object {
-       if c.graph == nil {
-               // We never analyzed any packages
-               return nil
-       }
-
-       var out []types.Object
-
-       if c.WholeProgram {
-               var ifaces []*types.Interface
-               var notIfaces []types.Type
-
-               // implement as many interfaces as possible
-               c.graph.seenTypes.Iterate(func(t types.Type, _ interface{}) {
-                       switch t := t.(type) {
-                       case *types.Interface:
-                               if t.NumMethods() > 0 {
-                                       ifaces = append(ifaces, t)
-                               }
-                       default:
-                               if _, ok := t.Underlying().(*types.Interface); !ok {
-                                       notIfaces = append(notIfaces, t)
-                               }
-                       }
-               })
-
-               for pkg := range c.allPackages {
-                       for _, iface := range interfacesFromExportData(pkg) {
-                               if iface.NumMethods() > 0 {
-                                       ifaces = append(ifaces, iface)
-                               }
-                       }
-               }
-
-               ctx := &context{
-                       g:         c.graph,
-                       seenTypes: &c.graph.seenTypes,
-               }
-               // (8.0) handle interfaces
-               // (e2) types aim to implement all exported interfaces from all packages
-               for _, t := range notIfaces {
-                       // OPT(dh): it is unfortunate that we do not have access
-                       // to a populated method set at this point.
-                       ms := types.NewMethodSet(t)
-                       for _, iface := range ifaces {
-                               if sels, ok := c.graph.implements(t, iface, ms); ok {
-                                       for _, sel := range sels {
-                                               c.graph.useMethod(ctx, t, sel, t, edgeImplements)
-                                       }
-                               }
-                       }
-               }
-       }
-
-       if c.Debug != nil {
-               debugNode := func(node *Node) {
-                       if node.obj == nil {
-                               c.debugf("n%d [label=\"Root\"];\n", node.id)
-                       } else {
-                               c.debugf("n%d [label=%q];\n", node.id, fmt.Sprintf("(%T) %s", node.obj, node.obj))
-                       }
-                       for _, e := range node.used {
-                               for i := edgeKind(1); i < 64; i++ {
-                                       if e.kind.is(1 << i) {
-                                               c.debugf("n%d -> n%d [label=%q];\n", node.id, e.node.id, edgeKind(1<<i))
-                                       }
-                               }
-                       }
-               }
-
-               c.debugf("digraph{\n")
-               debugNode(c.graph.Root)
-               for _, v := range c.graph.Nodes {
-                       debugNode(v)
-               }
-               c.graph.TypeNodes.Iterate(func(key types.Type, value interface{}) {
-                       debugNode(value.(*Node))
-               })
-
-               c.debugf("}\n")
-       }
-
-       c.graph.color(c.graph.Root)
-       // if a node is unused, don't report any of the node's
-       // children as unused. for example, if a function is unused,
-       // don't flag its receiver. if a named type is unused, don't
-       // flag its methods.
-
-       for _, v := range c.graph.Nodes {
-               c.graph.quieten(v)
-       }
-       c.graph.TypeNodes.Iterate(func(_ types.Type, value interface{}) {
-               c.graph.quieten(value.(*Node))
-       })
-
-       report := func(node *Node) {
-               if node.seen {
-                       return
-               }
-               if node.quiet {
-                       c.debugf("n%d [color=purple];\n", node.id)
-                       return
-               }
-
-               c.debugf("n%d [color=red];\n", node.id)
-               switch obj := node.obj.(type) {
-               case *types.Var:
-                       // don't report unnamed variables (interface embedding)
-                       if obj.Name() != "" || obj.IsField() {
-                               out = append(out, obj)
-                       }
-                       return
-               case types.Object:
-                       if obj.Name() != "_" {
-                               out = append(out, obj)
-                       }
-                       return
-               }
-               c.debugf("n%d [color=gray];\n", node.id)
-       }
-       for _, v := range c.graph.Nodes {
-               report(v)
-       }
-       c.graph.TypeNodes.Iterate(func(_ types.Type, value interface{}) {
-               report(value.(*Node))
-       })
-
-       return out
-}
-
-func (c *Checker) processPkg(graph *Graph, pkg *pkg) {
-       if pkg.Pkg.Path() == "unsafe" {
-               return
-       }
-       graph.entry(pkg)
-}
-
-func objNodeKeyFor(fset *token.FileSet, obj types.Object) objNodeKey {
-       var kind objType
-       switch obj.(type) {
-       case *types.PkgName:
-               kind = otPkgName
-       case *types.Const:
-               kind = otConst
-       case *types.TypeName:
-               kind = otTypeName
-       case *types.Var:
-               kind = otVar
-       case *types.Func:
-               kind = otFunc
-       case *types.Label:
-               kind = otLabel
-       case *types.Builtin:
-               kind = otBuiltin
-       case *types.Nil:
-               kind = otNil
-       default:
-               panic(fmt.Sprintf("unreachable: %T", obj))
-       }
-
-       position := fset.PositionFor(obj.Pos(), false)
-       position.Column = 0
-       position.Offset = 0
-       return objNodeKey{
-               position: position,
-               kind:     kind,
-               name:     obj.Name(),
-       }
-}
-
-type objType uint8
-
-const (
-       otPkgName objType = iota
-       otConst
-       otTypeName
-       otVar
-       otFunc
-       otLabel
-       otBuiltin
-       otNil
-)
-
-// An objNodeKey describes a types.Object node in the graph.
-//
-// Due to test variants we may end up with multiple instances of the
-// same object, which is why we have to deduplicate based on their
-// source position. And because export data lacks column information,
-// we also have to incorporate the object's string representation in
-// the key.
-//
-// Previously we used the object's full string representation
-// (types.ObjectString), but that causes a significant amount of
-// allocations. Currently we're using the object's type and name, in
-// the hope that it is impossible for two objects to have the same
-// type, name and file position.
-type objNodeKey struct {
-       position token.Position
-       kind     objType
-       name     string
-}
-
-type Graph struct {
-       // accessed atomically
-       nodeOffset uint64
-
-       // Safe for concurrent use
-       fset      *token.FileSet
-       Root      *Node
-       seenTypes typeutil.Map
-
-       // read-only
-       wholeProgram bool
-
-       // need synchronisation
-       mu        sync.Mutex
-       TypeNodes typeutil.Map
-       Nodes     map[interface{}]*Node
-       objNodes  map[objNodeKey]*Node
-}
-
-type context struct {
-       g           *Graph
-       pkg         *pkg
-       seenFns     map[string]struct{}
-       seenTypes   *typeutil.Map
-       nodeCounter uint64
-}
-
-func NewGraph() *Graph {
-       g := &Graph{
-               Nodes:    map[interface{}]*Node{},
-               objNodes: map[objNodeKey]*Node{},
-       }
-       g.Root = g.newNode(&context{}, nil)
-       return g
-}
-
-func (g *Graph) color(root *Node) {
-       if root.seen {
-               return
-       }
-       root.seen = true
-       for _, e := range root.used {
-               g.color(e.node)
-       }
-}
-
-type ConstGroup struct {
-       // give the struct a size to get unique pointers
-       _ byte
-}
-
-func (ConstGroup) String() string { return "const group" }
-
-type edge struct {
-       node *Node
-       kind edgeKind
-}
-
-type Node struct {
-       obj interface{}
-       id  uint64
-
-       mu   sync.Mutex
-       used []edge
-
-       // set during final graph walk if node is reachable
-       seen bool
-       // a parent node (e.g. the struct type containing a field) is
-       // already unused, don't report children
-       quiet bool
-}
-
-func (g *Graph) nodeMaybe(obj types.Object) (*Node, bool) {
-       g.mu.Lock()
-       defer g.mu.Unlock()
-       if node, ok := g.Nodes[obj]; ok {
-               return node, true
-       }
-       return nil, false
-}
-
-func (g *Graph) node(ctx *context, obj interface{}) (node *Node, new bool) {
-       g.mu.Lock()
-       defer g.mu.Unlock()
-       switch obj := obj.(type) {
-       case types.Type:
-               if v := g.TypeNodes.At(obj); v != nil {
-                       return v.(*Node), false
-               }
-               node := g.newNode(ctx, obj)
-               g.TypeNodes.Set(obj, node)
-               return node, true
-       case types.Object:
-               if node, ok := g.Nodes[obj]; ok {
-                       return node, false
-               }
-
-               key := objNodeKeyFor(g.fset, obj)
-               if onode, ok := g.objNodes[key]; ok {
-                       return onode, false
-               }
-
-               node = g.newNode(ctx, obj)
-               g.Nodes[obj] = node
-               g.objNodes[key] = node
-               return node, true
-       default:
-               if node, ok := g.Nodes[obj]; ok {
-                       return node, false
-               }
-
-               node = g.newNode(ctx, obj)
-               g.Nodes[obj] = node
-               return node, true
-       }
-}
-
-func (g *Graph) newNode(ctx *context, obj interface{}) *Node {
-       ctx.nodeCounter++
-       return &Node{
-               obj: obj,
-               id:  ctx.nodeCounter,
-       }
-}
-
-func (n *Node) use(node *Node, kind edgeKind) {
-       n.mu.Lock()
-       defer n.mu.Unlock()
-       assert(node != nil)
-       n.used = append(n.used, edge{node: node, kind: kind})
-}
-
-// isIrrelevant reports whether an object's presence in the graph is
-// of any relevance. A lot of objects will never have outgoing edges,
-// nor meaningful incoming ones. Examples are basic types and empty
-// signatures, among many others.
-//
-// Dropping these objects should have no effect on correctness, but
-// may improve performance. It also helps with debugging, as it
-// greatly reduces the size of the graph.
-func isIrrelevant(obj interface{}) bool {
-       if obj, ok := obj.(types.Object); ok {
-               switch obj := obj.(type) {
-               case *types.Var:
-                       if obj.IsField() {
-                               // We need to track package fields
-                               return false
-                       }
-                       if obj.Pkg() != nil && obj.Parent() == obj.Pkg().Scope() {
-                               // We need to track package-level variables
-                               return false
-                       }
-                       return isIrrelevant(obj.Type())
-               default:
-                       return false
-               }
-       }
-       if T, ok := obj.(types.Type); ok {
-               switch T := T.(type) {
-               case *types.Array:
-                       return isIrrelevant(T.Elem())
-               case *types.Slice:
-                       return isIrrelevant(T.Elem())
-               case *types.Basic:
-                       return true
-               case *types.Tuple:
-                       for i := 0; i < T.Len(); i++ {
-                               if !isIrrelevant(T.At(i).Type()) {
-                                       return false
-                               }
-                       }
-                       return true
-               case *types.Signature:
-                       if T.Recv() != nil {
-                               return false
-                       }
-                       for i := 0; i < T.Params().Len(); i++ {
-                               if !isIrrelevant(T.Params().At(i)) {
-                                       return false
-                               }
-                       }
-                       for i := 0; i < T.Results().Len(); i++ {
-                               if !isIrrelevant(T.Results().At(i)) {
-                                       return false
-                               }
-                       }
-                       return true
-               case *types.Interface:
-                       return T.NumMethods() == 0 && T.NumEmbeddeds() == 0
-               case *types.Pointer:
-                       return isIrrelevant(T.Elem())
-               case *types.Map:
-                       return isIrrelevant(T.Key()) && isIrrelevant(T.Elem())
-               case *types.Struct:
-                       return T.NumFields() == 0
-               case *types.Chan:
-                       return isIrrelevant(T.Elem())
-               default:
-                       return false
-               }
-       }
-       return false
-}
-
-func (ctx *context) see(obj interface{}) *Node {
-       if isIrrelevant(obj) {
-               return nil
-       }
-
-       assert(obj != nil)
-       // add new node to graph
-       node, _ := ctx.g.node(ctx, obj)
-       return node
-}
-
-func (ctx *context) use(used, by interface{}, kind edgeKind) {
-       if isIrrelevant(used) {
-               return
-       }
-
-       assert(used != nil)
-       if obj, ok := by.(types.Object); ok && obj.Pkg() != nil {
-               if !ctx.g.wholeProgram && obj.Pkg() != ctx.pkg.Pkg {
-                       return
-               }
-       }
-       usedNode, new := ctx.g.node(ctx, used)
-       assert(!new)
-       if by == nil {
-               ctx.g.Root.use(usedNode, kind)
-       } else {
-               byNode, new := ctx.g.node(ctx, by)
-               assert(!new)
-               byNode.use(usedNode, kind)
-       }
-}
-
-func (ctx *context) seeAndUse(used, by interface{}, kind edgeKind) *Node {
-       node := ctx.see(used)
-       ctx.use(used, by, kind)
-       return node
-}
-
-// trackExportedIdentifier reports whether obj should be considered
-// used due to being exported, checking various conditions that affect
-// the decision.
-func (g *Graph) trackExportedIdentifier(ctx *context, obj types.Object) bool {
-       if !obj.Exported() {
-               // object isn't exported, the question is moot
-               return false
-       }
-       path := g.fset.Position(obj.Pos()).Filename
-       if g.wholeProgram {
-               // Example functions without "Output:" comments aren't being
-               // run and thus don't show up in the graph.
-               if strings.HasSuffix(path, "_test.go") && strings.HasPrefix(obj.Name(), "Example") {
-                       return true
-               }
-               // whole program mode tracks exported identifiers accurately
-               return false
-       }
-
-       if ctx.pkg.Pkg.Name() == "main" && !strings.HasSuffix(path, "_test.go") {
-               // exported identifiers in package main can't be imported.
-               // However, test functions can be called, and xtest packages
-               // even have access to exported identifiers.
-               return false
-       }
-
-       if strings.HasSuffix(path, "_test.go") {
-               if strings.HasPrefix(obj.Name(), "Test") ||
-                       strings.HasPrefix(obj.Name(), "Benchmark") ||
-                       strings.HasPrefix(obj.Name(), "Example") {
-                       return true
-               }
-               return false
-       }
-
-       return true
-}
-
-func (g *Graph) entry(pkg *pkg) {
-       no := atomic.AddUint64(&g.nodeOffset, 1)
-       ctx := &context{
-               g:           g,
-               pkg:         pkg,
-               nodeCounter: no * 1e9,
-               seenFns:     map[string]struct{}{},
-       }
-       if g.wholeProgram {
-               ctx.seenTypes = &g.seenTypes
-       } else {
-               ctx.seenTypes = &typeutil.Map{}
-       }
-
-       scopes := map[*types.Scope]*ir.Function{}
-       for _, fn := range pkg.SrcFuncs {
-               if fn.Object() != nil {
-                       scope := fn.Object().(*types.Func).Scope()
-                       scopes[scope] = fn
-               }
-       }
-
-       for _, f := range pkg.Files {
-               for _, cg := range f.Comments {
-                       for _, c := range cg.List {
-                               if strings.HasPrefix(c.Text, "//go:linkname ") {
-                                       // FIXME(dh): we're looking at all comments. The
-                                       // compiler only looks at comments in the
-                                       // left-most column. The intention probably is to
-                                       // only look at top-level comments.
-
-                                       // (1.8) packages use symbols linked via go:linkname
-                                       fields := strings.Fields(c.Text)
-                                       if len(fields) == 3 {
-                                               if m, ok := pkg.IR.Members[fields[1]]; ok {
-                                                       var obj types.Object
-                                                       switch m := m.(type) {
-                                                       case *ir.Global:
-                                                               obj = m.Object()
-                                                       case *ir.Function:
-                                                               obj = m.Object()
-                                                       default:
-                                                               panic(fmt.Sprintf("unhandled type: %T", m))
-                                                       }
-                                                       assert(obj != nil)
-                                                       ctx.seeAndUse(obj, nil, edgeLinkname)
-                                               }
-                                       }
-                               }
-                       }
-               }
-       }
-
-       surroundingFunc := func(obj types.Object) *ir.Function {
-               scope := obj.Parent()
-               for scope != nil {
-                       if fn := scopes[scope]; fn != nil {
-                               return fn
-                       }
-                       scope = scope.Parent()
-               }
-               return nil
-       }
-
-       // IR form won't tell us about locally scoped types that aren't
-       // being used. Walk the list of Defs to get all named types.
-       //
-       // IR form also won't tell us about constants; use Defs and Uses
-       // to determine which constants exist and which are being used.
-       for _, obj := range pkg.TypesInfo.Defs {
-               switch obj := obj.(type) {
-               case *types.TypeName:
-                       // types are being handled by walking the AST
-               case *types.Const:
-                       ctx.see(obj)
-                       fn := surroundingFunc(obj)
-                       if fn == nil && g.trackExportedIdentifier(ctx, obj) {
-                               // (1.4) packages use exported constants (unless in package main)
-                               ctx.use(obj, nil, edgeExportedConstant)
-                       }
-                       g.typ(ctx, obj.Type(), nil)
-                       ctx.seeAndUse(obj.Type(), obj, edgeType)
-               }
-       }
-
-       // Find constants being used inside functions, find sinks in tests
-       for _, fn := range pkg.SrcFuncs {
-               if fn.Object() != nil {
-                       ctx.see(fn.Object())
-               }
-               node := fn.Source()
-               if node == nil {
-                       continue
-               }
-               ast.Inspect(node, func(node ast.Node) bool {
-                       switch node := node.(type) {
-                       case *ast.Ident:
-                               obj, ok := pkg.TypesInfo.Uses[node]
-                               if !ok {
-                                       return true
-                               }
-                               switch obj := obj.(type) {
-                               case *types.Const:
-                                       ctx.seeAndUse(obj, owningObject(fn), edgeUsedConstant)
-                               }
-                       case *ast.AssignStmt:
-                               for _, expr := range node.Lhs {
-                                       ident, ok := expr.(*ast.Ident)
-                                       if !ok {
-                                               continue
-                                       }
-                                       obj := pkg.TypesInfo.ObjectOf(ident)
-                                       if obj == nil {
-                                               continue
-                                       }
-                                       path := g.fset.File(obj.Pos()).Name()
-                                       if strings.HasSuffix(path, "_test.go") {
-                                               if obj.Parent() != nil && obj.Parent().Parent() != nil && obj.Parent().Parent().Parent() == nil {
-                                                       // object's scope is the package, whose
-                                                       // parent is the file, whose parent is nil
-
-                                                       // (4.9) functions use package-level variables they assign to iff in tests (sinks for benchmarks)
-                                                       // (9.7) variable _reads_ use variables, writes do not, except in tests
-                                                       ctx.seeAndUse(obj, owningObject(fn), edgeTestSink)
-                                               }
-                                       }
-                               }
-                       }
-
-                       return true
-               })
-       }
-       // Find constants being used in non-function contexts
-       for _, obj := range pkg.TypesInfo.Uses {
-               _, ok := obj.(*types.Const)
-               if !ok {
-                       continue
-               }
-               ctx.seeAndUse(obj, nil, edgeUsedConstant)
-       }
-
-       var fns []*types.Func
-       var fn *types.Func
-       var stack []ast.Node
-       for _, f := range pkg.Files {
-               ast.Inspect(f, func(n ast.Node) bool {
-                       if n == nil {
-                               pop := stack[len(stack)-1]
-                               stack = stack[:len(stack)-1]
-                               if _, ok := pop.(*ast.FuncDecl); ok {
-                                       fns = fns[:len(fns)-1]
-                                       if len(fns) == 0 {
-                                               fn = nil
-                                       } else {
-                                               fn = fns[len(fns)-1]
-                                       }
-                               }
-                               return true
-                       }
-                       stack = append(stack, n)
-                       switch n := n.(type) {
-                       case *ast.FuncDecl:
-                               fn = pkg.TypesInfo.ObjectOf(n.Name).(*types.Func)
-                               fns = append(fns, fn)
-                               ctx.see(fn)
-                       case *ast.GenDecl:
-                               switch n.Tok {
-                               case token.CONST:
-                                       groups := code.GroupSpecs(pkg.Fset, n.Specs)
-                                       for _, specs := range groups {
-                                               if len(specs) > 1 {
-                                                       cg := &ConstGroup{}
-                                                       ctx.see(cg)
-                                                       for _, spec := range specs {
-                                                               for _, name := range spec.(*ast.ValueSpec).Names {
-                                                                       obj := pkg.TypesInfo.ObjectOf(name)
-                                                                       // (10.1) const groups
-                                                                       ctx.seeAndUse(obj, cg, edgeConstGroup)
-                                                                       ctx.use(cg, obj, edgeConstGroup)
-                                                               }
-                                                       }
-                                               }
-                                       }
-                               case token.VAR:
-                                       for _, spec := range n.Specs {
-                                               v := spec.(*ast.ValueSpec)
-                                               for _, name := range v.Names {
-                                                       T := pkg.TypesInfo.TypeOf(name)
-                                                       if fn != nil {
-                                                               ctx.seeAndUse(T, fn, edgeVarDecl)
-                                                       } else {
-                                                               // TODO(dh): we likely want to make
-                                                               // the type used by the variable, not
-                                                               // the package containing the
-                                                               // variable. But then we have to take
-                                                               // special care of blank identifiers.
-                                                               ctx.seeAndUse(T, nil, edgeVarDecl)
-                                                       }
-                                                       g.typ(ctx, T, nil)
-                                               }
-                                       }
-                               case token.TYPE:
-                                       for _, spec := range n.Specs {
-                                               // go/types doesn't provide a way to go from a
-                                               // types.Named to the named type it was based on
-                                               // (the t1 in type t2 t1). Therefore we walk the
-                                               // AST and process GenDecls.
-                                               //
-                                               // (2.2) named types use the type they're based on
-                                               v := spec.(*ast.TypeSpec)
-                                               T := pkg.TypesInfo.TypeOf(v.Type)
-                                               obj := pkg.TypesInfo.ObjectOf(v.Name)
-                                               ctx.see(obj)
-                                               ctx.see(T)
-                                               ctx.use(T, obj, edgeType)
-                                               g.typ(ctx, obj.Type(), nil)
-                                               g.typ(ctx, T, nil)
-
-                                               if v.Assign != 0 {
-                                                       aliasFor := obj.(*types.TypeName).Type()
-                                                       // (2.3) named types use all their aliases. we can't easily track uses of aliases
-                                                       if isIrrelevant(aliasFor) {
-                                                               // We do not track the type this is an
-                                                               // alias for (for example builtins), so
-                                                               // just mark the alias used.
-                                                               //
-                                                               // FIXME(dh): what about aliases declared inside functions?
-                                                               ctx.use(obj, nil, edgeAlias)
-                                                       } else {
-                                                               ctx.see(aliasFor)
-                                                               ctx.seeAndUse(obj, aliasFor, edgeAlias)
-                                                       }
-                                               }
-                                       }
-                               }
-                       }
-                       return true
-               })
-       }
-
-       for _, m := range pkg.IR.Members {
-               switch m := m.(type) {
-               case *ir.NamedConst:
-                       // nothing to do, we collect all constants from Defs
-               case *ir.Global:
-                       if m.Object() != nil {
-                               ctx.see(m.Object())
-                               if g.trackExportedIdentifier(ctx, m.Object()) {
-                                       // (1.3) packages use exported variables (unless in package main)
-                                       ctx.use(m.Object(), nil, edgeExportedVariable)
-                               }
-                       }
-               case *ir.Function:
-                       mObj := owningObject(m)
-                       if mObj != nil {
-                               ctx.see(mObj)
-                       }
-                       //lint:ignore SA9003 handled implicitly
-                       if m.Name() == "init" {
-                               // (1.5) packages use init functions
-                               //
-                               // This is handled implicitly. The generated init
-                               // function has no object, thus everything in it will
-                               // be owned by the package.
-                       }
-                       // This branch catches top-level functions, not methods.
-                       if m.Object() != nil && g.trackExportedIdentifier(ctx, m.Object()) {
-                               // (1.2) packages use exported functions (unless in package main)
-                               ctx.use(mObj, nil, edgeExportedFunction)
-                       }
-                       if m.Name() == "main" && pkg.Pkg.Name() == "main" {
-                               // (1.7) packages use the main function iff in the main package
-                               ctx.use(mObj, nil, edgeMainFunction)
-                       }
-                       if pkg.Pkg.Path() == "runtime" && runtimeFuncs[m.Name()] {
-                               // (9.8) runtime functions that may be called from user code via the compiler
-                               ctx.use(mObj, nil, edgeRuntimeFunction)
-                       }
-                       if m.Source() != nil {
-                               doc := m.Source().(*ast.FuncDecl).Doc
-                               if doc != nil {
-                                       for _, cmt := range doc.List {
-                                               if strings.HasPrefix(cmt.Text, "//go:cgo_export_") {
-                                                       // (1.6) packages use functions exported to cgo
-                                                       ctx.use(mObj, nil, edgeCgoExported)
-                                               }
-                                       }
-                               }
-                       }
-                       g.function(ctx, m)
-               case *ir.Type:
-                       if m.Object() != nil {
-                               ctx.see(m.Object())
-                               if g.trackExportedIdentifier(ctx, m.Object()) {
-                                       // (1.1) packages use exported named types (unless in package main)
-                                       ctx.use(m.Object(), nil, edgeExportedType)
-                               }
-                       }
-                       g.typ(ctx, m.Type(), nil)
-               default:
-                       panic(fmt.Sprintf("unreachable: %T", m))
-               }
-       }
-
-       if !g.wholeProgram {
-               // When not in whole program mode we reset seenTypes after each package,
-               // which means g.seenTypes only contains types of
-               // interest to us. In whole program mode, we're better off
-               // processing all interfaces at once, globally, both for
-               // performance reasons and because in whole program mode we
-               // actually care about all interfaces, not just the subset
-               // that has unexported methods.
-
-               var ifaces []*types.Interface
-               var notIfaces []types.Type
-
-               ctx.seenTypes.Iterate(func(t types.Type, _ interface{}) {
-                       switch t := t.(type) {
-                       case *types.Interface:
-                               // OPT(dh): (8.1) we only need interfaces that have unexported methods
-                               ifaces = append(ifaces, t)
-                       default:
-                               if _, ok := t.Underlying().(*types.Interface); !ok {
-                                       notIfaces = append(notIfaces, t)
-                               }
-                       }
-               })
-
-               // (8.0) handle interfaces
-               for _, t := range notIfaces {
-                       ms := pkg.IR.Prog.MethodSets.MethodSet(t)
-                       for _, iface := range ifaces {
-                               if sels, ok := g.implements(t, iface, ms); ok {
-                                       for _, sel := range sels {
-                                               g.useMethod(ctx, t, sel, t, edgeImplements)
-                                       }
-                               }
-                       }
-               }
-       }
-}
-
-func (g *Graph) useMethod(ctx *context, t types.Type, sel *types.Selection, by interface{}, kind edgeKind) {
-       obj := sel.Obj()
-       path := sel.Index()
-       assert(obj != nil)
-       if len(path) > 1 {
-               base := code.Dereference(t).Underlying().(*types.Struct)
-               for _, idx := range path[:len(path)-1] {
-                       next := base.Field(idx)
-                       // (6.3) structs use embedded fields that help implement interfaces
-                       ctx.see(base)
-                       ctx.seeAndUse(next, base, edgeProvidesMethod)
-                       base, _ = code.Dereference(next.Type()).Underlying().(*types.Struct)
-               }
-       }
-       ctx.seeAndUse(obj, by, kind)
-}
-
-func owningObject(fn *ir.Function) types.Object {
-       if fn.Object() != nil {
-               return fn.Object()
-       }
-       if fn.Parent() != nil {
-               return owningObject(fn.Parent())
-       }
-       return nil
-}
-
-func (g *Graph) function(ctx *context, fn *ir.Function) {
-       if fn.Package() != nil && fn.Package() != ctx.pkg.IR {
-               return
-       }
-
-       name := fn.RelString(nil)
-       if _, ok := ctx.seenFns[name]; ok {
-               return
-       }
-       ctx.seenFns[name] = struct{}{}
-
-       // (4.1) functions use all their arguments, return parameters and receivers
-       g.signature(ctx, fn.Signature, owningObject(fn))
-       g.instructions(ctx, fn)
-       for _, anon := range fn.AnonFuncs {
-               // (4.2) functions use anonymous functions defined beneath them
-               //
-               // This fact is expressed implicitly. Anonymous functions have
-               // no types.Object, so their owner is the surrounding
-               // function.
-               g.function(ctx, anon)
-       }
-}
-
-func (g *Graph) typ(ctx *context, t types.Type, parent types.Type) {
-       if g.wholeProgram {
-               g.mu.Lock()
-       }
-       if ctx.seenTypes.At(t) != nil {
-               if g.wholeProgram {
-                       g.mu.Unlock()
-               }
-               return
-       }
-       if g.wholeProgram {
-               g.mu.Unlock()
-       }
-       if t, ok := t.(*types.Named); ok && t.Obj().Pkg() != nil {
-               if t.Obj().Pkg() != ctx.pkg.Pkg {
-                       return
-               }
-       }
-
-       if g.wholeProgram {
-               g.mu.Lock()
-       }
-       ctx.seenTypes.Set(t, struct{}{})
-       if g.wholeProgram {
-               g.mu.Unlock()
-       }
-       if isIrrelevant(t) {
-               return
-       }
-
-       ctx.see(t)
-       switch t := t.(type) {
-       case *types.Struct:
-               for i := 0; i < t.NumFields(); i++ {
-                       ctx.see(t.Field(i))
-                       if t.Field(i).Exported() {
-                               // (6.2) structs use exported fields
-                               ctx.use(t.Field(i), t, edgeExportedField)
-                       } else if t.Field(i).Name() == "_" {
-                               ctx.use(t.Field(i), t, edgeBlankField)
-                       } else if isNoCopyType(t.Field(i).Type()) {
-                               // (6.1) structs use fields of type NoCopy sentinel
-                               ctx.use(t.Field(i), t, edgeNoCopySentinel)
-                       } else if parent == nil {
-                               // (11.1) anonymous struct types use all their fields.
-                               ctx.use(t.Field(i), t, edgeAnonymousStruct)
-                       }
-                       if t.Field(i).Anonymous() {
-                               // (e3) exported identifiers aren't automatically used.
-                               if !g.wholeProgram {
-                                       // does the embedded field contribute exported methods to the method set?
-                                       T := t.Field(i).Type()
-                                       if _, ok := T.Underlying().(*types.Pointer); !ok {
-                                               // An embedded field is addressable, so check
-                                               // the pointer type to get the full method set
-                                               T = types.NewPointer(T)
-                                       }
-                                       ms := ctx.pkg.IR.Prog.MethodSets.MethodSet(T)
-                                       for j := 0; j < ms.Len(); j++ {
-                                               if ms.At(j).Obj().Exported() {
-                                                       // (6.4) structs use embedded fields that have exported methods (recursively)
-                                                       ctx.use(t.Field(i), t, edgeExtendsExportedMethodSet)
-                                                       break
-                                               }
-                                       }
-                               }
-
-                               seen := map[*types.Struct]struct{}{}
-                               var hasExportedField func(t types.Type) bool
-                               hasExportedField = func(T types.Type) bool {
-                                       t, ok := code.Dereference(T).Underlying().(*types.Struct)
-                                       if !ok {
-                                               return false
-                                       }
-                                       if _, ok := seen[t]; ok {
-                                               return false
-                                       }
-                                       seen[t] = struct{}{}
-                                       for i := 0; i < t.NumFields(); i++ {
-                                               field := t.Field(i)
-                                               if field.Exported() {
-                                                       return true
-                                               }
-                                               if field.Embedded() && hasExportedField(field.Type()) {
-                                                       return true
-                                               }
-                                       }
-                                       return false
-                               }
-                               // does the embedded field contribute exported fields?
-                               if hasExportedField(t.Field(i).Type()) {
-                                       // (6.5) structs use embedded structs that have exported fields (recursively)
-                                       ctx.use(t.Field(i), t, edgeExtendsExportedFields)
-                               }
-
-                       }
-                       g.variable(ctx, t.Field(i))
-               }
-       case *types.Basic:
-               // Nothing to do
-       case *types.Named:
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Underlying(), t, edgeUnderlyingType)
-               ctx.seeAndUse(t.Obj(), t, edgeTypeName)
-               ctx.seeAndUse(t, t.Obj(), edgeNamedType)
-
-               // (2.4) named types use the pointer type
-               if _, ok := t.Underlying().(*types.Interface); !ok && t.NumMethods() > 0 {
-                       ctx.seeAndUse(types.NewPointer(t), t, edgePointerType)
-               }
-
-               for i := 0; i < t.NumMethods(); i++ {
-                       ctx.see(t.Method(i))
-                       // don't use trackExportedIdentifier here, we care about
-                       // all exported methods, even in package main or in tests.
-                       if t.Method(i).Exported() && !g.wholeProgram {
-                               // (2.1) named types use exported methods
-                               ctx.use(t.Method(i), t, edgeExportedMethod)
-                       }
-                       g.function(ctx, ctx.pkg.IR.Prog.FuncValue(t.Method(i)))
-               }
-
-               g.typ(ctx, t.Underlying(), t)
-       case *types.Slice:
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Elem(), t, edgeElementType)
-               g.typ(ctx, t.Elem(), nil)
-       case *types.Map:
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Elem(), t, edgeElementType)
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Key(), t, edgeKeyType)
-               g.typ(ctx, t.Elem(), nil)
-               g.typ(ctx, t.Key(), nil)
-       case *types.Signature:
-               g.signature(ctx, t, nil)
-       case *types.Interface:
-               for i := 0; i < t.NumMethods(); i++ {
-                       m := t.Method(i)
-                       // (8.3) All interface methods are marked as used
-                       ctx.seeAndUse(m, t, edgeInterfaceMethod)
-                       ctx.seeAndUse(m.Type().(*types.Signature), m, edgeSignature)
-                       g.signature(ctx, m.Type().(*types.Signature), nil)
-               }
-               for i := 0; i < t.NumEmbeddeds(); i++ {
-                       tt := t.EmbeddedType(i)
-                       // (8.4) All embedded interfaces are marked as used
-                       ctx.seeAndUse(tt, t, edgeEmbeddedInterface)
-               }
-       case *types.Array:
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Elem(), t, edgeElementType)
-               g.typ(ctx, t.Elem(), nil)
-       case *types.Pointer:
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Elem(), t, edgeElementType)
-               g.typ(ctx, t.Elem(), nil)
-       case *types.Chan:
-               // (9.3) types use their underlying and element types
-               ctx.seeAndUse(t.Elem(), t, edgeElementType)
-               g.typ(ctx, t.Elem(), nil)
-       case *types.Tuple:
-               for i := 0; i < t.Len(); i++ {
-                       // (9.3) types use their underlying and element types
-                       ctx.seeAndUse(t.At(i).Type(), t, edgeTupleElement|edgeType)
-                       g.typ(ctx, t.At(i).Type(), nil)
-               }
-       default:
-               panic(fmt.Sprintf("unreachable: %T", t))
-       }
-}
-
-func (g *Graph) variable(ctx *context, v *types.Var) {
-       // (9.2) variables use their types
-       ctx.seeAndUse(v.Type(), v, edgeType)
-       g.typ(ctx, v.Type(), nil)
-}
-
-func (g *Graph) signature(ctx *context, sig *types.Signature, fn types.Object) {
-       var user interface{} = fn
-       if fn == nil {
-               user = sig
-               ctx.see(sig)
-       }
-       if sig.Recv() != nil {
-               ctx.seeAndUse(sig.Recv().Type(), user, edgeReceiver|edgeType)
-               g.typ(ctx, sig.Recv().Type(), nil)
-       }
-       for i := 0; i < sig.Params().Len(); i++ {
-               param := sig.Params().At(i)
-               ctx.seeAndUse(param.Type(), user, edgeFunctionArgument|edgeType)
-               g.typ(ctx, param.Type(), nil)
-       }
-       for i := 0; i < sig.Results().Len(); i++ {
-               param := sig.Results().At(i)
-               ctx.seeAndUse(param.Type(), user, edgeFunctionResult|edgeType)
-               g.typ(ctx, param.Type(), nil)
-       }
-}
-
-func (g *Graph) instructions(ctx *context, fn *ir.Function) {
-       fnObj := owningObject(fn)
-       for _, b := range fn.Blocks {
-               for _, instr := range b.Instrs {
-                       ops := instr.Operands(nil)
-                       switch instr.(type) {
-                       case *ir.Store:
-                               // (9.7) variable _reads_ use variables, writes do not
-                               ops = ops[1:]
-                       case *ir.DebugRef:
-                               ops = nil
-                       }
-                       for _, arg := range ops {
-                               walkPhi(*arg, func(v ir.Value) {
-                                       switch v := v.(type) {
-                                       case *ir.Function:
-                                               // (4.3) functions use closures and bound methods.
-                                               // (4.5) functions use functions they call
-                                               // (9.5) instructions use their operands
-                                               // (4.4) functions use functions they return. we assume that someone else will call the returned function
-                                               if owningObject(v) != nil {
-                                                       ctx.seeAndUse(owningObject(v), fnObj, edgeInstructionOperand)
-                                               }
-                                               g.function(ctx, v)
-                                       case *ir.Const:
-                                               // (9.6) instructions use their operands' types
-                                               ctx.seeAndUse(v.Type(), fnObj, edgeType)
-                                               g.typ(ctx, v.Type(), nil)
-                                       case *ir.Global:
-                                               if v.Object() != nil {
-                                                       // (9.5) instructions use their operands
-                                                       ctx.seeAndUse(v.Object(), fnObj, edgeInstructionOperand)
-                                               }
-                                       }
-                               })
-                       }
-                       if v, ok := instr.(ir.Value); ok {
-                               if _, ok := v.(*ir.Range); !ok {
-                                       // See https://github.com/golang/go/issues/19670
-
-                                       // (4.8) instructions use their types
-                                       // (9.4) conversions use the type they convert to
-                                       ctx.seeAndUse(v.Type(), fnObj, edgeType)
-                                       g.typ(ctx, v.Type(), nil)
-                               }
-                       }
-                       switch instr := instr.(type) {
-                       case *ir.Field:
-                               st := instr.X.Type().Underlying().(*types.Struct)
-                               field := st.Field(instr.Field)
-                               // (4.7) functions use fields they access
-                               ctx.seeAndUse(field, fnObj, edgeFieldAccess)
-                       case *ir.FieldAddr:
-                               st := code.Dereference(instr.X.Type()).Underlying().(*types.Struct)
-                               field := st.Field(instr.Field)
-                               // (4.7) functions use fields they access
-                               ctx.seeAndUse(field, fnObj, edgeFieldAccess)
-                       case *ir.Store:
-                               // nothing to do, handled generically by operands
-                       case *ir.Call:
-                               c := instr.Common()
-                               if !c.IsInvoke() {
-                                       // handled generically as an instruction operand
-
-                                       if g.wholeProgram {
-                                               // (e3) special case known reflection-based method callers
-                                               switch code.CallName(c) {
-                                               case "net/rpc.Register", "net/rpc.RegisterName", "(*net/rpc.Server).Register", "(*net/rpc.Server).RegisterName":
-                                                       var arg ir.Value
-                                                       switch code.CallName(c) {
-                                                       case "net/rpc.Register":
-                                                               arg = c.Args[0]
-                                                       case "net/rpc.RegisterName":
-                                                               arg = c.Args[1]
-                                                       case "(*net/rpc.Server).Register":
-                                                               arg = c.Args[1]
-                                                       case "(*net/rpc.Server).RegisterName":
-                                                               arg = c.Args[2]
-                                                       }
-                                                       walkPhi(arg, func(v ir.Value) {
-                                                               if v, ok := v.(*ir.MakeInterface); ok {
-                                                                       walkPhi(v.X, func(vv ir.Value) {
-                                                                               ms := ctx.pkg.IR.Prog.MethodSets.MethodSet(vv.Type())
-                                                                               for i := 0; i < ms.Len(); i++ {
-                                                                                       if ms.At(i).Obj().Exported() {
-                                                                                               g.useMethod(ctx, vv.Type(), ms.At(i), fnObj, edgeNetRPCRegister)
-                                                                                       }
-                                                                               }
-                                                                       })
-                                                               }
-                                                       })
-                                               }
-                                       }
-                               } else {
-                                       // (4.5) functions use functions/interface methods they call
-                                       ctx.seeAndUse(c.Method, fnObj, edgeInterfaceCall)
-                               }
-                       case *ir.Return:
-                               // nothing to do, handled generically by operands
-                       case *ir.ChangeType:
-                               // conversion type handled generically
-
-                               s1, ok1 := code.Dereference(instr.Type()).Underlying().(*types.Struct)
-                               s2, ok2 := code.Dereference(instr.X.Type()).Underlying().(*types.Struct)
-                               if ok1 && ok2 {
-                                       // Converting between two structs. The fields are
-                                       // relevant for the conversion, but only if the
-                                       // fields are also used outside of the conversion.
-                                       // Mark fields as used by each other.
-
-                                       assert(s1.NumFields() == s2.NumFields())
-                                       for i := 0; i < s1.NumFields(); i++ {
-                                               ctx.see(s1.Field(i))
-                                               ctx.see(s2.Field(i))
-                                               // (5.1) when converting between two equivalent structs, the fields in
-                                               // either struct use each other. the fields are relevant for the
-                                               // conversion, but only if the fields are also accessed outside the
-                                               // conversion.
-                                               ctx.seeAndUse(s1.Field(i), s2.Field(i), edgeStructConversion)
-                                               ctx.seeAndUse(s2.Field(i), s1.Field(i), edgeStructConversion)
-                                       }
-                               }
-                       case *ir.MakeInterface:
-                               // nothing to do, handled generically by operands
-                       case *ir.Slice:
-                               // nothing to do, handled generically by operands
-                       case *ir.RunDefers:
-                               // nothing to do, the deferred functions are already marked use by defering them.
-                       case *ir.Convert:
-                               // to unsafe.Pointer
-                               if typ, ok := instr.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
-                                       if ptr, ok := instr.X.Type().Underlying().(*types.Pointer); ok {
-                                               if st, ok := ptr.Elem().Underlying().(*types.Struct); ok {
-                                                       for i := 0; i < st.NumFields(); i++ {
-                                                               // (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
-                                                               ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion)
-                                                       }
-                                               }
-                                       }
-                               }
-                               // from unsafe.Pointer
-                               if typ, ok := instr.X.Type().(*types.Basic); ok && typ.Kind() == types.UnsafePointer {
-                                       if ptr, ok := instr.Type().Underlying().(*types.Pointer); ok {
-                                               if st, ok := ptr.Elem().Underlying().(*types.Struct); ok {
-                                                       for i := 0; i < st.NumFields(); i++ {
-                                                               // (5.2) when converting to or from unsafe.Pointer, mark all fields as used.
-                                                               ctx.seeAndUse(st.Field(i), fnObj, edgeUnsafeConversion)
-                                                       }
-                                               }
-                                       }
-                               }
-                       case *ir.TypeAssert:
-                               // nothing to do, handled generically by instruction
-                               // type (possibly a tuple, which contains the asserted
-                               // to type). redundantly handled by the type of
-                               // ir.Extract, too
-                       case *ir.MakeClosure:
-                               // nothing to do, handled generically by operands
-                       case *ir.Alloc:
-                               // nothing to do
-                       case *ir.UnOp:
-                               // nothing to do
-                       case *ir.BinOp:
-                               // nothing to do
-                       case *ir.If:
-                               // nothing to do
-                       case *ir.Jump:
-                               // nothing to do
-                       case *ir.Unreachable:
-                               // nothing to do
-                       case *ir.IndexAddr:
-                               // nothing to do
-                       case *ir.Extract:
-                               // nothing to do
-                       case *ir.Panic:
-                               // nothing to do
-                       case *ir.DebugRef:
-                               // nothing to do
-                       case *ir.BlankStore:
-                               // nothing to do
-                       case *ir.Phi:
-                               // nothing to do
-                       case *ir.Sigma:
-                               // nothing to do
-                       case *ir.MakeMap:
-                               // nothing to do
-                       case *ir.MapUpdate:
-                               // nothing to do
-                       case *ir.MapLookup:
-                               // nothing to do
-                       case *ir.StringLookup:
-                               // nothing to do
-                       case *ir.MakeSlice:
-                               // nothing to do
-                       case *ir.Send:
-                               // nothing to do
-                       case *ir.MakeChan:
-                               // nothing to do
-                       case *ir.Range:
-                               // nothing to do
-                       case *ir.Next:
-                               // nothing to do
-                       case *ir.Index:
-                               // nothing to do
-                       case *ir.Select:
-                               // nothing to do
-                       case *ir.ChangeInterface:
-                               // nothing to do
-                       case *ir.Load:
-                               // nothing to do
-                       case *ir.Go:
-                               // nothing to do
-                       case *ir.Defer:
-                               // nothing to do
-                       case *ir.Parameter:
-                               // nothing to do
-                       case *ir.Const:
-                               // nothing to do
-                       case *ir.Recv:
-                               // nothing to do
-                       case *ir.TypeSwitch:
-                               // nothing to do
-                       case *ir.ConstantSwitch:
-                               // nothing to do
-                       default:
-                               panic(fmt.Sprintf("unreachable: %T", instr))
-                       }
-               }
-       }
-}
-
-// isNoCopyType reports whether a type represents the NoCopy sentinel
-// type. The NoCopy type is a named struct with no fields and exactly
-// one method `func Lock()` that is empty.
-//
-// FIXME(dh): currently we're not checking that the function body is
-// empty.
-func isNoCopyType(typ types.Type) bool {
-       st, ok := typ.Underlying().(*types.Struct)
-       if !ok {
-               return false
-       }
-       if st.NumFields() != 0 {
-               return false
-       }
-
-       named, ok := typ.(*types.Named)
-       if !ok {
-               return false
-       }
-       if named.NumMethods() != 1 {
-               return false
-       }
-       meth := named.Method(0)
-       if meth.Name() != "Lock" {
-               return false
-       }
-       sig := meth.Type().(*types.Signature)
-       if sig.Params().Len() != 0 || sig.Results().Len() != 0 {
-               return false
-       }
-       return true
-}
-
-func walkPhi(v ir.Value, fn func(v ir.Value)) {
-       phi, ok := v.(*ir.Phi)
-       if !ok {
-               fn(v)
-               return
-       }
-
-       seen := map[ir.Value]struct{}{}
-       var impl func(v *ir.Phi)
-       impl = func(v *ir.Phi) {
-               if _, ok := seen[v]; ok {
-                       return
-               }
-               seen[v] = struct{}{}
-               for _, e := range v.Edges {
-                       if ev, ok := e.(*ir.Phi); ok {
-                               impl(ev)
-                       } else {
-                               fn(e)
-                       }
-               }
-       }
-       impl(phi)
-}
-
-func interfacesFromExportData(pkg *types.Package) []*types.Interface {
-       var out []*types.Interface
-       scope := pkg.Scope()
-       for _, name := range scope.Names() {
-               obj := scope.Lookup(name)
-               out = append(out, interfacesFromObject(obj)...)
-       }
-       return out
-}
-
-func interfacesFromObject(obj types.Object) []*types.Interface {
-       var out []*types.Interface
-       switch obj := obj.(type) {
-       case *types.Func:
-               sig := obj.Type().(*types.Signature)
-               for i := 0; i < sig.Results().Len(); i++ {
-                       out = append(out, interfacesFromObject(sig.Results().At(i))...)
-               }
-               for i := 0; i < sig.Params().Len(); i++ {
-                       out = append(out, interfacesFromObject(sig.Params().At(i))...)
-               }
-       case *types.TypeName:
-               if named, ok := obj.Type().(*types.Named); ok {
-                       for i := 0; i < named.NumMethods(); i++ {
-                               out = append(out, interfacesFromObject(named.Method(i))...)
-                       }
-
-                       if iface, ok := named.Underlying().(*types.Interface); ok {
-                               out = append(out, iface)
-                       }
-               }
-       case *types.Var:
-               // No call to Underlying here. We want unnamed interfaces
-               // only. Named interfaces are gotten directly from the
-               // package's scope.
-               if iface, ok := obj.Type().(*types.Interface); ok {
-                       out = append(out, iface)
-               }
-       case *types.Const:
-       case *types.Builtin:
-       default:
-               panic(fmt.Sprintf("unhandled type: %T", obj))
-       }
-       return out
-}