github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/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 inspects the control-flow graph of an SSA function 6 // and reports errors such as nil pointer dereferences and degenerate 7 // nil pointer comparisons. 8 package nilness 9 10 import ( 11 "fmt" 12 "go/token" 13 "go/types" 14 15 "golang.org/x/tools/go/analysis" 16 "golang.org/x/tools/go/analysis/passes/buildssa" 17 "golang.org/x/tools/go/ssa" 18 "golang.org/x/tools/internal/typeparams" 19 ) 20 21 const Doc = `check for redundant or impossible nil comparisons 22 23 The nilness checker inspects the control-flow graph of each function in 24 a package and reports nil pointer dereferences, degenerate nil 25 pointers, and panics with nil values. A degenerate comparison is of the form 26 x==nil or x!=nil where x is statically known to be nil or non-nil. These are 27 often a mistake, especially in control flow related to errors. Panics with nil 28 values are checked because they are not detectable by 29 30 if r := recover(); r != nil { 31 32 This check reports conditions such as: 33 34 if f == nil { // impossible condition (f is a function) 35 } 36 37 and: 38 39 p := &v 40 ... 41 if p != nil { // tautological condition 42 } 43 44 and: 45 46 if p == nil { 47 print(*p) // nil dereference 48 } 49 50 and: 51 52 if p == nil { 53 panic(p) 54 } 55 ` 56 57 var Analyzer = &analysis.Analyzer{ 58 Name: "nilness", 59 Doc: Doc, 60 Run: run, 61 Requires: []*analysis.Analyzer{buildssa.Analyzer}, 62 } 63 64 func run(pass *analysis.Pass) (interface{}, error) { 65 ssainput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 66 for _, fn := range ssainput.SrcFuncs { 67 runFunc(pass, fn) 68 } 69 return nil, nil 70 } 71 72 func runFunc(pass *analysis.Pass, fn *ssa.Function) { 73 reportf := func(category string, pos token.Pos, format string, args ...interface{}) { 74 pass.Report(analysis.Diagnostic{ 75 Pos: pos, 76 Category: category, 77 Message: fmt.Sprintf(format, args...), 78 }) 79 } 80 81 // notNil reports an error if v is provably nil. 82 notNil := func(stack []fact, instr ssa.Instruction, v ssa.Value, descr string) { 83 if nilnessOf(stack, v) == isnil { 84 reportf("nilderef", instr.Pos(), "nil dereference in "+descr) 85 } 86 } 87 88 // visit visits reachable blocks of the CFG in dominance order, 89 // maintaining a stack of dominating nilness facts. 90 // 91 // By traversing the dom tree, we can pop facts off the stack as 92 // soon as we've visited a subtree. Had we traversed the CFG, 93 // we would need to retain the set of facts for each block. 94 seen := make([]bool, len(fn.Blocks)) // seen[i] means visit should ignore block i 95 var visit func(b *ssa.BasicBlock, stack []fact) 96 visit = func(b *ssa.BasicBlock, stack []fact) { 97 if seen[b.Index] { 98 return 99 } 100 seen[b.Index] = true 101 102 // Report nil dereferences. 103 for _, instr := range b.Instrs { 104 switch instr := instr.(type) { 105 case ssa.CallInstruction: 106 // A nil receiver may be okay for type params. 107 cc := instr.Common() 108 if !(cc.IsInvoke() && typeparams.IsTypeParam(cc.Value.Type())) { 109 notNil(stack, instr, cc.Value, cc.Description()) 110 } 111 case *ssa.FieldAddr: 112 notNil(stack, instr, instr.X, "field selection") 113 case *ssa.IndexAddr: 114 notNil(stack, instr, instr.X, "index operation") 115 case *ssa.MapUpdate: 116 notNil(stack, instr, instr.Map, "map update") 117 case *ssa.Slice: 118 // A nilcheck occurs in ptr[:] iff ptr is a pointer to an array. 119 if _, ok := instr.X.Type().Underlying().(*types.Pointer); ok { 120 notNil(stack, instr, instr.X, "slice operation") 121 } 122 case *ssa.Store: 123 notNil(stack, instr, instr.Addr, "store") 124 case *ssa.TypeAssert: 125 if !instr.CommaOk { 126 notNil(stack, instr, instr.X, "type assertion") 127 } 128 case *ssa.UnOp: 129 if instr.Op == token.MUL { // *X 130 notNil(stack, instr, instr.X, "load") 131 } 132 } 133 } 134 135 // Look for panics with nil value 136 for _, instr := range b.Instrs { 137 switch instr := instr.(type) { 138 case *ssa.Panic: 139 if nilnessOf(stack, instr.X) == isnil { 140 reportf("nilpanic", instr.Pos(), "panic with nil value") 141 } 142 case *ssa.SliceToArrayPointer: 143 nn := nilnessOf(stack, instr.X) 144 if nn == isnil && slice2ArrayPtrLen(instr) > 0 { 145 reportf("conversionpanic", instr.Pos(), "nil slice being cast to an array of len > 0 will always panic") 146 } 147 } 148 } 149 150 // For nil comparison blocks, report an error if the condition 151 // is degenerate, and push a nilness fact on the stack when 152 // visiting its true and false successor blocks. 153 if binop, tsucc, fsucc := eq(b); binop != nil { 154 xnil := nilnessOf(stack, binop.X) 155 ynil := nilnessOf(stack, binop.Y) 156 157 if ynil != unknown && xnil != unknown && (xnil == isnil || ynil == isnil) { 158 // Degenerate condition: 159 // the nilness of both operands is known, 160 // and at least one of them is nil. 161 var adj string 162 if (xnil == ynil) == (binop.Op == token.EQL) { 163 adj = "tautological" 164 } else { 165 adj = "impossible" 166 } 167 reportf("cond", binop.Pos(), "%s condition: %s %s %s", adj, xnil, binop.Op, ynil) 168 169 // If tsucc's or fsucc's sole incoming edge is impossible, 170 // it is unreachable. Prune traversal of it and 171 // all the blocks it dominates. 172 // (We could be more precise with full dataflow 173 // analysis of control-flow joins.) 174 var skip *ssa.BasicBlock 175 if xnil == ynil { 176 skip = fsucc 177 } else { 178 skip = tsucc 179 } 180 for _, d := range b.Dominees() { 181 if d == skip && len(d.Preds) == 1 { 182 continue 183 } 184 visit(d, stack) 185 } 186 return 187 } 188 189 // "if x == nil" or "if nil == y" condition; x, y are unknown. 190 if xnil == isnil || ynil == isnil { 191 var newFacts facts 192 if xnil == isnil { 193 // x is nil, y is unknown: 194 // t successor learns y is nil. 195 newFacts = expandFacts(fact{binop.Y, isnil}) 196 } else { 197 // x is nil, y is unknown: 198 // t successor learns x is nil. 199 newFacts = expandFacts(fact{binop.X, isnil}) 200 } 201 202 for _, d := range b.Dominees() { 203 // Successor blocks learn a fact 204 // only at non-critical edges. 205 // (We could do be more precise with full dataflow 206 // analysis of control-flow joins.) 207 s := stack 208 if len(d.Preds) == 1 { 209 if d == tsucc { 210 s = append(s, newFacts...) 211 } else if d == fsucc { 212 s = append(s, newFacts.negate()...) 213 } 214 } 215 visit(d, s) 216 } 217 return 218 } 219 } 220 221 for _, d := range b.Dominees() { 222 visit(d, stack) 223 } 224 } 225 226 // Visit the entry block. No need to visit fn.Recover. 227 if fn.Blocks != nil { 228 visit(fn.Blocks[0], make([]fact, 0, 20)) // 20 is plenty 229 } 230 } 231 232 // A fact records that a block is dominated 233 // by the condition v == nil or v != nil. 234 type fact struct { 235 value ssa.Value 236 nilness nilness 237 } 238 239 func (f fact) negate() fact { return fact{f.value, -f.nilness} } 240 241 type nilness int 242 243 const ( 244 isnonnil = -1 245 unknown nilness = 0 246 isnil = 1 247 ) 248 249 var nilnessStrings = []string{"non-nil", "unknown", "nil"} 250 251 func (n nilness) String() string { return nilnessStrings[n+1] } 252 253 // nilnessOf reports whether v is definitely nil, definitely not nil, 254 // or unknown given the dominating stack of facts. 255 func nilnessOf(stack []fact, v ssa.Value) nilness { 256 switch v := v.(type) { 257 // unwrap ChangeInterface and Slice values recursively, to detect if underlying 258 // values have any facts recorded or are otherwise known with regard to nilness. 259 // 260 // This work must be in addition to expanding facts about 261 // ChangeInterfaces during inference/fact gathering because this covers 262 // cases where the nilness of a value is intrinsic, rather than based 263 // on inferred facts, such as a zero value interface variable. That 264 // said, this work alone would only inform us when facts are about 265 // underlying values, rather than outer values, when the analysis is 266 // transitive in both directions. 267 case *ssa.ChangeInterface: 268 if underlying := nilnessOf(stack, v.X); underlying != unknown { 269 return underlying 270 } 271 case *ssa.Slice: 272 if underlying := nilnessOf(stack, v.X); underlying != unknown { 273 return underlying 274 } 275 case *ssa.SliceToArrayPointer: 276 nn := nilnessOf(stack, v.X) 277 if slice2ArrayPtrLen(v) > 0 { 278 if nn == isnil { 279 // We know that *(*[1]byte)(nil) is going to panic because of the 280 // conversion. So return unknown to the caller, prevent useless 281 // nil deference reporting due to * operator. 282 return unknown 283 } 284 // Otherwise, the conversion will yield a non-nil pointer to array. 285 // Note that the instruction can still panic if array length greater 286 // than slice length. If the value is used by another instruction, 287 // that instruction can assume the panic did not happen when that 288 // instruction is reached. 289 return isnonnil 290 } 291 // In case array length is zero, the conversion result depends on nilness of the slice. 292 if nn != unknown { 293 return nn 294 } 295 } 296 297 // Is value intrinsically nil or non-nil? 298 switch v := v.(type) { 299 case *ssa.Alloc, 300 *ssa.FieldAddr, 301 *ssa.FreeVar, 302 *ssa.Function, 303 *ssa.Global, 304 *ssa.IndexAddr, 305 *ssa.MakeChan, 306 *ssa.MakeClosure, 307 *ssa.MakeInterface, 308 *ssa.MakeMap, 309 *ssa.MakeSlice: 310 return isnonnil 311 case *ssa.Const: 312 if v.IsNil() { 313 return isnil // nil or zero value of a pointer-like type 314 } else { 315 return unknown // non-pointer 316 } 317 } 318 319 // Search dominating control-flow facts. 320 for _, f := range stack { 321 if f.value == v { 322 return f.nilness 323 } 324 } 325 return unknown 326 } 327 328 func slice2ArrayPtrLen(v *ssa.SliceToArrayPointer) int64 { 329 return v.Type().(*types.Pointer).Elem().Underlying().(*types.Array).Len() 330 } 331 332 // If b ends with an equality comparison, eq returns the operation and 333 // its true (equal) and false (not equal) successors. 334 func eq(b *ssa.BasicBlock) (op *ssa.BinOp, tsucc, fsucc *ssa.BasicBlock) { 335 if If, ok := b.Instrs[len(b.Instrs)-1].(*ssa.If); ok { 336 if binop, ok := If.Cond.(*ssa.BinOp); ok { 337 switch binop.Op { 338 case token.EQL: 339 return binop, b.Succs[0], b.Succs[1] 340 case token.NEQ: 341 return binop, b.Succs[1], b.Succs[0] 342 } 343 } 344 } 345 return nil, nil, nil 346 } 347 348 // expandFacts takes a single fact and returns the set of facts that can be 349 // known about it or any of its related values. Some operations, like 350 // ChangeInterface, have transitive nilness, such that if you know the 351 // underlying value is nil, you also know the value itself is nil, and vice 352 // versa. This operation allows callers to match on any of the related values 353 // in analyses, rather than just the one form of the value that happened to 354 // appear in a comparison. 355 // 356 // This work must be in addition to unwrapping values within nilnessOf because 357 // while this work helps give facts about transitively known values based on 358 // inferred facts, the recursive check within nilnessOf covers cases where 359 // nilness facts are intrinsic to the underlying value, such as a zero value 360 // interface variables. 361 // 362 // ChangeInterface is the only expansion currently supported, but others, like 363 // Slice, could be added. At this time, this tool does not check slice 364 // operations in a way this expansion could help. See 365 // https://play.golang.org/p/mGqXEp7w4fR for an example. 366 func expandFacts(f fact) []fact { 367 ff := []fact{f} 368 369 Loop: 370 for { 371 switch v := f.value.(type) { 372 case *ssa.ChangeInterface: 373 f = fact{v.X, f.nilness} 374 ff = append(ff, f) 375 default: 376 break Loop 377 } 378 } 379 380 return ff 381 } 382 383 type facts []fact 384 385 func (ff facts) negate() facts { 386 nn := make([]fact, len(ff)) 387 for i, f := range ff { 388 nn[i] = f.negate() 389 } 390 return nn 391 }