9 "honnef.co/go/tools/go/ir"
10 "honnef.co/go/tools/go/ir/irutil"
11 "honnef.co/go/tools/internal/passes/buildir"
13 "golang.org/x/tools/go/analysis"
16 // alwaysTypedFact denotes that a function's return value will never
17 // be untyped nil. The analysis errs on the side of false negatives.
18 type alwaysTypedFact struct {
22 func (*alwaysTypedFact) AFact() {}
23 func (fact *alwaysTypedFact) String() string {
24 return fmt.Sprintf("always typed: %08b", fact.Rets)
28 m map[*types.Func]uint8
31 var Analysis = &analysis.Analyzer{
33 Doc: "Annotates return values that are always typed values",
35 Requires: []*analysis.Analyzer{buildir.Analyzer},
36 FactTypes: []analysis.Fact{(*alwaysTypedFact)(nil)},
37 ResultType: reflect.TypeOf((*Result)(nil)),
40 // MustReturnTyped reports whether the ret's return value of fn must
41 // be a typed value, i.e. an interface value containing a concrete
42 // type or trivially a concrete type. The value of ret is zero-based.
44 // The analysis has false negatives: MustReturnTyped may incorrectly
45 // report false, but never incorrectly reports true.
46 func (r *Result) MustReturnTyped(fn *types.Func, ret int) bool {
47 if _, ok := fn.Type().(*types.Signature).Results().At(ret).Type().Underlying().(*types.Interface); !ok {
50 return (r.m[fn] & (1 << ret)) != 0
53 func run(pass *analysis.Pass) (interface{}, error) {
54 seen := map[*ir.Function]struct{}{}
56 m: map[*types.Func]uint8{},
58 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
62 for _, fact := range pass.AllObjectFacts() {
63 out.m[fact.Object.(*types.Func)] = fact.Fact.(*alwaysTypedFact).Rets
69 func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out uint8) {
70 if fn.Signature.Results().Len() > 8 {
73 if fn.Object() == nil {
74 // TODO(dh): support closures
77 if fact := new(alwaysTypedFact); pass.ImportObjectFact(fn.Object(), fact) {
80 if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
86 if irutil.IsStub(fn) {
89 if _, ok := seenFns[fn]; ok {
94 seenFns[fn] = struct{}{}
96 for i := 0; i < fn.Signature.Results().Len(); i++ {
97 if _, ok := fn.Signature.Results().At(i).Type().Underlying().(*types.Interface); !ok {
98 // we don't need facts to know that non-interface
99 // types can't be untyped nil. zeroing out those bits
100 // may result in all bits being zero, in which case we
101 // don't have to save any fact.
106 pass.ExportObjectFact(fn.Object(), &alwaysTypedFact{out})
110 isUntypedNil := func(v ir.Value) bool {
111 k, ok := v.(*ir.Const)
115 if _, ok := k.Type().Underlying().(*types.Interface); !ok {
118 return k.Value == nil
121 var do func(v ir.Value, seen map[ir.Value]struct{}) bool
122 do = func(v ir.Value, seen map[ir.Value]struct{}) bool {
123 if _, ok := seen[v]; ok {
128 switch v := v.(type) {
130 // can't be a typed nil, because then we'd be returning the
131 // result of MakeInterface.
133 case *ir.ChangeInterface:
136 call, ok := v.Tuple.(*ir.Call)
138 // We only care about extracts of function results. For
139 // everything else (e.g. channel receives and map
140 // lookups), we can either not deduce any information, or
141 // will see a MakeInterface.
144 if callee := call.Call.StaticCallee(); callee != nil {
145 return impl(pass, callee, seenFns)&(1<<v.Index) != 0
147 // we don't know what function we're calling. no need
148 // to look at the signature, though. if it weren't an
149 // interface, we'd be seeing a MakeInterface
154 if callee := v.Call.StaticCallee(); callee != nil {
155 return impl(pass, callee, seenFns)&1 != 0
157 // we don't know what function we're calling. no need
158 // to look at the signature, though. if it weren't an
159 // interface, we'd be seeing a MakeInterface
164 iff, ok := v.From.Control().(*ir.If)
170 binop, ok := iff.Cond.(*ir.BinOp)
176 if (binop.X == v.X && isUntypedNil(binop.Y)) || (isUntypedNil(binop.X) && binop.Y == v.X) {
178 if v.From.Succs[0] != v.Block() {
179 // we're in the false branch, negate op
186 panic(fmt.Sprintf("internal error: unhandled token %v", op))
192 // returned value equals untyped nil
195 // returned value does not equal untyped nil
198 panic(fmt.Sprintf("internal error: unhandled token %v", op))
202 // TODO(dh): handle comparison with typed nil
207 for _, pv := range v.Edges {
213 case *ir.MakeInterface:
216 // type assertions fail for untyped nils. Either we have a
217 // single lhs and the type assertion succeeds or panics,
218 // or we have two lhs and we'll return Extract instead.
221 // we'll only see interface->interface conversions, which
222 // don't tell us anything about the nilness.
224 case *ir.MapLookup, *ir.Index, *ir.Recv, *ir.Parameter, *ir.Load, *ir.Field:
225 // All other instructions that tell us nothing about the
226 // typedness of interface values.
229 panic(fmt.Sprintf("internal error: unhandled type %T", v))
233 ret := fn.Exit.Control().(*ir.Return)
234 for i, v := range ret.Results {
235 if _, ok := fn.Signature.Results().At(i).Type().Underlying().(*types.Interface); ok {
236 if do(v, map[ir.Value]struct{}{}) {