GoPLS Viewer

Home|gopls/internal/facts/facts.go
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
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
35// data".
36package facts
37
38import (
39    "bytes"
40    "encoding/gob"
41    "fmt"
42    "go/types"
43    "io/ioutil"
44    "log"
45    "reflect"
46    "sort"
47    "sync"
48
49    "golang.org/x/tools/go/analysis"
50    "golang.org/x/tools/go/types/objectpath"
51)
52
53const debug = false
54
55// A Set is a set of analysis.Facts.
56//
57// Decode creates a Set of facts by reading from the imports of a given
58// package, and Encode writes out the set. Between these operation,
59// the Import and Export methods will query and update the set.
60//
61// All of Set's methods except String are safe to call concurrently.
62type Set struct {
63    pkg *types.Package
64    mu  sync.Mutex
65    m   map[key]analysis.Fact
66}
67
68type key struct {
69    pkg *types.Package
70    obj types.Object // (object facts only)
71    t   reflect.Type
72}
73
74// ImportObjectFact implements analysis.Pass.ImportObjectFact.
75func (s *SetImportObjectFact(obj types.Objectptr analysis.Factbool {
76    if obj == nil {
77        panic("nil object")
78    }
79    key := key{pkgobj.Pkg(), objobjtreflect.TypeOf(ptr)}
80    s.mu.Lock()
81    defer s.mu.Unlock()
82    if vok := s.m[key]; ok {
83        reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
84        return true
85    }
86    return false
87}
88
89// ExportObjectFact implements analysis.Pass.ExportObjectFact.
90func (s *SetExportObjectFact(obj types.Objectfact analysis.Fact) {
91    if obj.Pkg() != s.pkg {
92        log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package",
93            s.pkgobjfact)
94    }
95    key := key{pkgobj.Pkg(), objobjtreflect.TypeOf(fact)}
96    s.mu.Lock()
97    s.m[key] = fact // clobber any existing entry
98    s.mu.Unlock()
99}
100
101func (s *SetAllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
102    var facts []analysis.ObjectFact
103    s.mu.Lock()
104    for kv := range s.m {
105        if k.obj != nil && filter[k.t] {
106            facts = append(factsanalysis.ObjectFact{Objectk.objFactv})
107        }
108    }
109    s.mu.Unlock()
110    return facts
111}
112
113// ImportPackageFact implements analysis.Pass.ImportPackageFact.
114func (s *SetImportPackageFact(pkg *types.Packageptr analysis.Factbool {
115    if pkg == nil {
116        panic("nil package")
117    }
118    key := key{pkgpkgtreflect.TypeOf(ptr)}
119    s.mu.Lock()
120    defer s.mu.Unlock()
121    if vok := s.m[key]; ok {
122        reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
123        return true
124    }
125    return false
126}
127
128// ExportPackageFact implements analysis.Pass.ExportPackageFact.
129func (s *SetExportPackageFact(fact analysis.Fact) {
130    key := key{pkgs.pkgtreflect.TypeOf(fact)}
131    s.mu.Lock()
132    s.m[key] = fact // clobber any existing entry
133    s.mu.Unlock()
134}
135
136func (s *SetAllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
137    var facts []analysis.PackageFact
138    s.mu.Lock()
139    for kv := range s.m {
140        if k.obj == nil && filter[k.t] {
141            facts = append(factsanalysis.PackageFact{Packagek.pkgFactv})
142        }
143    }
144    s.mu.Unlock()
145    return facts
146}
147
148// gobFact is the Gob declaration of a serialized fact.
149type gobFact struct {
150    PkgPath string          // path of package
151    Object  objectpath.Path // optional path of object relative to package itself
152    Fact    analysis.Fact   // type and value of user-defined Fact
153}
154
155// A Decoder decodes the facts from the direct imports of the package
156// provided to NewEncoder. A single decoder may be used to decode
157// multiple fact sets (e.g. each for a different set of fact types)
158// for the same package. Each call to Decode returns an independent
159// fact set.
160type Decoder struct {
161    pkg      *types.Package
162    packages map[string]*types.Package
163}
164
165// NewDecoder returns a fact decoder for the specified package.
166func NewDecoder(pkg *types.Package) *Decoder {
167    // Compute the import map for this package.
168    // See the package doc comment.
169    return &Decoder{pkgimportMap(pkg.Imports())}
170}
171
172// Decode decodes all the facts relevant to the analysis of package pkg.
173// The read function reads serialized fact data from an external source
174// for one of of pkg's direct imports. The empty file is a valid
175// encoding of an empty fact set.
176//
177// It is the caller's responsibility to call gob.Register on all
178// necessary fact types.
179func (d *DecoderDecode(read func(*types.Package) ([]byteerror)) (*Seterror) {
180    // Read facts from imported packages.
181    // Facts may describe indirectly imported packages, or their objects.
182    m := make(map[key]analysis.Fact// one big bucket
183    for _imp := range d.pkg.Imports() {
184        logf := func(format stringargs ...interface{}) {
185            if debug {
186                prefix := fmt.Sprintf("in %s, importing %s: ",
187                    d.pkg.Path(), imp.Path())
188                log.Print(prefixfmt.Sprintf(formatargs...))
189            }
190        }
191
192        // Read the gob-encoded facts.
193        dataerr := read(imp)
194        if err != nil {
195            return nilfmt.Errorf("in %s, can't import facts for package %q: %v",
196                d.pkg.Path(), imp.Path(), err)
197        }
198        if len(data) == 0 {
199            continue // no facts
200        }
201        var gobFacts []gobFact
202        if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
203            return nilfmt.Errorf("decoding facts for %q: %v"imp.Path(), err)
204        }
205        if debug {
206            logf("decoded %d facts: %v"len(gobFacts), gobFacts)
207        }
208
209        // Parse each one into a key and a Fact.
210        for _f := range gobFacts {
211            factPkg := d.packages[f.PkgPath]
212            if factPkg == nil {
213                // Fact relates to a dependency that was
214                // unused in this translation unit. Skip.
215                logf("no package %q; discarding %v"f.PkgPathf.Fact)
216                continue
217            }
218            key := key{pkgfactPkgtreflect.TypeOf(f.Fact)}
219            if f.Object != "" {
220                // object fact
221                objerr := objectpath.Object(factPkgf.Object)
222                if err != nil {
223                    // (most likely due to unexported object)
224                    // TODO(adonovan): audit for other possibilities.
225                    logf("no object for path: %v; discarding %s"errf.Fact)
226                    continue
227                }
228                key.obj = obj
229                logf("read %T fact %s for %v"f.Factf.Factkey.obj)
230            } else {
231                // package fact
232                logf("read %T fact %s for %v"f.Factf.FactfactPkg)
233            }
234            m[key] = f.Fact
235        }
236    }
237
238    return &Set{pkgd.pkgmm}, nil
239}
240
241// Encode encodes a set of facts to a memory buffer.
242//
243// It may fail if one of the Facts could not be gob-encoded, but this is
244// a sign of a bug in an Analyzer.
245func (s *SetEncode() []byte {
246
247    // TODO(adonovan): opt: use a more efficient encoding
248    // that avoids repeating PkgPath for each fact.
249
250    // Gather all facts, including those from imported packages.
251    var gobFacts []gobFact
252
253    s.mu.Lock()
254    for kfact := range s.m {
255        if debug {
256            log.Printf("%v => %s\n"kfact)
257        }
258        var object objectpath.Path
259        if k.obj != nil {
260            patherr := objectpath.For(k.obj)
261            if err != nil {
262                if debug {
263                    log.Printf("discarding fact %s about %s\n"factk.obj)
264                }
265                continue // object not accessible from package API; discard fact
266            }
267            object = path
268        }
269        gobFacts = append(gobFactsgobFact{
270            PkgPathk.pkg.Path(),
271            Object:  object,
272            Fact:    fact,
273        })
274    }
275    s.mu.Unlock()
276
277    // Sort facts by (package, object, type) for determinism.
278    sort.Slice(gobFacts, func(ij intbool {
279        xy := gobFacts[i], gobFacts[j]
280        if x.PkgPath != y.PkgPath {
281            return x.PkgPath < y.PkgPath
282        }
283        if x.Object != y.Object {
284            return x.Object < y.Object
285        }
286        tx := reflect.TypeOf(x.Fact)
287        ty := reflect.TypeOf(y.Fact)
288        if tx != ty {
289            return tx.String() < ty.String()
290        }
291        return false // equal
292    })
293
294    var buf bytes.Buffer
295    if len(gobFacts) > 0 {
296        if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil {
297            // Fact encoding should never fail. Identify the culprit.
298            for _gf := range gobFacts {
299                if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil {
300                    fact := gf.Fact
301                    pkgpath := reflect.TypeOf(fact).Elem().PkgPath()
302                    log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q",
303                        facterrfactpkgpath)
304                }
305            }
306        }
307    }
308
309    if debug {
310        log.Printf("package %q: encode %d facts, %d bytes\n",
311            s.pkg.Path(), len(gobFacts), buf.Len())
312    }
313
314    return buf.Bytes()
315}
316
317// String is provided only for debugging, and must not be called
318// concurrent with any Import/Export method.
319func (s *SetString() string {
320    var buf bytes.Buffer
321    buf.WriteString("{")
322    for kf := range s.m {
323        if buf.Len() > 1 {
324            buf.WriteString(", ")
325        }
326        if k.obj != nil {
327            buf.WriteString(k.obj.String())
328        } else {
329            buf.WriteString(k.pkg.Path())
330        }
331        fmt.Fprintf(&buf": %v"f)
332    }
333    buf.WriteString("}")
334    return buf.String()
335}
336
MembersX
bytes
Set.ExportObjectFact
Set.AllPackageFacts.s
Set.AllPackageFacts.RangeStmt_4471.v
Decoder.packages
Decoder.Decode.read
Decoder.Decode.RangeStmt_6104.BlockStmt.RangeStmt_6895.BlockStmt.BlockStmt.err
Set.Encode.buf
Set.Encode.BlockStmt.BlockStmt.RangeStmt_9231.gf
sort
Set
Set.ExportObjectFact.fact
Decoder.Decode.d
Set.ImportObjectFact.s
Set.ExportObjectFact.s
Set.AllObjectFacts.s
Set.AllObjectFacts.RangeStmt_3535.k
gobFact.PkgPath
Decoder.Decode
key
key.obj
Set.ExportObjectFact.obj
Set.AllPackageFacts.facts
Set.String
Set.pkg
Set.ExportObjectFact.key
Set.Encode.RangeStmt_8152.fact
Set.Encode.BlockStmt.err
gob
key.t
Set.ImportObjectFact
Set.AllObjectFacts.facts
Decoder
Decoder.Decode.RangeStmt_6104.BlockStmt.RangeStmt_6895.BlockStmt.BlockStmt.obj
Set.Encode.gobFacts
Set.String.s
types
objectpath
key.pkg
Set.AllObjectFacts
Set.ImportPackageFact.key
Set.AllPackageFacts.filter
NewDecoder
Set.Encode.RangeStmt_8152.BlockStmt.object
fmt
reflect
Set.m
Set.ImportObjectFact.key
Set.ImportPackageFact.s
Set.ImportPackageFact
gobFact.Object
Decoder.Decode.m
Set.Encode.s
Set.Encode.BlockStmt.BlockStmt.RangeStmt_9231.BlockStmt.err
ioutil
Set.String.RangeStmt_9912.f
log
debug
Set.ExportPackageFact.s
Set.ExportPackageFact
gobFact.Fact
Decoder.pkg
Decoder.Decode.RangeStmt_6104.imp
Decoder.Decode.RangeStmt_6104.BlockStmt.data
Set.Encode.BlockStmt.BlockStmt.RangeStmt_9231.BlockStmt.BlockStmt.pkgpath
Set.ImportObjectFact.ptr
Set.AllPackageFacts
Set.AllPackageFacts.RangeStmt_4471.k
Set.Encode.RangeStmt_8152.k
gobFact
Decoder.Decode.RangeStmt_6104.BlockStmt.err
Set.Encode.RangeStmt_8152.BlockStmt.BlockStmt.err
Set.String.buf
sync
Set.AllObjectFacts.filter
Set.AllObjectFacts.RangeStmt_3535.v
Set.ImportPackageFact.pkg
Set.ImportPackageFact.ptr
NewDecoder.pkg
Decoder.Decode.RangeStmt_6104.BlockStmt.BlockStmt.BlockStmt.prefix
Set.Encode.BlockStmt.ty
Set.Encode.BlockStmt.BlockStmt.RangeStmt_9231.BlockStmt.BlockStmt.fact
Set.ExportPackageFact.key
Decoder.Decode.RangeStmt_6104.BlockStmt.gobFacts
Set.Encode.RangeStmt_8152.BlockStmt.BlockStmt.path
Set.Encode.BlockStmt.tx
analysis
Set.mu
Set.ImportObjectFact.obj
Set.ExportPackageFact.fact
Decoder.Decode.RangeStmt_6104.BlockStmt.RangeStmt_6895.f
Decoder.Decode.RangeStmt_6104.BlockStmt.RangeStmt_6895.BlockStmt.key
Set.Encode
Set.String.RangeStmt_9912.k
Members
X