...

Source file src/pkg/cmd/vendor/golang.org/x/tools/go/analysis/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 nonethless 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".
    36	//
    37	package facts
    38	
    39	import (
    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	
    54	const 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.
    63	type Set struct {
    64		pkg *types.Package
    65		mu  sync.Mutex
    66		m   map[key]analysis.Fact
    67	}
    68	
    69	type 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.
    76	func (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.
    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",
    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	
   102	// ImportPackageFact implements analysis.Pass.ImportPackageFact.
   103	func (s *Set) ImportPackageFact(pkg *types.Package, ptr analysis.Fact) bool {
   104		if pkg == nil {
   105			panic("nil package")
   106		}
   107		key := key{pkg: pkg, t: reflect.TypeOf(ptr)}
   108		s.mu.Lock()
   109		defer s.mu.Unlock()
   110		if v, ok := s.m[key]; ok {
   111			reflect.ValueOf(ptr).Elem().Set(reflect.ValueOf(v).Elem())
   112			return true
   113		}
   114		return false
   115	}
   116	
   117	// ExportPackageFact implements analysis.Pass.ExportPackageFact.
   118	func (s *Set) ExportPackageFact(fact analysis.Fact) {
   119		key := key{pkg: s.pkg, t: reflect.TypeOf(fact)}
   120		s.mu.Lock()
   121		s.m[key] = fact // clobber any existing entry
   122		s.mu.Unlock()
   123	}
   124	
   125	// gobFact is the Gob declaration of a serialized fact.
   126	type gobFact struct {
   127		PkgPath string          // path of package
   128		Object  objectpath.Path // optional path of object relative to package itself
   129		Fact    analysis.Fact   // type and value of user-defined Fact
   130	}
   131	
   132	// Decode decodes all the facts relevant to the analysis of package pkg.
   133	// The read function reads serialized fact data from an external source
   134	// for one of of pkg's direct imports. The empty file is a valid
   135	// encoding of an empty fact set.
   136	//
   137	// It is the caller's responsibility to call gob.Register on all
   138	// necessary fact types.
   139	func Decode(pkg *types.Package, read func(packagePath string) ([]byte, error)) (*Set, error) {
   140		// Compute the import map for this package.
   141		// See the package doc comment.
   142		packages := importMap(pkg.Imports())
   143	
   144		// Read facts from imported packages.
   145		// Facts may describe indirectly imported packages, or their objects.
   146		m := make(map[key]analysis.Fact) // one big bucket
   147		for _, imp := range pkg.Imports() {
   148			logf := func(format string, args ...interface{}) {
   149				if debug {
   150					prefix := fmt.Sprintf("in %s, importing %s: ",
   151						pkg.Path(), imp.Path())
   152					log.Print(prefix, fmt.Sprintf(format, args...))
   153				}
   154			}
   155	
   156			// Read the gob-encoded facts.
   157			data, err := read(imp.Path())
   158			if err != nil {
   159				return nil, fmt.Errorf("in %s, can't import facts for package %q: %v",
   160					pkg.Path(), imp.Path(), err)
   161			}
   162			if len(data) == 0 {
   163				continue // no facts
   164			}
   165			var gobFacts []gobFact
   166			if err := gob.NewDecoder(bytes.NewReader(data)).Decode(&gobFacts); err != nil {
   167				return nil, fmt.Errorf("decoding facts for %q: %v", imp.Path(), err)
   168			}
   169			if debug {
   170				logf("decoded %d facts: %v", len(gobFacts), gobFacts)
   171			}
   172	
   173			// Parse each one into a key and a Fact.
   174			for _, f := range gobFacts {
   175				factPkg := packages[f.PkgPath]
   176				if factPkg == nil {
   177					// Fact relates to a dependency that was
   178					// unused in this translation unit. Skip.
   179					logf("no package %q; discarding %v", f.PkgPath, f.Fact)
   180					continue
   181				}
   182				key := key{pkg: factPkg, t: reflect.TypeOf(f.Fact)}
   183				if f.Object != "" {
   184					// object fact
   185					obj, err := objectpath.Object(factPkg, f.Object)
   186					if err != nil {
   187						// (most likely due to unexported object)
   188						// TODO(adonovan): audit for other possibilities.
   189						logf("no object for path: %v; discarding %s", err, f.Fact)
   190						continue
   191					}
   192					key.obj = obj
   193					logf("read %T fact %s for %v", f.Fact, f.Fact, key.obj)
   194				} else {
   195					// package fact
   196					logf("read %T fact %s for %v", f.Fact, f.Fact, factPkg)
   197				}
   198				m[key] = f.Fact
   199			}
   200		}
   201	
   202		return &Set{pkg: pkg, m: m}, nil
   203	}
   204	
   205	// Encode encodes a set of facts to a memory buffer.
   206	//
   207	// It may fail if one of the Facts could not be gob-encoded, but this is
   208	// a sign of a bug in an Analyzer.
   209	func (s *Set) Encode() []byte {
   210	
   211		// TODO(adonovan): opt: use a more efficient encoding
   212		// that avoids repeating PkgPath for each fact.
   213	
   214		// Gather all facts, including those from imported packages.
   215		var gobFacts []gobFact
   216	
   217		s.mu.Lock()
   218		for k, fact := range s.m {
   219			if debug {
   220				log.Printf("%v => %s\n", k, fact)
   221			}
   222			var object objectpath.Path
   223			if k.obj != nil {
   224				path, err := objectpath.For(k.obj)
   225				if err != nil {
   226					if debug {
   227						log.Printf("discarding fact %s about %s\n", fact, k.obj)
   228					}
   229					continue // object not accessible from package API; discard fact
   230				}
   231				object = path
   232			}
   233			gobFacts = append(gobFacts, gobFact{
   234				PkgPath: k.pkg.Path(),
   235				Object:  object,
   236				Fact:    fact,
   237			})
   238		}
   239		s.mu.Unlock()
   240	
   241		// Sort facts by (package, object, type) for determinism.
   242		sort.Slice(gobFacts, func(i, j int) bool {
   243			x, y := gobFacts[i], gobFacts[j]
   244			if x.PkgPath != y.PkgPath {
   245				return x.PkgPath < y.PkgPath
   246			}
   247			if x.Object != y.Object {
   248				return x.Object < y.Object
   249			}
   250			tx := reflect.TypeOf(x.Fact)
   251			ty := reflect.TypeOf(y.Fact)
   252			if tx != ty {
   253				return tx.String() < ty.String()
   254			}
   255			return false // equal
   256		})
   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	
   273		if debug {
   274			log.Printf("package %q: encode %d facts, %d bytes\n",
   275				s.pkg.Path(), len(gobFacts), buf.Len())
   276		}
   277	
   278		return buf.Bytes()
   279	}
   280	
   281	// String is provided only for debugging, and must not be called
   282	// concurrent with any Import/Export method.
   283	func (s *Set) String() string {
   284		var buf bytes.Buffer
   285		buf.WriteString("{")
   286		for k, f := range s.m {
   287			if buf.Len() > 1 {
   288				buf.WriteString(", ")
   289			}
   290			if k.obj != nil {
   291				buf.WriteString(k.obj.String())
   292			} else {
   293				buf.WriteString(k.pkg.Path())
   294			}
   295			fmt.Fprintf(&buf, ": %v", f)
   296		}
   297		buf.WriteString("}")
   298		return buf.String()
   299	}
   300	

View as plain text