@@ -17,11 +17,9 @@ import (
1717
1818// declInfo holds information about a single declaration.
1919type declInfo struct {
20- name string // declaration name, for debugging only
2120 node ast.Node // "declaring node" for this decl, to be traversed
2221 tparams map [string ]bool // names declared by type parameters within this declaration
2322 file * ast.File // file containing this decl, for local imports
24- methods []* declInfo // method declarations for type declarations
2523}
2624
2725// Refs analyzes local syntax of the provided ParsedGoFiles to extract
@@ -35,12 +33,17 @@ type declInfo struct {
3533// does not) represent.
3634func Refs (pgfs []* source.ParsedGoFile , id source.PackageID , imports map [source.ImportPath ]* source.Metadata , pkgIndex * PackageIndex ) map [string ][]Ref {
3735 var (
38- // decls collects nodes that define top-level objects, to be walked
39- // later .
36+ // decls collects declaration nodes that collectively define the type of
37+ // each name in the package scope .
4038 //
41- // TODO(rfindley): we may have duplicate decls with the same name for
42- // invalid code. We must handle this case by take the union of edges.
43- decls = make (map [string ]* declInfo )
39+ // - For valid code, there may be multiple declarations recorded when that
40+ // name is a type that has methods.
41+ // - For invalid code, there may also be multiple declarations recorded due
42+ // to duplicate declarations.
43+ //
44+ // In either case, the algorithm is the same: we walk all declarations for
45+ // each name to collect referring identifiers.
46+ decls = make (map [string ][]* declInfo )
4447
4548 // localImports holds local import information, per file. The value is a
4649 // slice because multiple packages may be referenced by a given name in the
@@ -92,22 +95,11 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
9295 if name == "_" {
9396 continue
9497 }
95- info := decls [name ] // TODO(rfindley): handle the case of duplicate decls.
96- if info == nil {
97- info = & declInfo {}
98- decls [name ] = info
99- }
100- // Sanity check that the root node info has not been set.
101- //
102- // TODO(rfindley): this panic is incorrect in the presence of
103- // invalid code with duplicate decls.
104- if info .node != nil || info .tparams != nil {
105- panic (fmt .Sprintf ("root node already set for %s.%s" , id , name ))
106- }
107- info .name = name
108- info .file = file
109- info .node = spec
110- info .tparams = tparamsMap (typeparams .ForTypeSpec (spec ))
98+ decls [name ] = append (decls [name ], & declInfo {
99+ node : spec ,
100+ tparams : tparamsMap (typeparams .ForTypeSpec (spec )),
101+ file : file ,
102+ })
111103 }
112104
113105 case token .VAR , token .CONST :
@@ -117,8 +109,7 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
117109 if name .Name == "_" {
118110 continue
119111 }
120- // TODO(rfindley): handle dupes here too.
121- decls [name .Name ] = & declInfo {node : spec , name : name .Name , file : file }
112+ decls [name .Name ] = append (decls [name .Name ], & declInfo {node : spec , file : file })
122113 }
123114 }
124115 }
@@ -127,40 +118,32 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
127118 if d .Name .Name == "_" {
128119 continue
129120 }
130- // TODO(rfindley): handle dupes here too.
131121 // This check for NumFields() > 0 is consistent with go/types, which
132122 // reports an error but treats the declaration like a normal function
133123 // when Recv is non-nil but empty (as in func () f()).
134124 if d .Recv .NumFields () > 0 {
135125 // Method. Associate it with the receiver.
136126 _ , id , tparams := unpackRecv (d .Recv .List [0 ].Type )
137- recvInfo := decls [id .Name ]
138- if recvInfo == nil {
139- recvInfo = & declInfo {}
140- decls [id .Name ] = recvInfo
141- }
142127 methodInfo := & declInfo {
143- name : d .Name .Name ,
144128 node : d ,
145129 file : file ,
146130 }
147131 if len (tparams ) > 0 {
148132 methodInfo .tparams = make (map [string ]bool )
149- for _ , id := range tparams {
150- if id .Name != "_" {
151- methodInfo .tparams [id .Name ] = true
133+ for _ , tparam := range tparams {
134+ if tparam .Name != "_" {
135+ methodInfo .tparams [tparam .Name ] = true
152136 }
153137 }
154138 }
155- recvInfo . methods = append (recvInfo . methods , methodInfo )
139+ decls [ id . Name ] = append (decls [ id . Name ] , methodInfo )
156140 } else {
157141 // Non-method.
158- decls [d .Name .Name ] = & declInfo {
159- name : d .Name .Name ,
142+ decls [d .Name .Name ] = append (decls [d .Name .Name ], & declInfo {
160143 node : d ,
161- file : file ,
162144 tparams : tparamsMap (typeparams .ForFuncType (d .Type )),
163- }
145+ file : file ,
146+ })
164147 }
165148 }
166149 }
@@ -169,10 +152,7 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
169152 // mappedRefs maps each name in this package to the set
170153 // of (pkg, name) pairs it references.
171154 mappedRefs := make (map [string ]map [source.PackageID ]map [string ]bool )
172- for name , rootInfo := range decls {
173- // Consider method declarations to be part of the type declaration.
174- infos := append ([]* declInfo {rootInfo }, rootInfo .methods ... )
175-
155+ for name , infos := range decls {
176156 // recordEdge records the (id, name)->(id2, name) edge.
177157 recordEdge := func (id2 source.PackageID , name2 string ) {
178158 pkgRefs , ok := mappedRefs [name ]
@@ -206,32 +186,20 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
206186 // var y = struct {F int}{}
207187 if _ , ok := decls [name ]; ok {
208188 recordEdge (id , name )
209- return
210- }
211-
212- // name is not declared in the current package, so record edges to all
213- // imported objects it could refer to.
214-
215- // Conservatively, if name is exported we record an edge for every
216- // dot-imported package.
217- //
218- // Even though it is an error for a local import name and
219- // dot-imported name to collide, we don't handle this collision
220- // here because it is so vanishingly rare to have a package
221- // qualifier that starts with a capital letter at the same time as
222- // having a dot import.
223- //
224- // Just record edges to both.
225- if token .IsExported (name ) {
189+ } else if token .IsExported (name ) {
190+ // Only record an edge to dot-imported packages if there was no edge
191+ // to a local name. This assumes that there are no duplicate declarations.
226192 for _ , depID := range fileImports ["." ] {
227193 // Conservatively, assume that this name comes from every
228194 // dot-imported package.
229195 recordEdge (depID , name )
230196 }
231197 }
232198
233- // Similarly, if sel is exported we record an edge for every matching
234- // import.
199+ // Record an edge to an import if it matches the name, even if that
200+ // name collides with a package level name. Unlike the case of dotted
201+ // imports, we know the package is invalid here, and choose to fail
202+ // conservatively.
235203 if sel != "" && token .IsExported (sel ) {
236204 for _ , depID := range fileImports [name ] {
237205 recordEdge (depID , sel )
0 commit comments