1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package facts defines a serializable set of analysis.Fact.
7 // It provides a partial implementation of the Fact-related parts of the
8 // analysis.Pass interface for use in analysis drivers such as "go vet"
9 // and other build systems.
11 // The serial format is unspecified and may change, so the same version
12 // of this package must be used for reading and writing serialized facts.
14 // The handling of facts in the analysis system parallels the handling
15 // of type information in the compiler: during compilation of package P,
16 // the compiler emits an export data file that describes the type of
17 // every object (named thing) defined in package P, plus every object
18 // indirectly reachable from one of those objects. Thus the downstream
19 // compiler of package Q need only load one export data file per direct
20 // import of Q, and it will learn everything about the API of package P
21 // and everything it needs to know about the API of P's dependencies.
23 // Similarly, analysis of package P emits a fact set containing facts
24 // about all objects exported from P, plus additional facts about only
25 // those objects of P's dependencies that are reachable from the API of
26 // package P; the downstream analysis of Q need only load one fact set
27 // per direct import of Q.
29 // The notion of "exportedness" that matters here is that of the
30 // compiler. According to the language spec, a method pkg.T.f is
31 // unexported simply because its name starts with lowercase. But the
32 // compiler must nonetheless export f so that downstream compilations can
33 // accurately ascertain whether pkg.T implements an interface pkg.I
34 // defined as interface{f()}. Exported thus means "described in export
50 "golang.org/x/tools/go/analysis"
51 "golang.org/x/tools/go/types/objectpath"
56 // A Set is a set of analysis.Facts.
58 // Decode creates a Set of facts by reading from the imports of a given
59 // package, and Encode writes out the set. Between these operation,
60 // the Import and Export methods will query and update the set.
62 // All of Set's methods except String are safe to call concurrently.
66 m map[key]analysis.Fact
71 obj types.Object // (object facts only)
75 // ImportObjectFact implements analysis.Pass.ImportObjectFact.
76 func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool {
80 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)}
83 if v, ok := s.m[key]; ok {
84 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
90 // ExportObjectFact implements analysis.Pass.ExportObjectFact.
91 func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) {
92 if obj.Pkg() != s.pkg {
93 log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package",
96 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)}
98 s.m[key] = fact // clobber any existing entry
102 func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
103 var facts []analysis.ObjectFact
105 for k, v := range s.m {
106 if k.obj != nil && filter[k.t] {
107 facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v})
114 // ImportPackageFact implements analysis.Pass.ImportPackageFact.
115 func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
119 key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
122 if v, ok := s.m[key]; ok {
123 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
129 // ExportPackageFact implements analysis.Pass.ExportPackageFact.
130 func (s *Set) ExportPackageFact(fact analysis.Fact) {
131 key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
133 s.m[key] = fact // clobber any existing entry
137 func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
138 var facts []analysis.PackageFact
140 for k, v := range s.m {
141 if k.obj == nil && filter[k.t] {
142 facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v})
149 // gobFact is the Gob declaration of a serialized fact.
150 type gobFact struct {
151 PkgPath string // path of package
152 Object objectpath.Path // optional path of object relative to package itself
153 Fact analysis.Fact // type and value of user-defined Fact
156 // Decode decodes all the facts relevant to the analysis of package pkg.
157 // The read function reads serialized fact data from an external source
158 // for one of of pkg's direct imports. The empty file is a valid
159 // encoding of an empty fact set.
161 // It is the caller's responsibility to call gob.Register on all
162 // necessary fact types.
163 func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) {
164 // Compute the import map for this package.
165 // See the package doc comment.
166 packages := importMap(pkg.Imports())
168 // Read facts from imported packages.
169 // Facts may describe indirectly imported packages, or their objects.
170 m := make(map[key]analysis.Fact) // one big bucket
171 for _, imp := range pkg.Imports() {
172 logf := func(format string, args ...interface{}) {
174 prefix := fmt.Sprintf("in %s, importing %s: ",
175 pkg.Path(), imp.Path())
176 log.Print(prefix, fmt.Sprintf(format, args...))
180 // Read the gob-encoded facts.
181 data, err := read(imp.Path())
183 return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
184 pkg.Path(), imp.Path(), err)
189 var gobFacts []gobFact
190 if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
191 return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err)
194 logf("decoded %d facts: %v", len(gobFacts), gobFacts)
197 // Parse each one into a key and a Fact.
198 for _, f := range gobFacts {
199 factPkg := packages[f.PkgPath]
201 // Fact relates to a dependency that was
202 // unused in this translation unit. Skip.
203 logf("no package %q; discarding %v", f.PkgPath, f.Fact)
206 key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
209 obj, err := objectpath.Object(factPkg, f.Object)
211 // (most likely due to unexported object)
212 // TODO(adonovan): audit for other possibilities.
213 logf("no object for path: %v; discarding %s", err, f.Fact)
217 logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
220 logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
226 return &Set{pkg: pkg, m: m}, nil
229 // Encode encodes a set of facts to a memory buffer.
231 // It may fail if one of the Facts could not be gob-encoded, but this is
232 // a sign of a bug in an Analyzer.
233 func (s *Set) Encode() []byte {
235 // TODO(adonovan): opt: use a more efficient encoding
236 // that avoids repeating PkgPath for each fact.
238 // Gather all facts, including those from imported packages.
239 var gobFacts []gobFact
242 for k, fact := range s.m {
244 log.Printf("%v => %s\n", k, fact)
246 var object objectpath.Path
248 path, err := objectpath.For(k.obj)
251 log.Printf("discarding fact %s about %s\n", fact, k.obj)
253 continue // object not accessible from package API; discard fact
257 gobFacts = append(gobFacts, gobFact{
258 PkgPath: k.pkg.Path(),
265 // Sort facts by (package, object, type) for determinism.
266 sort.Slice(gobFacts, func(i, j int) bool {
267 x, y := gobFacts[i], gobFacts[j]
268 if x.PkgPath != y.PkgPath {
269 return x.PkgPath < y.PkgPath
271 if x.Object != y.Object {
272 return x.Object < y.Object
274 tx := reflect.TypeOf(x.Fact)
275 ty := reflect.TypeOf(y.Fact)
277 return tx.String() < ty.String()
279 return false // equal
283 if len(gobFacts) > 0 {
284 if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil {
285 // Fact encoding should never fail. Identify the culprit.
286 for _, gf := range gobFacts {
287 if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil {
289 pkgpath := reflect.TypeOf(fact).Elem().PkgPath()
290 log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q",
291 fact, err, fact, pkgpath)
298 log.Printf("package %q: encode %d facts, %d bytes\n",
299 s.pkg.Path(), len(gobFacts), buf.Len())
305 // String is provided only for debugging, and must not be called
306 // concurrent with any Import/Export method.
307 func (s *Set) String() string {
310 for k, f := range s.m {
312 buf.WriteString(", ")
315 buf.WriteString(k.obj.String())
317 buf.WriteString(k.pkg.Path())
319 fmt.Fprintf(&buf, ": %v", f)