]> git.ipfire.org Git - thirdparty/gcc.git/blame - libgo/go/golang.org/x/tools/go/analysis/internal/facts/facts.go
libgo: update to Go1.14beta1
[thirdparty/gcc.git] / libgo / go / golang.org / x / tools / go / analysis / internal / facts / facts.go
CommitLineData
4f4a855d
ILT
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.
4
5// Package facts defines a serializable set of analysis.Fact.
6//
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.
10//
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.
13//
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.
22//
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.
28//
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
5a8ea165 32// compiler must nonetheless export f so that downstream compilations can
4f4a855d
ILT
33// accurately ascertain whether pkg.T implements an interface pkg.I
34// defined as interface{f()}. Exported thus means "described in export
35// data".
36//
37package facts
38
39import (
40 "bytes"
41 "encoding/gob"
42 "fmt"
43 "go/types"
44 "io/ioutil"
45 "log"
46 "reflect"
47 "sort"
48 "sync"
49
50 "golang.org/x/tools/go/analysis"
51 "golang.org/x/tools/go/types/objectpath"
52)
53
54const debug = false
55
56// A Set is a set of analysis.Facts.
57//
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.
61//
62// All of Set's methods except String are safe to call concurrently.
63type Set struct {
64 pkg *types.Package
65 mu sync.Mutex
66 m map[key]analysis.Fact
67}
68
69type key struct {
70 pkg *types.Package
71 obj types.Object // (object facts only)
72 t reflect.Type
73}
74
75// ImportObjectFact implements analysis.Pass.ImportObjectFact.
76func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool {
77 if obj == nil {
78 panic("nil object")
79 }
80 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)}
81 s.mu.Lock()
82 defer s.mu.Unlock()
83 if v, ok := s.m[key]; ok {
84 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
85 return true
86 }
87 return false
88}
89
90// ExportObjectFact implements analysis.Pass.ExportObjectFact.
91func (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",
94 s.pkg, obj, fact)
95 }
96 key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)}
97 s.mu.Lock()
98 s.m[key] = fact // clobber any existing entry
99 s.mu.Unlock()
100}
101
5a8ea165
ILT
102func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
103 var facts []analysis.ObjectFact
104 s.mu.Lock()
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})
108 }
109 }
110 s.mu.Unlock()
111 return facts
112}
113
4f4a855d
ILT
114// ImportPackageFact implements analysis.Pass.ImportPackageFact.
115func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
116 if pkg == nil {
117 panic("nil package")
118 }
119 key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
120 s.mu.Lock()
121 defer s.mu.Unlock()
122 if v, ok := s.m[key]; ok {
123 reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
124 return true
125 }
126 return false
127}
128
129// ExportPackageFact implements analysis.Pass.ExportPackageFact.
130func (s *Set) ExportPackageFact(fact analysis.Fact) {
131 key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
132 s.mu.Lock()
133 s.m[key] = fact // clobber any existing entry
134 s.mu.Unlock()
135}
136
5a8ea165
ILT
137func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
138 var facts []analysis.PackageFact
139 s.mu.Lock()
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})
143 }
144 }
145 s.mu.Unlock()
146 return facts
147}
148
4f4a855d
ILT
149// gobFact is the Gob declaration of a serialized fact.
150type 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
154}
155
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.
160//
161// It is the caller's responsibility to call gob.Register on all
162// necessary fact types.
163func 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())
167
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{}) {
173 if debug {
174 prefix := fmt.Sprintf("in %s, importing %s: ",
175 pkg.Path(), imp.Path())
176 log.Print(prefix, fmt.Sprintf(format, args...))
177 }
178 }
179
180 // Read the gob-encoded facts.
181 data, err := read(imp.Path())
182 if err != nil {
183 return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
184 pkg.Path(), imp.Path(), err)
185 }
186 if len(data) == 0 {
187 continue // no facts
188 }
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)
192 }
193 if debug {
194 logf("decoded %d facts: %v", len(gobFacts), gobFacts)
195 }
196
197 // Parse each one into a key and a Fact.
198 for _, f := range gobFacts {
199 factPkg := packages[f.PkgPath]
200 if factPkg == nil {
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)
204 continue
205 }
206 key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
207 if f.Object != "" {
208 // object fact
209 obj, err := objectpath.Object(factPkg, f.Object)
210 if err != nil {
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)
214 continue
215 }
216 key.obj = obj
217 logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
218 } else {
219 // package fact
220 logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
221 }
222 m[key] = f.Fact
223 }
224 }
225
226 return &Set{pkg: pkg, m: m}, nil
227}
228
229// Encode encodes a set of facts to a memory buffer.
230//
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.
233func (s *Set) Encode() []byte {
234
235 // TODO(adonovan): opt: use a more efficient encoding
236 // that avoids repeating PkgPath for each fact.
237
238 // Gather all facts, including those from imported packages.
239 var gobFacts []gobFact
240
241 s.mu.Lock()
242 for k, fact := range s.m {
243 if debug {
244 log.Printf("%v => %s\n", k, fact)
245 }
246 var object objectpath.Path
247 if k.obj != nil {
248 path, err := objectpath.For(k.obj)
249 if err != nil {
250 if debug {
251 log.Printf("discarding fact %s about %s\n", fact, k.obj)
252 }
253 continue // object not accessible from package API; discard fact
254 }
255 object = path
256 }
257 gobFacts = append(gobFacts, gobFact{
258 PkgPath: k.pkg.Path(),
259 Object: object,
260 Fact: fact,
261 })
262 }
263 s.mu.Unlock()
264
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
270 }
271 if x.Object != y.Object {
272 return x.Object < y.Object
273 }
274 tx := reflect.TypeOf(x.Fact)
275 ty := reflect.TypeOf(y.Fact)
276 if tx != ty {
277 return tx.String() < ty.String()
278 }
279 return false // equal
280 })
281
282 var buf bytes.Buffer
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 {
288 fact := gf.Fact
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)
292 }
293 }
294 }
295 }
296
297 if debug {
298 log.Printf("package %q: encode %d facts, %d bytes\n",
299 s.pkg.Path(), len(gobFacts), buf.Len())
300 }
301
302 return buf.Bytes()
303}
304
305// String is provided only for debugging, and must not be called
306// concurrent with any Import/Export method.
307func (s *Set) String() string {
308 var buf bytes.Buffer
309 buf.WriteString("{")
310 for k, f := range s.m {
311 if buf.Len() > 1 {
312 buf.WriteString(", ")
313 }
314 if k.obj != nil {
315 buf.WriteString(k.obj.String())
316 } else {
317 buf.WriteString(k.pkg.Path())
318 }
319 fmt.Fprintf(&buf, ": %v", f)
320 }
321 buf.WriteString("}")
322 return buf.String()
323}