1// Package staticcheck contains a linter for Go source code. 2package staticcheck // import "honnef.co/go/tools/staticcheck" 3 4import ( 5 "fmt" 6 "go/ast" 7 "go/constant" 8 "go/token" 9 "go/types" 10 htmltemplate "html/template" 11 "net/http" 12 "reflect" 13 "regexp" 14 "regexp/syntax" 15 "sort" 16 "strconv" 17 "strings" 18 texttemplate "text/template" 19 "unicode" 20 21 . "honnef.co/go/tools/arg" 22 "honnef.co/go/tools/code" 23 "honnef.co/go/tools/deprecated" 24 "honnef.co/go/tools/edit" 25 "honnef.co/go/tools/facts" 26 "honnef.co/go/tools/functions" 27 "honnef.co/go/tools/internal/passes/buildir" 28 "honnef.co/go/tools/internal/sharedcheck" 29 "honnef.co/go/tools/ir" 30 "honnef.co/go/tools/ir/irutil" 31 "honnef.co/go/tools/lint" 32 . "honnef.co/go/tools/lint/lintdsl" 33 "honnef.co/go/tools/pattern" 34 "honnef.co/go/tools/printf" 35 "honnef.co/go/tools/report" 36 37 "golang.org/x/tools/go/analysis" 38 "golang.org/x/tools/go/analysis/passes/inspect" 39 "golang.org/x/tools/go/ast/astutil" 40 "golang.org/x/tools/go/ast/inspector" 41 "golang.org/x/tools/go/types/typeutil" 42) 43 44func checkSortSlice(call *Call) { 45 c := call.Instr.Common().StaticCallee() 46 arg := call.Args[0] 47 48 T := arg.Value.Value.Type().Underlying() 49 switch T.(type) { 50 case *types.Interface: 51 // we don't know. 52 // TODO(dh): if the value is a phi node we can look at its edges 53 if k, ok := arg.Value.Value.(*ir.Const); ok && k.Value == nil { 54 // literal nil, e.g. sort.Sort(nil, ...) 55 arg.Invalid(fmt.Sprintf("cannot call %s on nil literal", c)) 56 } 57 case *types.Slice: 58 // this is fine 59 default: 60 // this is not fine 61 arg.Invalid(fmt.Sprintf("%s must only be called on slices, was called on %s", c, T)) 62 } 63} 64 65func validRegexp(call *Call) { 66 arg := call.Args[0] 67 err := ValidateRegexp(arg.Value) 68 if err != nil { 69 arg.Invalid(err.Error()) 70 } 71} 72 73type runeSlice []rune 74 75func (rs runeSlice) Len() int { return len(rs) } 76func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] } 77func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] } 78 79func utf8Cutset(call *Call) { 80 arg := call.Args[1] 81 if InvalidUTF8(arg.Value) { 82 arg.Invalid(MsgInvalidUTF8) 83 } 84} 85 86func uniqueCutset(call *Call) { 87 arg := call.Args[1] 88 if !UniqueStringCutset(arg.Value) { 89 arg.Invalid(MsgNonUniqueCutset) 90 } 91} 92 93func unmarshalPointer(name string, arg int) CallCheck { 94 return func(call *Call) { 95 if !Pointer(call.Args[arg].Value) { 96 call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name)) 97 } 98 } 99} 100 101func pointlessIntMath(call *Call) { 102 if ConvertedFromInt(call.Args[0].Value) { 103 call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", code.CallName(call.Instr.Common()))) 104 } 105} 106 107func checkValidHostPort(arg int) CallCheck { 108 return func(call *Call) { 109 if !ValidHostPort(call.Args[arg].Value) { 110 call.Args[arg].Invalid(MsgInvalidHostPort) 111 } 112 } 113} 114 115var ( 116 checkRegexpRules = map[string]CallCheck{ 117 "regexp.MustCompile": validRegexp, 118 "regexp.Compile": validRegexp, 119 "regexp.Match": validRegexp, 120 "regexp.MatchReader": validRegexp, 121 "regexp.MatchString": validRegexp, 122 } 123 124 checkTimeParseRules = map[string]CallCheck{ 125 "time.Parse": func(call *Call) { 126 arg := call.Args[Arg("time.Parse.layout")] 127 err := ValidateTimeLayout(arg.Value) 128 if err != nil { 129 arg.Invalid(err.Error()) 130 } 131 }, 132 } 133 134 checkEncodingBinaryRules = map[string]CallCheck{ 135 "encoding/binary.Write": func(call *Call) { 136 arg := call.Args[Arg("encoding/binary.Write.data")] 137 if !CanBinaryMarshal(call.Pass, arg.Value) { 138 arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type())) 139 } 140 }, 141 } 142 143 checkURLsRules = map[string]CallCheck{ 144 "net/url.Parse": func(call *Call) { 145 arg := call.Args[Arg("net/url.Parse.rawurl")] 146 err := ValidateURL(arg.Value) 147 if err != nil { 148 arg.Invalid(err.Error()) 149 } 150 }, 151 } 152 153 checkSyncPoolValueRules = map[string]CallCheck{ 154 "(*sync.Pool).Put": func(call *Call) { 155 arg := call.Args[Arg("(*sync.Pool).Put.x")] 156 typ := arg.Value.Value.Type() 157 if !code.IsPointerLike(typ) { 158 arg.Invalid("argument should be pointer-like to avoid allocations") 159 } 160 }, 161 } 162 163 checkRegexpFindAllRules = map[string]CallCheck{ 164 "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1), 165 "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1), 166 "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1), 167 "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1), 168 "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1), 169 "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), 170 "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1), 171 "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), 172 } 173 174 checkUTF8CutsetRules = map[string]CallCheck{ 175 "strings.IndexAny": utf8Cutset, 176 "strings.LastIndexAny": utf8Cutset, 177 "strings.ContainsAny": utf8Cutset, 178 "strings.Trim": utf8Cutset, 179 "strings.TrimLeft": utf8Cutset, 180 "strings.TrimRight": utf8Cutset, 181 } 182 183 checkUniqueCutsetRules = map[string]CallCheck{ 184 "strings.Trim": uniqueCutset, 185 "strings.TrimLeft": uniqueCutset, 186 "strings.TrimRight": uniqueCutset, 187 } 188 189 checkUnmarshalPointerRules = map[string]CallCheck{ 190 "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1), 191 "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0), 192 "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0), 193 "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1), 194 "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0), 195 } 196 197 checkUnbufferedSignalChanRules = map[string]CallCheck{ 198 "os/signal.Notify": func(call *Call) { 199 arg := call.Args[Arg("os/signal.Notify.c")] 200 if UnbufferedChannel(arg.Value) { 201 arg.Invalid("the channel used with signal.Notify should be buffered") 202 } 203 }, 204 } 205 206 checkMathIntRules = map[string]CallCheck{ 207 "math.Ceil": pointlessIntMath, 208 "math.Floor": pointlessIntMath, 209 "math.IsNaN": pointlessIntMath, 210 "math.Trunc": pointlessIntMath, 211 "math.IsInf": pointlessIntMath, 212 } 213 214 checkStringsReplaceZeroRules = map[string]CallCheck{ 215 "strings.Replace": RepeatZeroTimes("strings.Replace", 3), 216 "bytes.Replace": RepeatZeroTimes("bytes.Replace", 3), 217 } 218 219 checkListenAddressRules = map[string]CallCheck{ 220 "net/http.ListenAndServe": checkValidHostPort(0), 221 "net/http.ListenAndServeTLS": checkValidHostPort(0), 222 } 223 224 checkBytesEqualIPRules = map[string]CallCheck{ 225 "bytes.Equal": func(call *Call) { 226 if ConvertedFrom(call.Args[Arg("bytes.Equal.a")].Value, "net.IP") && 227 ConvertedFrom(call.Args[Arg("bytes.Equal.b")].Value, "net.IP") { 228 call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal") 229 } 230 }, 231 } 232 233 checkRegexpMatchLoopRules = map[string]CallCheck{ 234 "regexp.Match": loopedRegexp("regexp.Match"), 235 "regexp.MatchReader": loopedRegexp("regexp.MatchReader"), 236 "regexp.MatchString": loopedRegexp("regexp.MatchString"), 237 } 238 239 checkNoopMarshal = map[string]CallCheck{ 240 // TODO(dh): should we really flag XML? Even an empty struct 241 // produces a non-zero amount of data, namely its type name. 242 // Let's see if we encounter any false positives. 243 // 244 // Also, should we flag gob? 245 "encoding/json.Marshal": checkNoopMarshalImpl(Arg("json.Marshal.v"), "MarshalJSON", "MarshalText"), 246 "encoding/xml.Marshal": checkNoopMarshalImpl(Arg("xml.Marshal.v"), "MarshalXML", "MarshalText"), 247 "(*encoding/json.Encoder).Encode": checkNoopMarshalImpl(Arg("(*encoding/json.Encoder).Encode.v"), "MarshalJSON", "MarshalText"), 248 "(*encoding/xml.Encoder).Encode": checkNoopMarshalImpl(Arg("(*encoding/xml.Encoder).Encode.v"), "MarshalXML", "MarshalText"), 249 250 "encoding/json.Unmarshal": checkNoopMarshalImpl(Arg("json.Unmarshal.v"), "UnmarshalJSON", "UnmarshalText"), 251 "encoding/xml.Unmarshal": checkNoopMarshalImpl(Arg("xml.Unmarshal.v"), "UnmarshalXML", "UnmarshalText"), 252 "(*encoding/json.Decoder).Decode": checkNoopMarshalImpl(Arg("(*encoding/json.Decoder).Decode.v"), "UnmarshalJSON", "UnmarshalText"), 253 "(*encoding/xml.Decoder).Decode": checkNoopMarshalImpl(Arg("(*encoding/xml.Decoder).Decode.v"), "UnmarshalXML", "UnmarshalText"), 254 } 255 256 checkUnsupportedMarshal = map[string]CallCheck{ 257 "encoding/json.Marshal": checkUnsupportedMarshalImpl(Arg("json.Marshal.v"), "json", "MarshalJSON", "MarshalText"), 258 "encoding/xml.Marshal": checkUnsupportedMarshalImpl(Arg("xml.Marshal.v"), "xml", "MarshalXML", "MarshalText"), 259 "(*encoding/json.Encoder).Encode": checkUnsupportedMarshalImpl(Arg("(*encoding/json.Encoder).Encode.v"), "json", "MarshalJSON", "MarshalText"), 260 "(*encoding/xml.Encoder).Encode": checkUnsupportedMarshalImpl(Arg("(*encoding/xml.Encoder).Encode.v"), "xml", "MarshalXML", "MarshalText"), 261 } 262 263 checkAtomicAlignment = map[string]CallCheck{ 264 "sync/atomic.AddInt64": checkAtomicAlignmentImpl, 265 "sync/atomic.AddUint64": checkAtomicAlignmentImpl, 266 "sync/atomic.CompareAndSwapInt64": checkAtomicAlignmentImpl, 267 "sync/atomic.CompareAndSwapUint64": checkAtomicAlignmentImpl, 268 "sync/atomic.LoadInt64": checkAtomicAlignmentImpl, 269 "sync/atomic.LoadUint64": checkAtomicAlignmentImpl, 270 "sync/atomic.StoreInt64": checkAtomicAlignmentImpl, 271 "sync/atomic.StoreUint64": checkAtomicAlignmentImpl, 272 "sync/atomic.SwapInt64": checkAtomicAlignmentImpl, 273 "sync/atomic.SwapUint64": checkAtomicAlignmentImpl, 274 } 275 276 // TODO(dh): detect printf wrappers 277 checkPrintfRules = map[string]CallCheck{ 278 "fmt.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) }, 279 "fmt.Printf": func(call *Call) { checkPrintfCall(call, 0, 1) }, 280 "fmt.Sprintf": func(call *Call) { checkPrintfCall(call, 0, 1) }, 281 "fmt.Fprintf": func(call *Call) { checkPrintfCall(call, 1, 2) }, 282 "golang.org/x/xerrors.Errorf": func(call *Call) { checkPrintfCall(call, 0, 1) }, 283 } 284 285 checkSortSliceRules = map[string]CallCheck{ 286 "sort.Slice": checkSortSlice, 287 "sort.SliceIsSorted": checkSortSlice, 288 "sort.SliceStable": checkSortSlice, 289 } 290 291 checkWithValueKeyRules = map[string]CallCheck{ 292 "context.WithValue": checkWithValueKey, 293 } 294) 295 296func checkPrintfCall(call *Call, fIdx, vIdx int) { 297 f := call.Args[fIdx] 298 var args []ir.Value 299 switch v := call.Args[vIdx].Value.Value.(type) { 300 case *ir.Slice: 301 var ok bool 302 args, ok = irutil.Vararg(v) 303 if !ok { 304 // We don't know what the actual arguments to the function are 305 return 306 } 307 case *ir.Const: 308 // nil, i.e. no arguments 309 default: 310 // We don't know what the actual arguments to the function are 311 return 312 } 313 checkPrintfCallImpl(f, f.Value.Value, args) 314} 315 316type verbFlag int 317 318const ( 319 isInt verbFlag = 1 << iota 320 isBool 321 isFP 322 isString 323 isPointer 324 // Verbs that accept "pseudo pointers" will sometimes dereference 325 // non-nil pointers. For example, %x on a non-nil *struct will print the 326 // individual fields, but on a nil pointer it will print the address. 327 isPseudoPointer 328 isSlice 329 isAny 330 noRecurse 331) 332 333var verbs = [...]verbFlag{ 334 'b': isPseudoPointer | isInt | isFP, 335 'c': isInt, 336 'd': isPseudoPointer | isInt, 337 'e': isFP, 338 'E': isFP, 339 'f': isFP, 340 'F': isFP, 341 'g': isFP, 342 'G': isFP, 343 'o': isPseudoPointer | isInt, 344 'O': isPseudoPointer | isInt, 345 'p': isSlice | isPointer | noRecurse, 346 'q': isInt | isString, 347 's': isString, 348 't': isBool, 349 'T': isAny, 350 'U': isInt, 351 'v': isAny, 352 'X': isPseudoPointer | isInt | isFP | isString, 353 'x': isPseudoPointer | isInt | isFP | isString, 354} 355 356func checkPrintfCallImpl(carg *Argument, f ir.Value, args []ir.Value) { 357 var msCache *typeutil.MethodSetCache 358 if f.Parent() != nil { 359 msCache = &f.Parent().Prog.MethodSets 360 } 361 362 elem := func(T types.Type, verb rune) ([]types.Type, bool) { 363 if verbs[verb]&noRecurse != 0 { 364 return []types.Type{T}, false 365 } 366 switch T := T.(type) { 367 case *types.Slice: 368 if verbs[verb]&isSlice != 0 { 369 return []types.Type{T}, false 370 } 371 if verbs[verb]&isString != 0 && code.IsType(T.Elem().Underlying(), "byte") { 372 return []types.Type{T}, false 373 } 374 return []types.Type{T.Elem()}, true 375 case *types.Map: 376 key := T.Key() 377 val := T.Elem() 378 return []types.Type{key, val}, true 379 case *types.Struct: 380 out := make([]types.Type, 0, T.NumFields()) 381 for i := 0; i < T.NumFields(); i++ { 382 out = append(out, T.Field(i).Type()) 383 } 384 return out, true 385 case *types.Array: 386 return []types.Type{T.Elem()}, true 387 default: 388 return []types.Type{T}, false 389 } 390 } 391 isInfo := func(T types.Type, info types.BasicInfo) bool { 392 basic, ok := T.Underlying().(*types.Basic) 393 return ok && basic.Info()&info != 0 394 } 395 396 isStringer := func(T types.Type, ms *types.MethodSet) bool { 397 sel := ms.Lookup(nil, "String") 398 if sel == nil { 399 return false 400 } 401 fn, ok := sel.Obj().(*types.Func) 402 if !ok { 403 // should be unreachable 404 return false 405 } 406 sig := fn.Type().(*types.Signature) 407 if sig.Params().Len() != 0 { 408 return false 409 } 410 if sig.Results().Len() != 1 { 411 return false 412 } 413 if !code.IsType(sig.Results().At(0).Type(), "string") { 414 return false 415 } 416 return true 417 } 418 isError := func(T types.Type, ms *types.MethodSet) bool { 419 sel := ms.Lookup(nil, "Error") 420 if sel == nil { 421 return false 422 } 423 fn, ok := sel.Obj().(*types.Func) 424 if !ok { 425 // should be unreachable 426 return false 427 } 428 sig := fn.Type().(*types.Signature) 429 if sig.Params().Len() != 0 { 430 return false 431 } 432 if sig.Results().Len() != 1 { 433 return false 434 } 435 if !code.IsType(sig.Results().At(0).Type(), "string") { 436 return false 437 } 438 return true 439 } 440 441 isFormatter := func(T types.Type, ms *types.MethodSet) bool { 442 sel := ms.Lookup(nil, "Format") 443 if sel == nil { 444 return false 445 } 446 fn, ok := sel.Obj().(*types.Func) 447 if !ok { 448 // should be unreachable 449 return false 450 } 451 sig := fn.Type().(*types.Signature) 452 if sig.Params().Len() != 2 { 453 return false 454 } 455 // TODO(dh): check the types of the arguments for more 456 // precision 457 if sig.Results().Len() != 0 { 458 return false 459 } 460 return true 461 } 462 463 seen := map[types.Type]bool{} 464 var checkType func(verb rune, T types.Type, top bool) bool 465 checkType = func(verb rune, T types.Type, top bool) bool { 466 if top { 467 for k := range seen { 468 delete(seen, k) 469 } 470 } 471 if seen[T] { 472 return true 473 } 474 seen[T] = true 475 if int(verb) >= len(verbs) { 476 // Unknown verb 477 return true 478 } 479 480 flags := verbs[verb] 481 if flags == 0 { 482 // Unknown verb 483 return true 484 } 485 486 ms := msCache.MethodSet(T) 487 if isFormatter(T, ms) { 488 // the value is responsible for formatting itself 489 return true 490 } 491 492 if flags&isString != 0 && (isStringer(T, ms) || isError(T, ms)) { 493 // Check for stringer early because we're about to dereference 494 return true 495 } 496 497 T = T.Underlying() 498 if flags&(isPointer|isPseudoPointer) == 0 && top { 499 T = code.Dereference(T) 500 } 501 if flags&isPseudoPointer != 0 && top { 502 t := code.Dereference(T) 503 if _, ok := t.Underlying().(*types.Struct); ok { 504 T = t 505 } 506 } 507 508 if _, ok := T.(*types.Interface); ok { 509 // We don't know what's in the interface 510 return true 511 } 512 513 var info types.BasicInfo 514 if flags&isInt != 0 { 515 info |= types.IsInteger 516 } 517 if flags&isBool != 0 { 518 info |= types.IsBoolean 519 } 520 if flags&isFP != 0 { 521 info |= types.IsFloat | types.IsComplex 522 } 523 if flags&isString != 0 { 524 info |= types.IsString 525 } 526 527 if info != 0 && isInfo(T, info) { 528 return true 529 } 530 531 if flags&isString != 0 { 532 isStringyElem := func(typ types.Type) bool { 533 if typ, ok := typ.Underlying().(*types.Basic); ok { 534 return typ.Kind() == types.Byte 535 } 536 return false 537 } 538 switch T := T.(type) { 539 case *types.Slice: 540 if isStringyElem(T.Elem()) { 541 return true 542 } 543 case *types.Array: 544 if isStringyElem(T.Elem()) { 545 return true 546 } 547 } 548 if isStringer(T, ms) || isError(T, ms) { 549 return true 550 } 551 } 552 553 if flags&isPointer != 0 && code.IsPointerLike(T) { 554 return true 555 } 556 if flags&isPseudoPointer != 0 { 557 switch U := T.Underlying().(type) { 558 case *types.Pointer: 559 if !top { 560 return true 561 } 562 563 if _, ok := U.Elem().Underlying().(*types.Struct); !ok { 564 // TODO(dh): can this condition ever be false? For 565 // *T, if T is a struct, we'll already have 566 // dereferenced it, meaning the *types.Pointer 567 // branch couldn't have been taken. For T that 568 // aren't structs, this condition will always 569 // evaluate to true. 570 return true 571 } 572 case *types.Chan, *types.Signature: 573 // Channels and functions are always treated as 574 // pointers and never recursed into. 575 return true 576 case *types.Basic: 577 if U.Kind() == types.UnsafePointer { 578 return true 579 } 580 case *types.Interface: 581 // we will already have bailed if the type is an 582 // interface. 583 panic("unreachable") 584 default: 585 // other pointer-like types, such as maps or slices, 586 // will be printed element-wise. 587 } 588 } 589 590 if flags&isSlice != 0 { 591 if _, ok := T.(*types.Slice); ok { 592 return true 593 } 594 } 595 596 if flags&isAny != 0 { 597 return true 598 } 599 600 elems, ok := elem(T.Underlying(), verb) 601 if !ok { 602 return false 603 } 604 for _, elem := range elems { 605 if !checkType(verb, elem, false) { 606 return false 607 } 608 } 609 610 return true 611 } 612 613 k, ok := f.(*ir.Const) 614 if !ok { 615 return 616 } 617 actions, err := printf.Parse(constant.StringVal(k.Value)) 618 if err != nil { 619 carg.Invalid("couldn't parse format string") 620 return 621 } 622 623 ptr := 1 624 hasExplicit := false 625 626 checkStar := func(verb printf.Verb, star printf.Argument) bool { 627 if star, ok := star.(printf.Star); ok { 628 idx := 0 629 if star.Index == -1 { 630 idx = ptr 631 ptr++ 632 } else { 633 hasExplicit = true 634 idx = star.Index 635 ptr = star.Index + 1 636 } 637 if idx == 0 { 638 carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw)) 639 return false 640 } 641 if idx > len(args) { 642 carg.Invalid( 643 fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args", 644 verb.Raw, idx, len(args))) 645 return false 646 } 647 if arg, ok := args[idx-1].(*ir.MakeInterface); ok { 648 if !isInfo(arg.X.Type(), types.IsInteger) { 649 carg.Invalid(fmt.Sprintf("Printf format %s reads non-int arg #%d as argument of *", verb.Raw, idx)) 650 } 651 } 652 } 653 return true 654 } 655 656 // We only report one problem per format string. Making a 657 // mistake with an index tends to invalidate all future 658 // implicit indices. 659 for _, action := range actions { 660 verb, ok := action.(printf.Verb) 661 if !ok { 662 continue 663 } 664 665 if !checkStar(verb, verb.Width) || !checkStar(verb, verb.Precision) { 666 return 667 } 668 669 off := ptr 670 if verb.Value != -1 { 671 hasExplicit = true 672 off = verb.Value 673 } 674 if off > len(args) { 675 carg.Invalid( 676 fmt.Sprintf("Printf format %s reads arg #%d, but call has only %d args", 677 verb.Raw, off, len(args))) 678 return 679 } else if verb.Value == 0 && verb.Letter != '%' { 680 carg.Invalid(fmt.Sprintf("Printf format %s reads invalid arg 0; indices are 1-based", verb.Raw)) 681 return 682 } else if off != 0 { 683 arg, ok := args[off-1].(*ir.MakeInterface) 684 if ok { 685 if !checkType(verb.Letter, arg.X.Type(), true) { 686 carg.Invalid(fmt.Sprintf("Printf format %s has arg #%d of wrong type %s", 687 verb.Raw, ptr, args[ptr-1].(*ir.MakeInterface).X.Type())) 688 return 689 } 690 } 691 } 692 693 switch verb.Value { 694 case -1: 695 // Consume next argument 696 ptr++ 697 case 0: 698 // Don't consume any arguments 699 default: 700 ptr = verb.Value + 1 701 } 702 } 703 704 if !hasExplicit && ptr <= len(args) { 705 carg.Invalid(fmt.Sprintf("Printf call needs %d args but has %d args", ptr-1, len(args))) 706 } 707} 708 709func checkAtomicAlignmentImpl(call *Call) { 710 sizes := call.Pass.TypesSizes 711 if sizes.Sizeof(types.Typ[types.Uintptr]) != 4 { 712 // Not running on a 32-bit platform 713 return 714 } 715 v, ok := call.Args[0].Value.Value.(*ir.FieldAddr) 716 if !ok { 717 // TODO(dh): also check indexing into arrays and slices 718 return 719 } 720 T := v.X.Type().Underlying().(*types.Pointer).Elem().Underlying().(*types.Struct) 721 fields := make([]*types.Var, 0, T.NumFields()) 722 for i := 0; i < T.NumFields() && i <= v.Field; i++ { 723 fields = append(fields, T.Field(i)) 724 } 725 726 off := sizes.Offsetsof(fields)[v.Field] 727 if off%8 != 0 { 728 msg := fmt.Sprintf("address of non 64-bit aligned field %s passed to %s", 729 T.Field(v.Field).Name(), 730 code.CallName(call.Instr.Common())) 731 call.Invalid(msg) 732 } 733} 734 735func checkNoopMarshalImpl(argN int, meths ...string) CallCheck { 736 return func(call *Call) { 737 if code.IsGenerated(call.Pass, call.Instr.Pos()) { 738 return 739 } 740 arg := call.Args[argN] 741 T := arg.Value.Value.Type() 742 Ts, ok := code.Dereference(T).Underlying().(*types.Struct) 743 if !ok { 744 return 745 } 746 if Ts.NumFields() == 0 { 747 return 748 } 749 fields := code.FlattenFields(Ts) 750 for _, field := range fields { 751 if field.Var.Exported() { 752 return 753 } 754 } 755 // OPT(dh): we could use a method set cache here 756 ms := call.Instr.Parent().Prog.MethodSets.MethodSet(T) 757 // TODO(dh): we're not checking the signature, which can cause false negatives. 758 // This isn't a huge problem, however, since vet complains about incorrect signatures. 759 for _, meth := range meths { 760 if ms.Lookup(nil, meth) != nil { 761 return 762 } 763 } 764 arg.Invalid("struct doesn't have any exported fields, nor custom marshaling") 765 } 766} 767 768func checkUnsupportedMarshalImpl(argN int, tag string, meths ...string) CallCheck { 769 // TODO(dh): flag slices and maps of unsupported types 770 return func(call *Call) { 771 msCache := &call.Instr.Parent().Prog.MethodSets 772 773 arg := call.Args[argN] 774 T := arg.Value.Value.Type() 775 Ts, ok := code.Dereference(T).Underlying().(*types.Struct) 776 if !ok { 777 return 778 } 779 ms := msCache.MethodSet(T) 780 // TODO(dh): we're not checking the signature, which can cause false negatives. 781 // This isn't a huge problem, however, since vet complains about incorrect signatures. 782 for _, meth := range meths { 783 if ms.Lookup(nil, meth) != nil { 784 return 785 } 786 } 787 fields := code.FlattenFields(Ts) 788 for _, field := range fields { 789 if !(field.Var.Exported()) { 790 continue 791 } 792 if reflect.StructTag(field.Tag).Get(tag) == "-" { 793 continue 794 } 795 ms := msCache.MethodSet(field.Var.Type()) 796 // TODO(dh): we're not checking the signature, which can cause false negatives. 797 // This isn't a huge problem, however, since vet complains about incorrect signatures. 798 for _, meth := range meths { 799 if ms.Lookup(nil, meth) != nil { 800 return 801 } 802 } 803 switch field.Var.Type().Underlying().(type) { 804 case *types.Chan, *types.Signature: 805 arg.Invalid(fmt.Sprintf("trying to marshal chan or func value, field %s", fieldPath(T, field.Path))) 806 } 807 } 808 } 809} 810 811func fieldPath(start types.Type, indices []int) string { 812 p := start.String() 813 for _, idx := range indices { 814 field := code.Dereference(start).Underlying().(*types.Struct).Field(idx) 815 start = field.Type() 816 p += "." + field.Name() 817 } 818 return p 819} 820 821func isInLoop(b *ir.BasicBlock) bool { 822 sets := functions.FindLoops(b.Parent()) 823 for _, set := range sets { 824 if set.Has(b) { 825 return true 826 } 827 } 828 return false 829} 830 831func CheckUntrappableSignal(pass *analysis.Pass) (interface{}, error) { 832 fn := func(node ast.Node) { 833 call := node.(*ast.CallExpr) 834 if !code.IsCallToAnyAST(pass, call, 835 "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") { 836 return 837 } 838 839 hasSigterm := false 840 for _, arg := range call.Args { 841 if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") { 842 arg = conv.Args[0] 843 } 844 845 if isName(pass, arg, "syscall.SIGTERM") { 846 hasSigterm = true 847 break 848 } 849 850 } 851 for i, arg := range call.Args { 852 if conv, ok := arg.(*ast.CallExpr); ok && isName(pass, conv.Fun, "os.Signal") { 853 arg = conv.Args[0] 854 } 855 856 if isName(pass, arg, "os.Kill") || isName(pass, arg, "syscall.SIGKILL") { 857 var fixes []analysis.SuggestedFix 858 if !hasSigterm { 859 nargs := make([]ast.Expr, len(call.Args)) 860 for j, a := range call.Args { 861 if i == j { 862 nargs[j] = Selector("syscall", "SIGTERM") 863 } else { 864 nargs[j] = a 865 } 866 } 867 ncall := *call 868 ncall.Args = nargs 869 fixes = append(fixes, edit.Fix(fmt.Sprintf("use syscall.SIGTERM instead of %s", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) 870 } 871 nargs := make([]ast.Expr, 0, len(call.Args)) 872 for j, a := range call.Args { 873 if i == j { 874 continue 875 } 876 nargs = append(nargs, a) 877 } 878 ncall := *call 879 ncall.Args = nargs 880 fixes = append(fixes, edit.Fix(fmt.Sprintf("remove %s from list of arguments", report.Render(pass, arg)), edit.ReplaceWithNode(pass.Fset, call, &ncall))) 881 report.Report(pass, arg, fmt.Sprintf("%s cannot be trapped (did you mean syscall.SIGTERM?)", report.Render(pass, arg)), report.Fixes(fixes...)) 882 } 883 if isName(pass, arg, "syscall.SIGSTOP") { 884 nargs := make([]ast.Expr, 0, len(call.Args)-1) 885 for j, a := range call.Args { 886 if i == j { 887 continue 888 } 889 nargs = append(nargs, a) 890 } 891 ncall := *call 892 ncall.Args = nargs 893 report.Report(pass, arg, "syscall.SIGSTOP cannot be trapped", report.Fixes(edit.Fix("remove syscall.SIGSTOP from list of arguments", edit.ReplaceWithNode(pass.Fset, call, &ncall)))) 894 } 895 } 896 } 897 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 898 return nil, nil 899} 900 901func CheckTemplate(pass *analysis.Pass) (interface{}, error) { 902 fn := func(node ast.Node) { 903 call := node.(*ast.CallExpr) 904 var kind string 905 switch code.CallNameAST(pass, call) { 906 case "(*text/template.Template).Parse": 907 kind = "text" 908 case "(*html/template.Template).Parse": 909 kind = "html" 910 default: 911 return 912 } 913 sel := call.Fun.(*ast.SelectorExpr) 914 if !code.IsCallToAnyAST(pass, sel.X, "text/template.New", "html/template.New") { 915 // TODO(dh): this is a cheap workaround for templates with 916 // different delims. A better solution with less false 917 // negatives would use data flow analysis to see where the 918 // template comes from and where it has been 919 return 920 } 921 s, ok := code.ExprToString(pass, call.Args[Arg("(*text/template.Template).Parse.text")]) 922 if !ok { 923 return 924 } 925 var err error 926 switch kind { 927 case "text": 928 _, err = texttemplate.New("").Parse(s) 929 case "html": 930 _, err = htmltemplate.New("").Parse(s) 931 } 932 if err != nil { 933 // TODO(dominikh): whitelist other parse errors, if any 934 if strings.Contains(err.Error(), "unexpected") { 935 report.Report(pass, call.Args[Arg("(*text/template.Template).Parse.text")], err.Error()) 936 } 937 } 938 } 939 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 940 return nil, nil 941} 942 943var ( 944 checkTimeSleepConstantPatternRns = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Nanosecond")))`) 945 checkTimeSleepConstantPatternRs = pattern.MustParse(`(BinaryExpr duration "*" (SelectorExpr (Ident "time") (Ident "Second")))`) 946) 947 948func CheckTimeSleepConstant(pass *analysis.Pass) (interface{}, error) { 949 fn := func(node ast.Node) { 950 call := node.(*ast.CallExpr) 951 if !code.IsCallToAST(pass, call, "time.Sleep") { 952 return 953 } 954 lit, ok := call.Args[Arg("time.Sleep.d")].(*ast.BasicLit) 955 if !ok { 956 return 957 } 958 n, err := strconv.Atoi(lit.Value) 959 if err != nil { 960 return 961 } 962 if n == 0 || n > 120 { 963 // time.Sleep(0) is a seldom used pattern in concurrency 964 // tests. >120 might be intentional. 120 was chosen 965 // because the user could've meant 2 minutes. 966 return 967 } 968 969 report.Report(pass, lit, 970 fmt.Sprintf("sleeping for %d nanoseconds is probably a bug; be explicit if it isn't", n), report.Fixes( 971 edit.Fix("explicitly use nanoseconds", edit.ReplaceWithPattern(pass, checkTimeSleepConstantPatternRns, pattern.State{"duration": lit}, lit)), 972 edit.Fix("use seconds", edit.ReplaceWithPattern(pass, checkTimeSleepConstantPatternRs, pattern.State{"duration": lit}, lit)))) 973 } 974 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 975 return nil, nil 976} 977 978var checkWaitgroupAddQ = pattern.MustParse(` 979 (GoStmt 980 (CallExpr 981 (FuncLit 982 _ 983 call@(CallExpr (Function "(*sync.WaitGroup).Add") _):_) _))`) 984 985func CheckWaitgroupAdd(pass *analysis.Pass) (interface{}, error) { 986 fn := func(node ast.Node) { 987 if m, ok := Match(pass, checkWaitgroupAddQ, node); ok { 988 call := m.State["call"].(ast.Node) 989 report.Report(pass, call, fmt.Sprintf("should call %s before starting the goroutine to avoid a race", report.Render(pass, call))) 990 } 991 } 992 code.Preorder(pass, fn, (*ast.GoStmt)(nil)) 993 return nil, nil 994} 995 996func CheckInfiniteEmptyLoop(pass *analysis.Pass) (interface{}, error) { 997 fn := func(node ast.Node) { 998 loop := node.(*ast.ForStmt) 999 if len(loop.Body.List) != 0 || loop.Post != nil { 1000 return 1001 } 1002 1003 if loop.Init != nil { 1004 // TODO(dh): this isn't strictly necessary, it just makes 1005 // the check easier. 1006 return 1007 } 1008 // An empty loop is bad news in two cases: 1) The loop has no 1009 // condition. In that case, it's just a loop that spins 1010 // forever and as fast as it can, keeping a core busy. 2) The 1011 // loop condition only consists of variable or field reads and 1012 // operators on those. The only way those could change their 1013 // value is with unsynchronised access, which constitutes a 1014 // data race. 1015 // 1016 // If the condition contains any function calls, its behaviour 1017 // is dynamic and the loop might terminate. Similarly for 1018 // channel receives. 1019 1020 if loop.Cond != nil { 1021 if code.MayHaveSideEffects(pass, loop.Cond, nil) { 1022 return 1023 } 1024 if ident, ok := loop.Cond.(*ast.Ident); ok { 1025 if k, ok := pass.TypesInfo.ObjectOf(ident).(*types.Const); ok { 1026 if !constant.BoolVal(k.Val()) { 1027 // don't flag `for false {}` loops. They're a debug aid. 1028 return 1029 } 1030 } 1031 } 1032 report.Report(pass, loop, "loop condition never changes or has a race condition") 1033 } 1034 report.Report(pass, loop, "this loop will spin, using 100%% CPU", report.ShortRange()) 1035 } 1036 code.Preorder(pass, fn, (*ast.ForStmt)(nil)) 1037 return nil, nil 1038} 1039 1040func CheckDeferInInfiniteLoop(pass *analysis.Pass) (interface{}, error) { 1041 fn := func(node ast.Node) { 1042 mightExit := false 1043 var defers []ast.Stmt 1044 loop := node.(*ast.ForStmt) 1045 if loop.Cond != nil { 1046 return 1047 } 1048 fn2 := func(node ast.Node) bool { 1049 switch stmt := node.(type) { 1050 case *ast.ReturnStmt: 1051 mightExit = true 1052 return false 1053 case *ast.BranchStmt: 1054 // TODO(dominikh): if this sees a break in a switch or 1055 // select, it doesn't check if it breaks the loop or 1056 // just the select/switch. This causes some false 1057 // negatives. 1058 if stmt.Tok == token.BREAK { 1059 mightExit = true 1060 return false 1061 } 1062 case *ast.DeferStmt: 1063 defers = append(defers, stmt) 1064 case *ast.FuncLit: 1065 // Don't look into function bodies 1066 return false 1067 } 1068 return true 1069 } 1070 ast.Inspect(loop.Body, fn2) 1071 if mightExit { 1072 return 1073 } 1074 for _, stmt := range defers { 1075 report.Report(pass, stmt, "defers in this infinite loop will never run") 1076 } 1077 } 1078 code.Preorder(pass, fn, (*ast.ForStmt)(nil)) 1079 return nil, nil 1080} 1081 1082func CheckDubiousDeferInChannelRangeLoop(pass *analysis.Pass) (interface{}, error) { 1083 fn := func(node ast.Node) { 1084 loop := node.(*ast.RangeStmt) 1085 typ := pass.TypesInfo.TypeOf(loop.X) 1086 _, ok := typ.Underlying().(*types.Chan) 1087 if !ok { 1088 return 1089 } 1090 fn2 := func(node ast.Node) bool { 1091 switch stmt := node.(type) { 1092 case *ast.DeferStmt: 1093 report.Report(pass, stmt, "defers in this range loop won't run unless the channel gets closed") 1094 case *ast.FuncLit: 1095 // Don't look into function bodies 1096 return false 1097 } 1098 return true 1099 } 1100 ast.Inspect(loop.Body, fn2) 1101 } 1102 code.Preorder(pass, fn, (*ast.RangeStmt)(nil)) 1103 return nil, nil 1104} 1105 1106func CheckTestMainExit(pass *analysis.Pass) (interface{}, error) { 1107 if code.IsGoVersion(pass, 15) { 1108 // Beginning with Go 1.15, the test framework will call 1109 // os.Exit for us. 1110 return nil, nil 1111 } 1112 1113 var ( 1114 fnmain ast.Node 1115 callsExit bool 1116 callsRun bool 1117 arg types.Object 1118 ) 1119 fn := func(node ast.Node, push bool) bool { 1120 if !push { 1121 if fnmain != nil && node == fnmain { 1122 if !callsExit && callsRun { 1123 report.Report(pass, fnmain, "TestMain should call os.Exit to set exit code") 1124 } 1125 fnmain = nil 1126 callsExit = false 1127 callsRun = false 1128 arg = nil 1129 } 1130 return true 1131 } 1132 1133 switch node := node.(type) { 1134 case *ast.FuncDecl: 1135 if fnmain != nil { 1136 return true 1137 } 1138 if !isTestMain(pass, node) { 1139 return false 1140 } 1141 fnmain = node 1142 arg = pass.TypesInfo.ObjectOf(node.Type.Params.List[0].Names[0]) 1143 return true 1144 case *ast.CallExpr: 1145 if code.IsCallToAST(pass, node, "os.Exit") { 1146 callsExit = true 1147 return false 1148 } 1149 sel, ok := node.Fun.(*ast.SelectorExpr) 1150 if !ok { 1151 return true 1152 } 1153 ident, ok := sel.X.(*ast.Ident) 1154 if !ok { 1155 return true 1156 } 1157 if arg != pass.TypesInfo.ObjectOf(ident) { 1158 return true 1159 } 1160 if sel.Sel.Name == "Run" { 1161 callsRun = true 1162 return false 1163 } 1164 return true 1165 default: 1166 ExhaustiveTypeSwitch(node) 1167 return true 1168 } 1169 } 1170 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.FuncDecl)(nil), (*ast.CallExpr)(nil)}, fn) 1171 return nil, nil 1172} 1173 1174func isTestMain(pass *analysis.Pass, decl *ast.FuncDecl) bool { 1175 if decl.Name.Name != "TestMain" { 1176 return false 1177 } 1178 if len(decl.Type.Params.List) != 1 { 1179 return false 1180 } 1181 arg := decl.Type.Params.List[0] 1182 if len(arg.Names) != 1 { 1183 return false 1184 } 1185 return code.IsOfType(pass, arg.Type, "*testing.M") 1186} 1187 1188func CheckExec(pass *analysis.Pass) (interface{}, error) { 1189 fn := func(node ast.Node) { 1190 call := node.(*ast.CallExpr) 1191 if !code.IsCallToAST(pass, call, "os/exec.Command") { 1192 return 1193 } 1194 val, ok := code.ExprToString(pass, call.Args[Arg("os/exec.Command.name")]) 1195 if !ok { 1196 return 1197 } 1198 if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") { 1199 return 1200 } 1201 report.Report(pass, call.Args[Arg("os/exec.Command.name")], 1202 "first argument to exec.Command looks like a shell command, but a program name or path are expected") 1203 } 1204 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 1205 return nil, nil 1206} 1207 1208func CheckLoopEmptyDefault(pass *analysis.Pass) (interface{}, error) { 1209 fn := func(node ast.Node) { 1210 loop := node.(*ast.ForStmt) 1211 if len(loop.Body.List) != 1 || loop.Cond != nil || loop.Init != nil { 1212 return 1213 } 1214 sel, ok := loop.Body.List[0].(*ast.SelectStmt) 1215 if !ok { 1216 return 1217 } 1218 for _, c := range sel.Body.List { 1219 // FIXME this leaves behind an empty line, and possibly 1220 // comments in the default branch. We can't easily fix 1221 // either. 1222 if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 { 1223 report.Report(pass, comm, "should not have an empty default case in a for+select loop; the loop will spin", 1224 report.Fixes(edit.Fix("remove empty default branch", edit.Delete(comm)))) 1225 // there can only be one default case 1226 break 1227 } 1228 } 1229 } 1230 code.Preorder(pass, fn, (*ast.ForStmt)(nil)) 1231 return nil, nil 1232} 1233 1234func CheckLhsRhsIdentical(pass *analysis.Pass) (interface{}, error) { 1235 var isFloat func(T types.Type) bool 1236 isFloat = func(T types.Type) bool { 1237 switch T := T.Underlying().(type) { 1238 case *types.Basic: 1239 kind := T.Kind() 1240 return kind == types.Float32 || kind == types.Float64 1241 case *types.Array: 1242 return isFloat(T.Elem()) 1243 case *types.Struct: 1244 for i := 0; i < T.NumFields(); i++ { 1245 if !isFloat(T.Field(i).Type()) { 1246 return false 1247 } 1248 } 1249 return true 1250 default: 1251 return false 1252 } 1253 } 1254 1255 // TODO(dh): this check ignores the existence of side-effects and 1256 // happily flags fn() == fn() – so far, we've had nobody complain 1257 // about a false positive, and it's caught several bugs in real 1258 // code. 1259 fn := func(node ast.Node) { 1260 op := node.(*ast.BinaryExpr) 1261 switch op.Op { 1262 case token.EQL, token.NEQ: 1263 if isFloat(pass.TypesInfo.TypeOf(op.X)) { 1264 // f == f and f != f might be used to check for NaN 1265 return 1266 } 1267 case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT, 1268 token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ: 1269 default: 1270 // For some ops, such as + and *, it can make sense to 1271 // have identical operands 1272 return 1273 } 1274 1275 if reflect.TypeOf(op.X) != reflect.TypeOf(op.Y) { 1276 return 1277 } 1278 if report.Render(pass, op.X) != report.Render(pass, op.Y) { 1279 return 1280 } 1281 l1, ok1 := op.X.(*ast.BasicLit) 1282 l2, ok2 := op.Y.(*ast.BasicLit) 1283 if ok1 && ok2 && l1.Kind == token.INT && l2.Kind == l1.Kind && l1.Value == "0" && l2.Value == l1.Value && code.IsGenerated(pass, l1.Pos()) { 1284 // cgo generates the following function call: 1285 // _cgoCheckPointer(_cgoBase0, 0 == 0) – it uses 0 == 0 1286 // instead of true in case the user shadowed the 1287 // identifier. Ideally we'd restrict this exception to 1288 // calls of _cgoCheckPointer, but it's not worth the 1289 // hassle of keeping track of the stack. <lit> <op> <lit> 1290 // are very rare to begin with, and we're mostly checking 1291 // for them to catch typos such as 1 == 1 where the user 1292 // meant to type i == 1. The odds of a false negative for 1293 // 0 == 0 are slim. 1294 return 1295 } 1296 report.Report(pass, op, fmt.Sprintf("identical expressions on the left and right side of the '%s' operator", op.Op)) 1297 } 1298 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) 1299 return nil, nil 1300} 1301 1302func CheckScopedBreak(pass *analysis.Pass) (interface{}, error) { 1303 fn := func(node ast.Node) { 1304 var body *ast.BlockStmt 1305 switch node := node.(type) { 1306 case *ast.ForStmt: 1307 body = node.Body 1308 case *ast.RangeStmt: 1309 body = node.Body 1310 default: 1311 ExhaustiveTypeSwitch(node) 1312 } 1313 for _, stmt := range body.List { 1314 var blocks [][]ast.Stmt 1315 switch stmt := stmt.(type) { 1316 case *ast.SwitchStmt: 1317 for _, c := range stmt.Body.List { 1318 blocks = append(blocks, c.(*ast.CaseClause).Body) 1319 } 1320 case *ast.SelectStmt: 1321 for _, c := range stmt.Body.List { 1322 blocks = append(blocks, c.(*ast.CommClause).Body) 1323 } 1324 default: 1325 continue 1326 } 1327 1328 for _, body := range blocks { 1329 if len(body) == 0 { 1330 continue 1331 } 1332 lasts := []ast.Stmt{body[len(body)-1]} 1333 // TODO(dh): unfold all levels of nested block 1334 // statements, not just a single level if statement 1335 if ifs, ok := lasts[0].(*ast.IfStmt); ok { 1336 if len(ifs.Body.List) == 0 { 1337 continue 1338 } 1339 lasts[0] = ifs.Body.List[len(ifs.Body.List)-1] 1340 1341 if block, ok := ifs.Else.(*ast.BlockStmt); ok { 1342 if len(block.List) != 0 { 1343 lasts = append(lasts, block.List[len(block.List)-1]) 1344 } 1345 } 1346 } 1347 for _, last := range lasts { 1348 branch, ok := last.(*ast.BranchStmt) 1349 if !ok || branch.Tok != token.BREAK || branch.Label != nil { 1350 continue 1351 } 1352 report.Report(pass, branch, "ineffective break statement. Did you mean to break out of the outer loop?") 1353 } 1354 } 1355 } 1356 } 1357 code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.RangeStmt)(nil)) 1358 return nil, nil 1359} 1360 1361func CheckUnsafePrintf(pass *analysis.Pass) (interface{}, error) { 1362 fn := func(node ast.Node) { 1363 call := node.(*ast.CallExpr) 1364 name := code.CallNameAST(pass, call) 1365 var arg int 1366 1367 switch name { 1368 case "fmt.Printf", "fmt.Sprintf", "log.Printf": 1369 arg = Arg("fmt.Printf.format") 1370 case "fmt.Fprintf": 1371 arg = Arg("fmt.Fprintf.format") 1372 default: 1373 return 1374 } 1375 if len(call.Args) != arg+1 { 1376 return 1377 } 1378 switch call.Args[arg].(type) { 1379 case *ast.CallExpr, *ast.Ident: 1380 default: 1381 return 1382 } 1383 1384 alt := name[:len(name)-1] 1385 report.Report(pass, call, 1386 "printf-style function with dynamic format string and no further arguments should use print-style function instead", 1387 report.Fixes(edit.Fix(fmt.Sprintf("use %s instead of %s", alt, name), edit.ReplaceWithString(pass.Fset, call.Fun, alt)))) 1388 } 1389 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 1390 return nil, nil 1391} 1392 1393func CheckEarlyDefer(pass *analysis.Pass) (interface{}, error) { 1394 fn := func(node ast.Node) { 1395 block := node.(*ast.BlockStmt) 1396 if len(block.List) < 2 { 1397 return 1398 } 1399 for i, stmt := range block.List { 1400 if i == len(block.List)-1 { 1401 break 1402 } 1403 assign, ok := stmt.(*ast.AssignStmt) 1404 if !ok { 1405 continue 1406 } 1407 if len(assign.Rhs) != 1 { 1408 continue 1409 } 1410 if len(assign.Lhs) < 2 { 1411 continue 1412 } 1413 if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" { 1414 continue 1415 } 1416 call, ok := assign.Rhs[0].(*ast.CallExpr) 1417 if !ok { 1418 continue 1419 } 1420 sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) 1421 if !ok { 1422 continue 1423 } 1424 if sig.Results().Len() < 2 { 1425 continue 1426 } 1427 last := sig.Results().At(sig.Results().Len() - 1) 1428 // FIXME(dh): check that it's error from universe, not 1429 // another type of the same name 1430 if last.Type().String() != "error" { 1431 continue 1432 } 1433 lhs, ok := assign.Lhs[0].(*ast.Ident) 1434 if !ok { 1435 continue 1436 } 1437 def, ok := block.List[i+1].(*ast.DeferStmt) 1438 if !ok { 1439 continue 1440 } 1441 sel, ok := def.Call.Fun.(*ast.SelectorExpr) 1442 if !ok { 1443 continue 1444 } 1445 ident, ok := selectorX(sel).(*ast.Ident) 1446 if !ok { 1447 continue 1448 } 1449 if ident.Obj != lhs.Obj { 1450 continue 1451 } 1452 if sel.Sel.Name != "Close" { 1453 continue 1454 } 1455 report.Report(pass, def, fmt.Sprintf("should check returned error before deferring %s", report.Render(pass, def.Call))) 1456 } 1457 } 1458 code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) 1459 return nil, nil 1460} 1461 1462func selectorX(sel *ast.SelectorExpr) ast.Node { 1463 switch x := sel.X.(type) { 1464 case *ast.SelectorExpr: 1465 return selectorX(x) 1466 default: 1467 return x 1468 } 1469} 1470 1471func CheckEmptyCriticalSection(pass *analysis.Pass) (interface{}, error) { 1472 if pass.Pkg.Path() == "sync_test" { 1473 // exception for the sync package's tests 1474 return nil, nil 1475 } 1476 1477 // Initially it might seem like this check would be easier to 1478 // implement using IR. After all, we're only checking for two 1479 // consecutive method calls. In reality, however, there may be any 1480 // number of other instructions between the lock and unlock, while 1481 // still constituting an empty critical section. For example, 1482 // given `m.x().Lock(); m.x().Unlock()`, there will be a call to 1483 // x(). In the AST-based approach, this has a tiny potential for a 1484 // false positive (the second call to x might be doing work that 1485 // is protected by the mutex). In an IR-based approach, however, 1486 // it would miss a lot of real bugs. 1487 1488 mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) { 1489 expr, ok := s.(*ast.ExprStmt) 1490 if !ok { 1491 return nil, "", false 1492 } 1493 call, ok := expr.X.(*ast.CallExpr) 1494 if !ok { 1495 return nil, "", false 1496 } 1497 sel, ok := call.Fun.(*ast.SelectorExpr) 1498 if !ok { 1499 return nil, "", false 1500 } 1501 1502 fn, ok := pass.TypesInfo.ObjectOf(sel.Sel).(*types.Func) 1503 if !ok { 1504 return nil, "", false 1505 } 1506 sig := fn.Type().(*types.Signature) 1507 if sig.Params().Len() != 0 || sig.Results().Len() != 0 { 1508 return nil, "", false 1509 } 1510 1511 return sel.X, fn.Name(), true 1512 } 1513 1514 fn := func(node ast.Node) { 1515 block := node.(*ast.BlockStmt) 1516 if len(block.List) < 2 { 1517 return 1518 } 1519 for i := range block.List[:len(block.List)-1] { 1520 sel1, method1, ok1 := mutexParams(block.List[i]) 1521 sel2, method2, ok2 := mutexParams(block.List[i+1]) 1522 1523 if !ok1 || !ok2 || report.Render(pass, sel1) != report.Render(pass, sel2) { 1524 continue 1525 } 1526 if (method1 == "Lock" && method2 == "Unlock") || 1527 (method1 == "RLock" && method2 == "RUnlock") { 1528 report.Report(pass, block.List[i+1], "empty critical section") 1529 } 1530 } 1531 } 1532 code.Preorder(pass, fn, (*ast.BlockStmt)(nil)) 1533 return nil, nil 1534} 1535 1536var ( 1537 // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't 1538 // want to flag. 1539 cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`) 1540 checkIneffectiveCopyQ1 = pattern.MustParse(`(UnaryExpr "&" (StarExpr obj))`) 1541 checkIneffectiveCopyQ2 = pattern.MustParse(`(StarExpr (UnaryExpr "&" _))`) 1542) 1543 1544func CheckIneffectiveCopy(pass *analysis.Pass) (interface{}, error) { 1545 fn := func(node ast.Node) { 1546 if m, ok := Match(pass, checkIneffectiveCopyQ1, node); ok { 1547 if ident, ok := m.State["obj"].(*ast.Ident); !ok || !cgoIdent.MatchString(ident.Name) { 1548 report.Report(pass, node, "&*x will be simplified to x. It will not copy x.") 1549 } 1550 } else if _, ok := Match(pass, checkIneffectiveCopyQ2, node); ok { 1551 report.Report(pass, node, "*&x will be simplified to x. It will not copy x.") 1552 } 1553 } 1554 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.StarExpr)(nil)) 1555 return nil, nil 1556} 1557 1558func CheckCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) { 1559 fn := func(node ast.Node, push bool) bool { 1560 if !push { 1561 return false 1562 } 1563 assign, ok := node.(*ast.AssignStmt) 1564 if ok { 1565 // TODO(dh): This risks missing some Header reads, for 1566 // example in `h1["foo"] = h2["foo"]` – these edge 1567 // cases are probably rare enough to ignore for now. 1568 for _, expr := range assign.Lhs { 1569 op, ok := expr.(*ast.IndexExpr) 1570 if !ok { 1571 continue 1572 } 1573 if code.IsOfType(pass, op.X, "net/http.Header") { 1574 return false 1575 } 1576 } 1577 return true 1578 } 1579 op, ok := node.(*ast.IndexExpr) 1580 if !ok { 1581 return true 1582 } 1583 if !code.IsOfType(pass, op.X, "net/http.Header") { 1584 return true 1585 } 1586 s, ok := code.ExprToString(pass, op.Index) 1587 if !ok { 1588 return true 1589 } 1590 canonical := http.CanonicalHeaderKey(s) 1591 if s == canonical { 1592 return true 1593 } 1594 var fix analysis.SuggestedFix 1595 switch op.Index.(type) { 1596 case *ast.BasicLit: 1597 fix = edit.Fix("canonicalize header key", edit.ReplaceWithString(pass.Fset, op.Index, strconv.Quote(canonical))) 1598 case *ast.Ident: 1599 call := &ast.CallExpr{ 1600 Fun: Selector("http", "CanonicalHeaderKey"), 1601 Args: []ast.Expr{op.Index}, 1602 } 1603 fix = edit.Fix("wrap in http.CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, op.Index, call)) 1604 } 1605 msg := fmt.Sprintf("keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s) 1606 if fix.Message != "" { 1607 report.Report(pass, op, msg, report.Fixes(fix)) 1608 } else { 1609 report.Report(pass, op, msg) 1610 } 1611 return true 1612 } 1613 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes([]ast.Node{(*ast.AssignStmt)(nil), (*ast.IndexExpr)(nil)}, fn) 1614 return nil, nil 1615} 1616 1617func CheckBenchmarkN(pass *analysis.Pass) (interface{}, error) { 1618 fn := func(node ast.Node) { 1619 assign := node.(*ast.AssignStmt) 1620 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { 1621 return 1622 } 1623 sel, ok := assign.Lhs[0].(*ast.SelectorExpr) 1624 if !ok { 1625 return 1626 } 1627 if sel.Sel.Name != "N" { 1628 return 1629 } 1630 if !code.IsOfType(pass, sel.X, "*testing.B") { 1631 return 1632 } 1633 report.Report(pass, assign, fmt.Sprintf("should not assign to %s", report.Render(pass, sel))) 1634 } 1635 code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) 1636 return nil, nil 1637} 1638 1639func CheckUnreadVariableValues(pass *analysis.Pass) (interface{}, error) { 1640 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 1641 if code.IsExample(fn) { 1642 continue 1643 } 1644 node := fn.Source() 1645 if node == nil { 1646 continue 1647 } 1648 if gen, ok := code.Generator(pass, node.Pos()); ok && gen == facts.Goyacc { 1649 // Don't flag unused values in code generated by goyacc. 1650 // There may be hundreds of those due to the way the state 1651 // machine is constructed. 1652 continue 1653 } 1654 1655 switchTags := map[ir.Value]struct{}{} 1656 ast.Inspect(node, func(node ast.Node) bool { 1657 s, ok := node.(*ast.SwitchStmt) 1658 if !ok { 1659 return true 1660 } 1661 v, _ := fn.ValueForExpr(s.Tag) 1662 switchTags[v] = struct{}{} 1663 return true 1664 }) 1665 1666 // OPT(dh): don't use a map, possibly use a bitset 1667 var hasUse func(v ir.Value, seen map[ir.Value]struct{}) bool 1668 hasUse = func(v ir.Value, seen map[ir.Value]struct{}) bool { 1669 if _, ok := seen[v]; ok { 1670 return false 1671 } 1672 if _, ok := switchTags[v]; ok { 1673 return true 1674 } 1675 refs := v.Referrers() 1676 if refs == nil { 1677 // TODO investigate why refs can be nil 1678 return true 1679 } 1680 for _, ref := range *refs { 1681 switch ref := ref.(type) { 1682 case *ir.DebugRef: 1683 case *ir.Sigma: 1684 if seen == nil { 1685 seen = map[ir.Value]struct{}{} 1686 } 1687 seen[v] = struct{}{} 1688 if hasUse(ref, seen) { 1689 return true 1690 } 1691 case *ir.Phi: 1692 if seen == nil { 1693 seen = map[ir.Value]struct{}{} 1694 } 1695 seen[v] = struct{}{} 1696 if hasUse(ref, seen) { 1697 return true 1698 } 1699 default: 1700 return true 1701 } 1702 } 1703 return false 1704 } 1705 1706 ast.Inspect(node, func(node ast.Node) bool { 1707 assign, ok := node.(*ast.AssignStmt) 1708 if !ok { 1709 return true 1710 } 1711 if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 { 1712 // Either a function call with multiple return values, 1713 // or a comma-ok assignment 1714 1715 val, _ := fn.ValueForExpr(assign.Rhs[0]) 1716 if val == nil { 1717 return true 1718 } 1719 refs := val.Referrers() 1720 if refs == nil { 1721 return true 1722 } 1723 for _, ref := range *refs { 1724 ex, ok := ref.(*ir.Extract) 1725 if !ok { 1726 continue 1727 } 1728 if !hasUse(ex, nil) { 1729 lhs := assign.Lhs[ex.Index] 1730 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { 1731 continue 1732 } 1733 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs)) 1734 } 1735 } 1736 return true 1737 } 1738 for i, lhs := range assign.Lhs { 1739 rhs := assign.Rhs[i] 1740 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { 1741 continue 1742 } 1743 val, _ := fn.ValueForExpr(rhs) 1744 if val == nil { 1745 continue 1746 } 1747 1748 if _, ok := val.(*ir.Const); ok { 1749 // a zero-valued constant, for example in 'foo := []string(nil)' 1750 continue 1751 } 1752 if !hasUse(val, nil) { 1753 report.Report(pass, assign, fmt.Sprintf("this value of %s is never used", lhs)) 1754 } 1755 } 1756 return true 1757 }) 1758 } 1759 return nil, nil 1760} 1761 1762func CheckPredeterminedBooleanExprs(pass *analysis.Pass) (interface{}, error) { 1763 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 1764 for _, block := range fn.Blocks { 1765 for _, ins := range block.Instrs { 1766 binop, ok := ins.(*ir.BinOp) 1767 if !ok { 1768 continue 1769 } 1770 switch binop.Op { 1771 case token.GTR, token.LSS, token.EQL, token.NEQ, token.LEQ, token.GEQ: 1772 default: 1773 continue 1774 } 1775 1776 xs, ok1 := consts(binop.X, nil, nil) 1777 ys, ok2 := consts(binop.Y, nil, nil) 1778 if !ok1 || !ok2 || len(xs) == 0 || len(ys) == 0 { 1779 continue 1780 } 1781 1782 trues := 0 1783 for _, x := range xs { 1784 for _, y := range ys { 1785 if x.Value == nil { 1786 if y.Value == nil { 1787 trues++ 1788 } 1789 continue 1790 } 1791 if constant.Compare(x.Value, binop.Op, y.Value) { 1792 trues++ 1793 } 1794 } 1795 } 1796 b := trues != 0 1797 if trues == 0 || trues == len(xs)*len(ys) { 1798 report.Report(pass, binop, fmt.Sprintf("binary expression is always %t for all possible values (%s %s %s)", b, xs, binop.Op, ys)) 1799 } 1800 } 1801 } 1802 } 1803 return nil, nil 1804} 1805 1806func CheckNilMaps(pass *analysis.Pass) (interface{}, error) { 1807 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 1808 for _, block := range fn.Blocks { 1809 for _, ins := range block.Instrs { 1810 mu, ok := ins.(*ir.MapUpdate) 1811 if !ok { 1812 continue 1813 } 1814 c, ok := mu.Map.(*ir.Const) 1815 if !ok { 1816 continue 1817 } 1818 if c.Value != nil { 1819 continue 1820 } 1821 report.Report(pass, mu, "assignment to nil map") 1822 } 1823 } 1824 } 1825 return nil, nil 1826} 1827 1828func CheckExtremeComparison(pass *analysis.Pass) (interface{}, error) { 1829 isobj := func(expr ast.Expr, name string) bool { 1830 sel, ok := expr.(*ast.SelectorExpr) 1831 if !ok { 1832 return false 1833 } 1834 return code.IsObject(pass.TypesInfo.ObjectOf(sel.Sel), name) 1835 } 1836 1837 fn := func(node ast.Node) { 1838 expr := node.(*ast.BinaryExpr) 1839 tx := pass.TypesInfo.TypeOf(expr.X) 1840 basic, ok := tx.Underlying().(*types.Basic) 1841 if !ok { 1842 return 1843 } 1844 1845 var max string 1846 var min string 1847 1848 switch basic.Kind() { 1849 case types.Uint8: 1850 max = "math.MaxUint8" 1851 case types.Uint16: 1852 max = "math.MaxUint16" 1853 case types.Uint32: 1854 max = "math.MaxUint32" 1855 case types.Uint64: 1856 max = "math.MaxUint64" 1857 case types.Uint: 1858 max = "math.MaxUint64" 1859 1860 case types.Int8: 1861 min = "math.MinInt8" 1862 max = "math.MaxInt8" 1863 case types.Int16: 1864 min = "math.MinInt16" 1865 max = "math.MaxInt16" 1866 case types.Int32: 1867 min = "math.MinInt32" 1868 max = "math.MaxInt32" 1869 case types.Int64: 1870 min = "math.MinInt64" 1871 max = "math.MaxInt64" 1872 case types.Int: 1873 min = "math.MinInt64" 1874 max = "math.MaxInt64" 1875 } 1876 1877 if (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.Y, max) || 1878 (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.X, max) { 1879 report.Report(pass, expr, fmt.Sprintf("no value of type %s is greater than %s", basic, max)) 1880 } 1881 if expr.Op == token.LEQ && isobj(expr.Y, max) || 1882 expr.Op == token.GEQ && isobj(expr.X, max) { 1883 report.Report(pass, expr, fmt.Sprintf("every value of type %s is <= %s", basic, max)) 1884 } 1885 1886 if (basic.Info() & types.IsUnsigned) != 0 { 1887 if (expr.Op == token.LSS && code.IsIntLiteral(expr.Y, "0")) || 1888 (expr.Op == token.GTR && code.IsIntLiteral(expr.X, "0")) { 1889 report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than 0", basic)) 1890 } 1891 if expr.Op == token.GEQ && code.IsIntLiteral(expr.Y, "0") || 1892 expr.Op == token.LEQ && code.IsIntLiteral(expr.X, "0") { 1893 report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= 0", basic)) 1894 } 1895 } else { 1896 if (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.Y, min) || 1897 (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.X, min) { 1898 report.Report(pass, expr, fmt.Sprintf("no value of type %s is less than %s", basic, min)) 1899 } 1900 if expr.Op == token.GEQ && isobj(expr.Y, min) || 1901 expr.Op == token.LEQ && isobj(expr.X, min) { 1902 report.Report(pass, expr, fmt.Sprintf("every value of type %s is >= %s", basic, min)) 1903 } 1904 } 1905 1906 } 1907 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) 1908 return nil, nil 1909} 1910 1911func consts(val ir.Value, out []*ir.Const, visitedPhis map[string]bool) ([]*ir.Const, bool) { 1912 if visitedPhis == nil { 1913 visitedPhis = map[string]bool{} 1914 } 1915 var ok bool 1916 switch val := val.(type) { 1917 case *ir.Phi: 1918 if visitedPhis[val.Name()] { 1919 break 1920 } 1921 visitedPhis[val.Name()] = true 1922 vals := val.Operands(nil) 1923 for _, phival := range vals { 1924 out, ok = consts(*phival, out, visitedPhis) 1925 if !ok { 1926 return nil, false 1927 } 1928 } 1929 case *ir.Const: 1930 out = append(out, val) 1931 case *ir.Convert: 1932 out, ok = consts(val.X, out, visitedPhis) 1933 if !ok { 1934 return nil, false 1935 } 1936 default: 1937 return nil, false 1938 } 1939 if len(out) < 2 { 1940 return out, true 1941 } 1942 uniq := []*ir.Const{out[0]} 1943 for _, val := range out[1:] { 1944 if val.Value == uniq[len(uniq)-1].Value { 1945 continue 1946 } 1947 uniq = append(uniq, val) 1948 } 1949 return uniq, true 1950} 1951 1952func CheckLoopCondition(pass *analysis.Pass) (interface{}, error) { 1953 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 1954 cb := func(node ast.Node) bool { 1955 loop, ok := node.(*ast.ForStmt) 1956 if !ok { 1957 return true 1958 } 1959 if loop.Init == nil || loop.Cond == nil || loop.Post == nil { 1960 return true 1961 } 1962 init, ok := loop.Init.(*ast.AssignStmt) 1963 if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 { 1964 return true 1965 } 1966 cond, ok := loop.Cond.(*ast.BinaryExpr) 1967 if !ok { 1968 return true 1969 } 1970 x, ok := cond.X.(*ast.Ident) 1971 if !ok { 1972 return true 1973 } 1974 lhs, ok := init.Lhs[0].(*ast.Ident) 1975 if !ok { 1976 return true 1977 } 1978 if x.Obj != lhs.Obj { 1979 return true 1980 } 1981 if _, ok := loop.Post.(*ast.IncDecStmt); !ok { 1982 return true 1983 } 1984 1985 v, isAddr := fn.ValueForExpr(cond.X) 1986 if v == nil || isAddr { 1987 return true 1988 } 1989 switch v := v.(type) { 1990 case *ir.Phi: 1991 ops := v.Operands(nil) 1992 if len(ops) != 2 { 1993 return true 1994 } 1995 _, ok := (*ops[0]).(*ir.Const) 1996 if !ok { 1997 return true 1998 } 1999 sigma, ok := (*ops[1]).(*ir.Sigma) 2000 if !ok { 2001 return true 2002 } 2003 if sigma.X != v { 2004 return true 2005 } 2006 case *ir.Load: 2007 return true 2008 } 2009 report.Report(pass, cond, "variable in loop condition never changes") 2010 2011 return true 2012 } 2013 Inspect(fn.Source(), cb) 2014 } 2015 return nil, nil 2016} 2017 2018func CheckArgOverwritten(pass *analysis.Pass) (interface{}, error) { 2019 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2020 cb := func(node ast.Node) bool { 2021 var typ *ast.FuncType 2022 var body *ast.BlockStmt 2023 switch fn := node.(type) { 2024 case *ast.FuncDecl: 2025 typ = fn.Type 2026 body = fn.Body 2027 case *ast.FuncLit: 2028 typ = fn.Type 2029 body = fn.Body 2030 } 2031 if body == nil { 2032 return true 2033 } 2034 if len(typ.Params.List) == 0 { 2035 return true 2036 } 2037 for _, field := range typ.Params.List { 2038 for _, arg := range field.Names { 2039 obj := pass.TypesInfo.ObjectOf(arg) 2040 var irobj *ir.Parameter 2041 for _, param := range fn.Params { 2042 if param.Object() == obj { 2043 irobj = param 2044 break 2045 } 2046 } 2047 if irobj == nil { 2048 continue 2049 } 2050 refs := irobj.Referrers() 2051 if refs == nil { 2052 continue 2053 } 2054 if len(code.FilterDebug(*refs)) != 0 { 2055 continue 2056 } 2057 2058 var assignment ast.Node 2059 ast.Inspect(body, func(node ast.Node) bool { 2060 if assignment != nil { 2061 return false 2062 } 2063 assign, ok := node.(*ast.AssignStmt) 2064 if !ok { 2065 return true 2066 } 2067 for _, lhs := range assign.Lhs { 2068 ident, ok := lhs.(*ast.Ident) 2069 if !ok { 2070 continue 2071 } 2072 if pass.TypesInfo.ObjectOf(ident) == obj { 2073 assignment = assign 2074 return false 2075 } 2076 } 2077 return true 2078 }) 2079 if assignment != nil { 2080 report.Report(pass, arg, fmt.Sprintf("argument %s is overwritten before first use", arg), 2081 report.Related(assignment, fmt.Sprintf("assignment to %s", arg))) 2082 } 2083 } 2084 } 2085 return true 2086 } 2087 Inspect(fn.Source(), cb) 2088 } 2089 return nil, nil 2090} 2091 2092func CheckIneffectiveLoop(pass *analysis.Pass) (interface{}, error) { 2093 // This check detects some, but not all unconditional loop exits. 2094 // We give up in the following cases: 2095 // 2096 // - a goto anywhere in the loop. The goto might skip over our 2097 // return, and we don't check that it doesn't. 2098 // 2099 // - any nested, unlabelled continue, even if it is in another 2100 // loop or closure. 2101 fn := func(node ast.Node) { 2102 var body *ast.BlockStmt 2103 switch fn := node.(type) { 2104 case *ast.FuncDecl: 2105 body = fn.Body 2106 case *ast.FuncLit: 2107 body = fn.Body 2108 default: 2109 ExhaustiveTypeSwitch(node) 2110 } 2111 if body == nil { 2112 return 2113 } 2114 labels := map[*ast.Object]ast.Stmt{} 2115 ast.Inspect(body, func(node ast.Node) bool { 2116 label, ok := node.(*ast.LabeledStmt) 2117 if !ok { 2118 return true 2119 } 2120 labels[label.Label.Obj] = label.Stmt 2121 return true 2122 }) 2123 2124 ast.Inspect(body, func(node ast.Node) bool { 2125 var loop ast.Node 2126 var body *ast.BlockStmt 2127 switch node := node.(type) { 2128 case *ast.ForStmt: 2129 body = node.Body 2130 loop = node 2131 case *ast.RangeStmt: 2132 typ := pass.TypesInfo.TypeOf(node.X) 2133 if _, ok := typ.Underlying().(*types.Map); ok { 2134 // looping once over a map is a valid pattern for 2135 // getting an arbitrary element. 2136 return true 2137 } 2138 body = node.Body 2139 loop = node 2140 default: 2141 return true 2142 } 2143 if len(body.List) < 2 { 2144 // avoid flagging the somewhat common pattern of using 2145 // a range loop to get the first element in a slice, 2146 // or the first rune in a string. 2147 return true 2148 } 2149 var unconditionalExit ast.Node 2150 hasBranching := false 2151 for _, stmt := range body.List { 2152 switch stmt := stmt.(type) { 2153 case *ast.BranchStmt: 2154 switch stmt.Tok { 2155 case token.BREAK: 2156 if stmt.Label == nil || labels[stmt.Label.Obj] == loop { 2157 unconditionalExit = stmt 2158 } 2159 case token.CONTINUE: 2160 if stmt.Label == nil || labels[stmt.Label.Obj] == loop { 2161 unconditionalExit = nil 2162 return false 2163 } 2164 } 2165 case *ast.ReturnStmt: 2166 unconditionalExit = stmt 2167 case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt: 2168 hasBranching = true 2169 } 2170 } 2171 if unconditionalExit == nil || !hasBranching { 2172 return false 2173 } 2174 ast.Inspect(body, func(node ast.Node) bool { 2175 if branch, ok := node.(*ast.BranchStmt); ok { 2176 2177 switch branch.Tok { 2178 case token.GOTO: 2179 unconditionalExit = nil 2180 return false 2181 case token.CONTINUE: 2182 if branch.Label != nil && labels[branch.Label.Obj] != loop { 2183 return true 2184 } 2185 unconditionalExit = nil 2186 return false 2187 } 2188 } 2189 return true 2190 }) 2191 if unconditionalExit != nil { 2192 report.Report(pass, unconditionalExit, "the surrounding loop is unconditionally terminated") 2193 } 2194 return true 2195 }) 2196 } 2197 code.Preorder(pass, fn, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil)) 2198 return nil, nil 2199} 2200 2201var checkNilContextQ = pattern.MustParse(`(CallExpr fun@(Function _) (Builtin "nil"):_)`) 2202 2203func CheckNilContext(pass *analysis.Pass) (interface{}, error) { 2204 todo := &ast.CallExpr{ 2205 Fun: Selector("context", "TODO"), 2206 } 2207 bg := &ast.CallExpr{ 2208 Fun: Selector("context", "Background"), 2209 } 2210 fn := func(node ast.Node) { 2211 m, ok := Match(pass, checkNilContextQ, node) 2212 if !ok { 2213 return 2214 } 2215 2216 call := node.(*ast.CallExpr) 2217 fun, ok := m.State["fun"].(*types.Func) 2218 if !ok { 2219 // it might also be a builtin 2220 return 2221 } 2222 sig := fun.Type().(*types.Signature) 2223 if sig.Params().Len() == 0 { 2224 // Our CallExpr might've matched a method expression, like 2225 // (*T).Foo(nil) – here, nil isn't the first argument of 2226 // the Foo method, but the method receiver. 2227 return 2228 } 2229 if !code.IsType(sig.Params().At(0).Type(), "context.Context") { 2230 return 2231 } 2232 report.Report(pass, call.Args[0], 2233 "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use", report.Fixes( 2234 edit.Fix("use context.TODO", edit.ReplaceWithNode(pass.Fset, call.Args[0], todo)), 2235 edit.Fix("use context.Background", edit.ReplaceWithNode(pass.Fset, call.Args[0], bg)))) 2236 } 2237 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 2238 return nil, nil 2239} 2240 2241var ( 2242 checkSeekerQ = pattern.MustParse(`(CallExpr fun@(SelectorExpr _ (Ident "Seek")) [arg1@(SelectorExpr (Ident "io") (Ident (Or "SeekStart" "SeekCurrent" "SeekEnd"))) arg2])`) 2243 checkSeekerR = pattern.MustParse(`(CallExpr fun [arg2 arg1])`) 2244) 2245 2246func CheckSeeker(pass *analysis.Pass) (interface{}, error) { 2247 fn := func(node ast.Node) { 2248 if _, edits, ok := MatchAndEdit(pass, checkSeekerQ, checkSeekerR, node); ok { 2249 report.Report(pass, node, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead", 2250 report.Fixes(edit.Fix("swap arguments", edits...))) 2251 } 2252 } 2253 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 2254 return nil, nil 2255} 2256 2257func CheckIneffectiveAppend(pass *analysis.Pass) (interface{}, error) { 2258 isAppend := func(ins ir.Value) bool { 2259 call, ok := ins.(*ir.Call) 2260 if !ok { 2261 return false 2262 } 2263 if call.Call.IsInvoke() { 2264 return false 2265 } 2266 if builtin, ok := call.Call.Value.(*ir.Builtin); !ok || builtin.Name() != "append" { 2267 return false 2268 } 2269 return true 2270 } 2271 2272 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2273 for _, block := range fn.Blocks { 2274 for _, ins := range block.Instrs { 2275 val, ok := ins.(ir.Value) 2276 if !ok || !isAppend(val) { 2277 continue 2278 } 2279 2280 isUsed := false 2281 visited := map[ir.Instruction]bool{} 2282 var walkRefs func(refs []ir.Instruction) 2283 walkRefs = func(refs []ir.Instruction) { 2284 loop: 2285 for _, ref := range refs { 2286 if visited[ref] { 2287 continue 2288 } 2289 visited[ref] = true 2290 if _, ok := ref.(*ir.DebugRef); ok { 2291 continue 2292 } 2293 switch ref := ref.(type) { 2294 case *ir.Phi: 2295 walkRefs(*ref.Referrers()) 2296 case *ir.Sigma: 2297 walkRefs(*ref.Referrers()) 2298 case ir.Value: 2299 if !isAppend(ref) { 2300 isUsed = true 2301 } else { 2302 walkRefs(*ref.Referrers()) 2303 } 2304 case ir.Instruction: 2305 isUsed = true 2306 break loop 2307 } 2308 } 2309 } 2310 2311 refs := val.Referrers() 2312 if refs == nil { 2313 continue 2314 } 2315 walkRefs(*refs) 2316 2317 if !isUsed { 2318 report.Report(pass, ins, "this result of append is never used, except maybe in other appends") 2319 } 2320 } 2321 } 2322 } 2323 return nil, nil 2324} 2325 2326func CheckConcurrentTesting(pass *analysis.Pass) (interface{}, error) { 2327 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2328 for _, block := range fn.Blocks { 2329 for _, ins := range block.Instrs { 2330 gostmt, ok := ins.(*ir.Go) 2331 if !ok { 2332 continue 2333 } 2334 var fn *ir.Function 2335 switch val := gostmt.Call.Value.(type) { 2336 case *ir.Function: 2337 fn = val 2338 case *ir.MakeClosure: 2339 fn = val.Fn.(*ir.Function) 2340 default: 2341 continue 2342 } 2343 if fn.Blocks == nil { 2344 continue 2345 } 2346 for _, block := range fn.Blocks { 2347 for _, ins := range block.Instrs { 2348 call, ok := ins.(*ir.Call) 2349 if !ok { 2350 continue 2351 } 2352 if call.Call.IsInvoke() { 2353 continue 2354 } 2355 callee := call.Call.StaticCallee() 2356 if callee == nil { 2357 continue 2358 } 2359 recv := callee.Signature.Recv() 2360 if recv == nil { 2361 continue 2362 } 2363 if !code.IsType(recv.Type(), "*testing.common") { 2364 continue 2365 } 2366 fn, ok := call.Call.StaticCallee().Object().(*types.Func) 2367 if !ok { 2368 continue 2369 } 2370 name := fn.Name() 2371 switch name { 2372 case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf": 2373 default: 2374 continue 2375 } 2376 // TODO(dh): don't report multiple diagnostics 2377 // for multiple calls to T.Fatal, but do 2378 // collect all of them as related information 2379 report.Report(pass, gostmt, fmt.Sprintf("the goroutine calls T.%s, which must be called in the same goroutine as the test", name), 2380 report.Related(call, fmt.Sprintf("call to T.%s", name))) 2381 } 2382 } 2383 } 2384 } 2385 } 2386 return nil, nil 2387} 2388 2389func eachCall(fn *ir.Function, cb func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function)) { 2390 for _, b := range fn.Blocks { 2391 for _, instr := range b.Instrs { 2392 if site, ok := instr.(ir.CallInstruction); ok { 2393 if g := site.Common().StaticCallee(); g != nil { 2394 cb(fn, site, g) 2395 } 2396 } 2397 } 2398 } 2399} 2400 2401func CheckCyclicFinalizer(pass *analysis.Pass) (interface{}, error) { 2402 cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { 2403 if callee.RelString(nil) != "runtime.SetFinalizer" { 2404 return 2405 } 2406 arg0 := site.Common().Args[Arg("runtime.SetFinalizer.obj")] 2407 if iface, ok := arg0.(*ir.MakeInterface); ok { 2408 arg0 = iface.X 2409 } 2410 load, ok := arg0.(*ir.Load) 2411 if !ok { 2412 return 2413 } 2414 v, ok := load.X.(*ir.Alloc) 2415 if !ok { 2416 return 2417 } 2418 arg1 := site.Common().Args[Arg("runtime.SetFinalizer.finalizer")] 2419 if iface, ok := arg1.(*ir.MakeInterface); ok { 2420 arg1 = iface.X 2421 } 2422 mc, ok := arg1.(*ir.MakeClosure) 2423 if !ok { 2424 return 2425 } 2426 for _, b := range mc.Bindings { 2427 if b == v { 2428 pos := lint.DisplayPosition(pass.Fset, mc.Fn.Pos()) 2429 report.Report(pass, site, fmt.Sprintf("the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos)) 2430 } 2431 } 2432 } 2433 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2434 eachCall(fn, cb) 2435 } 2436 return nil, nil 2437} 2438 2439/* 2440func CheckSliceOutOfBounds(pass *analysis.Pass) (interface{}, error) { 2441 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2442 for _, block := range fn.Blocks { 2443 for _, ins := range block.Instrs { 2444 ia, ok := ins.(*ir.IndexAddr) 2445 if !ok { 2446 continue 2447 } 2448 if _, ok := ia.X.Type().Underlying().(*types.Slice); !ok { 2449 continue 2450 } 2451 sr, ok1 := c.funcDescs.Get(fn).Ranges[ia.X].(vrp.SliceInterval) 2452 idxr, ok2 := c.funcDescs.Get(fn).Ranges[ia.Index].(vrp.IntInterval) 2453 if !ok1 || !ok2 || !sr.IsKnown() || !idxr.IsKnown() || sr.Length.Empty() || idxr.Empty() { 2454 continue 2455 } 2456 if idxr.Lower.Cmp(sr.Length.Upper) >= 0 { 2457 report.Nodef(pass, ia, "index out of bounds") 2458 } 2459 } 2460 } 2461 } 2462 return nil, nil 2463} 2464*/ 2465 2466func CheckDeferLock(pass *analysis.Pass) (interface{}, error) { 2467 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2468 for _, block := range fn.Blocks { 2469 instrs := code.FilterDebug(block.Instrs) 2470 if len(instrs) < 2 { 2471 continue 2472 } 2473 for i, ins := range instrs[:len(instrs)-1] { 2474 call, ok := ins.(*ir.Call) 2475 if !ok { 2476 continue 2477 } 2478 if !code.IsCallToAny(call.Common(), "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { 2479 continue 2480 } 2481 nins, ok := instrs[i+1].(*ir.Defer) 2482 if !ok { 2483 continue 2484 } 2485 if !code.IsCallToAny(&nins.Call, "(*sync.Mutex).Lock", "(*sync.RWMutex).RLock") { 2486 continue 2487 } 2488 if call.Common().Args[0] != nins.Call.Args[0] { 2489 continue 2490 } 2491 name := shortCallName(call.Common()) 2492 alt := "" 2493 switch name { 2494 case "Lock": 2495 alt = "Unlock" 2496 case "RLock": 2497 alt = "RUnlock" 2498 } 2499 report.Report(pass, nins, fmt.Sprintf("deferring %s right after having locked already; did you mean to defer %s?", name, alt)) 2500 } 2501 } 2502 } 2503 return nil, nil 2504} 2505 2506func CheckNaNComparison(pass *analysis.Pass) (interface{}, error) { 2507 isNaN := func(v ir.Value) bool { 2508 call, ok := v.(*ir.Call) 2509 if !ok { 2510 return false 2511 } 2512 return code.IsCallTo(call.Common(), "math.NaN") 2513 } 2514 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2515 for _, block := range fn.Blocks { 2516 for _, ins := range block.Instrs { 2517 ins, ok := ins.(*ir.BinOp) 2518 if !ok { 2519 continue 2520 } 2521 if isNaN(ins.X) || isNaN(ins.Y) { 2522 report.Report(pass, ins, "no value is equal to NaN, not even NaN itself") 2523 } 2524 } 2525 } 2526 } 2527 return nil, nil 2528} 2529 2530func CheckInfiniteRecursion(pass *analysis.Pass) (interface{}, error) { 2531 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2532 eachCall(fn, func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { 2533 if callee != fn { 2534 return 2535 } 2536 if _, ok := site.(*ir.Go); ok { 2537 // Recursively spawning goroutines doesn't consume 2538 // stack space infinitely, so don't flag it. 2539 return 2540 } 2541 2542 block := site.Block() 2543 canReturn := false 2544 for _, b := range fn.Blocks { 2545 if block.Dominates(b) { 2546 continue 2547 } 2548 if len(b.Instrs) == 0 { 2549 continue 2550 } 2551 if _, ok := b.Control().(*ir.Return); ok { 2552 canReturn = true 2553 break 2554 } 2555 } 2556 if canReturn { 2557 return 2558 } 2559 report.Report(pass, site, "infinite recursive call") 2560 }) 2561 } 2562 return nil, nil 2563} 2564 2565func objectName(obj types.Object) string { 2566 if obj == nil { 2567 return "<nil>" 2568 } 2569 var name string 2570 if obj.Pkg() != nil && obj.Pkg().Scope().Lookup(obj.Name()) == obj { 2571 s := obj.Pkg().Path() 2572 if s != "" { 2573 name += s + "." 2574 } 2575 } 2576 name += obj.Name() 2577 return name 2578} 2579 2580func isName(pass *analysis.Pass, expr ast.Expr, name string) bool { 2581 var obj types.Object 2582 switch expr := expr.(type) { 2583 case *ast.Ident: 2584 obj = pass.TypesInfo.ObjectOf(expr) 2585 case *ast.SelectorExpr: 2586 obj = pass.TypesInfo.ObjectOf(expr.Sel) 2587 } 2588 return objectName(obj) == name 2589} 2590 2591func CheckLeakyTimeTick(pass *analysis.Pass) (interface{}, error) { 2592 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2593 if code.IsMainLike(pass) || code.IsInTest(pass, fn) { 2594 continue 2595 } 2596 for _, block := range fn.Blocks { 2597 for _, ins := range block.Instrs { 2598 call, ok := ins.(*ir.Call) 2599 if !ok || !code.IsCallTo(call.Common(), "time.Tick") { 2600 continue 2601 } 2602 if !functions.Terminates(call.Parent()) { 2603 continue 2604 } 2605 report.Report(pass, call, "using time.Tick leaks the underlying ticker, consider using it only in endless functions, tests and the main package, and use time.NewTicker here") 2606 } 2607 } 2608 } 2609 return nil, nil 2610} 2611 2612var checkDoubleNegationQ = pattern.MustParse(`(UnaryExpr "!" single@(UnaryExpr "!" x))`) 2613 2614func CheckDoubleNegation(pass *analysis.Pass) (interface{}, error) { 2615 fn := func(node ast.Node) { 2616 if m, ok := Match(pass, checkDoubleNegationQ, node); ok { 2617 report.Report(pass, node, "negating a boolean twice has no effect; is this a typo?", report.Fixes( 2618 edit.Fix("turn into single negation", edit.ReplaceWithNode(pass.Fset, node, m.State["single"].(ast.Node))), 2619 edit.Fix("remove double negation", edit.ReplaceWithNode(pass.Fset, node, m.State["x"].(ast.Node))))) 2620 } 2621 } 2622 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil)) 2623 return nil, nil 2624} 2625 2626func CheckRepeatedIfElse(pass *analysis.Pass) (interface{}, error) { 2627 seen := map[ast.Node]bool{} 2628 2629 var collectConds func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) 2630 collectConds = func(ifstmt *ast.IfStmt, conds []ast.Expr) ([]ast.Expr, bool) { 2631 seen[ifstmt] = true 2632 // Bail if any if-statement has an Init statement or side effects in its condition 2633 if ifstmt.Init != nil { 2634 return nil, false 2635 } 2636 if code.MayHaveSideEffects(pass, ifstmt.Cond, nil) { 2637 return nil, false 2638 } 2639 2640 conds = append(conds, ifstmt.Cond) 2641 if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok { 2642 return collectConds(elsestmt, conds) 2643 } 2644 return conds, true 2645 } 2646 fn := func(node ast.Node) { 2647 ifstmt := node.(*ast.IfStmt) 2648 if seen[ifstmt] { 2649 // this if-statement is part of an if/else-if chain that we've already processed 2650 return 2651 } 2652 if ifstmt.Else == nil { 2653 // there can be at most one condition 2654 return 2655 } 2656 conds, ok := collectConds(ifstmt, nil) 2657 if !ok { 2658 return 2659 } 2660 if len(conds) < 2 { 2661 return 2662 } 2663 counts := map[string]int{} 2664 for _, cond := range conds { 2665 s := report.Render(pass, cond) 2666 counts[s]++ 2667 if counts[s] == 2 { 2668 report.Report(pass, cond, "this condition occurs multiple times in this if/else if chain") 2669 } 2670 } 2671 } 2672 code.Preorder(pass, fn, (*ast.IfStmt)(nil)) 2673 return nil, nil 2674} 2675 2676func CheckSillyBitwiseOps(pass *analysis.Pass) (interface{}, error) { 2677 // FIXME(dh): what happened here? 2678 if false { 2679 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2680 for _, block := range fn.Blocks { 2681 for _, ins := range block.Instrs { 2682 ins, ok := ins.(*ir.BinOp) 2683 if !ok { 2684 continue 2685 } 2686 2687 if c, ok := ins.Y.(*ir.Const); !ok || c.Value == nil || c.Value.Kind() != constant.Int || c.Uint64() != 0 { 2688 continue 2689 } 2690 switch ins.Op { 2691 case token.AND, token.OR, token.XOR: 2692 default: 2693 // we do not flag shifts because too often, x<<0 is part 2694 // of a pattern, x<<0, x<<8, x<<16, ... 2695 continue 2696 } 2697 path, _ := astutil.PathEnclosingInterval(code.File(pass, ins), ins.Pos(), ins.Pos()) 2698 if len(path) == 0 { 2699 continue 2700 } 2701 2702 if node, ok := path[0].(*ast.BinaryExpr); !ok || !code.IsIntLiteral(node.Y, "0") { 2703 continue 2704 } 2705 2706 switch ins.Op { 2707 case token.AND: 2708 report.Report(pass, ins, "x & 0 always equals 0") 2709 case token.OR, token.XOR: 2710 report.Report(pass, ins, fmt.Sprintf("x %s 0 always equals x", ins.Op)) 2711 } 2712 } 2713 } 2714 } 2715 } 2716 fn := func(node ast.Node) { 2717 binop := node.(*ast.BinaryExpr) 2718 b, ok := pass.TypesInfo.TypeOf(binop).Underlying().(*types.Basic) 2719 if !ok { 2720 return 2721 } 2722 if (b.Info() & types.IsInteger) == 0 { 2723 return 2724 } 2725 switch binop.Op { 2726 case token.AND, token.OR, token.XOR: 2727 default: 2728 // we do not flag shifts because too often, x<<0 is part 2729 // of a pattern, x<<0, x<<8, x<<16, ... 2730 return 2731 } 2732 switch y := binop.Y.(type) { 2733 case *ast.Ident: 2734 obj, ok := pass.TypesInfo.ObjectOf(y).(*types.Const) 2735 if !ok { 2736 return 2737 } 2738 if obj.Pkg() != pass.Pkg { 2739 // identifier was dot-imported 2740 return 2741 } 2742 if v, _ := constant.Int64Val(obj.Val()); v != 0 { 2743 return 2744 } 2745 path, _ := astutil.PathEnclosingInterval(code.File(pass, obj), obj.Pos(), obj.Pos()) 2746 if len(path) < 2 { 2747 return 2748 } 2749 spec, ok := path[1].(*ast.ValueSpec) 2750 if !ok { 2751 return 2752 } 2753 if len(spec.Names) != 1 || len(spec.Values) != 1 { 2754 // TODO(dh): we could support this 2755 return 2756 } 2757 ident, ok := spec.Values[0].(*ast.Ident) 2758 if !ok { 2759 return 2760 } 2761 if !isIota(pass.TypesInfo.ObjectOf(ident)) { 2762 return 2763 } 2764 switch binop.Op { 2765 case token.AND: 2766 report.Report(pass, node, 2767 fmt.Sprintf("%s always equals 0; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.Y), report.Render(pass, binop.Y))) 2768 case token.OR, token.XOR: 2769 report.Report(pass, node, 2770 fmt.Sprintf("%s always equals %s; %s is defined as iota and has value 0, maybe %s is meant to be 1 << iota?", report.Render(pass, binop), report.Render(pass, binop.X), report.Render(pass, binop.Y), report.Render(pass, binop.Y))) 2771 } 2772 case *ast.BasicLit: 2773 if !code.IsIntLiteral(binop.Y, "0") { 2774 return 2775 } 2776 switch binop.Op { 2777 case token.AND: 2778 report.Report(pass, node, fmt.Sprintf("%s always equals 0", report.Render(pass, binop))) 2779 case token.OR, token.XOR: 2780 report.Report(pass, node, fmt.Sprintf("%s always equals %s", report.Render(pass, binop), report.Render(pass, binop.X))) 2781 } 2782 default: 2783 return 2784 } 2785 } 2786 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) 2787 return nil, nil 2788} 2789 2790func isIota(obj types.Object) bool { 2791 if obj.Name() != "iota" { 2792 return false 2793 } 2794 c, ok := obj.(*types.Const) 2795 if !ok { 2796 return false 2797 } 2798 return c.Pkg() == nil 2799} 2800 2801func CheckNonOctalFileMode(pass *analysis.Pass) (interface{}, error) { 2802 fn := func(node ast.Node) { 2803 call := node.(*ast.CallExpr) 2804 sig, ok := pass.TypesInfo.TypeOf(call.Fun).(*types.Signature) 2805 if !ok { 2806 return 2807 } 2808 n := sig.Params().Len() 2809 for i := 0; i < n; i++ { 2810 typ := sig.Params().At(i).Type() 2811 if !code.IsType(typ, "os.FileMode") { 2812 continue 2813 } 2814 2815 lit, ok := call.Args[i].(*ast.BasicLit) 2816 if !ok { 2817 continue 2818 } 2819 if len(lit.Value) == 3 && 2820 lit.Value[0] != '0' && 2821 lit.Value[0] >= '0' && lit.Value[0] <= '7' && 2822 lit.Value[1] >= '0' && lit.Value[1] <= '7' && 2823 lit.Value[2] >= '0' && lit.Value[2] <= '7' { 2824 2825 v, err := strconv.ParseInt(lit.Value, 10, 64) 2826 if err != nil { 2827 continue 2828 } 2829 report.Report(pass, call.Args[i], fmt.Sprintf("file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value), 2830 report.Fixes(edit.Fix("fix octal literal", edit.ReplaceWithString(pass.Fset, call.Args[i], "0"+lit.Value)))) 2831 } 2832 } 2833 } 2834 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 2835 return nil, nil 2836} 2837 2838func CheckPureFunctions(pass *analysis.Pass) (interface{}, error) { 2839 pure := pass.ResultOf[facts.Purity].(facts.PurityResult) 2840 2841fnLoop: 2842 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 2843 if code.IsInTest(pass, fn) { 2844 params := fn.Signature.Params() 2845 for i := 0; i < params.Len(); i++ { 2846 param := params.At(i) 2847 if code.IsType(param.Type(), "*testing.B") { 2848 // Ignore discarded pure functions in code related 2849 // to benchmarks. Instead of matching BenchmarkFoo 2850 // functions, we match any function accepting a 2851 // *testing.B. Benchmarks sometimes call generic 2852 // functions for doing the actual work, and 2853 // checking for the parameter is a lot easier and 2854 // faster than analyzing call trees. 2855 continue fnLoop 2856 } 2857 } 2858 } 2859 2860 for _, b := range fn.Blocks { 2861 for _, ins := range b.Instrs { 2862 ins, ok := ins.(*ir.Call) 2863 if !ok { 2864 continue 2865 } 2866 refs := ins.Referrers() 2867 if refs == nil || len(code.FilterDebug(*refs)) > 0 { 2868 continue 2869 } 2870 2871 callee := ins.Common().StaticCallee() 2872 if callee == nil { 2873 continue 2874 } 2875 if callee.Object() == nil { 2876 // TODO(dh): support anonymous functions 2877 continue 2878 } 2879 if _, ok := pure[callee.Object().(*types.Func)]; ok { 2880 if pass.Pkg.Path() == "fmt_test" && callee.Object().(*types.Func).FullName() == "fmt.Sprintf" { 2881 // special case for benchmarks in the fmt package 2882 continue 2883 } 2884 report.Report(pass, ins, fmt.Sprintf("%s is a pure function but its return value is ignored", callee.Name())) 2885 } 2886 } 2887 } 2888 } 2889 return nil, nil 2890} 2891 2892func CheckDeprecated(pass *analysis.Pass) (interface{}, error) { 2893 deprs := pass.ResultOf[facts.Deprecated].(facts.DeprecatedResult) 2894 2895 // Selectors can appear outside of function literals, e.g. when 2896 // declaring package level variables. 2897 2898 var tfn types.Object 2899 stack := 0 2900 fn := func(node ast.Node, push bool) bool { 2901 if !push { 2902 stack-- 2903 return false 2904 } 2905 stack++ 2906 if stack == 1 { 2907 tfn = nil 2908 } 2909 if fn, ok := node.(*ast.FuncDecl); ok { 2910 tfn = pass.TypesInfo.ObjectOf(fn.Name) 2911 } 2912 sel, ok := node.(*ast.SelectorExpr) 2913 if !ok { 2914 return true 2915 } 2916 2917 obj := pass.TypesInfo.ObjectOf(sel.Sel) 2918 if obj.Pkg() == nil { 2919 return true 2920 } 2921 if pass.Pkg == obj.Pkg() || obj.Pkg().Path()+"_test" == pass.Pkg.Path() { 2922 // Don't flag stuff in our own package 2923 return true 2924 } 2925 if depr, ok := deprs.Objects[obj]; ok { 2926 // Look for the first available alternative, not the first 2927 // version something was deprecated in. If a function was 2928 // deprecated in Go 1.6, an alternative has been available 2929 // already in 1.0, and we're targeting 1.2, it still 2930 // makes sense to use the alternative from 1.0, to be 2931 // future-proof. 2932 minVersion := deprecated.Stdlib[code.SelectorName(pass, sel)].AlternativeAvailableSince 2933 if !code.IsGoVersion(pass, minVersion) { 2934 return true 2935 } 2936 2937 if tfn != nil { 2938 if _, ok := deprs.Objects[tfn]; ok { 2939 // functions that are deprecated may use deprecated 2940 // symbols 2941 return true 2942 } 2943 } 2944 report.Report(pass, sel, fmt.Sprintf("%s is deprecated: %s", report.Render(pass, sel), depr.Msg)) 2945 return true 2946 } 2947 return true 2948 } 2949 2950 fn2 := func(node ast.Node) { 2951 spec := node.(*ast.ImportSpec) 2952 var imp *types.Package 2953 if spec.Name != nil { 2954 imp = pass.TypesInfo.ObjectOf(spec.Name).(*types.PkgName).Imported() 2955 } else { 2956 imp = pass.TypesInfo.Implicits[spec].(*types.PkgName).Imported() 2957 } 2958 2959 p := spec.Path.Value 2960 path := p[1 : len(p)-1] 2961 if depr, ok := deprs.Packages[imp]; ok { 2962 if path == "github.com/golang/protobuf/proto" { 2963 gen, ok := code.Generator(pass, spec.Path.Pos()) 2964 if ok && gen == facts.ProtocGenGo { 2965 return 2966 } 2967 } 2968 report.Report(pass, spec, fmt.Sprintf("package %s is deprecated: %s", path, depr.Msg)) 2969 } 2970 } 2971 pass.ResultOf[inspect.Analyzer].(*inspector.Inspector).Nodes(nil, fn) 2972 code.Preorder(pass, fn2, (*ast.ImportSpec)(nil)) 2973 return nil, nil 2974} 2975 2976func callChecker(rules map[string]CallCheck) func(pass *analysis.Pass) (interface{}, error) { 2977 return func(pass *analysis.Pass) (interface{}, error) { 2978 return checkCalls(pass, rules) 2979 } 2980} 2981 2982func checkCalls(pass *analysis.Pass, rules map[string]CallCheck) (interface{}, error) { 2983 cb := func(caller *ir.Function, site ir.CallInstruction, callee *ir.Function) { 2984 obj, ok := callee.Object().(*types.Func) 2985 if !ok { 2986 return 2987 } 2988 2989 r, ok := rules[lint.FuncName(obj)] 2990 if !ok { 2991 return 2992 } 2993 var args []*Argument 2994 irargs := site.Common().Args 2995 if callee.Signature.Recv() != nil { 2996 irargs = irargs[1:] 2997 } 2998 for _, arg := range irargs { 2999 if iarg, ok := arg.(*ir.MakeInterface); ok { 3000 arg = iarg.X 3001 } 3002 args = append(args, &Argument{Value: Value{arg}}) 3003 } 3004 call := &Call{ 3005 Pass: pass, 3006 Instr: site, 3007 Args: args, 3008 Parent: site.Parent(), 3009 } 3010 r(call) 3011 path, _ := astutil.PathEnclosingInterval(code.File(pass, site), site.Pos(), site.Pos()) 3012 var astcall *ast.CallExpr 3013 for _, el := range path { 3014 if expr, ok := el.(*ast.CallExpr); ok { 3015 astcall = expr 3016 break 3017 } 3018 } 3019 for idx, arg := range call.Args { 3020 for _, e := range arg.invalids { 3021 if astcall != nil { 3022 report.Report(pass, astcall.Args[idx], e) 3023 } else { 3024 report.Report(pass, site, e) 3025 } 3026 } 3027 } 3028 for _, e := range call.invalids { 3029 report.Report(pass, call.Instr, e) 3030 } 3031 } 3032 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3033 eachCall(fn, cb) 3034 } 3035 return nil, nil 3036} 3037 3038func shortCallName(call *ir.CallCommon) string { 3039 if call.IsInvoke() { 3040 return "" 3041 } 3042 switch v := call.Value.(type) { 3043 case *ir.Function: 3044 fn, ok := v.Object().(*types.Func) 3045 if !ok { 3046 return "" 3047 } 3048 return fn.Name() 3049 case *ir.Builtin: 3050 return v.Name() 3051 } 3052 return "" 3053} 3054 3055func CheckWriterBufferModified(pass *analysis.Pass) (interface{}, error) { 3056 // TODO(dh): this might be a good candidate for taint analysis. 3057 // Taint the argument as MUST_NOT_MODIFY, then propagate that 3058 // through functions like bytes.Split 3059 3060 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3061 sig := fn.Signature 3062 if fn.Name() != "Write" || sig.Recv() == nil || sig.Params().Len() != 1 || sig.Results().Len() != 2 { 3063 continue 3064 } 3065 tArg, ok := sig.Params().At(0).Type().(*types.Slice) 3066 if !ok { 3067 continue 3068 } 3069 if basic, ok := tArg.Elem().(*types.Basic); !ok || basic.Kind() != types.Byte { 3070 continue 3071 } 3072 if basic, ok := sig.Results().At(0).Type().(*types.Basic); !ok || basic.Kind() != types.Int { 3073 continue 3074 } 3075 if named, ok := sig.Results().At(1).Type().(*types.Named); !ok || !code.IsType(named, "error") { 3076 continue 3077 } 3078 3079 for _, block := range fn.Blocks { 3080 for _, ins := range block.Instrs { 3081 switch ins := ins.(type) { 3082 case *ir.Store: 3083 addr, ok := ins.Addr.(*ir.IndexAddr) 3084 if !ok { 3085 continue 3086 } 3087 if addr.X != fn.Params[1] { 3088 continue 3089 } 3090 report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") 3091 case *ir.Call: 3092 if !code.IsCallTo(ins.Common(), "append") { 3093 continue 3094 } 3095 if ins.Common().Args[0] != fn.Params[1] { 3096 continue 3097 } 3098 report.Report(pass, ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") 3099 } 3100 } 3101 } 3102 } 3103 return nil, nil 3104} 3105 3106func loopedRegexp(name string) CallCheck { 3107 return func(call *Call) { 3108 if len(extractConsts(call.Args[0].Value.Value)) == 0 { 3109 return 3110 } 3111 if !isInLoop(call.Instr.Block()) { 3112 return 3113 } 3114 call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name)) 3115 } 3116} 3117 3118func CheckEmptyBranch(pass *analysis.Pass) (interface{}, error) { 3119 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3120 if fn.Source() == nil { 3121 continue 3122 } 3123 if code.IsExample(fn) { 3124 continue 3125 } 3126 cb := func(node ast.Node) bool { 3127 ifstmt, ok := node.(*ast.IfStmt) 3128 if !ok { 3129 return true 3130 } 3131 if ifstmt.Else != nil { 3132 b, ok := ifstmt.Else.(*ast.BlockStmt) 3133 if !ok || len(b.List) != 0 { 3134 return true 3135 } 3136 report.Report(pass, ifstmt.Else, "empty branch", report.FilterGenerated(), report.ShortRange()) 3137 } 3138 if len(ifstmt.Body.List) != 0 { 3139 return true 3140 } 3141 report.Report(pass, ifstmt, "empty branch", report.FilterGenerated(), report.ShortRange()) 3142 return true 3143 } 3144 Inspect(fn.Source(), cb) 3145 } 3146 return nil, nil 3147} 3148 3149func CheckMapBytesKey(pass *analysis.Pass) (interface{}, error) { 3150 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3151 for _, b := range fn.Blocks { 3152 insLoop: 3153 for _, ins := range b.Instrs { 3154 // find []byte -> string conversions 3155 conv, ok := ins.(*ir.Convert) 3156 if !ok || conv.Type() != types.Universe.Lookup("string").Type() { 3157 continue 3158 } 3159 if s, ok := conv.X.Type().(*types.Slice); !ok || s.Elem() != types.Universe.Lookup("byte").Type() { 3160 continue 3161 } 3162 refs := conv.Referrers() 3163 // need at least two (DebugRef) references: the 3164 // conversion and the *ast.Ident 3165 if refs == nil || len(*refs) < 2 { 3166 continue 3167 } 3168 ident := false 3169 // skip first reference, that's the conversion itself 3170 for _, ref := range (*refs)[1:] { 3171 switch ref := ref.(type) { 3172 case *ir.DebugRef: 3173 if _, ok := ref.Expr.(*ast.Ident); !ok { 3174 // the string seems to be used somewhere 3175 // unexpected; the default branch should 3176 // catch this already, but be safe 3177 continue insLoop 3178 } else { 3179 ident = true 3180 } 3181 case *ir.MapLookup: 3182 default: 3183 // the string is used somewhere else than a 3184 // map lookup 3185 continue insLoop 3186 } 3187 } 3188 3189 // the result of the conversion wasn't assigned to an 3190 // identifier 3191 if !ident { 3192 continue 3193 } 3194 report.Report(pass, conv, "m[string(key)] would be more efficient than k := string(key); m[k]") 3195 } 3196 } 3197 } 3198 return nil, nil 3199} 3200 3201func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) { 3202 return sharedcheck.CheckRangeStringRunes(pass) 3203} 3204 3205func CheckSelfAssignment(pass *analysis.Pass) (interface{}, error) { 3206 pure := pass.ResultOf[facts.Purity].(facts.PurityResult) 3207 3208 fn := func(node ast.Node) { 3209 assign := node.(*ast.AssignStmt) 3210 if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) { 3211 return 3212 } 3213 for i, lhs := range assign.Lhs { 3214 rhs := assign.Rhs[i] 3215 if reflect.TypeOf(lhs) != reflect.TypeOf(rhs) { 3216 continue 3217 } 3218 if code.MayHaveSideEffects(pass, lhs, pure) || code.MayHaveSideEffects(pass, rhs, pure) { 3219 continue 3220 } 3221 3222 rlh := report.Render(pass, lhs) 3223 rrh := report.Render(pass, rhs) 3224 if rlh == rrh { 3225 report.Report(pass, assign, fmt.Sprintf("self-assignment of %s to %s", rrh, rlh), report.FilterGenerated()) 3226 } 3227 } 3228 } 3229 code.Preorder(pass, fn, (*ast.AssignStmt)(nil)) 3230 return nil, nil 3231} 3232 3233func buildTagsIdentical(s1, s2 []string) bool { 3234 if len(s1) != len(s2) { 3235 return false 3236 } 3237 s1s := make([]string, len(s1)) 3238 copy(s1s, s1) 3239 sort.Strings(s1s) 3240 s2s := make([]string, len(s2)) 3241 copy(s2s, s2) 3242 sort.Strings(s2s) 3243 for i, s := range s1s { 3244 if s != s2s[i] { 3245 return false 3246 } 3247 } 3248 return true 3249} 3250 3251func CheckDuplicateBuildConstraints(pass *analysis.Pass) (interface{}, error) { 3252 for _, f := range pass.Files { 3253 constraints := buildTags(f) 3254 for i, constraint1 := range constraints { 3255 for j, constraint2 := range constraints { 3256 if i >= j { 3257 continue 3258 } 3259 if buildTagsIdentical(constraint1, constraint2) { 3260 msg := fmt.Sprintf("identical build constraints %q and %q", 3261 strings.Join(constraint1, " "), 3262 strings.Join(constraint2, " ")) 3263 report.Report(pass, f, msg, report.FilterGenerated(), report.ShortRange()) 3264 } 3265 } 3266 } 3267 } 3268 return nil, nil 3269} 3270 3271func CheckSillyRegexp(pass *analysis.Pass) (interface{}, error) { 3272 // We could use the rule checking engine for this, but the 3273 // arguments aren't really invalid. 3274 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3275 for _, b := range fn.Blocks { 3276 for _, ins := range b.Instrs { 3277 call, ok := ins.(*ir.Call) 3278 if !ok { 3279 continue 3280 } 3281 if !code.IsCallToAny(call.Common(), "regexp.MustCompile", "regexp.Compile", "regexp.Match", "regexp.MatchReader", "regexp.MatchString") { 3282 continue 3283 } 3284 c, ok := call.Common().Args[0].(*ir.Const) 3285 if !ok { 3286 continue 3287 } 3288 s := constant.StringVal(c.Value) 3289 re, err := syntax.Parse(s, 0) 3290 if err != nil { 3291 continue 3292 } 3293 if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch { 3294 continue 3295 } 3296 report.Report(pass, call, "regular expression does not contain any meta characters") 3297 } 3298 } 3299 } 3300 return nil, nil 3301} 3302 3303func CheckMissingEnumTypesInDeclaration(pass *analysis.Pass) (interface{}, error) { 3304 fn := func(node ast.Node) { 3305 decl := node.(*ast.GenDecl) 3306 if !decl.Lparen.IsValid() { 3307 return 3308 } 3309 if decl.Tok != token.CONST { 3310 return 3311 } 3312 3313 groups := code.GroupSpecs(pass.Fset, decl.Specs) 3314 groupLoop: 3315 for _, group := range groups { 3316 if len(group) < 2 { 3317 continue 3318 } 3319 if group[0].(*ast.ValueSpec).Type == nil { 3320 // first constant doesn't have a type 3321 continue groupLoop 3322 } 3323 for i, spec := range group { 3324 spec := spec.(*ast.ValueSpec) 3325 if len(spec.Names) != 1 || len(spec.Values) != 1 { 3326 continue groupLoop 3327 } 3328 switch v := spec.Values[0].(type) { 3329 case *ast.BasicLit: 3330 case *ast.UnaryExpr: 3331 if _, ok := v.X.(*ast.BasicLit); !ok { 3332 continue groupLoop 3333 } 3334 default: 3335 // if it's not a literal it might be typed, such as 3336 // time.Microsecond = 1000 * Nanosecond 3337 continue groupLoop 3338 } 3339 if i == 0 { 3340 continue 3341 } 3342 if spec.Type != nil { 3343 continue groupLoop 3344 } 3345 } 3346 var edits []analysis.TextEdit 3347 typ := group[0].(*ast.ValueSpec).Type 3348 for _, spec := range group[1:] { 3349 nspec := *spec.(*ast.ValueSpec) 3350 nspec.Type = typ 3351 edits = append(edits, edit.ReplaceWithNode(pass.Fset, spec, &nspec)) 3352 } 3353 report.Report(pass, group[0], "only the first constant in this group has an explicit type", report.Fixes(edit.Fix("add type to all constants in group", edits...))) 3354 } 3355 } 3356 code.Preorder(pass, fn, (*ast.GenDecl)(nil)) 3357 return nil, nil 3358} 3359 3360func CheckTimerResetReturnValue(pass *analysis.Pass) (interface{}, error) { 3361 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3362 for _, block := range fn.Blocks { 3363 for _, ins := range block.Instrs { 3364 call, ok := ins.(*ir.Call) 3365 if !ok { 3366 continue 3367 } 3368 if !code.IsCallTo(call.Common(), "(*time.Timer).Reset") { 3369 continue 3370 } 3371 refs := call.Referrers() 3372 if refs == nil { 3373 continue 3374 } 3375 for _, ref := range code.FilterDebug(*refs) { 3376 ifstmt, ok := ref.(*ir.If) 3377 if !ok { 3378 continue 3379 } 3380 3381 found := false 3382 for _, succ := range ifstmt.Block().Succs { 3383 if len(succ.Preds) != 1 { 3384 // Merge point, not a branch in the 3385 // syntactical sense. 3386 3387 // FIXME(dh): this is broken for if 3388 // statements a la "if x || y" 3389 continue 3390 } 3391 irutil.Walk(succ, func(b *ir.BasicBlock) bool { 3392 if !succ.Dominates(b) { 3393 // We've reached the end of the branch 3394 return false 3395 } 3396 for _, ins := range b.Instrs { 3397 // TODO(dh): we should check that 3398 // we're receiving from the channel of 3399 // a time.Timer to further reduce 3400 // false positives. Not a key 3401 // priority, considering the rarity of 3402 // Reset and the tiny likeliness of a 3403 // false positive 3404 if ins, ok := ins.(*ir.Recv); ok && code.IsType(ins.Chan.Type(), "<-chan time.Time") { 3405 found = true 3406 return false 3407 } 3408 } 3409 return true 3410 }) 3411 } 3412 3413 if found { 3414 report.Report(pass, call, "it is not possible to use Reset's return value correctly, as there is a race condition between draining the channel and the new timer expiring") 3415 } 3416 } 3417 } 3418 } 3419 } 3420 return nil, nil 3421} 3422 3423var ( 3424 checkToLowerToUpperComparisonQ = pattern.MustParse(` 3425 (BinaryExpr 3426 (CallExpr fun@(Function (Or "strings.ToLower" "strings.ToUpper")) [a]) 3427 tok@(Or "==" "!=") 3428 (CallExpr fun [b]))`) 3429 checkToLowerToUpperComparisonR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "strings") (Ident "EqualFold")) [a b])`) 3430) 3431 3432func CheckToLowerToUpperComparison(pass *analysis.Pass) (interface{}, error) { 3433 fn := func(node ast.Node) { 3434 m, ok := Match(pass, checkToLowerToUpperComparisonQ, node) 3435 if !ok { 3436 return 3437 } 3438 rn := pattern.NodeToAST(checkToLowerToUpperComparisonR.Root, m.State).(ast.Expr) 3439 if m.State["tok"].(token.Token) == token.NEQ { 3440 rn = &ast.UnaryExpr{ 3441 Op: token.NOT, 3442 X: rn, 3443 } 3444 } 3445 3446 report.Report(pass, node, "should use strings.EqualFold instead", report.Fixes(edit.Fix("replace with strings.EqualFold", edit.ReplaceWithNode(pass.Fset, node, rn)))) 3447 } 3448 3449 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) 3450 return nil, nil 3451} 3452 3453func CheckUnreachableTypeCases(pass *analysis.Pass) (interface{}, error) { 3454 // Check if T subsumes V in a type switch. T subsumes V if T is an interface and T's method set is a subset of V's method set. 3455 subsumes := func(T, V types.Type) bool { 3456 tIface, ok := T.Underlying().(*types.Interface) 3457 if !ok { 3458 return false 3459 } 3460 3461 return types.Implements(V, tIface) 3462 } 3463 3464 subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) { 3465 for _, T := range Ts { 3466 for _, V := range Vs { 3467 if subsumes(T, V) { 3468 return T, V, true 3469 } 3470 } 3471 } 3472 3473 return nil, nil, false 3474 } 3475 3476 fn := func(node ast.Node) { 3477 tsStmt := node.(*ast.TypeSwitchStmt) 3478 3479 type ccAndTypes struct { 3480 cc *ast.CaseClause 3481 types []types.Type 3482 } 3483 3484 // All asserted types in the order of case clauses. 3485 ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List)) 3486 for _, stmt := range tsStmt.Body.List { 3487 cc, _ := stmt.(*ast.CaseClause) 3488 3489 // Exclude the 'default' case. 3490 if len(cc.List) == 0 { 3491 continue 3492 } 3493 3494 Ts := make([]types.Type, len(cc.List)) 3495 for i, expr := range cc.List { 3496 Ts[i] = pass.TypesInfo.TypeOf(expr) 3497 } 3498 3499 ccs = append(ccs, ccAndTypes{cc: cc, types: Ts}) 3500 } 3501 3502 if len(ccs) <= 1 { 3503 // Zero or one case clauses, nothing to check. 3504 return 3505 } 3506 3507 // Check if case clauses following cc have types that are subsumed by cc. 3508 for i, cc := range ccs[:len(ccs)-1] { 3509 for _, next := range ccs[i+1:] { 3510 if T, V, yes := subsumesAny(cc.types, next.types); yes { 3511 report.Report(pass, next.cc, fmt.Sprintf("unreachable case clause: %s will always match before %s", T.String(), V.String()), 3512 report.ShortRange()) 3513 } 3514 } 3515 } 3516 } 3517 3518 code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil)) 3519 return nil, nil 3520} 3521 3522var checkSingleArgAppendQ = pattern.MustParse(`(CallExpr (Builtin "append") [_])`) 3523 3524func CheckSingleArgAppend(pass *analysis.Pass) (interface{}, error) { 3525 fn := func(node ast.Node) { 3526 _, ok := Match(pass, checkSingleArgAppendQ, node) 3527 if !ok { 3528 return 3529 } 3530 report.Report(pass, node, "x = append(y) is equivalent to x = y", report.FilterGenerated()) 3531 } 3532 code.Preorder(pass, fn, (*ast.CallExpr)(nil)) 3533 return nil, nil 3534} 3535 3536func CheckStructTags(pass *analysis.Pass) (interface{}, error) { 3537 importsGoFlags := false 3538 3539 // we use the AST instead of (*types.Package).Imports to work 3540 // around vendored packages in GOPATH mode. A vendored package's 3541 // path will include the vendoring subtree as a prefix. 3542 for _, f := range pass.Files { 3543 for _, imp := range f.Imports { 3544 v := imp.Path.Value 3545 if v[1:len(v)-1] == "github.com/jessevdk/go-flags" { 3546 importsGoFlags = true 3547 break 3548 } 3549 } 3550 } 3551 3552 fn := func(node ast.Node) { 3553 for _, field := range node.(*ast.StructType).Fields.List { 3554 if field.Tag == nil { 3555 continue 3556 } 3557 tags, err := parseStructTag(field.Tag.Value[1 : len(field.Tag.Value)-1]) 3558 if err != nil { 3559 report.Report(pass, field.Tag, fmt.Sprintf("unparseable struct tag: %s", err)) 3560 continue 3561 } 3562 for k, v := range tags { 3563 if len(v) > 1 { 3564 isGoFlagsTag := importsGoFlags && 3565 (k == "choice" || k == "optional-value" || k == "default") 3566 if !isGoFlagsTag { 3567 report.Report(pass, field.Tag, fmt.Sprintf("duplicate struct tag %q", k)) 3568 } 3569 } 3570 3571 switch k { 3572 case "json": 3573 checkJSONTag(pass, field, v[0]) 3574 case "xml": 3575 checkXMLTag(pass, field, v[0]) 3576 } 3577 } 3578 } 3579 } 3580 code.Preorder(pass, fn, (*ast.StructType)(nil)) 3581 return nil, nil 3582} 3583 3584func checkJSONTag(pass *analysis.Pass, field *ast.Field, tag string) { 3585 if pass.Pkg.Path() == "encoding/json" || pass.Pkg.Path() == "encoding/json_test" { 3586 // don't flag malformed JSON tags in the encoding/json 3587 // package; it knows what it is doing, and it is testing 3588 // itself. 3589 return 3590 } 3591 //lint:ignore SA9003 TODO(dh): should we flag empty tags? 3592 if len(tag) == 0 { 3593 } 3594 fields := strings.Split(tag, ",") 3595 for _, r := range fields[0] { 3596 if !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune("!#$%&()*+-./:<=>?@[]^_{|}~ ", r) { 3597 report.Report(pass, field.Tag, fmt.Sprintf("invalid JSON field name %q", fields[0])) 3598 } 3599 } 3600 var co, cs, ci int 3601 for _, s := range fields[1:] { 3602 switch s { 3603 case "omitempty": 3604 co++ 3605 case "": 3606 // allow stuff like "-," 3607 case "string": 3608 cs++ 3609 // only for string, floating point, integer and bool 3610 T := code.Dereference(pass.TypesInfo.TypeOf(field.Type).Underlying()).Underlying() 3611 basic, ok := T.(*types.Basic) 3612 if !ok || (basic.Info()&(types.IsBoolean|types.IsInteger|types.IsFloat|types.IsString)) == 0 { 3613 report.Report(pass, field.Tag, "the JSON string option only applies to fields of type string, floating point, integer or bool, or pointers to those") 3614 } 3615 case "inline": 3616 ci++ 3617 default: 3618 report.Report(pass, field.Tag, fmt.Sprintf("unknown JSON option %q", s)) 3619 } 3620 } 3621 if co > 1 { 3622 report.Report(pass, field.Tag, `duplicate JSON option "omitempty"`) 3623 } 3624 if cs > 1 { 3625 report.Report(pass, field.Tag, `duplicate JSON option "string"`) 3626 } 3627 if ci > 1 { 3628 report.Report(pass, field.Tag, `duplicate JSON option "inline"`) 3629 } 3630} 3631 3632func checkXMLTag(pass *analysis.Pass, field *ast.Field, tag string) { 3633 //lint:ignore SA9003 TODO(dh): should we flag empty tags? 3634 if len(tag) == 0 { 3635 } 3636 fields := strings.Split(tag, ",") 3637 counts := map[string]int{} 3638 var exclusives []string 3639 for _, s := range fields[1:] { 3640 switch s { 3641 case "attr", "chardata", "cdata", "innerxml", "comment": 3642 counts[s]++ 3643 if counts[s] == 1 { 3644 exclusives = append(exclusives, s) 3645 } 3646 case "omitempty", "any": 3647 counts[s]++ 3648 case "": 3649 default: 3650 report.Report(pass, field.Tag, fmt.Sprintf("unknown XML option %q", s)) 3651 } 3652 } 3653 for k, v := range counts { 3654 if v > 1 { 3655 report.Report(pass, field.Tag, fmt.Sprintf("duplicate XML option %q", k)) 3656 } 3657 } 3658 if len(exclusives) > 1 { 3659 report.Report(pass, field.Tag, fmt.Sprintf("XML options %s are mutually exclusive", strings.Join(exclusives, " and "))) 3660 } 3661} 3662 3663func CheckImpossibleTypeAssertion(pass *analysis.Pass) (interface{}, error) { 3664 type entry struct { 3665 l, r *types.Func 3666 } 3667 3668 msc := &pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg.Prog.MethodSets 3669 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3670 for _, b := range fn.Blocks { 3671 for _, instr := range b.Instrs { 3672 assert, ok := instr.(*ir.TypeAssert) 3673 if !ok { 3674 continue 3675 } 3676 var wrong []entry 3677 left := assert.X.Type() 3678 right := assert.AssertedType 3679 righti, ok := right.Underlying().(*types.Interface) 3680 3681 if !ok { 3682 // We only care about interface->interface 3683 // assertions. The Go compiler already catches 3684 // impossible interface->concrete assertions. 3685 continue 3686 } 3687 3688 ms := msc.MethodSet(left) 3689 for i := 0; i < righti.NumMethods(); i++ { 3690 mr := righti.Method(i) 3691 sel := ms.Lookup(mr.Pkg(), mr.Name()) 3692 if sel == nil { 3693 continue 3694 } 3695 ml := sel.Obj().(*types.Func) 3696 if types.AssignableTo(ml.Type(), mr.Type()) { 3697 continue 3698 } 3699 3700 wrong = append(wrong, entry{ml, mr}) 3701 } 3702 3703 if len(wrong) != 0 { 3704 s := fmt.Sprintf("impossible type assertion; %s and %s contradict each other:", 3705 types.TypeString(left, types.RelativeTo(pass.Pkg)), 3706 types.TypeString(right, types.RelativeTo(pass.Pkg))) 3707 for _, e := range wrong { 3708 s += fmt.Sprintf("\n\twrong type for %s method", e.l.Name()) 3709 s += fmt.Sprintf("\n\t\thave %s", e.l.Type()) 3710 s += fmt.Sprintf("\n\t\twant %s", e.r.Type()) 3711 } 3712 report.Report(pass, assert, s) 3713 } 3714 } 3715 } 3716 } 3717 return nil, nil 3718} 3719 3720func checkWithValueKey(call *Call) { 3721 arg := call.Args[1] 3722 T := arg.Value.Value.Type() 3723 if T, ok := T.(*types.Basic); ok { 3724 arg.Invalid( 3725 fmt.Sprintf("should not use built-in type %s as key for value; define your own type to avoid collisions", T)) 3726 } 3727 if !types.Comparable(T) { 3728 arg.Invalid(fmt.Sprintf("keys used with context.WithValue must be comparable, but type %s is not comparable", T)) 3729 } 3730} 3731 3732func CheckMaybeNil(pass *analysis.Pass) (interface{}, error) { 3733 // This is an extremely trivial check that doesn't try to reason 3734 // about control flow. That is, phis and sigmas do not propagate 3735 // any information. As such, we can flag this: 3736 // 3737 // _ = *x 3738 // if x == nil { return } 3739 // 3740 // but we cannot flag this: 3741 // 3742 // if x == nil { println(x) } 3743 // _ = *x 3744 // 3745 // nor many other variations of conditional uses of or assignments to x. 3746 // 3747 // However, even this trivial implementation finds plenty of 3748 // real-world bugs, such as dereference before nil pointer check, 3749 // or using t.Error instead of t.Fatal when encountering nil 3750 // pointers. 3751 // 3752 // On the flip side, our naive implementation avoids false positives in branches, such as 3753 // 3754 // if x != nil { _ = *x } 3755 // 3756 // due to the same lack of propagating information through sigma 3757 // nodes. x inside the branch will be independent of the x in the 3758 // nil pointer check. 3759 // 3760 // 3761 // We could implement a more powerful check, but then we'd be 3762 // getting false positives instead of false negatives because 3763 // we're incapable of deducing relationships between variables. 3764 // For example, a function might return a pointer and an error, 3765 // and the error being nil guarantees that the pointer is not nil. 3766 // Depending on the surrounding code, the pointer may still end up 3767 // being checked against nil in one place, and guarded by a check 3768 // on the error in another, which would lead to us marking some 3769 // loads as unsafe. 3770 // 3771 // Unfortunately, simply hard-coding the relationship between 3772 // return values wouldn't eliminate all false positives, either. 3773 // Many other more subtle relationships exist. An abridged example 3774 // from real code: 3775 // 3776 // if a == nil && b == nil { return } 3777 // c := fn(a) 3778 // if c != "" { _ = *a } 3779 // 3780 // where `fn` is guaranteed to return a non-empty string if a 3781 // isn't nil. 3782 // 3783 // We choose to err on the side of false negatives. 3784 3785 isNilConst := func(v ir.Value) bool { 3786 if code.IsPointerLike(v.Type()) { 3787 if k, ok := v.(*ir.Const); ok { 3788 return k.IsNil() 3789 } 3790 } 3791 return false 3792 } 3793 3794 for _, fn := range pass.ResultOf[buildir.Analyzer].(*buildir.IR).SrcFuncs { 3795 maybeNil := map[ir.Value]ir.Instruction{} 3796 for _, b := range fn.Blocks { 3797 for _, instr := range b.Instrs { 3798 if instr, ok := instr.(*ir.BinOp); ok { 3799 var ptr ir.Value 3800 if isNilConst(instr.X) { 3801 ptr = instr.Y 3802 } else if isNilConst(instr.Y) { 3803 ptr = instr.X 3804 } 3805 maybeNil[ptr] = instr 3806 } 3807 } 3808 } 3809 3810 for _, b := range fn.Blocks { 3811 for _, instr := range b.Instrs { 3812 var ptr ir.Value 3813 switch instr := instr.(type) { 3814 case *ir.Load: 3815 ptr = instr.X 3816 case *ir.Store: 3817 ptr = instr.Addr 3818 case *ir.IndexAddr: 3819 ptr = instr.X 3820 case *ir.FieldAddr: 3821 ptr = instr.X 3822 } 3823 if ptr != nil { 3824 switch ptr.(type) { 3825 case *ir.Alloc, *ir.FieldAddr, *ir.IndexAddr: 3826 // these cannot be nil 3827 continue 3828 } 3829 if r, ok := maybeNil[ptr]; ok { 3830 report.Report(pass, instr, "possible nil pointer dereference", 3831 report.Related(r, "this check suggests that the pointer can be nil")) 3832 } 3833 } 3834 } 3835 } 3836 } 3837 3838 return nil, nil 3839} 3840 3841var checkAddressIsNilQ = pattern.MustParse( 3842 `(BinaryExpr 3843 (UnaryExpr "&" _) 3844 (Or "==" "!=") 3845 (Builtin "nil"))`) 3846 3847func CheckAddressIsNil(pass *analysis.Pass) (interface{}, error) { 3848 fn := func(node ast.Node) { 3849 _, ok := Match(pass, checkAddressIsNilQ, node) 3850 if !ok { 3851 return 3852 } 3853 report.Report(pass, node, "the address of a variable cannot be nil") 3854 } 3855 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil)) 3856 return nil, nil 3857} 3858