@@ -6,7 +6,6 @@ package typerefs
6
6
7
7
import (
8
8
"context"
9
- "go/token"
10
9
"runtime"
11
10
"sync"
12
11
@@ -24,42 +23,20 @@ const (
24
23
25
24
// A Package holds reference information for a single package.
26
25
type Package struct {
27
- idx packageIdx // memoized index of this package's ID, to save map lookups
26
+ // metadata holds metadata about this package and its dependencies.
27
+ metadata * source.Metadata
28
28
29
- // Metadata holds metadata about this package and its dependencies.
30
- Metadata * source.Metadata
31
-
32
- // Refs records syntactic edges between declarations in this package and
33
- // declarations in this package or another package. See the package
34
- // documentation for a detailed description of what these edges do (and do
35
- // not) represent.
36
- Refs map [string ][]Ref
37
-
38
- // TransitiveRefs records, for each declaration in the package, the
29
+ // transitiveRefs records, for each exported declaration in the package, the
39
30
// transitive set of packages within the containing graph that are
40
31
// transitively reachable through references, starting with the given decl.
41
- TransitiveRefs map [string ]* PackageSet
32
+ transitiveRefs map [string ]* PackageSet
42
33
43
34
// ReachesViaDeps records the set of packages in the containing graph whose
44
35
// syntax may affect the current package's types. See the package
45
36
// documentation for more details of what this means.
46
37
ReachesByDeps * PackageSet
47
38
}
48
39
49
- // A Ref is a referenced declaration.
50
- //
51
- // Unpack it using the Unpack method, with the PackageIndex instance that was
52
- // used to construct the references.
53
- type Ref struct {
54
- pkg packageIdx
55
- name string
56
- }
57
-
58
- // UnpackRef unpacks the actual PackageID an name encoded in ref.
59
- func (r Ref ) Unpack (index * PackageIndex ) (PackageID source.PackageID , name string ) {
60
- return index .id (r .pkg ), r .name
61
- }
62
-
63
40
// A PackageGraph represents a fully analyzed graph of packages and their
64
41
// dependencies.
65
42
type PackageGraph struct {
@@ -139,111 +116,78 @@ func (g *PackageGraph) Package(ctx context.Context, id source.PackageID) (*Packa
139
116
// only be called from Package.
140
117
func (g * PackageGraph ) buildPackage (ctx context.Context , id source.PackageID ) (* Package , error ) {
141
118
p := & Package {
142
- idx : g .pkgIndex .idx (id ),
143
- Metadata : g .meta .Metadata (id ),
144
- Refs : make (map [string ][]Ref ),
145
- TransitiveRefs : make (map [string ]* PackageSet ),
119
+ metadata : g .meta .Metadata (id ),
120
+ transitiveRefs : make (map [string ]* PackageSet ),
146
121
}
147
122
var files []* source.ParsedGoFile
148
- for _ , filename := range p .Metadata .CompiledGoFiles {
123
+ for _ , filename := range p .metadata .CompiledGoFiles {
149
124
f , err := g .parse (ctx , filename )
150
125
if err != nil {
151
126
return nil , err
152
127
}
153
128
files = append (files , f )
154
129
}
155
130
imports := make (map [source.ImportPath ]* source.Metadata )
156
- for impPath , depID := range p .Metadata .DepsByImpPath {
131
+ for impPath , depID := range p .metadata .DepsByImpPath {
157
132
if depID != "" {
158
133
imports [impPath ] = g .meta .Metadata (depID )
159
134
}
160
135
}
161
- p .Refs = Refs (files , id , imports , g .pkgIndex )
162
-
163
- // Compute packages reachable from each exported symbol of this package.
164
- for name := range p .Refs {
165
- if token .IsExported (name ) {
166
- set := g .pkgIndex .New ()
167
- g .reachableByName (ctx , p , name , set , make (map [string ]bool ))
168
- p .TransitiveRefs [name ] = set
169
- }
170
- }
171
136
172
- var err error
173
- p .ReachesByDeps , err = g .reachesByDeps (ctx , p .Metadata )
174
- if err != nil {
175
- return nil , err
176
- }
177
- return p , nil
178
- }
179
-
180
- // ExternalRefs returns a new map whose keys are the exported symbols
181
- // of the package (of the specified id, pkgIndex, and refs). The
182
- // corresponding value of each key is the set of exported symbols
183
- // indirectly referenced by it.
184
- //
185
- // TODO(adonovan): simplify the API once the SCC-based optimization lands.
186
- func ExternalRefs (pkgIndex * PackageIndex , id source.PackageID , refs map [string ][]Ref ) map [string ]map [Ref ]bool {
187
- // (This intrapackage recursion will go away in a follow-up CL.)
188
- var visit func (name string , res map [Ref ]bool , seen map [string ]bool )
189
- visit = func (name string , res map [Ref ]bool , seen map [string ]bool ) {
190
- if ! seen [name ] {
191
- seen [name ] = true
192
- for _ , ref := range refs [name ] {
193
- if pkgIndex .id (ref .pkg ) == id {
194
- visit (ref .name , res , seen ) // intrapackage recursion
195
- } else {
196
- res [ref ] = true // cross-package ref
197
- }
198
- }
199
- }
200
- }
137
+ // Compute the symbol-level dependencies through this package.
138
+ //
139
+ // refs records syntactic edges between declarations in this
140
+ // package and declarations in this package or another
141
+ // package. See the package documentation for a detailed
142
+ // description of what these edges do (and do not) represent.
143
+ //
144
+ // TODO(adonovan): opt: serialize and deserialize the refs
145
+ // result computed above and persist it in the filecache.
146
+ refs := Refs (files , id , imports )
201
147
202
- results := make (map [string ]map [Ref ]bool )
203
- for name := range refs {
204
- if token .IsExported (name ) {
205
- res := make (map [Ref ]bool )
206
- seen := make (map [string ]bool )
207
- visit (name , res , seen )
208
- results [name ] = res
209
- }
210
- }
211
- return results
212
- }
148
+ // This point separates the local preprocessing
149
+ // -- of a single package (above) from the global --
150
+ // transitive reachability query (below).
213
151
214
- // reachableByName computes the set of packages that are reachable through
215
- // references, starting with the declaration for name in package p.
216
- func (g * PackageGraph ) reachableByName (ctx context.Context , p * Package , name string , set * PackageSet , seen map [string ]bool ) error {
217
- if seen [name ] {
218
- return nil
219
- }
220
- seen [name ] = true
221
-
222
- // Opt: when we compact reachable edges inside the Refs algorithm, we handle
223
- // all edges to a given package in a batch, so they should be adjacent to
224
- // each other in the resulting slice. Therefore remembering the last P here
225
- // can save on lookups.
226
- depP := p
227
- for _ , node := range p .Refs [name ] {
228
- if node .pkg == p .idx {
229
- // same package
230
- g .reachableByName (ctx , p , node .name , set , seen )
231
- } else {
232
- // cross-package ref
233
- if depP .idx != node .pkg {
234
- id := g .pkgIndex .id (node .pkg )
152
+ // Now compute the transitive closure of packages reachable
153
+ // from any exported symbol of this package.
154
+ //
155
+ // TODO(adonovan): opt: many elements of refs[name] are
156
+ // identical, so this does redundant work. Choose a data type
157
+ // for the result of Refs() that expresses the M:N structure
158
+ // explicitly.
159
+ for name , nodes := range refs {
160
+ set := g .pkgIndex .New ()
161
+
162
+ // The nodes slice is sorted by (package, name),
163
+ // so we can economize by calling g.Package only
164
+ // when the package id changes.
165
+ depP := p
166
+ for _ , node := range nodes {
167
+ assert (node .PkgID != id , "intra-package edge" )
168
+ if depP .metadata .ID != node .PkgID {
169
+ // package changed
235
170
var err error
236
- depP , err = g .Package (ctx , id )
171
+ depP , err = g .Package (ctx , node . PkgID )
237
172
if err != nil {
238
- return err
173
+ return nil , err
239
174
}
240
175
}
241
- set .add (node .pkg )
242
- set .Union (depP .TransitiveRefs [node .name ])
176
+ set .add (g . pkgIndex . idx ( node .PkgID ) )
177
+ set .Union (depP .transitiveRefs [node .Name ])
243
178
}
179
+ p .transitiveRefs [name ] = set
180
+ }
181
+
182
+ // Finally compute the union of transitiveRefs
183
+ // across the direct deps of this package.
184
+ byDeps , err := g .reachesByDeps (ctx , p .metadata )
185
+ if err != nil {
186
+ return nil , err
244
187
}
188
+ p .ReachesByDeps = byDeps
245
189
246
- return nil
190
+ return p , nil
247
191
}
248
192
249
193
// reachesByDeps computes the set of packages that are reachable through
@@ -255,11 +199,9 @@ func (g *PackageGraph) reachesByDeps(ctx context.Context, m *source.Metadata) (*
255
199
if err != nil {
256
200
return nil , err
257
201
}
258
- transitive .add (dep .idx )
259
- for name , set := range dep .TransitiveRefs {
260
- if token .IsExported (name ) {
261
- transitive .Union (set )
262
- }
202
+ transitive .add (g .pkgIndex .idx (dep .metadata .ID ))
203
+ for _ , set := range dep .transitiveRefs {
204
+ transitive .Union (set )
263
205
}
264
206
}
265
207
return transitive , nil
0 commit comments