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 }
100