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