1package typedness
2
3import (
4	"fmt"
5	"go/token"
6	"go/types"
7	"reflect"
8
9	"honnef.co/go/tools/go/ir"
10	"honnef.co/go/tools/go/ir/irutil"
11	"honnef.co/go/tools/internal/passes/buildir"
12
13	"golang.org/x/tools/go/analysis"
14)
15
16// alwaysTypedFact denotes that a function's return value will never
17// be untyped nil. The analysis errs on the side of false negatives.
18type alwaysTypedFact struct {
19	Rets uint8
20}
21
22func (*alwaysTypedFact) AFact() {}
23func (fact *alwaysTypedFact) String() string {
24	return fmt.Sprintf("always typed: %08b", fact.Rets)
25}
26
27type Result struct {
28	m map[*types.Func]uint8
29}
30
31var Analysis = &analysis.Analyzer{
32	Name:       "typedness",
33	Doc:        "Annotates return values that are always typed values",
34	Run:        run,
35	Requires:   []*analysis.Analyzer{buildir.Analyzer},
36	FactTypes:  []analysis.Fact{(*alwaysTypedFact)(nil)},
37	ResultType: reflect.TypeOf((*Result)(nil)),
38}
39
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.
43//
44// The analysis has false negatives: MustReturnTyped may incorrectly
45// report false, but never incorrectly reports true.
46func (r *Result) MustReturnTyped(fn *types.Func, ret int) bool {
47	if _, ok := fn.Type().(*types.Signature).Results().At(ret).Type().Underlying().(*types.Interface); !ok {
48		return true
49	}
50	return (r.m[fn] & (1 << ret)) != 0
51}
52
53func run(pass *analysis.Pass) (interface{}, error) {
54	seen := map[*ir.Function]struct{}{}
55	out := &Result{
56		m: map[*types.Func]uint8{},
57	}
58	for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs {
59		impl(pass, fn, seen)
60	}
61
62	for _, fact := range pass.AllObjectFacts() {
63		out.m[fact.Object.(*types.Func)] = fact.Fact.(*alwaysTypedFact).Rets
64	}
65
66	return out, nil
67}
68
69func impl(pass *analysis.Pass, fn *ir.Function, seenFns map[*ir.Function]struct{}) (out uint8) {
70	if fn.Signature.Results().Len() > 8 {
71		return 0
72	}
73	if fn.Object() == nil {
74		// TODO(dh): support closures
75		return 0
76	}
77	if fact := new(alwaysTypedFact); pass.ImportObjectFact(fn.Object(), fact) {
78		return fact.Rets
79	}
80	if fn.Pkg != pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg {
81		return 0
82	}
83	if fn.Blocks == nil {
84		return 0
85	}
86	if irutil.IsStub(fn) {
87		return 0
88	}
89	if _, ok := seenFns[fn]; ok {
90		// break recursion
91		return 0
92	}
93
94	seenFns[fn] = struct{}{}
95	defer func() {
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.
102				out &= ^(1 << i)
103			}
104		}
105		if out > 0 {
106			pass.ExportObjectFact(fn.Object(), &alwaysTypedFact{out})
107		}
108	}()
109
110	isUntypedNil := func(v ir.Value) bool {
111		k, ok := v.(*ir.Const)
112		if !ok {
113			return false
114		}
115		if _, ok := k.Type().Underlying().(*types.Interface); !ok {
116			return false
117		}
118		return k.Value == nil
119	}
120
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 {
124			// break cycle
125			return false
126		}
127		seen[v] = struct{}{}
128		switch v := v.(type) {
129		case *ir.Const:
130			// can't be a typed nil, because then we'd be returning the
131			// result of MakeInterface.
132			return false
133		case *ir.ChangeInterface:
134			return do(v.X, seen)
135		case *ir.Extract:
136			call, ok := v.Tuple.(*ir.Call)
137			if !ok {
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.
142				return false
143			}
144			if callee := call.Call.StaticCallee(); callee != nil {
145				return impl(pass, callee, seenFns)&(1<<v.Index) != 0
146			} else {
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
150				// instruction.
151				return false
152			}
153		case *ir.Call:
154			if callee := v.Call.StaticCallee(); callee != nil {
155				return impl(pass, callee, seenFns)&1 != 0
156			} else {
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
160				// instruction.
161				return false
162			}
163		case *ir.Sigma:
164			iff, ok := v.From.Control().(*ir.If)
165			if !ok {
166				// give up
167				return false
168			}
169
170			binop, ok := iff.Cond.(*ir.BinOp)
171			if !ok {
172				// give up
173				return false
174			}
175
176			if (binop.X == v.X && isUntypedNil(binop.Y)) || (isUntypedNil(binop.X) && binop.Y == v.X) {
177				op := binop.Op
178				if v.From.Succs[0] != v.Block() {
179					// we're in the false branch, negate op
180					switch op {
181					case token.EQL:
182						op = token.NEQ
183					case token.NEQ:
184						op = token.EQL
185					default:
186						panic(fmt.Sprintf("internal error: unhandled token %v", op))
187					}
188				}
189
190				switch op {
191				case token.EQL:
192					// returned value equals untyped nil
193					return false
194				case token.NEQ:
195					// returned value does not equal untyped nil
196					return true
197				default:
198					panic(fmt.Sprintf("internal error: unhandled token %v", op))
199				}
200			}
201
202			// TODO(dh): handle comparison with typed nil
203
204			// give up
205			return false
206		case *ir.Phi:
207			for _, pv := range v.Edges {
208				if !do(pv, seen) {
209					return false
210				}
211			}
212			return true
213		case *ir.MakeInterface:
214			return true
215		case *ir.TypeAssert:
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.
219			return true
220		case *ir.ChangeType:
221			// we'll only see interface->interface conversions, which
222			// don't tell us anything about the nilness.
223			return false
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.
227			return false
228		default:
229			panic(fmt.Sprintf("internal error: unhandled type %T", v))
230		}
231	}
232
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{}{}) {
237				out |= 1 << i
238			}
239		}
240	}
241	return out
242}
243