github.com/nozzle/golangci-lint@v1.49.0-nz3/pkg/golinters/goanalysis/runner_facts.go (about)

     1  package goanalysis
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/gob"
     6  	"fmt"
     7  	"go/types"
     8  	"reflect"
     9  
    10  	"golang.org/x/tools/go/analysis"
    11  )
    12  
    13  type objectFactKey struct {
    14  	obj types.Object
    15  	typ reflect.Type
    16  }
    17  
    18  type packageFactKey struct {
    19  	pkg *types.Package
    20  	typ reflect.Type
    21  }
    22  
    23  type Fact struct {
    24  	Path string // non-empty only for object facts
    25  	Fact analysis.Fact
    26  }
    27  
    28  // inheritFacts populates act.facts with
    29  // those it obtains from its dependency, dep.
    30  func inheritFacts(act, dep *action) {
    31  	serialize := false
    32  
    33  	for key, fact := range dep.objectFacts {
    34  		// Filter out facts related to objects
    35  		// that are irrelevant downstream
    36  		// (equivalently: not in the compiler export data).
    37  		if !exportedFrom(key.obj, dep.pkg.Types) {
    38  			factsInheritDebugf("%v: discarding %T fact from %s for %s: %s", act, fact, dep, key.obj, fact)
    39  			continue
    40  		}
    41  
    42  		// Optionally serialize/deserialize fact
    43  		// to verify that it works across address spaces.
    44  		if serialize {
    45  			var err error
    46  			fact, err = codeFact(fact)
    47  			if err != nil {
    48  				act.r.log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
    49  			}
    50  		}
    51  
    52  		factsInheritDebugf("%v: inherited %T fact for %s: %s", act, fact, key.obj, fact)
    53  		act.objectFacts[key] = fact
    54  	}
    55  
    56  	for key, fact := range dep.packageFacts {
    57  		// TODO: filter out facts that belong to
    58  		// packages not mentioned in the export data
    59  		// to prevent side channels.
    60  
    61  		// Optionally serialize/deserialize fact
    62  		// to verify that it works across address spaces
    63  		// and is deterministic.
    64  		if serialize {
    65  			var err error
    66  			fact, err = codeFact(fact)
    67  			if err != nil {
    68  				act.r.log.Panicf("internal error: encoding of %T fact failed in %v", fact, act)
    69  			}
    70  		}
    71  
    72  		factsInheritDebugf("%v: inherited %T fact for %s: %s", act, fact, key.pkg.Path(), fact)
    73  		act.packageFacts[key] = fact
    74  	}
    75  }
    76  
    77  // codeFact encodes then decodes a fact,
    78  // just to exercise that logic.
    79  func codeFact(fact analysis.Fact) (analysis.Fact, error) {
    80  	// We encode facts one at a time.
    81  	// A real modular driver would emit all facts
    82  	// into one encoder to improve gob efficiency.
    83  	var buf bytes.Buffer
    84  	if err := gob.NewEncoder(&buf).Encode(fact); err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	// Encode it twice and assert that we get the same bits.
    89  	// This helps detect nondeterministic Gob encoding (e.g. of maps).
    90  	var buf2 bytes.Buffer
    91  	if err := gob.NewEncoder(&buf2).Encode(fact); err != nil {
    92  		return nil, err
    93  	}
    94  	if !bytes.Equal(buf.Bytes(), buf2.Bytes()) {
    95  		return nil, fmt.Errorf("encoding of %T fact is nondeterministic", fact)
    96  	}
    97  
    98  	newFact := reflect.New(reflect.TypeOf(fact).Elem()).Interface().(analysis.Fact)
    99  	if err := gob.NewDecoder(&buf).Decode(newFact); err != nil {
   100  		return nil, err
   101  	}
   102  	return newFact, nil
   103  }
   104  
   105  // exportedFrom reports whether obj may be visible to a package that imports pkg.
   106  // This includes not just the exported members of pkg, but also unexported
   107  // constants, types, fields, and methods, perhaps belonging to other packages,
   108  // that find there way into the API.
   109  // This is an over-approximation of the more accurate approach used by
   110  // gc export data, which walks the type graph, but it's much simpler.
   111  //
   112  // TODO(adonovan): do more accurate filtering by walking the type graph.
   113  func exportedFrom(obj types.Object, pkg *types.Package) bool {
   114  	switch obj := obj.(type) {
   115  	case *types.Func:
   116  		return obj.Exported() && obj.Pkg() == pkg ||
   117  			obj.Type().(*types.Signature).Recv() != nil
   118  	case *types.Var:
   119  		return obj.Exported() && obj.Pkg() == pkg ||
   120  			obj.IsField()
   121  	case *types.TypeName, *types.Const:
   122  		return true
   123  	}
   124  	return false // Nil, Builtin, Label, or PkgName
   125  }