github.com/gwaycc/gometalinter@v3.0.0+incompatible/_linters/src/honnef.co/go/tools/staticcheck/lint.go (about) 1 // Package staticcheck contains a linter for Go source code. 2 package staticcheck // import "honnef.co/go/tools/staticcheck" 3 4 import ( 5 "fmt" 6 "go/ast" 7 "go/constant" 8 "go/token" 9 "go/types" 10 htmltemplate "html/template" 11 "net/http" 12 "regexp" 13 "regexp/syntax" 14 "sort" 15 "strconv" 16 "strings" 17 "sync" 18 texttemplate "text/template" 19 20 . "honnef.co/go/tools/arg" 21 "honnef.co/go/tools/deprecated" 22 "honnef.co/go/tools/functions" 23 "honnef.co/go/tools/internal/sharedcheck" 24 "honnef.co/go/tools/lint" 25 . "honnef.co/go/tools/lint/lintdsl" 26 "honnef.co/go/tools/ssa" 27 "honnef.co/go/tools/ssautil" 28 "honnef.co/go/tools/staticcheck/vrp" 29 30 "golang.org/x/tools/go/ast/astutil" 31 "golang.org/x/tools/go/packages" 32 ) 33 34 func validRegexp(call *Call) { 35 arg := call.Args[0] 36 err := ValidateRegexp(arg.Value) 37 if err != nil { 38 arg.Invalid(err.Error()) 39 } 40 } 41 42 type runeSlice []rune 43 44 func (rs runeSlice) Len() int { return len(rs) } 45 func (rs runeSlice) Less(i int, j int) bool { return rs[i] < rs[j] } 46 func (rs runeSlice) Swap(i int, j int) { rs[i], rs[j] = rs[j], rs[i] } 47 48 func utf8Cutset(call *Call) { 49 arg := call.Args[1] 50 if InvalidUTF8(arg.Value) { 51 arg.Invalid(MsgInvalidUTF8) 52 } 53 } 54 55 func uniqueCutset(call *Call) { 56 arg := call.Args[1] 57 if !UniqueStringCutset(arg.Value) { 58 arg.Invalid(MsgNonUniqueCutset) 59 } 60 } 61 62 func unmarshalPointer(name string, arg int) CallCheck { 63 return func(call *Call) { 64 if !Pointer(call.Args[arg].Value) { 65 call.Args[arg].Invalid(fmt.Sprintf("%s expects to unmarshal into a pointer, but the provided value is not a pointer", name)) 66 } 67 } 68 } 69 70 func pointlessIntMath(call *Call) { 71 if ConvertedFromInt(call.Args[0].Value) { 72 call.Invalid(fmt.Sprintf("calling %s on a converted integer is pointless", CallName(call.Instr.Common()))) 73 } 74 } 75 76 func checkValidHostPort(arg int) CallCheck { 77 return func(call *Call) { 78 if !ValidHostPort(call.Args[arg].Value) { 79 call.Args[arg].Invalid(MsgInvalidHostPort) 80 } 81 } 82 } 83 84 var ( 85 checkRegexpRules = map[string]CallCheck{ 86 "regexp.MustCompile": validRegexp, 87 "regexp.Compile": validRegexp, 88 "regexp.Match": validRegexp, 89 "regexp.MatchReader": validRegexp, 90 "regexp.MatchString": validRegexp, 91 } 92 93 checkTimeParseRules = map[string]CallCheck{ 94 "time.Parse": func(call *Call) { 95 arg := call.Args[Arg("time.Parse.layout")] 96 err := ValidateTimeLayout(arg.Value) 97 if err != nil { 98 arg.Invalid(err.Error()) 99 } 100 }, 101 } 102 103 checkEncodingBinaryRules = map[string]CallCheck{ 104 "encoding/binary.Write": func(call *Call) { 105 arg := call.Args[Arg("encoding/binary.Write.data")] 106 if !CanBinaryMarshal(call.Job, arg.Value) { 107 arg.Invalid(fmt.Sprintf("value of type %s cannot be used with binary.Write", arg.Value.Value.Type())) 108 } 109 }, 110 } 111 112 checkURLsRules = map[string]CallCheck{ 113 "net/url.Parse": func(call *Call) { 114 arg := call.Args[Arg("net/url.Parse.rawurl")] 115 err := ValidateURL(arg.Value) 116 if err != nil { 117 arg.Invalid(err.Error()) 118 } 119 }, 120 } 121 122 checkSyncPoolValueRules = map[string]CallCheck{ 123 "(*sync.Pool).Put": func(call *Call) { 124 arg := call.Args[Arg("(*sync.Pool).Put.x")] 125 typ := arg.Value.Value.Type() 126 if !IsPointerLike(typ) { 127 arg.Invalid("argument should be pointer-like to avoid allocations") 128 } 129 }, 130 } 131 132 checkRegexpFindAllRules = map[string]CallCheck{ 133 "(*regexp.Regexp).FindAll": RepeatZeroTimes("a FindAll method", 1), 134 "(*regexp.Regexp).FindAllIndex": RepeatZeroTimes("a FindAll method", 1), 135 "(*regexp.Regexp).FindAllString": RepeatZeroTimes("a FindAll method", 1), 136 "(*regexp.Regexp).FindAllStringIndex": RepeatZeroTimes("a FindAll method", 1), 137 "(*regexp.Regexp).FindAllStringSubmatch": RepeatZeroTimes("a FindAll method", 1), 138 "(*regexp.Regexp).FindAllStringSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), 139 "(*regexp.Regexp).FindAllSubmatch": RepeatZeroTimes("a FindAll method", 1), 140 "(*regexp.Regexp).FindAllSubmatchIndex": RepeatZeroTimes("a FindAll method", 1), 141 } 142 143 checkUTF8CutsetRules = map[string]CallCheck{ 144 "strings.IndexAny": utf8Cutset, 145 "strings.LastIndexAny": utf8Cutset, 146 "strings.ContainsAny": utf8Cutset, 147 "strings.Trim": utf8Cutset, 148 "strings.TrimLeft": utf8Cutset, 149 "strings.TrimRight": utf8Cutset, 150 } 151 152 checkUniqueCutsetRules = map[string]CallCheck{ 153 "strings.Trim": uniqueCutset, 154 "strings.TrimLeft": uniqueCutset, 155 "strings.TrimRight": uniqueCutset, 156 } 157 158 checkUnmarshalPointerRules = map[string]CallCheck{ 159 "encoding/xml.Unmarshal": unmarshalPointer("xml.Unmarshal", 1), 160 "(*encoding/xml.Decoder).Decode": unmarshalPointer("Decode", 0), 161 "(*encoding/xml.Decoder).DecodeElement": unmarshalPointer("DecodeElement", 0), 162 "encoding/json.Unmarshal": unmarshalPointer("json.Unmarshal", 1), 163 "(*encoding/json.Decoder).Decode": unmarshalPointer("Decode", 0), 164 } 165 166 checkUnbufferedSignalChanRules = map[string]CallCheck{ 167 "os/signal.Notify": func(call *Call) { 168 arg := call.Args[Arg("os/signal.Notify.c")] 169 if UnbufferedChannel(arg.Value) { 170 arg.Invalid("the channel used with signal.Notify should be buffered") 171 } 172 }, 173 } 174 175 checkMathIntRules = map[string]CallCheck{ 176 "math.Ceil": pointlessIntMath, 177 "math.Floor": pointlessIntMath, 178 "math.IsNaN": pointlessIntMath, 179 "math.Trunc": pointlessIntMath, 180 "math.IsInf": pointlessIntMath, 181 } 182 183 checkStringsReplaceZeroRules = map[string]CallCheck{ 184 "strings.Replace": RepeatZeroTimes("strings.Replace", 3), 185 "bytes.Replace": RepeatZeroTimes("bytes.Replace", 3), 186 } 187 188 checkListenAddressRules = map[string]CallCheck{ 189 "net/http.ListenAndServe": checkValidHostPort(0), 190 "net/http.ListenAndServeTLS": checkValidHostPort(0), 191 } 192 193 checkBytesEqualIPRules = map[string]CallCheck{ 194 "bytes.Equal": func(call *Call) { 195 if ConvertedFrom(call.Args[Arg("bytes.Equal.a")].Value, "net.IP") && 196 ConvertedFrom(call.Args[Arg("bytes.Equal.b")].Value, "net.IP") { 197 call.Invalid("use net.IP.Equal to compare net.IPs, not bytes.Equal") 198 } 199 }, 200 } 201 202 checkRegexpMatchLoopRules = map[string]CallCheck{ 203 "regexp.Match": loopedRegexp("regexp.Match"), 204 "regexp.MatchReader": loopedRegexp("regexp.MatchReader"), 205 "regexp.MatchString": loopedRegexp("regexp.MatchString"), 206 } 207 ) 208 209 type Checker struct { 210 CheckGenerated bool 211 funcDescs *functions.Descriptions 212 deprecatedObjs map[types.Object]string 213 } 214 215 func NewChecker() *Checker { 216 return &Checker{} 217 } 218 219 func (*Checker) Name() string { return "staticcheck" } 220 func (*Checker) Prefix() string { return "SA" } 221 222 func (c *Checker) Checks() []lint.Check { 223 return []lint.Check{ 224 {ID: "SA1000", FilterGenerated: false, Fn: c.callChecker(checkRegexpRules)}, 225 {ID: "SA1001", FilterGenerated: false, Fn: c.CheckTemplate}, 226 {ID: "SA1002", FilterGenerated: false, Fn: c.callChecker(checkTimeParseRules)}, 227 {ID: "SA1003", FilterGenerated: false, Fn: c.callChecker(checkEncodingBinaryRules)}, 228 {ID: "SA1004", FilterGenerated: false, Fn: c.CheckTimeSleepConstant}, 229 {ID: "SA1005", FilterGenerated: false, Fn: c.CheckExec}, 230 {ID: "SA1006", FilterGenerated: false, Fn: c.CheckUnsafePrintf}, 231 {ID: "SA1007", FilterGenerated: false, Fn: c.callChecker(checkURLsRules)}, 232 {ID: "SA1008", FilterGenerated: false, Fn: c.CheckCanonicalHeaderKey}, 233 {ID: "SA1010", FilterGenerated: false, Fn: c.callChecker(checkRegexpFindAllRules)}, 234 {ID: "SA1011", FilterGenerated: false, Fn: c.callChecker(checkUTF8CutsetRules)}, 235 {ID: "SA1012", FilterGenerated: false, Fn: c.CheckNilContext}, 236 {ID: "SA1013", FilterGenerated: false, Fn: c.CheckSeeker}, 237 {ID: "SA1014", FilterGenerated: false, Fn: c.callChecker(checkUnmarshalPointerRules)}, 238 {ID: "SA1015", FilterGenerated: false, Fn: c.CheckLeakyTimeTick}, 239 {ID: "SA1016", FilterGenerated: false, Fn: c.CheckUntrappableSignal}, 240 {ID: "SA1017", FilterGenerated: false, Fn: c.callChecker(checkUnbufferedSignalChanRules)}, 241 {ID: "SA1018", FilterGenerated: false, Fn: c.callChecker(checkStringsReplaceZeroRules)}, 242 {ID: "SA1019", FilterGenerated: false, Fn: c.CheckDeprecated}, 243 {ID: "SA1020", FilterGenerated: false, Fn: c.callChecker(checkListenAddressRules)}, 244 {ID: "SA1021", FilterGenerated: false, Fn: c.callChecker(checkBytesEqualIPRules)}, 245 {ID: "SA1023", FilterGenerated: false, Fn: c.CheckWriterBufferModified}, 246 {ID: "SA1024", FilterGenerated: false, Fn: c.callChecker(checkUniqueCutsetRules)}, 247 {ID: "SA1025", FilterGenerated: false, Fn: c.CheckTimerResetReturnValue}, 248 249 {ID: "SA2000", FilterGenerated: false, Fn: c.CheckWaitgroupAdd}, 250 {ID: "SA2001", FilterGenerated: false, Fn: c.CheckEmptyCriticalSection}, 251 {ID: "SA2002", FilterGenerated: false, Fn: c.CheckConcurrentTesting}, 252 {ID: "SA2003", FilterGenerated: false, Fn: c.CheckDeferLock}, 253 254 {ID: "SA3000", FilterGenerated: false, Fn: c.CheckTestMainExit}, 255 {ID: "SA3001", FilterGenerated: false, Fn: c.CheckBenchmarkN}, 256 257 {ID: "SA4000", FilterGenerated: false, Fn: c.CheckLhsRhsIdentical}, 258 {ID: "SA4001", FilterGenerated: false, Fn: c.CheckIneffectiveCopy}, 259 {ID: "SA4002", FilterGenerated: false, Fn: c.CheckDiffSizeComparison}, 260 {ID: "SA4003", FilterGenerated: false, Fn: c.CheckExtremeComparison}, 261 {ID: "SA4004", FilterGenerated: false, Fn: c.CheckIneffectiveLoop}, 262 {ID: "SA4006", FilterGenerated: false, Fn: c.CheckUnreadVariableValues}, 263 {ID: "SA4008", FilterGenerated: false, Fn: c.CheckLoopCondition}, 264 {ID: "SA4009", FilterGenerated: false, Fn: c.CheckArgOverwritten}, 265 {ID: "SA4010", FilterGenerated: false, Fn: c.CheckIneffectiveAppend}, 266 {ID: "SA4011", FilterGenerated: false, Fn: c.CheckScopedBreak}, 267 {ID: "SA4012", FilterGenerated: false, Fn: c.CheckNaNComparison}, 268 {ID: "SA4013", FilterGenerated: false, Fn: c.CheckDoubleNegation}, 269 {ID: "SA4014", FilterGenerated: false, Fn: c.CheckRepeatedIfElse}, 270 {ID: "SA4015", FilterGenerated: false, Fn: c.callChecker(checkMathIntRules)}, 271 {ID: "SA4016", FilterGenerated: false, Fn: c.CheckSillyBitwiseOps}, 272 {ID: "SA4017", FilterGenerated: false, Fn: c.CheckPureFunctions}, 273 {ID: "SA4018", FilterGenerated: true, Fn: c.CheckSelfAssignment}, 274 {ID: "SA4019", FilterGenerated: true, Fn: c.CheckDuplicateBuildConstraints}, 275 {ID: "SA4020", FilterGenerated: false, Fn: c.CheckUnreachableTypeCases}, 276 277 {ID: "SA5000", FilterGenerated: false, Fn: c.CheckNilMaps}, 278 {ID: "SA5001", FilterGenerated: false, Fn: c.CheckEarlyDefer}, 279 {ID: "SA5002", FilterGenerated: false, Fn: c.CheckInfiniteEmptyLoop}, 280 {ID: "SA5003", FilterGenerated: false, Fn: c.CheckDeferInInfiniteLoop}, 281 {ID: "SA5004", FilterGenerated: false, Fn: c.CheckLoopEmptyDefault}, 282 {ID: "SA5005", FilterGenerated: false, Fn: c.CheckCyclicFinalizer}, 283 {ID: "SA5007", FilterGenerated: false, Fn: c.CheckInfiniteRecursion}, 284 285 {ID: "SA6000", FilterGenerated: false, Fn: c.callChecker(checkRegexpMatchLoopRules)}, 286 {ID: "SA6001", FilterGenerated: false, Fn: c.CheckMapBytesKey}, 287 {ID: "SA6002", FilterGenerated: false, Fn: c.callChecker(checkSyncPoolValueRules)}, 288 {ID: "SA6003", FilterGenerated: false, Fn: c.CheckRangeStringRunes}, 289 // {ID: "SA6004", FilterGenerated: false, Fn: c.CheckSillyRegexp}, 290 {ID: "SA6005", FilterGenerated: false, Fn: c.CheckToLowerToUpperComparison}, 291 292 {ID: "SA9001", FilterGenerated: false, Fn: c.CheckDubiousDeferInChannelRangeLoop}, 293 {ID: "SA9002", FilterGenerated: false, Fn: c.CheckNonOctalFileMode}, 294 {ID: "SA9003", FilterGenerated: false, Fn: c.CheckEmptyBranch}, 295 {ID: "SA9004", FilterGenerated: false, Fn: c.CheckMissingEnumTypesInDeclaration}, 296 } 297 298 // "SA5006": c.CheckSliceOutOfBounds, 299 // "SA4007": c.CheckPredeterminedBooleanExprs, 300 } 301 302 func (c *Checker) findDeprecated(prog *lint.Program) { 303 var docs []*ast.CommentGroup 304 var names []*ast.Ident 305 306 doDocs := func(pkg *packages.Package, names []*ast.Ident, docs []*ast.CommentGroup) { 307 var alt string 308 for _, doc := range docs { 309 if doc == nil { 310 continue 311 } 312 parts := strings.Split(doc.Text(), "\n\n") 313 last := parts[len(parts)-1] 314 if !strings.HasPrefix(last, "Deprecated: ") { 315 continue 316 } 317 alt = last[len("Deprecated: "):] 318 alt = strings.Replace(alt, "\n", " ", -1) 319 break 320 } 321 if alt == "" { 322 return 323 } 324 325 for _, name := range names { 326 obj := pkg.TypesInfo.ObjectOf(name) 327 c.deprecatedObjs[obj] = alt 328 } 329 } 330 331 for _, pkg := range prog.AllPackages { 332 for _, f := range pkg.Syntax { 333 fn := func(node ast.Node) bool { 334 if node == nil { 335 return true 336 } 337 var ret bool 338 switch node := node.(type) { 339 case *ast.GenDecl: 340 switch node.Tok { 341 case token.TYPE, token.CONST, token.VAR: 342 docs = append(docs, node.Doc) 343 return true 344 default: 345 return false 346 } 347 case *ast.FuncDecl: 348 docs = append(docs, node.Doc) 349 names = []*ast.Ident{node.Name} 350 ret = false 351 case *ast.TypeSpec: 352 docs = append(docs, node.Doc) 353 names = []*ast.Ident{node.Name} 354 ret = true 355 case *ast.ValueSpec: 356 docs = append(docs, node.Doc) 357 names = node.Names 358 ret = false 359 case *ast.File: 360 return true 361 case *ast.StructType: 362 for _, field := range node.Fields.List { 363 doDocs(pkg, field.Names, []*ast.CommentGroup{field.Doc}) 364 } 365 return false 366 case *ast.InterfaceType: 367 for _, field := range node.Methods.List { 368 doDocs(pkg, field.Names, []*ast.CommentGroup{field.Doc}) 369 } 370 return false 371 default: 372 return false 373 } 374 if len(names) == 0 || len(docs) == 0 { 375 return ret 376 } 377 doDocs(pkg, names, docs) 378 379 docs = docs[:0] 380 names = nil 381 return ret 382 } 383 ast.Inspect(f, fn) 384 } 385 } 386 } 387 388 func (c *Checker) Init(prog *lint.Program) { 389 wg := &sync.WaitGroup{} 390 wg.Add(2) 391 go func() { 392 c.funcDescs = functions.NewDescriptions(prog.SSA) 393 for _, fn := range prog.AllFunctions { 394 if fn.Blocks != nil { 395 applyStdlibKnowledge(fn) 396 ssa.OptimizeBlocks(fn) 397 } 398 } 399 wg.Done() 400 }() 401 402 go func() { 403 c.deprecatedObjs = map[types.Object]string{} 404 c.findDeprecated(prog) 405 wg.Done() 406 }() 407 408 wg.Wait() 409 } 410 411 func (c *Checker) isInLoop(b *ssa.BasicBlock) bool { 412 sets := c.funcDescs.Get(b.Parent()).Loops 413 for _, set := range sets { 414 if set[b] { 415 return true 416 } 417 } 418 return false 419 } 420 421 func applyStdlibKnowledge(fn *ssa.Function) { 422 if len(fn.Blocks) == 0 { 423 return 424 } 425 426 // comma-ok receiving from a time.Tick channel will never return 427 // ok == false, so any branching on the value of ok can be 428 // replaced with an unconditional jump. This will primarily match 429 // `for range time.Tick(x)` loops, but it can also match 430 // user-written code. 431 for _, block := range fn.Blocks { 432 if len(block.Instrs) < 3 { 433 continue 434 } 435 if len(block.Succs) != 2 { 436 continue 437 } 438 var instrs []*ssa.Instruction 439 for i, ins := range block.Instrs { 440 if _, ok := ins.(*ssa.DebugRef); ok { 441 continue 442 } 443 instrs = append(instrs, &block.Instrs[i]) 444 } 445 446 for i, ins := range instrs { 447 unop, ok := (*ins).(*ssa.UnOp) 448 if !ok || unop.Op != token.ARROW { 449 continue 450 } 451 call, ok := unop.X.(*ssa.Call) 452 if !ok { 453 continue 454 } 455 if !IsCallTo(call.Common(), "time.Tick") { 456 continue 457 } 458 ex, ok := (*instrs[i+1]).(*ssa.Extract) 459 if !ok || ex.Tuple != unop || ex.Index != 1 { 460 continue 461 } 462 463 ifstmt, ok := (*instrs[i+2]).(*ssa.If) 464 if !ok || ifstmt.Cond != ex { 465 continue 466 } 467 468 *instrs[i+2] = ssa.NewJump(block) 469 succ := block.Succs[1] 470 block.Succs = block.Succs[0:1] 471 succ.RemovePred(block) 472 } 473 } 474 } 475 476 func hasType(j *lint.Job, expr ast.Expr, name string) bool { 477 T := TypeOf(j, expr) 478 return IsType(T, name) 479 } 480 481 func (c *Checker) CheckUntrappableSignal(j *lint.Job) { 482 fn := func(node ast.Node) bool { 483 call, ok := node.(*ast.CallExpr) 484 if !ok { 485 return true 486 } 487 if !IsCallToAnyAST(j, call, 488 "os/signal.Ignore", "os/signal.Notify", "os/signal.Reset") { 489 return true 490 } 491 for _, arg := range call.Args { 492 if conv, ok := arg.(*ast.CallExpr); ok && isName(j, conv.Fun, "os.Signal") { 493 arg = conv.Args[0] 494 } 495 496 if isName(j, arg, "os.Kill") || isName(j, arg, "syscall.SIGKILL") { 497 j.Errorf(arg, "%s cannot be trapped (did you mean syscall.SIGTERM?)", Render(j, arg)) 498 } 499 if isName(j, arg, "syscall.SIGSTOP") { 500 j.Errorf(arg, "%s signal cannot be trapped", Render(j, arg)) 501 } 502 } 503 return true 504 } 505 for _, f := range j.Program.Files { 506 ast.Inspect(f, fn) 507 } 508 } 509 510 func (c *Checker) CheckTemplate(j *lint.Job) { 511 fn := func(node ast.Node) bool { 512 call, ok := node.(*ast.CallExpr) 513 if !ok { 514 return true 515 } 516 var kind string 517 if IsCallToAST(j, call, "(*text/template.Template).Parse") { 518 kind = "text" 519 } else if IsCallToAST(j, call, "(*html/template.Template).Parse") { 520 kind = "html" 521 } else { 522 return true 523 } 524 sel := call.Fun.(*ast.SelectorExpr) 525 if !IsCallToAST(j, sel.X, "text/template.New") && 526 !IsCallToAST(j, sel.X, "html/template.New") { 527 // TODO(dh): this is a cheap workaround for templates with 528 // different delims. A better solution with less false 529 // negatives would use data flow analysis to see where the 530 // template comes from and where it has been 531 return true 532 } 533 s, ok := ExprToString(j, call.Args[Arg("(*text/template.Template).Parse.text")]) 534 if !ok { 535 return true 536 } 537 var err error 538 switch kind { 539 case "text": 540 _, err = texttemplate.New("").Parse(s) 541 case "html": 542 _, err = htmltemplate.New("").Parse(s) 543 } 544 if err != nil { 545 // TODO(dominikh): whitelist other parse errors, if any 546 if strings.Contains(err.Error(), "unexpected") { 547 j.Errorf(call.Args[Arg("(*text/template.Template).Parse.text")], "%s", err) 548 } 549 } 550 return true 551 } 552 for _, f := range j.Program.Files { 553 ast.Inspect(f, fn) 554 } 555 } 556 557 func (c *Checker) CheckTimeSleepConstant(j *lint.Job) { 558 fn := func(node ast.Node) bool { 559 call, ok := node.(*ast.CallExpr) 560 if !ok { 561 return true 562 } 563 if !IsCallToAST(j, call, "time.Sleep") { 564 return true 565 } 566 lit, ok := call.Args[Arg("time.Sleep.d")].(*ast.BasicLit) 567 if !ok { 568 return true 569 } 570 n, err := strconv.Atoi(lit.Value) 571 if err != nil { 572 return true 573 } 574 if n == 0 || n > 120 { 575 // time.Sleep(0) is a seldom used pattern in concurrency 576 // tests. >120 might be intentional. 120 was chosen 577 // because the user could've meant 2 minutes. 578 return true 579 } 580 recommendation := "time.Sleep(time.Nanosecond)" 581 if n != 1 { 582 recommendation = fmt.Sprintf("time.Sleep(%d * time.Nanosecond)", n) 583 } 584 j.Errorf(call.Args[Arg("time.Sleep.d")], 585 "sleeping for %d nanoseconds is probably a bug. Be explicit if it isn't: %s", n, recommendation) 586 return true 587 } 588 for _, f := range j.Program.Files { 589 ast.Inspect(f, fn) 590 } 591 } 592 593 func (c *Checker) CheckWaitgroupAdd(j *lint.Job) { 594 fn := func(node ast.Node) bool { 595 g, ok := node.(*ast.GoStmt) 596 if !ok { 597 return true 598 } 599 fun, ok := g.Call.Fun.(*ast.FuncLit) 600 if !ok { 601 return true 602 } 603 if len(fun.Body.List) == 0 { 604 return true 605 } 606 stmt, ok := fun.Body.List[0].(*ast.ExprStmt) 607 if !ok { 608 return true 609 } 610 call, ok := stmt.X.(*ast.CallExpr) 611 if !ok { 612 return true 613 } 614 sel, ok := call.Fun.(*ast.SelectorExpr) 615 if !ok { 616 return true 617 } 618 fn, ok := ObjectOf(j, sel.Sel).(*types.Func) 619 if !ok { 620 return true 621 } 622 if fn.FullName() == "(*sync.WaitGroup).Add" { 623 j.Errorf(sel, "should call %s before starting the goroutine to avoid a race", 624 Render(j, stmt)) 625 } 626 return true 627 } 628 for _, f := range j.Program.Files { 629 ast.Inspect(f, fn) 630 } 631 } 632 633 func (c *Checker) CheckInfiniteEmptyLoop(j *lint.Job) { 634 fn := func(node ast.Node) bool { 635 loop, ok := node.(*ast.ForStmt) 636 if !ok || len(loop.Body.List) != 0 || loop.Post != nil { 637 return true 638 } 639 640 if loop.Init != nil { 641 // TODO(dh): this isn't strictly necessary, it just makes 642 // the check easier. 643 return true 644 } 645 // An empty loop is bad news in two cases: 1) The loop has no 646 // condition. In that case, it's just a loop that spins 647 // forever and as fast as it can, keeping a core busy. 2) The 648 // loop condition only consists of variable or field reads and 649 // operators on those. The only way those could change their 650 // value is with unsynchronised access, which constitutes a 651 // data race. 652 // 653 // If the condition contains any function calls, its behaviour 654 // is dynamic and the loop might terminate. Similarly for 655 // channel receives. 656 657 if loop.Cond != nil { 658 if hasSideEffects(loop.Cond) { 659 return true 660 } 661 if ident, ok := loop.Cond.(*ast.Ident); ok { 662 if k, ok := ObjectOf(j, ident).(*types.Const); ok { 663 if !constant.BoolVal(k.Val()) { 664 // don't flag `for false {}` loops. They're a debug aid. 665 return true 666 } 667 } 668 } 669 j.Errorf(loop, "loop condition never changes or has a race condition") 670 } 671 j.Errorf(loop, "this loop will spin, using 100%% CPU") 672 673 return true 674 } 675 for _, f := range j.Program.Files { 676 ast.Inspect(f, fn) 677 } 678 } 679 680 func (c *Checker) CheckDeferInInfiniteLoop(j *lint.Job) { 681 fn := func(node ast.Node) bool { 682 mightExit := false 683 var defers []ast.Stmt 684 loop, ok := node.(*ast.ForStmt) 685 if !ok || loop.Cond != nil { 686 return true 687 } 688 fn2 := func(node ast.Node) bool { 689 switch stmt := node.(type) { 690 case *ast.ReturnStmt: 691 mightExit = true 692 case *ast.BranchStmt: 693 // TODO(dominikh): if this sees a break in a switch or 694 // select, it doesn't check if it breaks the loop or 695 // just the select/switch. This causes some false 696 // negatives. 697 if stmt.Tok == token.BREAK { 698 mightExit = true 699 } 700 case *ast.DeferStmt: 701 defers = append(defers, stmt) 702 case *ast.FuncLit: 703 // Don't look into function bodies 704 return false 705 } 706 return true 707 } 708 ast.Inspect(loop.Body, fn2) 709 if mightExit { 710 return true 711 } 712 for _, stmt := range defers { 713 j.Errorf(stmt, "defers in this infinite loop will never run") 714 } 715 return true 716 } 717 for _, f := range j.Program.Files { 718 ast.Inspect(f, fn) 719 } 720 } 721 722 func (c *Checker) CheckDubiousDeferInChannelRangeLoop(j *lint.Job) { 723 fn := func(node ast.Node) bool { 724 loop, ok := node.(*ast.RangeStmt) 725 if !ok { 726 return true 727 } 728 typ := TypeOf(j, loop.X) 729 _, ok = typ.Underlying().(*types.Chan) 730 if !ok { 731 return true 732 } 733 fn2 := func(node ast.Node) bool { 734 switch stmt := node.(type) { 735 case *ast.DeferStmt: 736 j.Errorf(stmt, "defers in this range loop won't run unless the channel gets closed") 737 case *ast.FuncLit: 738 // Don't look into function bodies 739 return false 740 } 741 return true 742 } 743 ast.Inspect(loop.Body, fn2) 744 return true 745 } 746 for _, f := range j.Program.Files { 747 ast.Inspect(f, fn) 748 } 749 } 750 751 func (c *Checker) CheckTestMainExit(j *lint.Job) { 752 fn := func(node ast.Node) bool { 753 if !isTestMain(j, node) { 754 return true 755 } 756 757 arg := ObjectOf(j, node.(*ast.FuncDecl).Type.Params.List[0].Names[0]) 758 callsRun := false 759 fn2 := func(node ast.Node) bool { 760 call, ok := node.(*ast.CallExpr) 761 if !ok { 762 return true 763 } 764 sel, ok := call.Fun.(*ast.SelectorExpr) 765 if !ok { 766 return true 767 } 768 ident, ok := sel.X.(*ast.Ident) 769 if !ok { 770 return true 771 } 772 if arg != ObjectOf(j, ident) { 773 return true 774 } 775 if sel.Sel.Name == "Run" { 776 callsRun = true 777 return false 778 } 779 return true 780 } 781 ast.Inspect(node.(*ast.FuncDecl).Body, fn2) 782 783 callsExit := false 784 fn3 := func(node ast.Node) bool { 785 if IsCallToAST(j, node, "os.Exit") { 786 callsExit = true 787 return false 788 } 789 return true 790 } 791 ast.Inspect(node.(*ast.FuncDecl).Body, fn3) 792 if !callsExit && callsRun { 793 j.Errorf(node, "TestMain should call os.Exit to set exit code") 794 } 795 return true 796 } 797 for _, f := range j.Program.Files { 798 ast.Inspect(f, fn) 799 } 800 } 801 802 func isTestMain(j *lint.Job, node ast.Node) bool { 803 decl, ok := node.(*ast.FuncDecl) 804 if !ok { 805 return false 806 } 807 if decl.Name.Name != "TestMain" { 808 return false 809 } 810 if len(decl.Type.Params.List) != 1 { 811 return false 812 } 813 arg := decl.Type.Params.List[0] 814 if len(arg.Names) != 1 { 815 return false 816 } 817 return IsOfType(j, arg.Type, "*testing.M") 818 } 819 820 func (c *Checker) CheckExec(j *lint.Job) { 821 fn := func(node ast.Node) bool { 822 call, ok := node.(*ast.CallExpr) 823 if !ok { 824 return true 825 } 826 if !IsCallToAST(j, call, "os/exec.Command") { 827 return true 828 } 829 val, ok := ExprToString(j, call.Args[Arg("os/exec.Command.name")]) 830 if !ok { 831 return true 832 } 833 if !strings.Contains(val, " ") || strings.Contains(val, `\`) || strings.Contains(val, "/") { 834 return true 835 } 836 j.Errorf(call.Args[Arg("os/exec.Command.name")], 837 "first argument to exec.Command looks like a shell command, but a program name or path are expected") 838 return true 839 } 840 for _, f := range j.Program.Files { 841 ast.Inspect(f, fn) 842 } 843 } 844 845 func (c *Checker) CheckLoopEmptyDefault(j *lint.Job) { 846 fn := func(node ast.Node) bool { 847 loop, ok := node.(*ast.ForStmt) 848 if !ok || len(loop.Body.List) != 1 || loop.Cond != nil || loop.Init != nil { 849 return true 850 } 851 sel, ok := loop.Body.List[0].(*ast.SelectStmt) 852 if !ok { 853 return true 854 } 855 for _, c := range sel.Body.List { 856 if comm, ok := c.(*ast.CommClause); ok && comm.Comm == nil && len(comm.Body) == 0 { 857 j.Errorf(comm, "should not have an empty default case in a for+select loop. The loop will spin.") 858 } 859 } 860 return true 861 } 862 for _, f := range j.Program.Files { 863 ast.Inspect(f, fn) 864 } 865 } 866 867 func (c *Checker) CheckLhsRhsIdentical(j *lint.Job) { 868 fn := func(node ast.Node) bool { 869 op, ok := node.(*ast.BinaryExpr) 870 if !ok { 871 return true 872 } 873 switch op.Op { 874 case token.EQL, token.NEQ: 875 if basic, ok := TypeOf(j, op.X).Underlying().(*types.Basic); ok { 876 if kind := basic.Kind(); kind == types.Float32 || kind == types.Float64 { 877 // f == f and f != f might be used to check for NaN 878 return true 879 } 880 } 881 case token.SUB, token.QUO, token.AND, token.REM, token.OR, token.XOR, token.AND_NOT, 882 token.LAND, token.LOR, token.LSS, token.GTR, token.LEQ, token.GEQ: 883 default: 884 // For some ops, such as + and *, it can make sense to 885 // have identical operands 886 return true 887 } 888 889 if Render(j, op.X) != Render(j, op.Y) { 890 return true 891 } 892 j.Errorf(op, "identical expressions on the left and right side of the '%s' operator", op.Op) 893 return true 894 } 895 for _, f := range j.Program.Files { 896 ast.Inspect(f, fn) 897 } 898 } 899 900 func (c *Checker) CheckScopedBreak(j *lint.Job) { 901 fn := func(node ast.Node) bool { 902 var body *ast.BlockStmt 903 switch node := node.(type) { 904 case *ast.ForStmt: 905 body = node.Body 906 case *ast.RangeStmt: 907 body = node.Body 908 default: 909 return true 910 } 911 for _, stmt := range body.List { 912 var blocks [][]ast.Stmt 913 switch stmt := stmt.(type) { 914 case *ast.SwitchStmt: 915 for _, c := range stmt.Body.List { 916 blocks = append(blocks, c.(*ast.CaseClause).Body) 917 } 918 case *ast.SelectStmt: 919 for _, c := range stmt.Body.List { 920 blocks = append(blocks, c.(*ast.CommClause).Body) 921 } 922 default: 923 continue 924 } 925 926 for _, body := range blocks { 927 if len(body) == 0 { 928 continue 929 } 930 lasts := []ast.Stmt{body[len(body)-1]} 931 // TODO(dh): unfold all levels of nested block 932 // statements, not just a single level if statement 933 if ifs, ok := lasts[0].(*ast.IfStmt); ok { 934 if len(ifs.Body.List) == 0 { 935 continue 936 } 937 lasts[0] = ifs.Body.List[len(ifs.Body.List)-1] 938 939 if block, ok := ifs.Else.(*ast.BlockStmt); ok { 940 if len(block.List) != 0 { 941 lasts = append(lasts, block.List[len(block.List)-1]) 942 } 943 } 944 } 945 for _, last := range lasts { 946 branch, ok := last.(*ast.BranchStmt) 947 if !ok || branch.Tok != token.BREAK || branch.Label != nil { 948 continue 949 } 950 j.Errorf(branch, "ineffective break statement. Did you mean to break out of the outer loop?") 951 } 952 } 953 } 954 return true 955 } 956 for _, f := range j.Program.Files { 957 ast.Inspect(f, fn) 958 } 959 } 960 961 func (c *Checker) CheckUnsafePrintf(j *lint.Job) { 962 fn := func(node ast.Node) bool { 963 call, ok := node.(*ast.CallExpr) 964 if !ok { 965 return true 966 } 967 var arg int 968 if IsCallToAnyAST(j, call, "fmt.Printf", "fmt.Sprintf", "log.Printf") { 969 arg = Arg("fmt.Printf.format") 970 } else if IsCallToAnyAST(j, call, "fmt.Fprintf") { 971 arg = Arg("fmt.Fprintf.format") 972 } else { 973 return true 974 } 975 if len(call.Args) != arg+1 { 976 return true 977 } 978 switch call.Args[arg].(type) { 979 case *ast.CallExpr, *ast.Ident: 980 default: 981 return true 982 } 983 j.Errorf(call.Args[arg], 984 "printf-style function with dynamic format string and no further arguments should use print-style function instead") 985 return true 986 } 987 for _, f := range j.Program.Files { 988 ast.Inspect(f, fn) 989 } 990 } 991 992 func (c *Checker) CheckEarlyDefer(j *lint.Job) { 993 fn := func(node ast.Node) bool { 994 block, ok := node.(*ast.BlockStmt) 995 if !ok { 996 return true 997 } 998 if len(block.List) < 2 { 999 return true 1000 } 1001 for i, stmt := range block.List { 1002 if i == len(block.List)-1 { 1003 break 1004 } 1005 assign, ok := stmt.(*ast.AssignStmt) 1006 if !ok { 1007 continue 1008 } 1009 if len(assign.Rhs) != 1 { 1010 continue 1011 } 1012 if len(assign.Lhs) < 2 { 1013 continue 1014 } 1015 if lhs, ok := assign.Lhs[len(assign.Lhs)-1].(*ast.Ident); ok && lhs.Name == "_" { 1016 continue 1017 } 1018 call, ok := assign.Rhs[0].(*ast.CallExpr) 1019 if !ok { 1020 continue 1021 } 1022 sig, ok := TypeOf(j, call.Fun).(*types.Signature) 1023 if !ok { 1024 continue 1025 } 1026 if sig.Results().Len() < 2 { 1027 continue 1028 } 1029 last := sig.Results().At(sig.Results().Len() - 1) 1030 // FIXME(dh): check that it's error from universe, not 1031 // another type of the same name 1032 if last.Type().String() != "error" { 1033 continue 1034 } 1035 lhs, ok := assign.Lhs[0].(*ast.Ident) 1036 if !ok { 1037 continue 1038 } 1039 def, ok := block.List[i+1].(*ast.DeferStmt) 1040 if !ok { 1041 continue 1042 } 1043 sel, ok := def.Call.Fun.(*ast.SelectorExpr) 1044 if !ok { 1045 continue 1046 } 1047 ident, ok := selectorX(sel).(*ast.Ident) 1048 if !ok { 1049 continue 1050 } 1051 if ident.Obj != lhs.Obj { 1052 continue 1053 } 1054 if sel.Sel.Name != "Close" { 1055 continue 1056 } 1057 j.Errorf(def, "should check returned error before deferring %s", Render(j, def.Call)) 1058 } 1059 return true 1060 } 1061 for _, f := range j.Program.Files { 1062 ast.Inspect(f, fn) 1063 } 1064 } 1065 1066 func selectorX(sel *ast.SelectorExpr) ast.Node { 1067 switch x := sel.X.(type) { 1068 case *ast.SelectorExpr: 1069 return selectorX(x) 1070 default: 1071 return x 1072 } 1073 } 1074 1075 func (c *Checker) CheckEmptyCriticalSection(j *lint.Job) { 1076 // Initially it might seem like this check would be easier to 1077 // implement in SSA. After all, we're only checking for two 1078 // consecutive method calls. In reality, however, there may be any 1079 // number of other instructions between the lock and unlock, while 1080 // still constituting an empty critical section. For example, 1081 // given `m.x().Lock(); m.x().Unlock()`, there will be a call to 1082 // x(). In the AST-based approach, this has a tiny potential for a 1083 // false positive (the second call to x might be doing work that 1084 // is protected by the mutex). In an SSA-based approach, however, 1085 // it would miss a lot of real bugs. 1086 1087 mutexParams := func(s ast.Stmt) (x ast.Expr, funcName string, ok bool) { 1088 expr, ok := s.(*ast.ExprStmt) 1089 if !ok { 1090 return nil, "", false 1091 } 1092 call, ok := expr.X.(*ast.CallExpr) 1093 if !ok { 1094 return nil, "", false 1095 } 1096 sel, ok := call.Fun.(*ast.SelectorExpr) 1097 if !ok { 1098 return nil, "", false 1099 } 1100 1101 fn, ok := ObjectOf(j, sel.Sel).(*types.Func) 1102 if !ok { 1103 return nil, "", false 1104 } 1105 sig := fn.Type().(*types.Signature) 1106 if sig.Params().Len() != 0 || sig.Results().Len() != 0 { 1107 return nil, "", false 1108 } 1109 1110 return sel.X, fn.Name(), true 1111 } 1112 1113 fn := func(node ast.Node) bool { 1114 block, ok := node.(*ast.BlockStmt) 1115 if !ok { 1116 return true 1117 } 1118 if len(block.List) < 2 { 1119 return true 1120 } 1121 for i := range block.List[:len(block.List)-1] { 1122 sel1, method1, ok1 := mutexParams(block.List[i]) 1123 sel2, method2, ok2 := mutexParams(block.List[i+1]) 1124 1125 if !ok1 || !ok2 || Render(j, sel1) != Render(j, sel2) { 1126 continue 1127 } 1128 if (method1 == "Lock" && method2 == "Unlock") || 1129 (method1 == "RLock" && method2 == "RUnlock") { 1130 j.Errorf(block.List[i+1], "empty critical section") 1131 } 1132 } 1133 return true 1134 } 1135 for _, f := range j.Program.Files { 1136 ast.Inspect(f, fn) 1137 } 1138 } 1139 1140 // cgo produces code like fn(&*_Cvar_kSomeCallbacks) which we don't 1141 // want to flag. 1142 var cgoIdent = regexp.MustCompile(`^_C(func|var)_.+$`) 1143 1144 func (c *Checker) CheckIneffectiveCopy(j *lint.Job) { 1145 fn := func(node ast.Node) bool { 1146 if unary, ok := node.(*ast.UnaryExpr); ok { 1147 if star, ok := unary.X.(*ast.StarExpr); ok && unary.Op == token.AND { 1148 ident, ok := star.X.(*ast.Ident) 1149 if !ok || !cgoIdent.MatchString(ident.Name) { 1150 j.Errorf(unary, "&*x will be simplified to x. It will not copy x.") 1151 } 1152 } 1153 } 1154 1155 if star, ok := node.(*ast.StarExpr); ok { 1156 if unary, ok := star.X.(*ast.UnaryExpr); ok && unary.Op == token.AND { 1157 j.Errorf(star, "*&x will be simplified to x. It will not copy x.") 1158 } 1159 } 1160 return true 1161 } 1162 for _, f := range j.Program.Files { 1163 ast.Inspect(f, fn) 1164 } 1165 } 1166 1167 func (c *Checker) CheckDiffSizeComparison(j *lint.Job) { 1168 for _, ssafn := range j.Program.InitialFunctions { 1169 for _, b := range ssafn.Blocks { 1170 for _, ins := range b.Instrs { 1171 binop, ok := ins.(*ssa.BinOp) 1172 if !ok { 1173 continue 1174 } 1175 if binop.Op != token.EQL && binop.Op != token.NEQ { 1176 continue 1177 } 1178 _, ok1 := binop.X.(*ssa.Slice) 1179 _, ok2 := binop.Y.(*ssa.Slice) 1180 if !ok1 && !ok2 { 1181 continue 1182 } 1183 r := c.funcDescs.Get(ssafn).Ranges 1184 r1, ok1 := r.Get(binop.X).(vrp.StringInterval) 1185 r2, ok2 := r.Get(binop.Y).(vrp.StringInterval) 1186 if !ok1 || !ok2 { 1187 continue 1188 } 1189 if r1.Length.Intersection(r2.Length).Empty() { 1190 j.Errorf(binop, "comparing strings of different sizes for equality will always return false") 1191 } 1192 } 1193 } 1194 } 1195 } 1196 1197 func (c *Checker) CheckCanonicalHeaderKey(j *lint.Job) { 1198 fn := func(node ast.Node) bool { 1199 assign, ok := node.(*ast.AssignStmt) 1200 if ok { 1201 // TODO(dh): This risks missing some Header reads, for 1202 // example in `h1["foo"] = h2["foo"]` – these edge 1203 // cases are probably rare enough to ignore for now. 1204 for _, expr := range assign.Lhs { 1205 op, ok := expr.(*ast.IndexExpr) 1206 if !ok { 1207 continue 1208 } 1209 if hasType(j, op.X, "net/http.Header") { 1210 return false 1211 } 1212 } 1213 return true 1214 } 1215 op, ok := node.(*ast.IndexExpr) 1216 if !ok { 1217 return true 1218 } 1219 if !hasType(j, op.X, "net/http.Header") { 1220 return true 1221 } 1222 s, ok := ExprToString(j, op.Index) 1223 if !ok { 1224 return true 1225 } 1226 if s == http.CanonicalHeaderKey(s) { 1227 return true 1228 } 1229 j.Errorf(op, "keys in http.Header are canonicalized, %q is not canonical; fix the constant or use http.CanonicalHeaderKey", s) 1230 return true 1231 } 1232 for _, f := range j.Program.Files { 1233 ast.Inspect(f, fn) 1234 } 1235 } 1236 1237 func (c *Checker) CheckBenchmarkN(j *lint.Job) { 1238 fn := func(node ast.Node) bool { 1239 assign, ok := node.(*ast.AssignStmt) 1240 if !ok { 1241 return true 1242 } 1243 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { 1244 return true 1245 } 1246 sel, ok := assign.Lhs[0].(*ast.SelectorExpr) 1247 if !ok { 1248 return true 1249 } 1250 if sel.Sel.Name != "N" { 1251 return true 1252 } 1253 if !hasType(j, sel.X, "*testing.B") { 1254 return true 1255 } 1256 j.Errorf(assign, "should not assign to %s", Render(j, sel)) 1257 return true 1258 } 1259 for _, f := range j.Program.Files { 1260 ast.Inspect(f, fn) 1261 } 1262 } 1263 1264 func (c *Checker) CheckUnreadVariableValues(j *lint.Job) { 1265 for _, ssafn := range j.Program.InitialFunctions { 1266 if IsExample(ssafn) { 1267 continue 1268 } 1269 node := ssafn.Syntax() 1270 if node == nil { 1271 continue 1272 } 1273 1274 ast.Inspect(node, func(node ast.Node) bool { 1275 assign, ok := node.(*ast.AssignStmt) 1276 if !ok { 1277 return true 1278 } 1279 if len(assign.Lhs) > 1 && len(assign.Rhs) == 1 { 1280 // Either a function call with multiple return values, 1281 // or a comma-ok assignment 1282 1283 val, _ := ssafn.ValueForExpr(assign.Rhs[0]) 1284 if val == nil { 1285 return true 1286 } 1287 refs := val.Referrers() 1288 if refs == nil { 1289 return true 1290 } 1291 for _, ref := range *refs { 1292 ex, ok := ref.(*ssa.Extract) 1293 if !ok { 1294 continue 1295 } 1296 exrefs := ex.Referrers() 1297 if exrefs == nil { 1298 continue 1299 } 1300 if len(FilterDebug(*exrefs)) == 0 { 1301 lhs := assign.Lhs[ex.Index] 1302 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { 1303 continue 1304 } 1305 j.Errorf(lhs, "this value of %s is never used", lhs) 1306 } 1307 } 1308 return true 1309 } 1310 for i, lhs := range assign.Lhs { 1311 rhs := assign.Rhs[i] 1312 if ident, ok := lhs.(*ast.Ident); !ok || ok && ident.Name == "_" { 1313 continue 1314 } 1315 val, _ := ssafn.ValueForExpr(rhs) 1316 if val == nil { 1317 continue 1318 } 1319 1320 refs := val.Referrers() 1321 if refs == nil { 1322 // TODO investigate why refs can be nil 1323 return true 1324 } 1325 if len(FilterDebug(*refs)) == 0 { 1326 j.Errorf(lhs, "this value of %s is never used", lhs) 1327 } 1328 } 1329 return true 1330 }) 1331 } 1332 } 1333 1334 func (c *Checker) CheckPredeterminedBooleanExprs(j *lint.Job) { 1335 for _, ssafn := range j.Program.InitialFunctions { 1336 for _, block := range ssafn.Blocks { 1337 for _, ins := range block.Instrs { 1338 ssabinop, ok := ins.(*ssa.BinOp) 1339 if !ok { 1340 continue 1341 } 1342 switch ssabinop.Op { 1343 case token.GTR, token.LSS, token.EQL, token.NEQ, token.LEQ, token.GEQ: 1344 default: 1345 continue 1346 } 1347 1348 xs, ok1 := consts(ssabinop.X, nil, nil) 1349 ys, ok2 := consts(ssabinop.Y, nil, nil) 1350 if !ok1 || !ok2 || len(xs) == 0 || len(ys) == 0 { 1351 continue 1352 } 1353 1354 trues := 0 1355 for _, x := range xs { 1356 for _, y := range ys { 1357 if x.Value == nil { 1358 if y.Value == nil { 1359 trues++ 1360 } 1361 continue 1362 } 1363 if constant.Compare(x.Value, ssabinop.Op, y.Value) { 1364 trues++ 1365 } 1366 } 1367 } 1368 b := trues != 0 1369 if trues == 0 || trues == len(xs)*len(ys) { 1370 j.Errorf(ssabinop, "binary expression is always %t for all possible values (%s %s %s)", 1371 b, xs, ssabinop.Op, ys) 1372 } 1373 } 1374 } 1375 } 1376 } 1377 1378 func (c *Checker) CheckNilMaps(j *lint.Job) { 1379 for _, ssafn := range j.Program.InitialFunctions { 1380 for _, block := range ssafn.Blocks { 1381 for _, ins := range block.Instrs { 1382 mu, ok := ins.(*ssa.MapUpdate) 1383 if !ok { 1384 continue 1385 } 1386 c, ok := mu.Map.(*ssa.Const) 1387 if !ok { 1388 continue 1389 } 1390 if c.Value != nil { 1391 continue 1392 } 1393 j.Errorf(mu, "assignment to nil map") 1394 } 1395 } 1396 } 1397 } 1398 1399 func (c *Checker) CheckExtremeComparison(j *lint.Job) { 1400 isobj := func(expr ast.Expr, name string) bool { 1401 sel, ok := expr.(*ast.SelectorExpr) 1402 if !ok { 1403 return false 1404 } 1405 return IsObject(ObjectOf(j, sel.Sel), name) 1406 } 1407 1408 fn := func(node ast.Node) bool { 1409 expr, ok := node.(*ast.BinaryExpr) 1410 if !ok { 1411 return true 1412 } 1413 tx := TypeOf(j, expr.X) 1414 basic, ok := tx.Underlying().(*types.Basic) 1415 if !ok { 1416 return true 1417 } 1418 1419 var max string 1420 var min string 1421 1422 switch basic.Kind() { 1423 case types.Uint8: 1424 max = "math.MaxUint8" 1425 case types.Uint16: 1426 max = "math.MaxUint16" 1427 case types.Uint32: 1428 max = "math.MaxUint32" 1429 case types.Uint64: 1430 max = "math.MaxUint64" 1431 case types.Uint: 1432 max = "math.MaxUint64" 1433 1434 case types.Int8: 1435 min = "math.MinInt8" 1436 max = "math.MaxInt8" 1437 case types.Int16: 1438 min = "math.MinInt16" 1439 max = "math.MaxInt16" 1440 case types.Int32: 1441 min = "math.MinInt32" 1442 max = "math.MaxInt32" 1443 case types.Int64: 1444 min = "math.MinInt64" 1445 max = "math.MaxInt64" 1446 case types.Int: 1447 min = "math.MinInt64" 1448 max = "math.MaxInt64" 1449 } 1450 1451 if (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.Y, max) || 1452 (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.X, max) { 1453 j.Errorf(expr, "no value of type %s is greater than %s", basic, max) 1454 } 1455 if expr.Op == token.LEQ && isobj(expr.Y, max) || 1456 expr.Op == token.GEQ && isobj(expr.X, max) { 1457 j.Errorf(expr, "every value of type %s is <= %s", basic, max) 1458 } 1459 1460 if (basic.Info() & types.IsUnsigned) != 0 { 1461 if (expr.Op == token.LSS || expr.Op == token.LEQ) && IsIntLiteral(expr.Y, "0") || 1462 (expr.Op == token.GTR || expr.Op == token.GEQ) && IsIntLiteral(expr.X, "0") { 1463 j.Errorf(expr, "no value of type %s is less than 0", basic) 1464 } 1465 if expr.Op == token.GEQ && IsIntLiteral(expr.Y, "0") || 1466 expr.Op == token.LEQ && IsIntLiteral(expr.X, "0") { 1467 j.Errorf(expr, "every value of type %s is >= 0", basic) 1468 } 1469 } else { 1470 if (expr.Op == token.LSS || expr.Op == token.LEQ) && isobj(expr.Y, min) || 1471 (expr.Op == token.GTR || expr.Op == token.GEQ) && isobj(expr.X, min) { 1472 j.Errorf(expr, "no value of type %s is less than %s", basic, min) 1473 } 1474 if expr.Op == token.GEQ && isobj(expr.Y, min) || 1475 expr.Op == token.LEQ && isobj(expr.X, min) { 1476 j.Errorf(expr, "every value of type %s is >= %s", basic, min) 1477 } 1478 } 1479 1480 return true 1481 } 1482 for _, f := range j.Program.Files { 1483 ast.Inspect(f, fn) 1484 } 1485 } 1486 1487 func consts(val ssa.Value, out []*ssa.Const, visitedPhis map[string]bool) ([]*ssa.Const, bool) { 1488 if visitedPhis == nil { 1489 visitedPhis = map[string]bool{} 1490 } 1491 var ok bool 1492 switch val := val.(type) { 1493 case *ssa.Phi: 1494 if visitedPhis[val.Name()] { 1495 break 1496 } 1497 visitedPhis[val.Name()] = true 1498 vals := val.Operands(nil) 1499 for _, phival := range vals { 1500 out, ok = consts(*phival, out, visitedPhis) 1501 if !ok { 1502 return nil, false 1503 } 1504 } 1505 case *ssa.Const: 1506 out = append(out, val) 1507 case *ssa.Convert: 1508 out, ok = consts(val.X, out, visitedPhis) 1509 if !ok { 1510 return nil, false 1511 } 1512 default: 1513 return nil, false 1514 } 1515 if len(out) < 2 { 1516 return out, true 1517 } 1518 uniq := []*ssa.Const{out[0]} 1519 for _, val := range out[1:] { 1520 if val.Value == uniq[len(uniq)-1].Value { 1521 continue 1522 } 1523 uniq = append(uniq, val) 1524 } 1525 return uniq, true 1526 } 1527 1528 func (c *Checker) CheckLoopCondition(j *lint.Job) { 1529 for _, ssafn := range j.Program.InitialFunctions { 1530 fn := func(node ast.Node) bool { 1531 loop, ok := node.(*ast.ForStmt) 1532 if !ok { 1533 return true 1534 } 1535 if loop.Init == nil || loop.Cond == nil || loop.Post == nil { 1536 return true 1537 } 1538 init, ok := loop.Init.(*ast.AssignStmt) 1539 if !ok || len(init.Lhs) != 1 || len(init.Rhs) != 1 { 1540 return true 1541 } 1542 cond, ok := loop.Cond.(*ast.BinaryExpr) 1543 if !ok { 1544 return true 1545 } 1546 x, ok := cond.X.(*ast.Ident) 1547 if !ok { 1548 return true 1549 } 1550 lhs, ok := init.Lhs[0].(*ast.Ident) 1551 if !ok { 1552 return true 1553 } 1554 if x.Obj != lhs.Obj { 1555 return true 1556 } 1557 if _, ok := loop.Post.(*ast.IncDecStmt); !ok { 1558 return true 1559 } 1560 1561 v, isAddr := ssafn.ValueForExpr(cond.X) 1562 if v == nil || isAddr { 1563 return true 1564 } 1565 switch v := v.(type) { 1566 case *ssa.Phi: 1567 ops := v.Operands(nil) 1568 if len(ops) != 2 { 1569 return true 1570 } 1571 _, ok := (*ops[0]).(*ssa.Const) 1572 if !ok { 1573 return true 1574 } 1575 sigma, ok := (*ops[1]).(*ssa.Sigma) 1576 if !ok { 1577 return true 1578 } 1579 if sigma.X != v { 1580 return true 1581 } 1582 case *ssa.UnOp: 1583 return true 1584 } 1585 j.Errorf(cond, "variable in loop condition never changes") 1586 1587 return true 1588 } 1589 Inspect(ssafn.Syntax(), fn) 1590 } 1591 } 1592 1593 func (c *Checker) CheckArgOverwritten(j *lint.Job) { 1594 for _, ssafn := range j.Program.InitialFunctions { 1595 fn := func(node ast.Node) bool { 1596 var typ *ast.FuncType 1597 var body *ast.BlockStmt 1598 switch fn := node.(type) { 1599 case *ast.FuncDecl: 1600 typ = fn.Type 1601 body = fn.Body 1602 case *ast.FuncLit: 1603 typ = fn.Type 1604 body = fn.Body 1605 } 1606 if body == nil { 1607 return true 1608 } 1609 if len(typ.Params.List) == 0 { 1610 return true 1611 } 1612 for _, field := range typ.Params.List { 1613 for _, arg := range field.Names { 1614 obj := ObjectOf(j, arg) 1615 var ssaobj *ssa.Parameter 1616 for _, param := range ssafn.Params { 1617 if param.Object() == obj { 1618 ssaobj = param 1619 break 1620 } 1621 } 1622 if ssaobj == nil { 1623 continue 1624 } 1625 refs := ssaobj.Referrers() 1626 if refs == nil { 1627 continue 1628 } 1629 if len(FilterDebug(*refs)) != 0 { 1630 continue 1631 } 1632 1633 assigned := false 1634 ast.Inspect(body, func(node ast.Node) bool { 1635 assign, ok := node.(*ast.AssignStmt) 1636 if !ok { 1637 return true 1638 } 1639 for _, lhs := range assign.Lhs { 1640 ident, ok := lhs.(*ast.Ident) 1641 if !ok { 1642 continue 1643 } 1644 if ObjectOf(j, ident) == obj { 1645 assigned = true 1646 return false 1647 } 1648 } 1649 return true 1650 }) 1651 if assigned { 1652 j.Errorf(arg, "argument %s is overwritten before first use", arg) 1653 } 1654 } 1655 } 1656 return true 1657 } 1658 Inspect(ssafn.Syntax(), fn) 1659 } 1660 } 1661 1662 func (c *Checker) CheckIneffectiveLoop(j *lint.Job) { 1663 // This check detects some, but not all unconditional loop exits. 1664 // We give up in the following cases: 1665 // 1666 // - a goto anywhere in the loop. The goto might skip over our 1667 // return, and we don't check that it doesn't. 1668 // 1669 // - any nested, unlabelled continue, even if it is in another 1670 // loop or closure. 1671 fn := func(node ast.Node) bool { 1672 var body *ast.BlockStmt 1673 switch fn := node.(type) { 1674 case *ast.FuncDecl: 1675 body = fn.Body 1676 case *ast.FuncLit: 1677 body = fn.Body 1678 default: 1679 return true 1680 } 1681 if body == nil { 1682 return true 1683 } 1684 labels := map[*ast.Object]ast.Stmt{} 1685 ast.Inspect(body, func(node ast.Node) bool { 1686 label, ok := node.(*ast.LabeledStmt) 1687 if !ok { 1688 return true 1689 } 1690 labels[label.Label.Obj] = label.Stmt 1691 return true 1692 }) 1693 1694 ast.Inspect(body, func(node ast.Node) bool { 1695 var loop ast.Node 1696 var body *ast.BlockStmt 1697 switch node := node.(type) { 1698 case *ast.ForStmt: 1699 body = node.Body 1700 loop = node 1701 case *ast.RangeStmt: 1702 typ := TypeOf(j, node.X) 1703 if _, ok := typ.Underlying().(*types.Map); ok { 1704 // looping once over a map is a valid pattern for 1705 // getting an arbitrary element. 1706 return true 1707 } 1708 body = node.Body 1709 loop = node 1710 default: 1711 return true 1712 } 1713 if len(body.List) < 2 { 1714 // avoid flagging the somewhat common pattern of using 1715 // a range loop to get the first element in a slice, 1716 // or the first rune in a string. 1717 return true 1718 } 1719 var unconditionalExit ast.Node 1720 hasBranching := false 1721 for _, stmt := range body.List { 1722 switch stmt := stmt.(type) { 1723 case *ast.BranchStmt: 1724 switch stmt.Tok { 1725 case token.BREAK: 1726 if stmt.Label == nil || labels[stmt.Label.Obj] == loop { 1727 unconditionalExit = stmt 1728 } 1729 case token.CONTINUE: 1730 if stmt.Label == nil || labels[stmt.Label.Obj] == loop { 1731 unconditionalExit = nil 1732 return false 1733 } 1734 } 1735 case *ast.ReturnStmt: 1736 unconditionalExit = stmt 1737 case *ast.IfStmt, *ast.ForStmt, *ast.RangeStmt, *ast.SwitchStmt, *ast.SelectStmt: 1738 hasBranching = true 1739 } 1740 } 1741 if unconditionalExit == nil || !hasBranching { 1742 return false 1743 } 1744 ast.Inspect(body, func(node ast.Node) bool { 1745 if branch, ok := node.(*ast.BranchStmt); ok { 1746 1747 switch branch.Tok { 1748 case token.GOTO: 1749 unconditionalExit = nil 1750 return false 1751 case token.CONTINUE: 1752 if branch.Label != nil && labels[branch.Label.Obj] != loop { 1753 return true 1754 } 1755 unconditionalExit = nil 1756 return false 1757 } 1758 } 1759 return true 1760 }) 1761 if unconditionalExit != nil { 1762 j.Errorf(unconditionalExit, "the surrounding loop is unconditionally terminated") 1763 } 1764 return true 1765 }) 1766 return true 1767 } 1768 for _, f := range j.Program.Files { 1769 ast.Inspect(f, fn) 1770 } 1771 } 1772 1773 func (c *Checker) CheckNilContext(j *lint.Job) { 1774 fn := func(node ast.Node) bool { 1775 call, ok := node.(*ast.CallExpr) 1776 if !ok { 1777 return true 1778 } 1779 if len(call.Args) == 0 { 1780 return true 1781 } 1782 if typ, ok := TypeOf(j, call.Args[0]).(*types.Basic); !ok || typ.Kind() != types.UntypedNil { 1783 return true 1784 } 1785 sig, ok := TypeOf(j, call.Fun).(*types.Signature) 1786 if !ok { 1787 return true 1788 } 1789 if sig.Params().Len() == 0 { 1790 return true 1791 } 1792 if !IsType(sig.Params().At(0).Type(), "context.Context") { 1793 return true 1794 } 1795 j.Errorf(call.Args[0], 1796 "do not pass a nil Context, even if a function permits it; pass context.TODO if you are unsure about which Context to use") 1797 return true 1798 } 1799 for _, f := range j.Program.Files { 1800 ast.Inspect(f, fn) 1801 } 1802 } 1803 1804 func (c *Checker) CheckSeeker(j *lint.Job) { 1805 fn := func(node ast.Node) bool { 1806 call, ok := node.(*ast.CallExpr) 1807 if !ok { 1808 return true 1809 } 1810 sel, ok := call.Fun.(*ast.SelectorExpr) 1811 if !ok { 1812 return true 1813 } 1814 if sel.Sel.Name != "Seek" { 1815 return true 1816 } 1817 if len(call.Args) != 2 { 1818 return true 1819 } 1820 arg0, ok := call.Args[Arg("(io.Seeker).Seek.offset")].(*ast.SelectorExpr) 1821 if !ok { 1822 return true 1823 } 1824 switch arg0.Sel.Name { 1825 case "SeekStart", "SeekCurrent", "SeekEnd": 1826 default: 1827 return true 1828 } 1829 pkg, ok := arg0.X.(*ast.Ident) 1830 if !ok { 1831 return true 1832 } 1833 if pkg.Name != "io" { 1834 return true 1835 } 1836 j.Errorf(call, "the first argument of io.Seeker is the offset, but an io.Seek* constant is being used instead") 1837 return true 1838 } 1839 for _, f := range j.Program.Files { 1840 ast.Inspect(f, fn) 1841 } 1842 } 1843 1844 func (c *Checker) CheckIneffectiveAppend(j *lint.Job) { 1845 isAppend := func(ins ssa.Value) bool { 1846 call, ok := ins.(*ssa.Call) 1847 if !ok { 1848 return false 1849 } 1850 if call.Call.IsInvoke() { 1851 return false 1852 } 1853 if builtin, ok := call.Call.Value.(*ssa.Builtin); !ok || builtin.Name() != "append" { 1854 return false 1855 } 1856 return true 1857 } 1858 1859 for _, ssafn := range j.Program.InitialFunctions { 1860 for _, block := range ssafn.Blocks { 1861 for _, ins := range block.Instrs { 1862 val, ok := ins.(ssa.Value) 1863 if !ok || !isAppend(val) { 1864 continue 1865 } 1866 1867 isUsed := false 1868 visited := map[ssa.Instruction]bool{} 1869 var walkRefs func(refs []ssa.Instruction) 1870 walkRefs = func(refs []ssa.Instruction) { 1871 loop: 1872 for _, ref := range refs { 1873 if visited[ref] { 1874 continue 1875 } 1876 visited[ref] = true 1877 if _, ok := ref.(*ssa.DebugRef); ok { 1878 continue 1879 } 1880 switch ref := ref.(type) { 1881 case *ssa.Phi: 1882 walkRefs(*ref.Referrers()) 1883 case *ssa.Sigma: 1884 walkRefs(*ref.Referrers()) 1885 case ssa.Value: 1886 if !isAppend(ref) { 1887 isUsed = true 1888 } else { 1889 walkRefs(*ref.Referrers()) 1890 } 1891 case ssa.Instruction: 1892 isUsed = true 1893 break loop 1894 } 1895 } 1896 } 1897 refs := val.Referrers() 1898 if refs == nil { 1899 continue 1900 } 1901 walkRefs(*refs) 1902 if !isUsed { 1903 j.Errorf(ins, "this result of append is never used, except maybe in other appends") 1904 } 1905 } 1906 } 1907 } 1908 } 1909 1910 func (c *Checker) CheckConcurrentTesting(j *lint.Job) { 1911 for _, ssafn := range j.Program.InitialFunctions { 1912 for _, block := range ssafn.Blocks { 1913 for _, ins := range block.Instrs { 1914 gostmt, ok := ins.(*ssa.Go) 1915 if !ok { 1916 continue 1917 } 1918 var fn *ssa.Function 1919 switch val := gostmt.Call.Value.(type) { 1920 case *ssa.Function: 1921 fn = val 1922 case *ssa.MakeClosure: 1923 fn = val.Fn.(*ssa.Function) 1924 default: 1925 continue 1926 } 1927 if fn.Blocks == nil { 1928 continue 1929 } 1930 for _, block := range fn.Blocks { 1931 for _, ins := range block.Instrs { 1932 call, ok := ins.(*ssa.Call) 1933 if !ok { 1934 continue 1935 } 1936 if call.Call.IsInvoke() { 1937 continue 1938 } 1939 callee := call.Call.StaticCallee() 1940 if callee == nil { 1941 continue 1942 } 1943 recv := callee.Signature.Recv() 1944 if recv == nil { 1945 continue 1946 } 1947 if !IsType(recv.Type(), "*testing.common") { 1948 continue 1949 } 1950 fn, ok := call.Call.StaticCallee().Object().(*types.Func) 1951 if !ok { 1952 continue 1953 } 1954 name := fn.Name() 1955 switch name { 1956 case "FailNow", "Fatal", "Fatalf", "SkipNow", "Skip", "Skipf": 1957 default: 1958 continue 1959 } 1960 j.Errorf(gostmt, "the goroutine calls T.%s, which must be called in the same goroutine as the test", name) 1961 } 1962 } 1963 } 1964 } 1965 } 1966 } 1967 1968 func (c *Checker) CheckCyclicFinalizer(j *lint.Job) { 1969 for _, ssafn := range j.Program.InitialFunctions { 1970 node := c.funcDescs.CallGraph.CreateNode(ssafn) 1971 for _, edge := range node.Out { 1972 if edge.Callee.Func.RelString(nil) != "runtime.SetFinalizer" { 1973 continue 1974 } 1975 arg0 := edge.Site.Common().Args[Arg("runtime.SetFinalizer.obj")] 1976 if iface, ok := arg0.(*ssa.MakeInterface); ok { 1977 arg0 = iface.X 1978 } 1979 unop, ok := arg0.(*ssa.UnOp) 1980 if !ok { 1981 continue 1982 } 1983 v, ok := unop.X.(*ssa.Alloc) 1984 if !ok { 1985 continue 1986 } 1987 arg1 := edge.Site.Common().Args[Arg("runtime.SetFinalizer.finalizer")] 1988 if iface, ok := arg1.(*ssa.MakeInterface); ok { 1989 arg1 = iface.X 1990 } 1991 mc, ok := arg1.(*ssa.MakeClosure) 1992 if !ok { 1993 continue 1994 } 1995 for _, b := range mc.Bindings { 1996 if b == v { 1997 pos := j.Program.DisplayPosition(mc.Fn.Pos()) 1998 j.Errorf(edge.Site, "the finalizer closes over the object, preventing the finalizer from ever running (at %s)", pos) 1999 } 2000 } 2001 } 2002 } 2003 } 2004 2005 func (c *Checker) CheckSliceOutOfBounds(j *lint.Job) { 2006 for _, ssafn := range j.Program.InitialFunctions { 2007 for _, block := range ssafn.Blocks { 2008 for _, ins := range block.Instrs { 2009 ia, ok := ins.(*ssa.IndexAddr) 2010 if !ok { 2011 continue 2012 } 2013 if _, ok := ia.X.Type().Underlying().(*types.Slice); !ok { 2014 continue 2015 } 2016 sr, ok1 := c.funcDescs.Get(ssafn).Ranges[ia.X].(vrp.SliceInterval) 2017 idxr, ok2 := c.funcDescs.Get(ssafn).Ranges[ia.Index].(vrp.IntInterval) 2018 if !ok1 || !ok2 || !sr.IsKnown() || !idxr.IsKnown() || sr.Length.Empty() || idxr.Empty() { 2019 continue 2020 } 2021 if idxr.Lower.Cmp(sr.Length.Upper) >= 0 { 2022 j.Errorf(ia, "index out of bounds") 2023 } 2024 } 2025 } 2026 } 2027 } 2028 2029 func (c *Checker) CheckDeferLock(j *lint.Job) { 2030 for _, ssafn := range j.Program.InitialFunctions { 2031 for _, block := range ssafn.Blocks { 2032 instrs := FilterDebug(block.Instrs) 2033 if len(instrs) < 2 { 2034 continue 2035 } 2036 for i, ins := range instrs[:len(instrs)-1] { 2037 call, ok := ins.(*ssa.Call) 2038 if !ok { 2039 continue 2040 } 2041 if !IsCallTo(call.Common(), "(*sync.Mutex).Lock") && !IsCallTo(call.Common(), "(*sync.RWMutex).RLock") { 2042 continue 2043 } 2044 nins, ok := instrs[i+1].(*ssa.Defer) 2045 if !ok { 2046 continue 2047 } 2048 if !IsCallTo(&nins.Call, "(*sync.Mutex).Lock") && !IsCallTo(&nins.Call, "(*sync.RWMutex).RLock") { 2049 continue 2050 } 2051 if call.Common().Args[0] != nins.Call.Args[0] { 2052 continue 2053 } 2054 name := shortCallName(call.Common()) 2055 alt := "" 2056 switch name { 2057 case "Lock": 2058 alt = "Unlock" 2059 case "RLock": 2060 alt = "RUnlock" 2061 } 2062 j.Errorf(nins, "deferring %s right after having locked already; did you mean to defer %s?", name, alt) 2063 } 2064 } 2065 } 2066 } 2067 2068 func (c *Checker) CheckNaNComparison(j *lint.Job) { 2069 isNaN := func(v ssa.Value) bool { 2070 call, ok := v.(*ssa.Call) 2071 if !ok { 2072 return false 2073 } 2074 return IsCallTo(call.Common(), "math.NaN") 2075 } 2076 for _, ssafn := range j.Program.InitialFunctions { 2077 for _, block := range ssafn.Blocks { 2078 for _, ins := range block.Instrs { 2079 ins, ok := ins.(*ssa.BinOp) 2080 if !ok { 2081 continue 2082 } 2083 if isNaN(ins.X) || isNaN(ins.Y) { 2084 j.Errorf(ins, "no value is equal to NaN, not even NaN itself") 2085 } 2086 } 2087 } 2088 } 2089 } 2090 2091 func (c *Checker) CheckInfiniteRecursion(j *lint.Job) { 2092 for _, ssafn := range j.Program.InitialFunctions { 2093 node := c.funcDescs.CallGraph.CreateNode(ssafn) 2094 for _, edge := range node.Out { 2095 if edge.Callee != node { 2096 continue 2097 } 2098 if _, ok := edge.Site.(*ssa.Go); ok { 2099 // Recursively spawning goroutines doesn't consume 2100 // stack space infinitely, so don't flag it. 2101 continue 2102 } 2103 2104 block := edge.Site.Block() 2105 canReturn := false 2106 for _, b := range ssafn.Blocks { 2107 if block.Dominates(b) { 2108 continue 2109 } 2110 if len(b.Instrs) == 0 { 2111 continue 2112 } 2113 if _, ok := b.Instrs[len(b.Instrs)-1].(*ssa.Return); ok { 2114 canReturn = true 2115 break 2116 } 2117 } 2118 if canReturn { 2119 continue 2120 } 2121 j.Errorf(edge.Site, "infinite recursive call") 2122 } 2123 } 2124 } 2125 2126 func objectName(obj types.Object) string { 2127 if obj == nil { 2128 return "<nil>" 2129 } 2130 var name string 2131 if obj.Pkg() != nil && obj.Pkg().Scope().Lookup(obj.Name()) == obj { 2132 s := obj.Pkg().Path() 2133 if s != "" { 2134 name += s + "." 2135 } 2136 } 2137 name += obj.Name() 2138 return name 2139 } 2140 2141 func isName(j *lint.Job, expr ast.Expr, name string) bool { 2142 var obj types.Object 2143 switch expr := expr.(type) { 2144 case *ast.Ident: 2145 obj = ObjectOf(j, expr) 2146 case *ast.SelectorExpr: 2147 obj = ObjectOf(j, expr.Sel) 2148 } 2149 return objectName(obj) == name 2150 } 2151 2152 func (c *Checker) CheckLeakyTimeTick(j *lint.Job) { 2153 for _, ssafn := range j.Program.InitialFunctions { 2154 if IsInMain(j, ssafn) || IsInTest(j, ssafn) { 2155 continue 2156 } 2157 for _, block := range ssafn.Blocks { 2158 for _, ins := range block.Instrs { 2159 call, ok := ins.(*ssa.Call) 2160 if !ok || !IsCallTo(call.Common(), "time.Tick") { 2161 continue 2162 } 2163 if c.funcDescs.Get(call.Parent()).Infinite { 2164 continue 2165 } 2166 j.Errorf(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") 2167 } 2168 } 2169 } 2170 } 2171 2172 func (c *Checker) CheckDoubleNegation(j *lint.Job) { 2173 fn := func(node ast.Node) bool { 2174 unary1, ok := node.(*ast.UnaryExpr) 2175 if !ok { 2176 return true 2177 } 2178 unary2, ok := unary1.X.(*ast.UnaryExpr) 2179 if !ok { 2180 return true 2181 } 2182 if unary1.Op != token.NOT || unary2.Op != token.NOT { 2183 return true 2184 } 2185 j.Errorf(unary1, "negating a boolean twice has no effect; is this a typo?") 2186 return true 2187 } 2188 for _, f := range j.Program.Files { 2189 ast.Inspect(f, fn) 2190 } 2191 } 2192 2193 func hasSideEffects(node ast.Node) bool { 2194 dynamic := false 2195 ast.Inspect(node, func(node ast.Node) bool { 2196 switch node := node.(type) { 2197 case *ast.CallExpr: 2198 dynamic = true 2199 return false 2200 case *ast.UnaryExpr: 2201 if node.Op == token.ARROW { 2202 dynamic = true 2203 return false 2204 } 2205 } 2206 return true 2207 }) 2208 return dynamic 2209 } 2210 2211 func (c *Checker) CheckRepeatedIfElse(j *lint.Job) { 2212 seen := map[ast.Node]bool{} 2213 2214 var collectConds func(ifstmt *ast.IfStmt, inits []ast.Stmt, conds []ast.Expr) ([]ast.Stmt, []ast.Expr) 2215 collectConds = func(ifstmt *ast.IfStmt, inits []ast.Stmt, conds []ast.Expr) ([]ast.Stmt, []ast.Expr) { 2216 seen[ifstmt] = true 2217 if ifstmt.Init != nil { 2218 inits = append(inits, ifstmt.Init) 2219 } 2220 conds = append(conds, ifstmt.Cond) 2221 if elsestmt, ok := ifstmt.Else.(*ast.IfStmt); ok { 2222 return collectConds(elsestmt, inits, conds) 2223 } 2224 return inits, conds 2225 } 2226 fn := func(node ast.Node) bool { 2227 ifstmt, ok := node.(*ast.IfStmt) 2228 if !ok { 2229 return true 2230 } 2231 if seen[ifstmt] { 2232 return true 2233 } 2234 inits, conds := collectConds(ifstmt, nil, nil) 2235 if len(inits) > 0 { 2236 return true 2237 } 2238 for _, cond := range conds { 2239 if hasSideEffects(cond) { 2240 return true 2241 } 2242 } 2243 counts := map[string]int{} 2244 for _, cond := range conds { 2245 s := Render(j, cond) 2246 counts[s]++ 2247 if counts[s] == 2 { 2248 j.Errorf(cond, "this condition occurs multiple times in this if/else if chain") 2249 } 2250 } 2251 return true 2252 } 2253 for _, f := range j.Program.Files { 2254 ast.Inspect(f, fn) 2255 } 2256 } 2257 2258 func (c *Checker) CheckSillyBitwiseOps(j *lint.Job) { 2259 for _, ssafn := range j.Program.InitialFunctions { 2260 for _, block := range ssafn.Blocks { 2261 for _, ins := range block.Instrs { 2262 ins, ok := ins.(*ssa.BinOp) 2263 if !ok { 2264 continue 2265 } 2266 2267 if c, ok := ins.Y.(*ssa.Const); !ok || c.Value == nil || c.Value.Kind() != constant.Int || c.Uint64() != 0 { 2268 continue 2269 } 2270 switch ins.Op { 2271 case token.AND, token.OR, token.XOR: 2272 default: 2273 // we do not flag shifts because too often, x<<0 is part 2274 // of a pattern, x<<0, x<<8, x<<16, ... 2275 continue 2276 } 2277 path, _ := astutil.PathEnclosingInterval(j.File(ins), ins.Pos(), ins.Pos()) 2278 if len(path) == 0 { 2279 continue 2280 } 2281 if node, ok := path[0].(*ast.BinaryExpr); !ok || !IsZero(node.Y) { 2282 continue 2283 } 2284 2285 switch ins.Op { 2286 case token.AND: 2287 j.Errorf(ins, "x & 0 always equals 0") 2288 case token.OR, token.XOR: 2289 j.Errorf(ins, "x %s 0 always equals x", ins.Op) 2290 } 2291 } 2292 } 2293 } 2294 } 2295 2296 func (c *Checker) CheckNonOctalFileMode(j *lint.Job) { 2297 fn := func(node ast.Node) bool { 2298 call, ok := node.(*ast.CallExpr) 2299 if !ok { 2300 return true 2301 } 2302 sig, ok := TypeOf(j, call.Fun).(*types.Signature) 2303 if !ok { 2304 return true 2305 } 2306 n := sig.Params().Len() 2307 var args []int 2308 for i := 0; i < n; i++ { 2309 typ := sig.Params().At(i).Type() 2310 if IsType(typ, "os.FileMode") { 2311 args = append(args, i) 2312 } 2313 } 2314 for _, i := range args { 2315 lit, ok := call.Args[i].(*ast.BasicLit) 2316 if !ok { 2317 continue 2318 } 2319 if len(lit.Value) == 3 && 2320 lit.Value[0] != '0' && 2321 lit.Value[0] >= '0' && lit.Value[0] <= '7' && 2322 lit.Value[1] >= '0' && lit.Value[1] <= '7' && 2323 lit.Value[2] >= '0' && lit.Value[2] <= '7' { 2324 2325 v, err := strconv.ParseInt(lit.Value, 10, 64) 2326 if err != nil { 2327 continue 2328 } 2329 j.Errorf(call.Args[i], "file mode '%s' evaluates to %#o; did you mean '0%s'?", lit.Value, v, lit.Value) 2330 } 2331 } 2332 return true 2333 } 2334 for _, f := range j.Program.Files { 2335 ast.Inspect(f, fn) 2336 } 2337 } 2338 2339 func (c *Checker) CheckPureFunctions(j *lint.Job) { 2340 fnLoop: 2341 for _, ssafn := range j.Program.InitialFunctions { 2342 if IsInTest(j, ssafn) { 2343 params := ssafn.Signature.Params() 2344 for i := 0; i < params.Len(); i++ { 2345 param := params.At(i) 2346 if IsType(param.Type(), "*testing.B") { 2347 // Ignore discarded pure functions in code related 2348 // to benchmarks. Instead of matching BenchmarkFoo 2349 // functions, we match any function accepting a 2350 // *testing.B. Benchmarks sometimes call generic 2351 // functions for doing the actual work, and 2352 // checking for the parameter is a lot easier and 2353 // faster than analyzing call trees. 2354 continue fnLoop 2355 } 2356 } 2357 } 2358 2359 for _, b := range ssafn.Blocks { 2360 for _, ins := range b.Instrs { 2361 ins, ok := ins.(*ssa.Call) 2362 if !ok { 2363 continue 2364 } 2365 refs := ins.Referrers() 2366 if refs == nil || len(FilterDebug(*refs)) > 0 { 2367 continue 2368 } 2369 callee := ins.Common().StaticCallee() 2370 if callee == nil { 2371 continue 2372 } 2373 if c.funcDescs.Get(callee).Pure && !c.funcDescs.Get(callee).Stub { 2374 j.Errorf(ins, "%s is a pure function but its return value is ignored", callee.Name()) 2375 continue 2376 } 2377 } 2378 } 2379 } 2380 } 2381 2382 func (c *Checker) isDeprecated(j *lint.Job, ident *ast.Ident) (bool, string) { 2383 obj := ObjectOf(j, ident) 2384 if obj.Pkg() == nil { 2385 return false, "" 2386 } 2387 alt := c.deprecatedObjs[obj] 2388 return alt != "", alt 2389 } 2390 2391 func (c *Checker) CheckDeprecated(j *lint.Job) { 2392 // Selectors can appear outside of function literals, e.g. when 2393 // declaring package level variables. 2394 2395 var ssafn *ssa.Function 2396 stack := 0 2397 fn := func(node ast.Node) bool { 2398 if node == nil { 2399 stack-- 2400 } else { 2401 stack++ 2402 } 2403 if stack == 1 { 2404 ssafn = nil 2405 } 2406 if fn, ok := node.(*ast.FuncDecl); ok { 2407 ssafn = j.Program.SSA.FuncValue(ObjectOf(j, fn.Name).(*types.Func)) 2408 } 2409 sel, ok := node.(*ast.SelectorExpr) 2410 if !ok { 2411 return true 2412 } 2413 2414 obj := ObjectOf(j, sel.Sel) 2415 if obj.Pkg() == nil { 2416 return true 2417 } 2418 nodePkg := j.NodePackage(node).Types 2419 if nodePkg == obj.Pkg() || obj.Pkg().Path()+"_test" == nodePkg.Path() { 2420 // Don't flag stuff in our own package 2421 return true 2422 } 2423 if ok, alt := c.isDeprecated(j, sel.Sel); ok { 2424 // Look for the first available alternative, not the first 2425 // version something was deprecated in. If a function was 2426 // deprecated in Go 1.6, an alternative has been available 2427 // already in 1.0, and we're targeting 1.2, it still 2428 // makes sense to use the alternative from 1.0, to be 2429 // future-proof. 2430 minVersion := deprecated.Stdlib[SelectorName(j, sel)].AlternativeAvailableSince 2431 if !IsGoVersion(j, minVersion) { 2432 return true 2433 } 2434 2435 if ssafn != nil { 2436 if _, ok := c.deprecatedObjs[ssafn.Object()]; ok { 2437 // functions that are deprecated may use deprecated 2438 // symbols 2439 return true 2440 } 2441 } 2442 j.Errorf(sel, "%s is deprecated: %s", Render(j, sel), alt) 2443 return true 2444 } 2445 return true 2446 } 2447 for _, f := range j.Program.Files { 2448 ast.Inspect(f, fn) 2449 } 2450 } 2451 2452 func (c *Checker) callChecker(rules map[string]CallCheck) func(j *lint.Job) { 2453 return func(j *lint.Job) { 2454 c.checkCalls(j, rules) 2455 } 2456 } 2457 2458 func (c *Checker) checkCalls(j *lint.Job, rules map[string]CallCheck) { 2459 for _, ssafn := range j.Program.InitialFunctions { 2460 node := c.funcDescs.CallGraph.CreateNode(ssafn) 2461 for _, edge := range node.Out { 2462 callee := edge.Callee.Func 2463 obj, ok := callee.Object().(*types.Func) 2464 if !ok { 2465 continue 2466 } 2467 2468 r, ok := rules[obj.FullName()] 2469 if !ok { 2470 continue 2471 } 2472 var args []*Argument 2473 ssaargs := edge.Site.Common().Args 2474 if callee.Signature.Recv() != nil { 2475 ssaargs = ssaargs[1:] 2476 } 2477 for _, arg := range ssaargs { 2478 if iarg, ok := arg.(*ssa.MakeInterface); ok { 2479 arg = iarg.X 2480 } 2481 vr := c.funcDescs.Get(edge.Site.Parent()).Ranges[arg] 2482 args = append(args, &Argument{Value: Value{arg, vr}}) 2483 } 2484 call := &Call{ 2485 Job: j, 2486 Instr: edge.Site, 2487 Args: args, 2488 Checker: c, 2489 Parent: edge.Site.Parent(), 2490 } 2491 r(call) 2492 for idx, arg := range call.Args { 2493 _ = idx 2494 for _, e := range arg.invalids { 2495 // path, _ := astutil.PathEnclosingInterval(f.File, edge.Site.Pos(), edge.Site.Pos()) 2496 // if len(path) < 2 { 2497 // continue 2498 // } 2499 // astcall, ok := path[0].(*ast.CallExpr) 2500 // if !ok { 2501 // continue 2502 // } 2503 // j.Errorf(astcall.Args[idx], "%s", e) 2504 2505 j.Errorf(edge.Site, "%s", e) 2506 } 2507 } 2508 for _, e := range call.invalids { 2509 j.Errorf(call.Instr.Common(), "%s", e) 2510 } 2511 } 2512 } 2513 } 2514 2515 func shortCallName(call *ssa.CallCommon) string { 2516 if call.IsInvoke() { 2517 return "" 2518 } 2519 switch v := call.Value.(type) { 2520 case *ssa.Function: 2521 fn, ok := v.Object().(*types.Func) 2522 if !ok { 2523 return "" 2524 } 2525 return fn.Name() 2526 case *ssa.Builtin: 2527 return v.Name() 2528 } 2529 return "" 2530 } 2531 2532 func (c *Checker) CheckWriterBufferModified(j *lint.Job) { 2533 // TODO(dh): this might be a good candidate for taint analysis. 2534 // Taint the argument as MUST_NOT_MODIFY, then propagate that 2535 // through functions like bytes.Split 2536 2537 for _, ssafn := range j.Program.InitialFunctions { 2538 sig := ssafn.Signature 2539 if ssafn.Name() != "Write" || sig.Recv() == nil || sig.Params().Len() != 1 || sig.Results().Len() != 2 { 2540 continue 2541 } 2542 tArg, ok := sig.Params().At(0).Type().(*types.Slice) 2543 if !ok { 2544 continue 2545 } 2546 if basic, ok := tArg.Elem().(*types.Basic); !ok || basic.Kind() != types.Byte { 2547 continue 2548 } 2549 if basic, ok := sig.Results().At(0).Type().(*types.Basic); !ok || basic.Kind() != types.Int { 2550 continue 2551 } 2552 if named, ok := sig.Results().At(1).Type().(*types.Named); !ok || !IsType(named, "error") { 2553 continue 2554 } 2555 2556 for _, block := range ssafn.Blocks { 2557 for _, ins := range block.Instrs { 2558 switch ins := ins.(type) { 2559 case *ssa.Store: 2560 addr, ok := ins.Addr.(*ssa.IndexAddr) 2561 if !ok { 2562 continue 2563 } 2564 if addr.X != ssafn.Params[1] { 2565 continue 2566 } 2567 j.Errorf(ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") 2568 case *ssa.Call: 2569 if !IsCallTo(ins.Common(), "append") { 2570 continue 2571 } 2572 if ins.Common().Args[0] != ssafn.Params[1] { 2573 continue 2574 } 2575 j.Errorf(ins, "io.Writer.Write must not modify the provided buffer, not even temporarily") 2576 } 2577 } 2578 } 2579 } 2580 } 2581 2582 func loopedRegexp(name string) CallCheck { 2583 return func(call *Call) { 2584 if len(extractConsts(call.Args[0].Value.Value)) == 0 { 2585 return 2586 } 2587 if !call.Checker.isInLoop(call.Instr.Block()) { 2588 return 2589 } 2590 call.Invalid(fmt.Sprintf("calling %s in a loop has poor performance, consider using regexp.Compile", name)) 2591 } 2592 } 2593 2594 func (c *Checker) CheckEmptyBranch(j *lint.Job) { 2595 for _, ssafn := range j.Program.InitialFunctions { 2596 if ssafn.Syntax() == nil { 2597 continue 2598 } 2599 if IsGenerated(j.File(ssafn.Syntax())) { 2600 continue 2601 } 2602 if IsExample(ssafn) { 2603 continue 2604 } 2605 fn := func(node ast.Node) bool { 2606 ifstmt, ok := node.(*ast.IfStmt) 2607 if !ok { 2608 return true 2609 } 2610 if ifstmt.Else != nil { 2611 b, ok := ifstmt.Else.(*ast.BlockStmt) 2612 if !ok || len(b.List) != 0 { 2613 return true 2614 } 2615 j.Errorf(ifstmt.Else, "empty branch") 2616 } 2617 if len(ifstmt.Body.List) != 0 { 2618 return true 2619 } 2620 j.Errorf(ifstmt, "empty branch") 2621 return true 2622 } 2623 Inspect(ssafn.Syntax(), fn) 2624 } 2625 } 2626 2627 func (c *Checker) CheckMapBytesKey(j *lint.Job) { 2628 for _, fn := range j.Program.InitialFunctions { 2629 for _, b := range fn.Blocks { 2630 insLoop: 2631 for _, ins := range b.Instrs { 2632 // find []byte -> string conversions 2633 conv, ok := ins.(*ssa.Convert) 2634 if !ok || conv.Type() != types.Universe.Lookup("string").Type() { 2635 continue 2636 } 2637 if s, ok := conv.X.Type().(*types.Slice); !ok || s.Elem() != types.Universe.Lookup("byte").Type() { 2638 continue 2639 } 2640 refs := conv.Referrers() 2641 // need at least two (DebugRef) references: the 2642 // conversion and the *ast.Ident 2643 if refs == nil || len(*refs) < 2 { 2644 continue 2645 } 2646 ident := false 2647 // skip first reference, that's the conversion itself 2648 for _, ref := range (*refs)[1:] { 2649 switch ref := ref.(type) { 2650 case *ssa.DebugRef: 2651 if _, ok := ref.Expr.(*ast.Ident); !ok { 2652 // the string seems to be used somewhere 2653 // unexpected; the default branch should 2654 // catch this already, but be safe 2655 continue insLoop 2656 } else { 2657 ident = true 2658 } 2659 case *ssa.Lookup: 2660 default: 2661 // the string is used somewhere else than a 2662 // map lookup 2663 continue insLoop 2664 } 2665 } 2666 2667 // the result of the conversion wasn't assigned to an 2668 // identifier 2669 if !ident { 2670 continue 2671 } 2672 j.Errorf(conv, "m[string(key)] would be more efficient than k := string(key); m[k]") 2673 } 2674 } 2675 } 2676 } 2677 2678 func (c *Checker) CheckRangeStringRunes(j *lint.Job) { 2679 sharedcheck.CheckRangeStringRunes(j) 2680 } 2681 2682 func (c *Checker) CheckSelfAssignment(j *lint.Job) { 2683 fn := func(node ast.Node) bool { 2684 assign, ok := node.(*ast.AssignStmt) 2685 if !ok { 2686 return true 2687 } 2688 if assign.Tok != token.ASSIGN || len(assign.Lhs) != len(assign.Rhs) { 2689 return true 2690 } 2691 for i, stmt := range assign.Lhs { 2692 rlh := Render(j, stmt) 2693 rrh := Render(j, assign.Rhs[i]) 2694 if rlh == rrh { 2695 j.Errorf(assign, "self-assignment of %s to %s", rrh, rlh) 2696 } 2697 } 2698 return true 2699 } 2700 for _, f := range j.Program.Files { 2701 ast.Inspect(f, fn) 2702 } 2703 } 2704 2705 func buildTagsIdentical(s1, s2 []string) bool { 2706 if len(s1) != len(s2) { 2707 return false 2708 } 2709 s1s := make([]string, len(s1)) 2710 copy(s1s, s1) 2711 sort.Strings(s1s) 2712 s2s := make([]string, len(s2)) 2713 copy(s2s, s2) 2714 sort.Strings(s2s) 2715 for i, s := range s1s { 2716 if s != s2s[i] { 2717 return false 2718 } 2719 } 2720 return true 2721 } 2722 2723 func (c *Checker) CheckDuplicateBuildConstraints(job *lint.Job) { 2724 for _, f := range job.Program.Files { 2725 constraints := buildTags(f) 2726 for i, constraint1 := range constraints { 2727 for j, constraint2 := range constraints { 2728 if i >= j { 2729 continue 2730 } 2731 if buildTagsIdentical(constraint1, constraint2) { 2732 job.Errorf(f, "identical build constraints %q and %q", 2733 strings.Join(constraint1, " "), 2734 strings.Join(constraint2, " ")) 2735 } 2736 } 2737 } 2738 } 2739 } 2740 2741 func (c *Checker) CheckSillyRegexp(j *lint.Job) { 2742 // We could use the rule checking engine for this, but the 2743 // arguments aren't really invalid. 2744 for _, fn := range j.Program.InitialFunctions { 2745 for _, b := range fn.Blocks { 2746 for _, ins := range b.Instrs { 2747 call, ok := ins.(*ssa.Call) 2748 if !ok { 2749 continue 2750 } 2751 switch CallName(call.Common()) { 2752 case "regexp.MustCompile", "regexp.Compile", "regexp.Match", "regexp.MatchReader", "regexp.MatchString": 2753 default: 2754 continue 2755 } 2756 c, ok := call.Common().Args[0].(*ssa.Const) 2757 if !ok { 2758 continue 2759 } 2760 s := constant.StringVal(c.Value) 2761 re, err := syntax.Parse(s, 0) 2762 if err != nil { 2763 continue 2764 } 2765 if re.Op != syntax.OpLiteral && re.Op != syntax.OpEmptyMatch { 2766 continue 2767 } 2768 j.Errorf(call, "regular expression does not contain any meta characters") 2769 } 2770 } 2771 } 2772 } 2773 2774 func (c *Checker) CheckMissingEnumTypesInDeclaration(j *lint.Job) { 2775 fn := func(node ast.Node) bool { 2776 decl, ok := node.(*ast.GenDecl) 2777 if !ok { 2778 return true 2779 } 2780 if !decl.Lparen.IsValid() { 2781 return true 2782 } 2783 if decl.Tok != token.CONST { 2784 return true 2785 } 2786 2787 groups := GroupSpecs(j, decl.Specs) 2788 groupLoop: 2789 for _, group := range groups { 2790 if len(group) < 2 { 2791 continue 2792 } 2793 if group[0].(*ast.ValueSpec).Type == nil { 2794 // first constant doesn't have a type 2795 continue groupLoop 2796 } 2797 for i, spec := range group { 2798 spec := spec.(*ast.ValueSpec) 2799 if len(spec.Names) != 1 || len(spec.Values) != 1 { 2800 continue groupLoop 2801 } 2802 switch v := spec.Values[0].(type) { 2803 case *ast.BasicLit: 2804 case *ast.UnaryExpr: 2805 if _, ok := v.X.(*ast.BasicLit); !ok { 2806 continue groupLoop 2807 } 2808 default: 2809 // if it's not a literal it might be typed, such as 2810 // time.Microsecond = 1000 * Nanosecond 2811 continue groupLoop 2812 } 2813 if i == 0 { 2814 continue 2815 } 2816 if spec.Type != nil { 2817 continue groupLoop 2818 } 2819 } 2820 j.Errorf(group[0], "only the first constant in this group has an explicit type") 2821 } 2822 return true 2823 } 2824 for _, f := range j.Program.Files { 2825 ast.Inspect(f, fn) 2826 } 2827 } 2828 2829 func (c *Checker) CheckTimerResetReturnValue(j *lint.Job) { 2830 for _, fn := range j.Program.InitialFunctions { 2831 for _, block := range fn.Blocks { 2832 for _, ins := range block.Instrs { 2833 call, ok := ins.(*ssa.Call) 2834 if !ok { 2835 continue 2836 } 2837 if !IsCallTo(call.Common(), "(*time.Timer).Reset") { 2838 continue 2839 } 2840 refs := call.Referrers() 2841 if refs == nil { 2842 continue 2843 } 2844 for _, ref := range FilterDebug(*refs) { 2845 ifstmt, ok := ref.(*ssa.If) 2846 if !ok { 2847 continue 2848 } 2849 2850 found := false 2851 for _, succ := range ifstmt.Block().Succs { 2852 if len(succ.Preds) != 1 { 2853 // Merge point, not a branch in the 2854 // syntactical sense. 2855 2856 // FIXME(dh): this is broken for if 2857 // statements a la "if x || y" 2858 continue 2859 } 2860 ssautil.Walk(succ, func(b *ssa.BasicBlock) bool { 2861 if !succ.Dominates(b) { 2862 // We've reached the end of the branch 2863 return false 2864 } 2865 for _, ins := range b.Instrs { 2866 // TODO(dh): we should check that 2867 // we're receiving from the channel of 2868 // a time.Timer to further reduce 2869 // false positives. Not a key 2870 // priority, considering the rarity of 2871 // Reset and the tiny likeliness of a 2872 // false positive 2873 if ins, ok := ins.(*ssa.UnOp); ok && ins.Op == token.ARROW && IsType(ins.X.Type(), "<-chan time.Time") { 2874 found = true 2875 return false 2876 } 2877 } 2878 return true 2879 }) 2880 } 2881 2882 if found { 2883 j.Errorf(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") 2884 } 2885 } 2886 } 2887 } 2888 } 2889 } 2890 2891 func (c *Checker) CheckToLowerToUpperComparison(j *lint.Job) { 2892 fn := func(node ast.Node) bool { 2893 binExpr, ok := node.(*ast.BinaryExpr) 2894 if !ok { 2895 return true 2896 } 2897 2898 var negative bool 2899 switch binExpr.Op { 2900 case token.EQL: 2901 negative = false 2902 case token.NEQ: 2903 negative = true 2904 default: 2905 return true 2906 } 2907 2908 const ( 2909 lo = "strings.ToLower" 2910 up = "strings.ToUpper" 2911 ) 2912 2913 var call string 2914 if IsCallToAST(j, binExpr.X, lo) && IsCallToAST(j, binExpr.Y, lo) { 2915 call = lo 2916 } else if IsCallToAST(j, binExpr.X, up) && IsCallToAST(j, binExpr.Y, up) { 2917 call = up 2918 } else { 2919 return true 2920 } 2921 2922 bang := "" 2923 if negative { 2924 bang = "!" 2925 } 2926 2927 j.Errorf(binExpr, "should use %sstrings.EqualFold(a, b) instead of %s(a) %s %s(b)", bang, call, binExpr.Op, call) 2928 return true 2929 } 2930 2931 for _, f := range j.Program.Files { 2932 ast.Inspect(f, fn) 2933 } 2934 } 2935 2936 func (c *Checker) CheckUnreachableTypeCases(j *lint.Job) { 2937 // 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. 2938 subsumes := func(T, V types.Type) bool { 2939 tIface, ok := T.Underlying().(*types.Interface) 2940 if !ok { 2941 return false 2942 } 2943 2944 return types.Implements(V, tIface) 2945 } 2946 2947 subsumesAny := func(Ts, Vs []types.Type) (types.Type, types.Type, bool) { 2948 for _, T := range Ts { 2949 for _, V := range Vs { 2950 if subsumes(T, V) { 2951 return T, V, true 2952 } 2953 } 2954 } 2955 2956 return nil, nil, false 2957 } 2958 2959 fn := func(node ast.Node) bool { 2960 tsStmt, ok := node.(*ast.TypeSwitchStmt) 2961 if !ok { 2962 return true 2963 } 2964 2965 type ccAndTypes struct { 2966 cc *ast.CaseClause 2967 types []types.Type 2968 } 2969 2970 // All asserted types in the order of case clauses. 2971 ccs := make([]ccAndTypes, 0, len(tsStmt.Body.List)) 2972 for _, stmt := range tsStmt.Body.List { 2973 cc, _ := stmt.(*ast.CaseClause) 2974 2975 // Exclude the 'default' case. 2976 if len(cc.List) == 0 { 2977 continue 2978 } 2979 2980 Ts := make([]types.Type, len(cc.List)) 2981 for i, expr := range cc.List { 2982 Ts[i] = TypeOf(j, expr) 2983 } 2984 2985 ccs = append(ccs, ccAndTypes{cc: cc, types: Ts}) 2986 } 2987 2988 if len(ccs) <= 1 { 2989 // Zero or one case clauses, nothing to check. 2990 return true 2991 } 2992 2993 // Check if case clauses following cc have types that are subsumed by cc. 2994 for i, cc := range ccs[:len(ccs)-1] { 2995 for _, next := range ccs[i+1:] { 2996 if T, V, yes := subsumesAny(cc.types, next.types); yes { 2997 j.Errorf(next.cc, "unreachable case clause: %s will always match before %s", T.String(), V.String()) 2998 } 2999 } 3000 } 3001 3002 return true 3003 } 3004 3005 for _, f := range j.Program.Files { 3006 ast.Inspect(f, fn) 3007 } 3008 }