github.com/amarpal/go-tools@v0.0.0-20240422043104-40142f59f616/unused/serialize.go (about) 1 package unused 2 3 import ( 4 "fmt" 5 "go/token" 6 "os" 7 8 "golang.org/x/tools/go/types/objectpath" 9 ) 10 11 type ObjectPath struct { 12 PkgPath string 13 ObjPath objectpath.Path 14 } 15 16 // XXX make sure that node 0 always exists and is always the root 17 18 type SerializedGraph struct { 19 nodes []Node 20 nodesByPath map[ObjectPath]NodeID 21 // XXX deduplicating on position is dubious for `switch x := foo.(type)`, where x will be declared many times for 22 // the different types, but all at the same position. On the other hand, merging these nodes is probably fine. 23 nodesByPosition map[token.Position]NodeID 24 } 25 26 func trace(f string, args ...interface{}) { 27 fmt.Fprintf(os.Stderr, f, args...) 28 fmt.Fprintln(os.Stderr) 29 } 30 31 func (g *SerializedGraph) Merge(nodes []Node) { 32 if g.nodesByPath == nil { 33 g.nodesByPath = map[ObjectPath]NodeID{} 34 } 35 if g.nodesByPosition == nil { 36 g.nodesByPosition = map[token.Position]NodeID{} 37 } 38 if len(g.nodes) == 0 { 39 // Seed nodes with a root node 40 g.nodes = append(g.nodes, Node{}) 41 } 42 // OPT(dh): reuse storage between calls to Merge 43 remapping := make([]NodeID, len(nodes)) 44 45 // First pass: compute remapping of IDs of to-be-merged nodes 46 for _, n := range nodes { 47 // XXX Column is never 0. it's 1 if there is no column information in the export data. which sucks, because 48 // objects can also genuinely be in column 1. 49 if n.id != 0 && n.obj.Path == (ObjectPath{}) && n.obj.Position.Column == 0 { 50 // If the object has no path, then it couldn't have come from export data, which means it needs to have full 51 // position information including a column. 52 panic(fmt.Sprintf("object %q has no path but also no column information", n.obj.Name)) 53 } 54 55 if orig, ok := g.nodesByPath[n.obj.Path]; ok { 56 // We already have a node for this object 57 trace("deduplicating %d -> %d based on path %s", n.id, orig, n.obj.Path) 58 remapping[n.id] = orig 59 } else if orig, ok := g.nodesByPosition[n.obj.Position]; ok && n.obj.Position.Column != 0 { 60 // We already have a node for this object 61 trace("deduplicating %d -> %d based on position %s", n.id, orig, n.obj.Position) 62 remapping[n.id] = orig 63 } else { 64 // This object is new to us; change ID to avoid collision 65 newID := NodeID(len(g.nodes)) 66 trace("new node, remapping %d -> %d", n.id, newID) 67 remapping[n.id] = newID 68 g.nodes = append(g.nodes, Node{ 69 id: newID, 70 obj: n.obj, 71 uses: make([]NodeID, 0, len(n.uses)), 72 owns: make([]NodeID, 0, len(n.owns)), 73 }) 74 if n.id == 0 { 75 // Our root uses all the roots of the subgraphs 76 g.nodes[0].uses = append(g.nodes[0].uses, newID) 77 } 78 if n.obj.Path != (ObjectPath{}) { 79 g.nodesByPath[n.obj.Path] = newID 80 } 81 if n.obj.Position.Column != 0 { 82 g.nodesByPosition[n.obj.Position] = newID 83 } 84 } 85 } 86 87 // Second step: apply remapping 88 for _, n := range nodes { 89 n.id = remapping[n.id] 90 for i := range n.uses { 91 n.uses[i] = remapping[n.uses[i]] 92 } 93 for i := range n.owns { 94 n.owns[i] = remapping[n.owns[i]] 95 } 96 g.nodes[n.id].uses = append(g.nodes[n.id].uses, n.uses...) 97 g.nodes[n.id].owns = append(g.nodes[n.id].owns, n.owns...) 98 } 99 }