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 }