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