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