You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

facts.go 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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. // Package facts defines a serializable set of analysis.Fact.
  5. //
  6. // It provides a partial implementation of the Fact-related parts of the
  7. // analysis.Pass interface for use in analysis drivers such as "go vet"
  8. // and other build systems.
  9. //
  10. // The serial format is unspecified and may change, so the same version
  11. // of this package must be used for reading and writing serialized facts.
  12. //
  13. // The handling of facts in the analysis system parallels the handling
  14. // of type information in the compiler: during compilation of package P,
  15. // the compiler emits an export data file that describes the type of
  16. // every object (named thing) defined in package P, plus every object
  17. // indirectly reachable from one of those objects. Thus the downstream
  18. // compiler of package Q need only load one export data file per direct
  19. // import of Q, and it will learn everything about the API of package P
  20. // and everything it needs to know about the API of P's dependencies.
  21. //
  22. // Similarly, analysis of package P emits a fact set containing facts
  23. // about all objects exported from P, plus additional facts about only
  24. // those objects of P's dependencies that are reachable from the API of
  25. // package P; the downstream analysis of Q need only load one fact set
  26. // per direct import of Q.
  27. //
  28. // The notion of "exportedness" that matters here is that of the
  29. // compiler. According to the language spec, a method pkg.T.f is
  30. // unexported simply because its name starts with lowercase. But the
  31. // compiler must nonetheless export f so that downstream compilations can
  32. // accurately ascertain whether pkg.T implements an interface pkg.I
  33. // defined as interface{f()}. Exported thus means "described in export
  34. // data".
  35. //
  36. package facts
  37. import (
  38. "bytes"
  39. "encoding/gob"
  40. "fmt"
  41. "go/types"
  42. "io/ioutil"
  43. "log"
  44. "reflect"
  45. "sort"
  46. "sync"
  47. "golang.org/x/tools/go/analysis"
  48. "golang.org/x/tools/go/types/objectpath"
  49. )
  50. const debug = false
  51. // A Set is a set of analysis.Facts.
  52. //
  53. // Decode creates a Set of facts by reading from the imports of a given
  54. // package, and Encode writes out the set. Between these operation,
  55. // the Import and Export methods will query and update the set.
  56. //
  57. // All of Set's methods except String are safe to call concurrently.
  58. type Set struct {
  59. pkg *types.Package
  60. mu sync.Mutex
  61. m map[key]analysis.Fact
  62. }
  63. type key struct {
  64. pkg *types.Package
  65. obj types.Object // (object facts only)
  66. t reflect.Type
  67. }
  68. // ImportObjectFact implements analysis.Pass.ImportObjectFact.
  69. func (s *Set) ImportObjectFact(obj types.Object, ptr analysis.Fact) bool {
  70. if obj == nil {
  71. panic("nil object")
  72. }
  73. key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(ptr)}
  74. s.mu.Lock()
  75. defer s.mu.Unlock()
  76. if v, ok := s.m[key]; ok {
  77. reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
  78. return true
  79. }
  80. return false
  81. }
  82. // ExportObjectFact implements analysis.Pass.ExportObjectFact.
  83. func (s *Set) ExportObjectFact(obj types.Object, fact analysis.Fact) {
  84. if obj.Pkg() != s.pkg {
  85. log.Panicf("in package %s: ExportObjectFact(%s, %T): can't set fact on object belonging another package",
  86. s.pkg, obj, fact)
  87. }
  88. key := key{pkg: obj.Pkg(), obj: obj, t: reflect.TypeOf(fact)}
  89. s.mu.Lock()
  90. s.m[key] = fact // clobber any existing entry
  91. s.mu.Unlock()
  92. }
  93. func (s *Set) AllObjectFacts(filter map[reflect.Type]bool) []analysis.ObjectFact {
  94. var facts []analysis.ObjectFact
  95. s.mu.Lock()
  96. for k, v := range s.m {
  97. if k.obj != nil && filter[k.t] {
  98. facts = append(facts, analysis.ObjectFact{Object: k.obj, Fact: v})
  99. }
  100. }
  101. s.mu.Unlock()
  102. return facts
  103. }
  104. // ImportPackageFact implements analysis.Pass.ImportPackageFact.
  105. func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
  106. if pkg == nil {
  107. panic("nil package")
  108. }
  109. key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
  110. s.mu.Lock()
  111. defer s.mu.Unlock()
  112. if v, ok := s.m[key]; ok {
  113. reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
  114. return true
  115. }
  116. return false
  117. }
  118. // ExportPackageFact implements analysis.Pass.ExportPackageFact.
  119. func (s *Set) ExportPackageFact(fact analysis.Fact) {
  120. key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
  121. s.mu.Lock()
  122. s.m[key] = fact // clobber any existing entry
  123. s.mu.Unlock()
  124. }
  125. func (s *Set) AllPackageFacts(filter map[reflect.Type]bool) []analysis.PackageFact {
  126. var facts []analysis.PackageFact
  127. s.mu.Lock()
  128. for k, v := range s.m {
  129. if k.obj == nil && filter[k.t] {
  130. facts = append(facts, analysis.PackageFact{Package: k.pkg, Fact: v})
  131. }
  132. }
  133. s.mu.Unlock()
  134. return facts
  135. }
  136. // gobFact is the Gob declaration of a serialized fact.
  137. type gobFact struct {
  138. PkgPath string // path of package
  139. Object objectpath.Path // optional path of object relative to package itself
  140. Fact analysis.Fact // type and value of user-defined Fact
  141. }
  142. // Decode decodes all the facts relevant to the analysis of package pkg.
  143. // The read function reads serialized fact data from an external source
  144. // for one of of pkg's direct imports. The empty file is a valid
  145. // encoding of an empty fact set.
  146. //
  147. // It is the caller's responsibility to call gob.Register on all
  148. // necessary fact types.
  149. func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) {
  150. // Compute the import map for this package.
  151. // See the package doc comment.
  152. packages := importMap(pkg.Imports())
  153. // Read facts from imported packages.
  154. // Facts may describe indirectly imported packages, or their objects.
  155. m := make(map[key]analysis.Fact) // one big bucket
  156. for _, imp := range pkg.Imports() {
  157. logf := func(format string, args ...interface{}) {
  158. if debug {
  159. prefix := fmt.Sprintf("in %s, importing %s: ",
  160. pkg.Path(), imp.Path())
  161. log.Print(prefix, fmt.Sprintf(format, args...))
  162. }
  163. }
  164. // Read the gob-encoded facts.
  165. data, err := read(imp.Path())
  166. if err != nil {
  167. return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
  168. pkg.Path(), imp.Path(), err)
  169. }
  170. if len(data) == 0 {
  171. continue // no facts
  172. }
  173. var gobFacts []gobFact
  174. if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
  175. return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err)
  176. }
  177. if debug {
  178. logf("decoded %d facts: %v", len(gobFacts), gobFacts)
  179. }
  180. // Parse each one into a key and a Fact.
  181. for _, f := range gobFacts {
  182. factPkg := packages[f.PkgPath]
  183. if factPkg == nil {
  184. // Fact relates to a dependency that was
  185. // unused in this translation unit. Skip.
  186. logf("no package %q; discarding %v", f.PkgPath, f.Fact)
  187. continue
  188. }
  189. key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
  190. if f.Object != "" {
  191. // object fact
  192. obj, err := objectpath.Object(factPkg, f.Object)
  193. if err != nil {
  194. // (most likely due to unexported object)
  195. // TODO(adonovan): audit for other possibilities.
  196. logf("no object for path: %v; discarding %s", err, f.Fact)
  197. continue
  198. }
  199. key.obj = obj
  200. logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
  201. } else {
  202. // package fact
  203. logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
  204. }
  205. m[key] = f.Fact
  206. }
  207. }
  208. return &Set{pkg: pkg, m: m}, nil
  209. }
  210. // Encode encodes a set of facts to a memory buffer.
  211. //
  212. // It may fail if one of the Facts could not be gob-encoded, but this is
  213. // a sign of a bug in an Analyzer.
  214. func (s *Set) Encode() []byte {
  215. // TODO(adonovan): opt: use a more efficient encoding
  216. // that avoids repeating PkgPath for each fact.
  217. // Gather all facts, including those from imported packages.
  218. var gobFacts []gobFact
  219. s.mu.Lock()
  220. for k, fact := range s.m {
  221. if debug {
  222. log.Printf("%v => %s\n", k, fact)
  223. }
  224. var object objectpath.Path
  225. if k.obj != nil {
  226. path, err := objectpath.For(k.obj)
  227. if err != nil {
  228. if debug {
  229. log.Printf("discarding fact %s about %s\n", fact, k.obj)
  230. }
  231. continue // object not accessible from package API; discard fact
  232. }
  233. object = path
  234. }
  235. gobFacts = append(gobFacts, gobFact{
  236. PkgPath: k.pkg.Path(),
  237. Object: object,
  238. Fact: fact,
  239. })
  240. }
  241. s.mu.Unlock()
  242. // Sort facts by (package, object, type) for determinism.
  243. sort.Slice(gobFacts, func(i, j int) bool {
  244. x, y := gobFacts[i], gobFacts[j]
  245. if x.PkgPath != y.PkgPath {
  246. return x.PkgPath < y.PkgPath
  247. }
  248. if x.Object != y.Object {
  249. return x.Object < y.Object
  250. }
  251. tx := reflect.TypeOf(x.Fact)
  252. ty := reflect.TypeOf(y.Fact)
  253. if tx != ty {
  254. return tx.String() < ty.String()
  255. }
  256. return false // equal
  257. })
  258. var buf bytes.Buffer
  259. if len(gobFacts) > 0 {
  260. if err := gob.NewEncoder(&buf).Encode(gobFacts); err != nil {
  261. // Fact encoding should never fail. Identify the culprit.
  262. for _, gf := range gobFacts {
  263. if err := gob.NewEncoder(ioutil.Discard).Encode(gf); err != nil {
  264. fact := gf.Fact
  265. pkgpath := reflect.TypeOf(fact).Elem().PkgPath()
  266. log.Panicf("internal error: gob encoding of analysis fact %s failed: %v; please report a bug against fact %T in package %q",
  267. fact, err, fact, pkgpath)
  268. }
  269. }
  270. }
  271. }
  272. if debug {
  273. log.Printf("package %q: encode %d facts, %d bytes\n",
  274. s.pkg.Path(), len(gobFacts), buf.Len())
  275. }
  276. return buf.Bytes()
  277. }
  278. // String is provided only for debugging, and must not be called
  279. // concurrent with any Import/Export method.
  280. func (s *Set) String() string {
  281. var buf bytes.Buffer
  282. buf.WriteString("{")
  283. for k, f := range s.m {
  284. if buf.Len() > 1 {
  285. buf.WriteString(", ")
  286. }
  287. if k.obj != nil {
  288. buf.WriteString(k.obj.String())
  289. } else {
  290. buf.WriteString(k.pkg.Path())
  291. }
  292. fmt.Fprintf(&buf, ": %v", f)
  293. }
  294. buf.WriteString("}")
  295. return buf.String()
  296. }