@@ -17,11 +17,9 @@ import (
17
17
18
18
// declInfo holds information about a single declaration.
19
19
type declInfo struct {
20
- name string // declaration name, for debugging only
21
20
node ast.Node // "declaring node" for this decl, to be traversed
22
21
tparams map [string ]bool // names declared by type parameters within this declaration
23
22
file * ast.File // file containing this decl, for local imports
24
- methods []* declInfo // method declarations for type declarations
25
23
}
26
24
27
25
// Refs analyzes local syntax of the provided ParsedGoFiles to extract
@@ -35,12 +33,17 @@ type declInfo struct {
35
33
// does not) represent.
36
34
func Refs (pgfs []* source.ParsedGoFile , id source.PackageID , imports map [source.ImportPath ]* source.Metadata , pkgIndex * PackageIndex ) map [string ][]Ref {
37
35
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 .
40
38
//
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 )
44
47
45
48
// localImports holds local import information, per file. The value is a
46
49
// 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
92
95
if name == "_" {
93
96
continue
94
97
}
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
+ })
111
103
}
112
104
113
105
case token .VAR , token .CONST :
@@ -117,8 +109,7 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
117
109
if name .Name == "_" {
118
110
continue
119
111
}
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 })
122
113
}
123
114
}
124
115
}
@@ -127,40 +118,32 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
127
118
if d .Name .Name == "_" {
128
119
continue
129
120
}
130
- // TODO(rfindley): handle dupes here too.
131
121
// This check for NumFields() > 0 is consistent with go/types, which
132
122
// reports an error but treats the declaration like a normal function
133
123
// when Recv is non-nil but empty (as in func () f()).
134
124
if d .Recv .NumFields () > 0 {
135
125
// Method. Associate it with the receiver.
136
126
_ , 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
- }
142
127
methodInfo := & declInfo {
143
- name : d .Name .Name ,
144
128
node : d ,
145
129
file : file ,
146
130
}
147
131
if len (tparams ) > 0 {
148
132
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
152
136
}
153
137
}
154
138
}
155
- recvInfo . methods = append (recvInfo . methods , methodInfo )
139
+ decls [ id . Name ] = append (decls [ id . Name ] , methodInfo )
156
140
} else {
157
141
// Non-method.
158
- decls [d .Name .Name ] = & declInfo {
159
- name : d .Name .Name ,
142
+ decls [d .Name .Name ] = append (decls [d .Name .Name ], & declInfo {
160
143
node : d ,
161
- file : file ,
162
144
tparams : tparamsMap (typeparams .ForFuncType (d .Type )),
163
- }
145
+ file : file ,
146
+ })
164
147
}
165
148
}
166
149
}
@@ -169,10 +152,7 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
169
152
// mappedRefs maps each name in this package to the set
170
153
// of (pkg, name) pairs it references.
171
154
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 {
176
156
// recordEdge records the (id, name)->(id2, name) edge.
177
157
recordEdge := func (id2 source.PackageID , name2 string ) {
178
158
pkgRefs , ok := mappedRefs [name ]
@@ -206,32 +186,20 @@ func Refs(pgfs []*source.ParsedGoFile, id source.PackageID, imports map[source.I
206
186
// var y = struct {F int}{}
207
187
if _ , ok := decls [name ]; ok {
208
188
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.
226
192
for _ , depID := range fileImports ["." ] {
227
193
// Conservatively, assume that this name comes from every
228
194
// dot-imported package.
229
195
recordEdge (depID , name )
230
196
}
231
197
}
232
198
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.
235
203
if sel != "" && token .IsExported (sel ) {
236
204
for _ , depID := range fileImports [name ] {
237
205
recordEdge (depID , sel )
0 commit comments