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