golang.org/x/tools@v0.21.0/go/analysis/passes/nilness/nilness.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package nilness 6 7 import ( 8 _ "embed" 9 "fmt" 10 "go/token" 11 "go/types" 12 13 "golang.org/x/tools/go/analysis" 14 "golang.org/x/tools/go/analysis/passes/buildssa" 15 "golang.org/x/tools/go/analysis/passes/internal/analysisutil" 16 "golang.org/x/tools/go/ssa" 17 "golang.org/x/tools/internal/typeparams" 18 ) 19 20 //go:embed doc.go 21 var doc string 22 23 var Analyzer = &analysis.Analyzer{ 24 Name: "nilness", 25 Doc: analysisutil.MustExtractDoc(doc, "nilness"), 26 URL: "https://pkg.go.dev/golang.org/x/tools/go/analysis/passes/nilness", 27 Run: run, 28 Requires: []*analysis.Analyzer{buildssa.Analyzer}, 29 } 30 31 func run(pass *analysis.Pass) (interface{}, error) { 32 ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 33 for _, fn := range ssainput.SrcFuncs { 34 runFunc(pass, fn) 35 } 36 return nil, nil 37 } 38 39 func runFunc(pass *analysis.Pass, fn *ssa.Function) { 40 reportf := func(category string, pos token.Pos, format string, args ...interface{}) { 41 // We ignore nil-checking ssa.Instructions 42 // that don't correspond to syntax. 43 if pos.IsValid() { 44 pass.Report(analysis.Diagnostic{ 45 Pos: pos, 46 Category: category, 47 Message: fmt.Sprintf(format, args...), 48 }) 49 } 50 } 51 52 // notNil reports an error if v is provably nil. 53 notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) { 54 if nilnessOf(stack, v) == isnil { 55 reportf("nilderef", instr.Pos(), descr) 56 } 57 } 58 59 // visit visits reachable blocks of the CFG in dominance order, 60 // maintaining a stack of dominating nilness facts. 61 // 62 // By traversing the dom tree, we can pop facts off the stack as 63 // soon as we've visited a subtree. Had we traversed the CFG, 64 // we would need to retain the set of facts for each block. 65 seen := make([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i 66 var visit func(b *ssa.BasicBlock, stack []fact) 67 visit = func(b *ssa.BasicBlock, stack []fact) { 68 if seen[b.Index] { 69 return 70 } 71 seen[b.Index] = true 72 73 // Report nil dereferences. 74 for _, instr := range b.Instrs { 75 switch instr := instr.(type) { 76 case ssa.CallInstruction: 77 // A nil receiver may be okay for type params. 78 cc := instr.Common() 79 if !(cc.IsInvoke() && typeparams.IsTypeParam(cc.Value.Type())) { 80 notNil(stack, instr, cc.Value, "nil dereference in "+cc.Description()) 81 } 82 case *ssa.FieldAddr: 83 notNil(stack, instr, instr.X, "nil dereference in field selection") 84 case *ssa.IndexAddr: 85 switch typeparams.CoreType(instr.X.Type()).(type) { 86 case *types.Pointer: // *array 87 notNil(stack, instr, instr.X, "nil dereference in array index operation") 88 case *types.Slice: 89 // This is not necessarily a runtime error, because 90 // it is usually dominated by a bounds check. 91 if isRangeIndex(instr) { 92 notNil(stack, instr, instr.X, "range of nil slice") 93 } else { 94 notNil(stack, instr, instr.X, "index of nil slice") 95 } 96 } 97 case *ssa.MapUpdate: 98 notNil(stack, instr, instr.Map, "nil dereference in map update") 99 case *ssa.Range: 100 // (Not a runtime error, but a likely mistake.) 101 notNil(stack, instr, instr.X, "range over nil map") 102 case *ssa.Slice: 103 // A nilcheck occurs in ptr[:] iff ptr is a pointer to an array. 104 if is[*types.Pointer](instr.X.Type().Underlying()) { 105 notNil(stack, instr, instr.X, "nil dereference in slice operation") 106 } 107 case *ssa.Store: 108 notNil(stack, instr, instr.Addr, "nil dereference in store") 109 case *ssa.TypeAssert: 110 if !instr.CommaOk { 111 notNil(stack, instr, instr.X, "nil dereference in type assertion") 112 } 113 case *ssa.UnOp: 114 switch instr.Op { 115 case token.MUL: // *X 116 notNil(stack, instr, instr.X, "nil dereference in load") 117 case token.ARROW: // <-ch 118 // (Not a runtime error, but a likely mistake.) 119 notNil(stack, instr, instr.X, "receive from nil channel") 120 } 121 case *ssa.Send: 122 // (Not a runtime error, but a likely mistake.) 123 notNil(stack, instr, instr.Chan, "send to nil channel") 124 } 125 } 126 127 // Look for panics with nil value 128 for _, instr := range b.Instrs { 129 switch instr := instr.(type) { 130 case *ssa.Panic: 131 if nilnessOf(stack, instr.X) == isnil { 132 reportf("nilpanic", instr.Pos(), "panic with nil value") 133 } 134 case *ssa.SliceToArrayPointer: 135 nn := nilnessOf(stack, instr.X) 136 if nn == isnil && slice2ArrayPtrLen(instr) > 0 { 137 reportf("conversionpanic", instr.Pos(), "nil slice being cast to an array of len > 0 will always panic") 138 } 139 } 140 } 141 142 // For nil comparison blocks, report an error if the condition 143 // is degenerate, and push a nilness fact on the stack when 144 // visiting its true and false successor blocks. 145 if binop, tsucc, fsucc := eq(b); binop != nil { 146 xnil := nilnessOf(stack, binop.X) 147 ynil := nilnessOf(stack, binop.Y) 148 149 if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) { 150 // Degenerate condition: 151 // the nilness of both operands is known, 152 // and at least one of them is nil. 153 var adj string 154 if (xnil == ynil) == (binop.Op == token.EQL) { 155 adj = "tautological" 156 } else { 157 adj = "impossible" 158 } 159 reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil) 160 161 // If tsucc's or fsucc's sole incoming edge is impossible, 162 // it is unreachable. Prune traversal of it and 163 // all the blocks it dominates. 164 // (We could be more precise with full dataflow 165 // analysis of control-flow joins.) 166 var skip *ssa.BasicBlock 167 if xnil == ynil { 168 skip = fsucc 169 } else { 170 skip = tsucc 171 } 172 for _, d := range b.Dominees() { 173 if d == skip && len(d.Preds) == 1 { 174 continue 175 } 176 visit(d, stack) 177 } 178 return 179 } 180 181 // "if x == nil" or "if nil == y" condition; x, y are unknown. 182 if xnil == isnil || ynil == isnil { 183 var newFacts facts 184 if xnil == isnil { 185 // x is nil, y is unknown: 186 // t successor learns y is nil. 187 newFacts = expandFacts(fact{binop.Y, isnil}) 188 } else { 189 // x is nil, y is unknown: 190 // t successor learns x is nil. 191 newFacts = expandFacts(fact{binop.X, isnil}) 192 } 193 194 for _, d := range b.Dominees() { 195 // Successor blocks learn a fact 196 // only at non-critical edges. 197 // (We could do be more precise with full dataflow 198 // analysis of control-flow joins.) 199 s := stack 200 if len(d.Preds) == 1 { 201 if d == tsucc { 202 s = append(s, newFacts...) 203 } else if d == fsucc { 204 s = append(s, newFacts.negate()...) 205 } 206 } 207 visit(d, s) 208 } 209 return 210 } 211 } 212 213 // In code of the form: 214 // 215 // if ptr, ok := x.(*T); ok { ... } else { fsucc } 216 // 217 // the fsucc block learns that ptr == nil, 218 // since that's its zero value. 219 if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok { 220 // Handle "if ok" and "if !ok" variants. 221 cond, fsucc := If.Cond, b.Succs[1] 222 if unop, ok := cond.(*ssa.UnOp); ok && unop.Op == token.NOT { 223 cond, fsucc = unop.X, b.Succs[0] 224 } 225 226 // Match pattern: 227 // t0 = typeassert (pointerlike) 228 // t1 = extract t0 #0 // ptr 229 // t2 = extract t0 #1 // ok 230 // if t2 goto tsucc, fsucc 231 if extract1, ok := cond.(*ssa.Extract); ok && extract1.Index == 1 { 232 if assert, ok := extract1.Tuple.(*ssa.TypeAssert); ok && 233 isNillable(assert.AssertedType) { 234 for _, pinstr := range *assert.Referrers() { 235 if extract0, ok := pinstr.(*ssa.Extract); ok && 236 extract0.Index == 0 && 237 extract0.Tuple == extract1.Tuple { 238 for _, d := range b.Dominees() { 239 if len(d.Preds) == 1 && d == fsucc { 240 visit(d, append(stack, fact{extract0, isnil})) 241 } 242 } 243 } 244 } 245 } 246 } 247 } 248 249 for _, d := range b.Dominees() { 250 visit(d, stack) 251 } 252 } 253 254 // Visit the entry block. No need to visit fn.Recover. 255 if fn.Blocks != nil { 256 visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty 257 } 258 } 259 260 // A fact records that a block is dominated 261 // by the condition v == nil or v != nil. 262 type fact struct { 263 value ssa.Value 264 nilness nilness 265 } 266 267 func (f fact) negate() fact { return fact{f.value, -f.nilness} } 268 269 type nilness int 270 271 const ( 272 isnonnil = -1 273 unknown nilness = 0 274 isnil = 1 275 ) 276 277 var nilnessStrings = []string{"non-nil", "unknown", "nil"} 278 279 func (n nilness) String() string { return nilnessStrings[n+1] } 280 281 // nilnessOf reports whether v is definitely nil, definitely not nil, 282 // or unknown given the dominating stack of facts. 283 func nilnessOf(stack []fact, v ssa.Value) nilness { 284 switch v := v.(type) { 285 // unwrap ChangeInterface and Slice values recursively, to detect if underlying 286 // values have any facts recorded or are otherwise known with regard to nilness. 287 // 288 // This work must be in addition to expanding facts about 289 // ChangeInterfaces during inference/fact gathering because this covers 290 // cases where the nilness of a value is intrinsic, rather than based 291 // on inferred facts, such as a zero value interface variable. That 292 // said, this work alone would only inform us when facts are about 293 // underlying values, rather than outer values, when the analysis is 294 // transitive in both directions. 295 case *ssa.ChangeInterface: 296 if underlying := nilnessOf(stack, v.X); underlying != unknown { 297 return underlying 298 } 299 case *ssa.Slice: 300 if underlying := nilnessOf(stack, v.X); underlying != unknown { 301 return underlying 302 } 303 case *ssa.SliceToArrayPointer: 304 nn := nilnessOf(stack, v.X) 305 if slice2ArrayPtrLen(v) > 0 { 306 if nn == isnil { 307 // We know that *(*[1]byte)(nil) is going to panic because of the 308 // conversion. So return unknown to the caller, prevent useless 309 // nil deference reporting due to * operator. 310 return unknown 311 } 312 // Otherwise, the conversion will yield a non-nil pointer to array. 313 // Note that the instruction can still panic if array length greater 314 // than slice length. If the value is used by another instruction, 315 // that instruction can assume the panic did not happen when that 316 // instruction is reached. 317 return isnonnil 318 } 319 // In case array length is zero, the conversion result depends on nilness of the slice. 320 if nn != unknown { 321 return nn 322 } 323 } 324 325 // Is value intrinsically nil or non-nil? 326 switch v := v.(type) { 327 case *ssa.Alloc, 328 *ssa.FieldAddr, 329 *ssa.FreeVar, 330 *ssa.Function, 331 *ssa.Global, 332 *ssa.IndexAddr, 333 *ssa.MakeChan, 334 *ssa.MakeClosure, 335 *ssa.MakeInterface, 336 *ssa.MakeMap, 337 *ssa.MakeSlice: 338 return isnonnil 339 case *ssa.Const: 340 if v.IsNil() { 341 return isnil // nil or zero value of a pointer-like type 342 } else { 343 return unknown // non-pointer 344 } 345 } 346 347 // Search dominating control-flow facts. 348 for _, f := range stack { 349 if f.value == v { 350 return f.nilness 351 } 352 } 353 return unknown 354 } 355 356 func slice2ArrayPtrLen(v *ssa.SliceToArrayPointer) int64 { 357 return v.Type().(*types.Pointer).Elem().Underlying().(*types.Array).Len() 358 } 359 360 // If b ends with an equality comparison, eq returns the operation and 361 // its true (equal) and false (not equal) successors. 362 func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) { 363 if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok { 364 if binop, ok := If.Cond.(*ssa.BinOp); ok { 365 switch binop.Op { 366 case token.EQL: 367 return binop, b.Succs[0], b.Succs[1] 368 case token.NEQ: 369 return binop, b.Succs[1], b.Succs[0] 370 } 371 } 372 } 373 return nil, nil, nil 374 } 375 376 // expandFacts takes a single fact and returns the set of facts that can be 377 // known about it or any of its related values. Some operations, like 378 // ChangeInterface, have transitive nilness, such that if you know the 379 // underlying value is nil, you also know the value itself is nil, and vice 380 // versa. This operation allows callers to match on any of the related values 381 // in analyses, rather than just the one form of the value that happened to 382 // appear in a comparison. 383 // 384 // This work must be in addition to unwrapping values within nilnessOf because 385 // while this work helps give facts about transitively known values based on 386 // inferred facts, the recursive check within nilnessOf covers cases where 387 // nilness facts are intrinsic to the underlying value, such as a zero value 388 // interface variables. 389 // 390 // ChangeInterface is the only expansion currently supported, but others, like 391 // Slice, could be added. At this time, this tool does not check slice 392 // operations in a way this expansion could help. See 393 // https://play.golang.org/p/mGqXEp7w4fR for an example. 394 func expandFacts(f fact) []fact { 395 ff := []fact{f} 396 397 Loop: 398 for { 399 switch v := f.value.(type) { 400 case *ssa.ChangeInterface: 401 f = fact{v.X, f.nilness} 402 ff = append(ff, f) 403 default: 404 break Loop 405 } 406 } 407 408 return ff 409 } 410 411 type facts []fact 412 413 func (ff facts) negate() facts { 414 nn := make([]fact, len(ff)) 415 for i, f := range ff { 416 nn[i] = f.negate() 417 } 418 return nn 419 } 420 421 func is[T any](x any) bool { 422 _, ok := x.(T) 423 return ok 424 } 425 426 func isNillable(t types.Type) bool { 427 switch t := typeparams.CoreType(t).(type) { 428 case *types.Pointer, 429 *types.Map, 430 *types.Signature, 431 *types.Chan, 432 *types.Interface, 433 *types.Slice: 434 return true 435 case *types.Basic: 436 return t == types.Typ[types.UnsafePointer] 437 } 438 return false 439 } 440 441 // isRangeIndex reports whether the instruction is a slice indexing 442 // operation slice[i] within a "for range slice" loop. The operation 443 // could be explicit, such as slice[i] within (or even after) the 444 // loop, or it could be implicit, such as "for i, v := range slice {}". 445 // (These cannot be reliably distinguished.) 446 func isRangeIndex(instr *ssa.IndexAddr) bool { 447 // Here we reverse-engineer the go/ssa lowering of range-over-slice: 448 // 449 // n = len(x) 450 // jump loop 451 // loop: "rangeindex.loop" 452 // phi = φ(-1, incr) #rangeindex 453 // incr = phi + 1 454 // cond = incr < n 455 // if cond goto body else done 456 // body: "rangeindex.body" 457 // instr = &x[incr] 458 // ... 459 // done: 460 if incr, ok := instr.Index.(*ssa.BinOp); ok && incr.Op == token.ADD { 461 if b := incr.Block(); b.Comment == "rangeindex.loop" { 462 if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok { 463 if cond := If.Cond.(*ssa.BinOp); cond.X == incr && cond.Op == token.LSS { 464 if call, ok := cond.Y.(*ssa.Call); ok { 465 common := call.Common() 466 if blt, ok := common.Value.(*ssa.Builtin); ok && blt.Name() == "len" { 467 return common.Args[0] == instr.X 468 } 469 } 470 } 471 } 472 } 473 } 474 return false 475 }