github.com/gwaycc/gometalinter@v3.0.0+incompatible/_linters/src/honnef.co/go/tools/simple/lint.go (about) 1 // Package simple contains a linter for Go source code. 2 package simple // import "honnef.co/go/tools/simple" 3 4 import ( 5 "go/ast" 6 "go/constant" 7 "go/token" 8 "go/types" 9 "reflect" 10 "strings" 11 12 . "honnef.co/go/tools/arg" 13 "honnef.co/go/tools/internal/sharedcheck" 14 "honnef.co/go/tools/lint" 15 . "honnef.co/go/tools/lint/lintdsl" 16 17 "golang.org/x/tools/go/types/typeutil" 18 ) 19 20 type Checker struct { 21 CheckGenerated bool 22 MS *typeutil.MethodSetCache 23 } 24 25 func NewChecker() *Checker { 26 return &Checker{ 27 MS: &typeutil.MethodSetCache{}, 28 } 29 } 30 31 func (*Checker) Name() string { return "gosimple" } 32 func (*Checker) Prefix() string { return "S" } 33 34 func (c *Checker) Init(prog *lint.Program) {} 35 36 func (c *Checker) Checks() []lint.Check { 37 return []lint.Check{ 38 {ID: "S1000", FilterGenerated: true, Fn: c.LintSingleCaseSelect}, 39 {ID: "S1001", FilterGenerated: true, Fn: c.LintLoopCopy}, 40 {ID: "S1002", FilterGenerated: true, Fn: c.LintIfBoolCmp}, 41 {ID: "S1003", FilterGenerated: true, Fn: c.LintStringsContains}, 42 {ID: "S1004", FilterGenerated: true, Fn: c.LintBytesCompare}, 43 {ID: "S1005", FilterGenerated: true, Fn: c.LintUnnecessaryBlank}, 44 {ID: "S1006", FilterGenerated: true, Fn: c.LintForTrue}, 45 {ID: "S1007", FilterGenerated: true, Fn: c.LintRegexpRaw}, 46 {ID: "S1008", FilterGenerated: true, Fn: c.LintIfReturn}, 47 {ID: "S1009", FilterGenerated: true, Fn: c.LintRedundantNilCheckWithLen}, 48 {ID: "S1010", FilterGenerated: true, Fn: c.LintSlicing}, 49 {ID: "S1011", FilterGenerated: true, Fn: c.LintLoopAppend}, 50 {ID: "S1012", FilterGenerated: true, Fn: c.LintTimeSince}, 51 {ID: "S1016", FilterGenerated: true, Fn: c.LintSimplerStructConversion}, 52 {ID: "S1017", FilterGenerated: true, Fn: c.LintTrim}, 53 {ID: "S1018", FilterGenerated: true, Fn: c.LintLoopSlide}, 54 {ID: "S1019", FilterGenerated: true, Fn: c.LintMakeLenCap}, 55 {ID: "S1020", FilterGenerated: true, Fn: c.LintAssertNotNil}, 56 {ID: "S1021", FilterGenerated: true, Fn: c.LintDeclareAssign}, 57 {ID: "S1023", FilterGenerated: true, Fn: c.LintRedundantBreak}, 58 {ID: "S1024", FilterGenerated: true, Fn: c.LintTimeUntil}, 59 {ID: "S1025", FilterGenerated: true, Fn: c.LintRedundantSprintf}, 60 {ID: "S1028", FilterGenerated: true, Fn: c.LintErrorsNewSprintf}, 61 {ID: "S1029", FilterGenerated: false, Fn: c.LintRangeStringRunes}, 62 {ID: "S1030", FilterGenerated: true, Fn: c.LintBytesBufferConversions}, 63 {ID: "S1031", FilterGenerated: true, Fn: c.LintNilCheckAroundRange}, 64 {ID: "S1032", FilterGenerated: true, Fn: c.LintSortHelpers}, 65 {ID: "S1033", FilterGenerated: true, Fn: c.LintGuardedDelete}, 66 {ID: "S1034", FilterGenerated: true, Fn: c.LintSimplifyTypeSwitch}, 67 } 68 } 69 70 func (c *Checker) LintSingleCaseSelect(j *lint.Job) { 71 isSingleSelect := func(node ast.Node) bool { 72 v, ok := node.(*ast.SelectStmt) 73 if !ok { 74 return false 75 } 76 return len(v.Body.List) == 1 77 } 78 79 seen := map[ast.Node]struct{}{} 80 fn := func(node ast.Node) bool { 81 switch v := node.(type) { 82 case *ast.ForStmt: 83 if len(v.Body.List) != 1 { 84 return true 85 } 86 if !isSingleSelect(v.Body.List[0]) { 87 return true 88 } 89 if _, ok := v.Body.List[0].(*ast.SelectStmt).Body.List[0].(*ast.CommClause).Comm.(*ast.SendStmt); ok { 90 // Don't suggest using range for channel sends 91 return true 92 } 93 seen[v.Body.List[0]] = struct{}{} 94 j.Errorf(node, "should use for range instead of for { select {} }") 95 case *ast.SelectStmt: 96 if _, ok := seen[v]; ok { 97 return true 98 } 99 if !isSingleSelect(v) { 100 return true 101 } 102 j.Errorf(node, "should use a simple channel send/receive instead of select with a single case") 103 return true 104 } 105 return true 106 } 107 for _, f := range j.Program.Files { 108 ast.Inspect(f, fn) 109 } 110 } 111 112 func (c *Checker) LintLoopCopy(j *lint.Job) { 113 fn := func(node ast.Node) bool { 114 loop, ok := node.(*ast.RangeStmt) 115 if !ok { 116 return true 117 } 118 119 if loop.Key == nil { 120 return true 121 } 122 if len(loop.Body.List) != 1 { 123 return true 124 } 125 stmt, ok := loop.Body.List[0].(*ast.AssignStmt) 126 if !ok { 127 return true 128 } 129 if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 { 130 return true 131 } 132 lhs, ok := stmt.Lhs[0].(*ast.IndexExpr) 133 if !ok { 134 return true 135 } 136 137 if _, ok := TypeOf(j, lhs.X).(*types.Slice); !ok { 138 return true 139 } 140 lidx, ok := lhs.Index.(*ast.Ident) 141 if !ok { 142 return true 143 } 144 key, ok := loop.Key.(*ast.Ident) 145 if !ok { 146 return true 147 } 148 if TypeOf(j, lhs) == nil || TypeOf(j, stmt.Rhs[0]) == nil { 149 return true 150 } 151 if ObjectOf(j, lidx) != ObjectOf(j, key) { 152 return true 153 } 154 if !types.Identical(TypeOf(j, lhs), TypeOf(j, stmt.Rhs[0])) { 155 return true 156 } 157 if _, ok := TypeOf(j, loop.X).(*types.Slice); !ok { 158 return true 159 } 160 161 if rhs, ok := stmt.Rhs[0].(*ast.IndexExpr); ok { 162 rx, ok := rhs.X.(*ast.Ident) 163 _ = rx 164 if !ok { 165 return true 166 } 167 ridx, ok := rhs.Index.(*ast.Ident) 168 if !ok { 169 return true 170 } 171 if ObjectOf(j, ridx) != ObjectOf(j, key) { 172 return true 173 } 174 } else if rhs, ok := stmt.Rhs[0].(*ast.Ident); ok { 175 value, ok := loop.Value.(*ast.Ident) 176 if !ok { 177 return true 178 } 179 if ObjectOf(j, rhs) != ObjectOf(j, value) { 180 return true 181 } 182 } else { 183 return true 184 } 185 j.Errorf(loop, "should use copy() instead of a loop") 186 return true 187 } 188 for _, f := range j.Program.Files { 189 ast.Inspect(f, fn) 190 } 191 } 192 193 func (c *Checker) LintIfBoolCmp(j *lint.Job) { 194 fn := func(node ast.Node) bool { 195 expr, ok := node.(*ast.BinaryExpr) 196 if !ok || (expr.Op != token.EQL && expr.Op != token.NEQ) { 197 return true 198 } 199 x := IsBoolConst(j, expr.X) 200 y := IsBoolConst(j, expr.Y) 201 if !x && !y { 202 return true 203 } 204 var other ast.Expr 205 var val bool 206 if x { 207 val = BoolConst(j, expr.X) 208 other = expr.Y 209 } else { 210 val = BoolConst(j, expr.Y) 211 other = expr.X 212 } 213 basic, ok := TypeOf(j, other).Underlying().(*types.Basic) 214 if !ok || basic.Kind() != types.Bool { 215 return true 216 } 217 op := "" 218 if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) { 219 op = "!" 220 } 221 r := op + Render(j, other) 222 l1 := len(r) 223 r = strings.TrimLeft(r, "!") 224 if (l1-len(r))%2 == 1 { 225 r = "!" + r 226 } 227 if IsInTest(j, node) { 228 return true 229 } 230 j.Errorf(expr, "should omit comparison to bool constant, can be simplified to %s", r) 231 return true 232 } 233 for _, f := range j.Program.Files { 234 ast.Inspect(f, fn) 235 } 236 } 237 238 func (c *Checker) LintBytesBufferConversions(j *lint.Job) { 239 fn := func(node ast.Node) bool { 240 call, ok := node.(*ast.CallExpr) 241 if !ok || len(call.Args) != 1 { 242 return true 243 } 244 245 argCall, ok := call.Args[0].(*ast.CallExpr) 246 if !ok { 247 return true 248 } 249 sel, ok := argCall.Fun.(*ast.SelectorExpr) 250 if !ok { 251 return true 252 } 253 254 typ := TypeOf(j, call.Fun) 255 if typ == types.Universe.Lookup("string").Type() && IsCallToAST(j, call.Args[0], "(*bytes.Buffer).Bytes") { 256 j.Errorf(call, "should use %v.String() instead of %v", Render(j, sel.X), Render(j, call)) 257 } else if typ, ok := typ.(*types.Slice); ok && typ.Elem() == types.Universe.Lookup("byte").Type() && IsCallToAST(j, call.Args[0], "(*bytes.Buffer).String") { 258 j.Errorf(call, "should use %v.Bytes() instead of %v", Render(j, sel.X), Render(j, call)) 259 } 260 261 return true 262 } 263 for _, f := range j.Program.Files { 264 ast.Inspect(f, fn) 265 } 266 } 267 268 func (c *Checker) LintStringsContains(j *lint.Job) { 269 // map of value to token to bool value 270 allowed := map[int64]map[token.Token]bool{ 271 -1: {token.GTR: true, token.NEQ: true, token.EQL: false}, 272 0: {token.GEQ: true, token.LSS: false}, 273 } 274 fn := func(node ast.Node) bool { 275 expr, ok := node.(*ast.BinaryExpr) 276 if !ok { 277 return true 278 } 279 switch expr.Op { 280 case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL: 281 default: 282 return true 283 } 284 285 value, ok := ExprToInt(j, expr.Y) 286 if !ok { 287 return true 288 } 289 290 allowedOps, ok := allowed[value] 291 if !ok { 292 return true 293 } 294 b, ok := allowedOps[expr.Op] 295 if !ok { 296 return true 297 } 298 299 call, ok := expr.X.(*ast.CallExpr) 300 if !ok { 301 return true 302 } 303 sel, ok := call.Fun.(*ast.SelectorExpr) 304 if !ok { 305 return true 306 } 307 pkgIdent, ok := sel.X.(*ast.Ident) 308 if !ok { 309 return true 310 } 311 funIdent := sel.Sel 312 if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" { 313 return true 314 } 315 newFunc := "" 316 switch funIdent.Name { 317 case "IndexRune": 318 newFunc = "ContainsRune" 319 case "IndexAny": 320 newFunc = "ContainsAny" 321 case "Index": 322 newFunc = "Contains" 323 default: 324 return true 325 } 326 327 prefix := "" 328 if !b { 329 prefix = "!" 330 } 331 j.Errorf(node, "should use %s%s.%s(%s) instead", prefix, pkgIdent.Name, newFunc, RenderArgs(j, call.Args)) 332 333 return true 334 } 335 for _, f := range j.Program.Files { 336 ast.Inspect(f, fn) 337 } 338 } 339 340 func (c *Checker) LintBytesCompare(j *lint.Job) { 341 fn := func(node ast.Node) bool { 342 expr, ok := node.(*ast.BinaryExpr) 343 if !ok { 344 return true 345 } 346 if expr.Op != token.NEQ && expr.Op != token.EQL { 347 return true 348 } 349 call, ok := expr.X.(*ast.CallExpr) 350 if !ok { 351 return true 352 } 353 if !IsCallToAST(j, call, "bytes.Compare") { 354 return true 355 } 356 value, ok := ExprToInt(j, expr.Y) 357 if !ok || value != 0 { 358 return true 359 } 360 args := RenderArgs(j, call.Args) 361 prefix := "" 362 if expr.Op == token.NEQ { 363 prefix = "!" 364 } 365 j.Errorf(node, "should use %sbytes.Equal(%s) instead", prefix, args) 366 return true 367 } 368 for _, f := range j.Program.Files { 369 ast.Inspect(f, fn) 370 } 371 } 372 373 func (c *Checker) LintForTrue(j *lint.Job) { 374 fn := func(node ast.Node) bool { 375 loop, ok := node.(*ast.ForStmt) 376 if !ok { 377 return true 378 } 379 if loop.Init != nil || loop.Post != nil { 380 return true 381 } 382 if !IsBoolConst(j, loop.Cond) || !BoolConst(j, loop.Cond) { 383 return true 384 } 385 j.Errorf(loop, "should use for {} instead of for true {}") 386 return true 387 } 388 for _, f := range j.Program.Files { 389 ast.Inspect(f, fn) 390 } 391 } 392 393 func (c *Checker) LintRegexpRaw(j *lint.Job) { 394 fn := func(node ast.Node) bool { 395 call, ok := node.(*ast.CallExpr) 396 if !ok { 397 return true 398 } 399 if !IsCallToAST(j, call, "regexp.MustCompile") && 400 !IsCallToAST(j, call, "regexp.Compile") { 401 return true 402 } 403 sel, ok := call.Fun.(*ast.SelectorExpr) 404 if !ok { 405 return true 406 } 407 if len(call.Args) != 1 { 408 // invalid function call 409 return true 410 } 411 lit, ok := call.Args[Arg("regexp.Compile.expr")].(*ast.BasicLit) 412 if !ok { 413 // TODO(dominikh): support string concat, maybe support constants 414 return true 415 } 416 if lit.Kind != token.STRING { 417 // invalid function call 418 return true 419 } 420 if lit.Value[0] != '"' { 421 // already a raw string 422 return true 423 } 424 val := lit.Value 425 if !strings.Contains(val, `\\`) { 426 return true 427 } 428 if strings.Contains(val, "`") { 429 return true 430 } 431 432 bs := false 433 for _, c := range val { 434 if !bs && c == '\\' { 435 bs = true 436 continue 437 } 438 if bs && c == '\\' { 439 bs = false 440 continue 441 } 442 if bs { 443 // backslash followed by non-backslash -> escape sequence 444 return true 445 } 446 } 447 448 j.Errorf(call, "should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name) 449 return true 450 } 451 for _, f := range j.Program.Files { 452 ast.Inspect(f, fn) 453 } 454 } 455 456 func (c *Checker) LintIfReturn(j *lint.Job) { 457 fn := func(node ast.Node) bool { 458 block, ok := node.(*ast.BlockStmt) 459 if !ok { 460 return true 461 } 462 l := len(block.List) 463 if l < 2 { 464 return true 465 } 466 n1, n2 := block.List[l-2], block.List[l-1] 467 468 if len(block.List) >= 3 { 469 if _, ok := block.List[l-3].(*ast.IfStmt); ok { 470 // Do not flag a series of if statements 471 return true 472 } 473 } 474 // if statement with no init, no else, a single condition 475 // checking an identifier or function call and just a return 476 // statement in the body, that returns a boolean constant 477 ifs, ok := n1.(*ast.IfStmt) 478 if !ok { 479 return true 480 } 481 if ifs.Else != nil || ifs.Init != nil { 482 return true 483 } 484 if len(ifs.Body.List) != 1 { 485 return true 486 } 487 if op, ok := ifs.Cond.(*ast.BinaryExpr); ok { 488 switch op.Op { 489 case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ: 490 default: 491 return true 492 } 493 } 494 ret1, ok := ifs.Body.List[0].(*ast.ReturnStmt) 495 if !ok { 496 return true 497 } 498 if len(ret1.Results) != 1 { 499 return true 500 } 501 if !IsBoolConst(j, ret1.Results[0]) { 502 return true 503 } 504 505 ret2, ok := n2.(*ast.ReturnStmt) 506 if !ok { 507 return true 508 } 509 if len(ret2.Results) != 1 { 510 return true 511 } 512 if !IsBoolConst(j, ret2.Results[0]) { 513 return true 514 } 515 j.Errorf(n1, "should use 'return <expr>' instead of 'if <expr> { return <bool> }; return <bool>'") 516 return true 517 } 518 for _, f := range j.Program.Files { 519 ast.Inspect(f, fn) 520 } 521 } 522 523 // LintRedundantNilCheckWithLen checks for the following reduntant nil-checks: 524 // 525 // if x == nil || len(x) == 0 {} 526 // if x != nil && len(x) != 0 {} 527 // if x != nil && len(x) == N {} (where N != 0) 528 // if x != nil && len(x) > N {} 529 // if x != nil && len(x) >= N {} (where N != 0) 530 // 531 func (c *Checker) LintRedundantNilCheckWithLen(j *lint.Job) { 532 isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) { 533 _, ok := expr.(*ast.BasicLit) 534 if ok { 535 return true, IsZero(expr) 536 } 537 id, ok := expr.(*ast.Ident) 538 if !ok { 539 return false, false 540 } 541 c, ok := ObjectOf(j, id).(*types.Const) 542 if !ok { 543 return false, false 544 } 545 return true, c.Val().Kind() == constant.Int && c.Val().String() == "0" 546 } 547 548 fn := func(node ast.Node) bool { 549 // check that expr is "x || y" or "x && y" 550 expr, ok := node.(*ast.BinaryExpr) 551 if !ok { 552 return true 553 } 554 if expr.Op != token.LOR && expr.Op != token.LAND { 555 return true 556 } 557 eqNil := expr.Op == token.LOR 558 559 // check that x is "xx == nil" or "xx != nil" 560 x, ok := expr.X.(*ast.BinaryExpr) 561 if !ok { 562 return true 563 } 564 if eqNil && x.Op != token.EQL { 565 return true 566 } 567 if !eqNil && x.Op != token.NEQ { 568 return true 569 } 570 xx, ok := x.X.(*ast.Ident) 571 if !ok { 572 return true 573 } 574 if !IsNil(j, x.Y) { 575 return true 576 } 577 578 // check that y is "len(xx) == 0" or "len(xx) ... " 579 y, ok := expr.Y.(*ast.BinaryExpr) 580 if !ok { 581 return true 582 } 583 if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0 584 return false 585 } 586 yx, ok := y.X.(*ast.CallExpr) 587 if !ok { 588 return true 589 } 590 yxFun, ok := yx.Fun.(*ast.Ident) 591 if !ok || yxFun.Name != "len" || len(yx.Args) != 1 { 592 return true 593 } 594 yxArg, ok := yx.Args[Arg("len.v")].(*ast.Ident) 595 if !ok { 596 return true 597 } 598 if yxArg.Name != xx.Name { 599 return true 600 } 601 602 if eqNil && !IsZero(y.Y) { // must be len(x) == *0* 603 return true 604 } 605 606 if !eqNil { 607 isConst, isZero := isConstZero(y.Y) 608 if !isConst { 609 return true 610 } 611 switch y.Op { 612 case token.EQL: 613 // avoid false positive for "xx != nil && len(xx) == 0" 614 if isZero { 615 return true 616 } 617 case token.GEQ: 618 // avoid false positive for "xx != nil && len(xx) >= 0" 619 if isZero { 620 return true 621 } 622 case token.NEQ: 623 // avoid false positive for "xx != nil && len(xx) != <non-zero>" 624 if !isZero { 625 return true 626 } 627 case token.GTR: 628 // ok 629 default: 630 return true 631 } 632 } 633 634 // finally check that xx type is one of array, slice, map or chan 635 // this is to prevent false positive in case if xx is a pointer to an array 636 var nilType string 637 switch TypeOf(j, xx).(type) { 638 case *types.Slice: 639 nilType = "nil slices" 640 case *types.Map: 641 nilType = "nil maps" 642 case *types.Chan: 643 nilType = "nil channels" 644 default: 645 return true 646 } 647 j.Errorf(expr, "should omit nil check; len() for %s is defined as zero", nilType) 648 return true 649 } 650 for _, f := range j.Program.Files { 651 ast.Inspect(f, fn) 652 } 653 } 654 655 func (c *Checker) LintSlicing(j *lint.Job) { 656 fn := func(node ast.Node) bool { 657 n, ok := node.(*ast.SliceExpr) 658 if !ok { 659 return true 660 } 661 if n.Max != nil { 662 return true 663 } 664 s, ok := n.X.(*ast.Ident) 665 if !ok || s.Obj == nil { 666 return true 667 } 668 call, ok := n.High.(*ast.CallExpr) 669 if !ok || len(call.Args) != 1 || call.Ellipsis.IsValid() { 670 return true 671 } 672 fun, ok := call.Fun.(*ast.Ident) 673 if !ok || fun.Name != "len" { 674 return true 675 } 676 if _, ok := ObjectOf(j, fun).(*types.Builtin); !ok { 677 return true 678 } 679 arg, ok := call.Args[Arg("len.v")].(*ast.Ident) 680 if !ok || arg.Obj != s.Obj { 681 return true 682 } 683 j.Errorf(n, "should omit second index in slice, s[a:len(s)] is identical to s[a:]") 684 return true 685 } 686 for _, f := range j.Program.Files { 687 ast.Inspect(f, fn) 688 } 689 } 690 691 func refersTo(j *lint.Job, expr ast.Expr, ident *ast.Ident) bool { 692 found := false 693 fn := func(node ast.Node) bool { 694 ident2, ok := node.(*ast.Ident) 695 if !ok { 696 return true 697 } 698 if ObjectOf(j, ident) == ObjectOf(j, ident2) { 699 found = true 700 return false 701 } 702 return true 703 } 704 ast.Inspect(expr, fn) 705 return found 706 } 707 708 func (c *Checker) LintLoopAppend(j *lint.Job) { 709 fn := func(node ast.Node) bool { 710 loop, ok := node.(*ast.RangeStmt) 711 if !ok { 712 return true 713 } 714 if !IsBlank(loop.Key) { 715 return true 716 } 717 val, ok := loop.Value.(*ast.Ident) 718 if !ok { 719 return true 720 } 721 if len(loop.Body.List) != 1 { 722 return true 723 } 724 stmt, ok := loop.Body.List[0].(*ast.AssignStmt) 725 if !ok { 726 return true 727 } 728 if stmt.Tok != token.ASSIGN || len(stmt.Lhs) != 1 || len(stmt.Rhs) != 1 { 729 return true 730 } 731 if refersTo(j, stmt.Lhs[0], val) { 732 return true 733 } 734 call, ok := stmt.Rhs[0].(*ast.CallExpr) 735 if !ok { 736 return true 737 } 738 if len(call.Args) != 2 || call.Ellipsis.IsValid() { 739 return true 740 } 741 fun, ok := call.Fun.(*ast.Ident) 742 if !ok { 743 return true 744 } 745 obj := ObjectOf(j, fun) 746 fn, ok := obj.(*types.Builtin) 747 if !ok || fn.Name() != "append" { 748 return true 749 } 750 751 src := TypeOf(j, loop.X) 752 dst := TypeOf(j, call.Args[Arg("append.slice")]) 753 // TODO(dominikh) remove nil check once Go issue #15173 has 754 // been fixed 755 if src == nil { 756 return true 757 } 758 if !types.Identical(src, dst) { 759 return true 760 } 761 762 if Render(j, stmt.Lhs[0]) != Render(j, call.Args[Arg("append.slice")]) { 763 return true 764 } 765 766 el, ok := call.Args[Arg("append.elems")].(*ast.Ident) 767 if !ok { 768 return true 769 } 770 if ObjectOf(j, val) != ObjectOf(j, el) { 771 return true 772 } 773 j.Errorf(loop, "should replace loop with %s = append(%s, %s...)", 774 Render(j, stmt.Lhs[0]), Render(j, call.Args[Arg("append.slice")]), Render(j, loop.X)) 775 return true 776 } 777 for _, f := range j.Program.Files { 778 ast.Inspect(f, fn) 779 } 780 } 781 782 func (c *Checker) LintTimeSince(j *lint.Job) { 783 fn := func(node ast.Node) bool { 784 call, ok := node.(*ast.CallExpr) 785 if !ok { 786 return true 787 } 788 sel, ok := call.Fun.(*ast.SelectorExpr) 789 if !ok { 790 return true 791 } 792 if !IsCallToAST(j, sel.X, "time.Now") { 793 return true 794 } 795 if sel.Sel.Name != "Sub" { 796 return true 797 } 798 j.Errorf(call, "should use time.Since instead of time.Now().Sub") 799 return true 800 } 801 for _, f := range j.Program.Files { 802 ast.Inspect(f, fn) 803 } 804 } 805 806 func (c *Checker) LintTimeUntil(j *lint.Job) { 807 if !IsGoVersion(j, 8) { 808 return 809 } 810 fn := func(node ast.Node) bool { 811 call, ok := node.(*ast.CallExpr) 812 if !ok { 813 return true 814 } 815 if !IsCallToAST(j, call, "(time.Time).Sub") { 816 return true 817 } 818 if !IsCallToAST(j, call.Args[Arg("(time.Time).Sub.u")], "time.Now") { 819 return true 820 } 821 j.Errorf(call, "should use time.Until instead of t.Sub(time.Now())") 822 return true 823 } 824 for _, f := range j.Program.Files { 825 ast.Inspect(f, fn) 826 } 827 } 828 829 func (c *Checker) LintUnnecessaryBlank(j *lint.Job) { 830 fn1 := func(node ast.Node) { 831 assign, ok := node.(*ast.AssignStmt) 832 if !ok { 833 return 834 } 835 if len(assign.Lhs) != 2 || len(assign.Rhs) != 1 { 836 return 837 } 838 if !IsBlank(assign.Lhs[1]) { 839 return 840 } 841 switch rhs := assign.Rhs[0].(type) { 842 case *ast.IndexExpr: 843 // The type-checker should make sure that it's a map, but 844 // let's be safe. 845 if _, ok := TypeOf(j, rhs.X).Underlying().(*types.Map); !ok { 846 return 847 } 848 case *ast.UnaryExpr: 849 if rhs.Op != token.ARROW { 850 return 851 } 852 default: 853 return 854 } 855 cp := *assign 856 cp.Lhs = cp.Lhs[0:1] 857 j.Errorf(assign, "should write %s instead of %s", Render(j, &cp), Render(j, assign)) 858 } 859 860 fn2 := func(node ast.Node) { 861 stmt, ok := node.(*ast.AssignStmt) 862 if !ok { 863 return 864 } 865 if len(stmt.Lhs) != len(stmt.Rhs) { 866 return 867 } 868 for i, lh := range stmt.Lhs { 869 rh := stmt.Rhs[i] 870 if !IsBlank(lh) { 871 continue 872 } 873 expr, ok := rh.(*ast.UnaryExpr) 874 if !ok { 875 continue 876 } 877 if expr.Op != token.ARROW { 878 continue 879 } 880 j.Errorf(lh, "'_ = <-ch' can be simplified to '<-ch'") 881 } 882 } 883 884 fn3 := func(node ast.Node) { 885 rs, ok := node.(*ast.RangeStmt) 886 if !ok { 887 return 888 } 889 890 // for x, _ 891 if !IsBlank(rs.Key) && IsBlank(rs.Value) { 892 j.Errorf(rs.Value, "should omit value from range; this loop is equivalent to `for %s %s range ...`", Render(j, rs.Key), rs.Tok) 893 } 894 // for _, _ || for _ 895 if IsBlank(rs.Key) && (IsBlank(rs.Value) || rs.Value == nil) { 896 j.Errorf(rs.Key, "should omit values from range; this loop is equivalent to `for range ...`") 897 } 898 } 899 900 fn := func(node ast.Node) bool { 901 fn1(node) 902 fn2(node) 903 if IsGoVersion(j, 4) { 904 fn3(node) 905 } 906 return true 907 } 908 for _, f := range j.Program.Files { 909 ast.Inspect(f, fn) 910 } 911 } 912 913 func (c *Checker) LintSimplerStructConversion(j *lint.Job) { 914 var skip ast.Node 915 fn := func(node ast.Node) bool { 916 // Do not suggest type conversion between pointers 917 if unary, ok := node.(*ast.UnaryExpr); ok && unary.Op == token.AND { 918 if lit, ok := unary.X.(*ast.CompositeLit); ok { 919 skip = lit 920 } 921 return true 922 } 923 924 if node == skip { 925 return true 926 } 927 928 lit, ok := node.(*ast.CompositeLit) 929 if !ok { 930 return true 931 } 932 typ1, _ := TypeOf(j, lit.Type).(*types.Named) 933 if typ1 == nil { 934 return true 935 } 936 s1, ok := typ1.Underlying().(*types.Struct) 937 if !ok { 938 return true 939 } 940 941 var typ2 *types.Named 942 var ident *ast.Ident 943 getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) { 944 sel, ok := expr.(*ast.SelectorExpr) 945 if !ok { 946 return nil, nil, false 947 } 948 ident, ok := sel.X.(*ast.Ident) 949 if !ok { 950 return nil, nil, false 951 } 952 typ := TypeOf(j, sel.X) 953 return typ, ident, typ != nil 954 } 955 if len(lit.Elts) == 0 { 956 return true 957 } 958 if s1.NumFields() != len(lit.Elts) { 959 return true 960 } 961 for i, elt := range lit.Elts { 962 var t types.Type 963 var id *ast.Ident 964 var ok bool 965 switch elt := elt.(type) { 966 case *ast.SelectorExpr: 967 t, id, ok = getSelType(elt) 968 if !ok { 969 return true 970 } 971 if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name { 972 return true 973 } 974 case *ast.KeyValueExpr: 975 var sel *ast.SelectorExpr 976 sel, ok = elt.Value.(*ast.SelectorExpr) 977 if !ok { 978 return true 979 } 980 981 if elt.Key.(*ast.Ident).Name != sel.Sel.Name { 982 return true 983 } 984 t, id, ok = getSelType(elt.Value) 985 } 986 if !ok { 987 return true 988 } 989 // All fields must be initialized from the same object 990 if ident != nil && ident.Obj != id.Obj { 991 return true 992 } 993 typ2, _ = t.(*types.Named) 994 if typ2 == nil { 995 return true 996 } 997 ident = id 998 } 999 1000 if typ2 == nil { 1001 return true 1002 } 1003 1004 if typ1.Obj().Pkg() != typ2.Obj().Pkg() { 1005 // Do not suggest type conversions between different 1006 // packages. Types in different packages might only match 1007 // by coincidence. Furthermore, if the dependency ever 1008 // adds more fields to its type, it could break the code 1009 // that relies on the type conversion to work. 1010 return true 1011 } 1012 1013 s2, ok := typ2.Underlying().(*types.Struct) 1014 if !ok { 1015 return true 1016 } 1017 if typ1 == typ2 { 1018 return true 1019 } 1020 if IsGoVersion(j, 8) { 1021 if !types.IdenticalIgnoreTags(s1, s2) { 1022 return true 1023 } 1024 } else { 1025 if !types.Identical(s1, s2) { 1026 return true 1027 } 1028 } 1029 j.Errorf(node, "should convert %s (type %s) to %s instead of using struct literal", 1030 ident.Name, typ2.Obj().Name(), typ1.Obj().Name()) 1031 return true 1032 } 1033 for _, f := range j.Program.Files { 1034 ast.Inspect(f, fn) 1035 } 1036 } 1037 1038 func (c *Checker) LintTrim(j *lint.Job) { 1039 sameNonDynamic := func(node1, node2 ast.Node) bool { 1040 if reflect.TypeOf(node1) != reflect.TypeOf(node2) { 1041 return false 1042 } 1043 1044 switch node1 := node1.(type) { 1045 case *ast.Ident: 1046 return node1.Obj == node2.(*ast.Ident).Obj 1047 case *ast.SelectorExpr: 1048 return Render(j, node1) == Render(j, node2) 1049 case *ast.IndexExpr: 1050 return Render(j, node1) == Render(j, node2) 1051 } 1052 return false 1053 } 1054 1055 isLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool { 1056 call, ok := fn.(*ast.CallExpr) 1057 if !ok { 1058 return false 1059 } 1060 if fn, ok := call.Fun.(*ast.Ident); !ok || fn.Name != "len" { 1061 return false 1062 } 1063 if len(call.Args) != 1 { 1064 return false 1065 } 1066 return sameNonDynamic(call.Args[Arg("len.v")], ident) 1067 } 1068 1069 fn := func(node ast.Node) bool { 1070 var pkg string 1071 var fun string 1072 1073 ifstmt, ok := node.(*ast.IfStmt) 1074 if !ok { 1075 return true 1076 } 1077 if ifstmt.Init != nil { 1078 return true 1079 } 1080 if ifstmt.Else != nil { 1081 return true 1082 } 1083 if len(ifstmt.Body.List) != 1 { 1084 return true 1085 } 1086 condCall, ok := ifstmt.Cond.(*ast.CallExpr) 1087 if !ok { 1088 return true 1089 } 1090 switch { 1091 case IsCallToAST(j, condCall, "strings.HasPrefix"): 1092 pkg = "strings" 1093 fun = "HasPrefix" 1094 case IsCallToAST(j, condCall, "strings.HasSuffix"): 1095 pkg = "strings" 1096 fun = "HasSuffix" 1097 case IsCallToAST(j, condCall, "strings.Contains"): 1098 pkg = "strings" 1099 fun = "Contains" 1100 case IsCallToAST(j, condCall, "bytes.HasPrefix"): 1101 pkg = "bytes" 1102 fun = "HasPrefix" 1103 case IsCallToAST(j, condCall, "bytes.HasSuffix"): 1104 pkg = "bytes" 1105 fun = "HasSuffix" 1106 case IsCallToAST(j, condCall, "bytes.Contains"): 1107 pkg = "bytes" 1108 fun = "Contains" 1109 default: 1110 return true 1111 } 1112 1113 assign, ok := ifstmt.Body.List[0].(*ast.AssignStmt) 1114 if !ok { 1115 return true 1116 } 1117 if assign.Tok != token.ASSIGN { 1118 return true 1119 } 1120 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { 1121 return true 1122 } 1123 if !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) { 1124 return true 1125 } 1126 1127 switch rhs := assign.Rhs[0].(type) { 1128 case *ast.CallExpr: 1129 if len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) { 1130 return true 1131 } 1132 if IsCallToAST(j, condCall, "strings.HasPrefix") && IsCallToAST(j, rhs, "strings.TrimPrefix") || 1133 IsCallToAST(j, condCall, "strings.HasSuffix") && IsCallToAST(j, rhs, "strings.TrimSuffix") || 1134 IsCallToAST(j, condCall, "strings.Contains") && IsCallToAST(j, rhs, "strings.Replace") || 1135 IsCallToAST(j, condCall, "bytes.HasPrefix") && IsCallToAST(j, rhs, "bytes.TrimPrefix") || 1136 IsCallToAST(j, condCall, "bytes.HasSuffix") && IsCallToAST(j, rhs, "bytes.TrimSuffix") || 1137 IsCallToAST(j, condCall, "bytes.Contains") && IsCallToAST(j, rhs, "bytes.Replace") { 1138 j.Errorf(ifstmt, "should replace this if statement with an unconditional %s", CallNameAST(j, rhs)) 1139 } 1140 return true 1141 case *ast.SliceExpr: 1142 slice := rhs 1143 if !ok { 1144 return true 1145 } 1146 if slice.Slice3 { 1147 return true 1148 } 1149 if !sameNonDynamic(slice.X, condCall.Args[0]) { 1150 return true 1151 } 1152 var index ast.Expr 1153 switch fun { 1154 case "HasPrefix": 1155 // TODO(dh) We could detect a High that is len(s), but another 1156 // rule will already flag that, anyway. 1157 if slice.High != nil { 1158 return true 1159 } 1160 index = slice.Low 1161 case "HasSuffix": 1162 if slice.Low != nil { 1163 n, ok := ExprToInt(j, slice.Low) 1164 if !ok || n != 0 { 1165 return true 1166 } 1167 } 1168 index = slice.High 1169 } 1170 1171 switch index := index.(type) { 1172 case *ast.CallExpr: 1173 if fun != "HasPrefix" { 1174 return true 1175 } 1176 if fn, ok := index.Fun.(*ast.Ident); !ok || fn.Name != "len" { 1177 return true 1178 } 1179 if len(index.Args) != 1 { 1180 return true 1181 } 1182 id3 := index.Args[Arg("len.v")] 1183 switch oid3 := condCall.Args[1].(type) { 1184 case *ast.BasicLit: 1185 if pkg != "strings" { 1186 return false 1187 } 1188 lit, ok := id3.(*ast.BasicLit) 1189 if !ok { 1190 return true 1191 } 1192 s1, ok1 := ExprToString(j, lit) 1193 s2, ok2 := ExprToString(j, condCall.Args[1]) 1194 if !ok1 || !ok2 || s1 != s2 { 1195 return true 1196 } 1197 default: 1198 if !sameNonDynamic(id3, oid3) { 1199 return true 1200 } 1201 } 1202 case *ast.BasicLit, *ast.Ident: 1203 if fun != "HasPrefix" { 1204 return true 1205 } 1206 if pkg != "strings" { 1207 return true 1208 } 1209 string, ok1 := ExprToString(j, condCall.Args[1]) 1210 int, ok2 := ExprToInt(j, slice.Low) 1211 if !ok1 || !ok2 || int != int64(len(string)) { 1212 return true 1213 } 1214 case *ast.BinaryExpr: 1215 if fun != "HasSuffix" { 1216 return true 1217 } 1218 if index.Op != token.SUB { 1219 return true 1220 } 1221 if !isLenOnIdent(index.X, condCall.Args[0]) || 1222 !isLenOnIdent(index.Y, condCall.Args[1]) { 1223 return true 1224 } 1225 default: 1226 return true 1227 } 1228 1229 var replacement string 1230 switch fun { 1231 case "HasPrefix": 1232 replacement = "TrimPrefix" 1233 case "HasSuffix": 1234 replacement = "TrimSuffix" 1235 } 1236 j.Errorf(ifstmt, "should replace this if statement with an unconditional %s.%s", pkg, replacement) 1237 return true 1238 default: 1239 return true 1240 } 1241 } 1242 for _, f := range j.Program.Files { 1243 ast.Inspect(f, fn) 1244 } 1245 } 1246 1247 func (c *Checker) LintLoopSlide(j *lint.Job) { 1248 // TODO(dh): detect bs[i+offset] in addition to bs[offset+i] 1249 // TODO(dh): consider merging this function with LintLoopCopy 1250 // TODO(dh): detect length that is an expression, not a variable name 1251 // TODO(dh): support sliding to a different offset than the beginning of the slice 1252 1253 fn := func(node ast.Node) bool { 1254 /* 1255 for i := 0; i < n; i++ { 1256 bs[i] = bs[offset+i] 1257 } 1258 1259 ↓ 1260 1261 copy(bs[:n], bs[offset:offset+n]) 1262 */ 1263 1264 loop, ok := node.(*ast.ForStmt) 1265 if !ok || len(loop.Body.List) != 1 || loop.Init == nil || loop.Cond == nil || loop.Post == nil { 1266 return true 1267 } 1268 assign, ok := loop.Init.(*ast.AssignStmt) 1269 if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 || !IsZero(assign.Rhs[0]) { 1270 return true 1271 } 1272 initvar, ok := assign.Lhs[0].(*ast.Ident) 1273 if !ok { 1274 return true 1275 } 1276 post, ok := loop.Post.(*ast.IncDecStmt) 1277 if !ok || post.Tok != token.INC { 1278 return true 1279 } 1280 postvar, ok := post.X.(*ast.Ident) 1281 if !ok || ObjectOf(j, postvar) != ObjectOf(j, initvar) { 1282 return true 1283 } 1284 bin, ok := loop.Cond.(*ast.BinaryExpr) 1285 if !ok || bin.Op != token.LSS { 1286 return true 1287 } 1288 binx, ok := bin.X.(*ast.Ident) 1289 if !ok || ObjectOf(j, binx) != ObjectOf(j, initvar) { 1290 return true 1291 } 1292 biny, ok := bin.Y.(*ast.Ident) 1293 if !ok { 1294 return true 1295 } 1296 1297 assign, ok = loop.Body.List[0].(*ast.AssignStmt) 1298 if !ok || len(assign.Lhs) != 1 || len(assign.Rhs) != 1 || assign.Tok != token.ASSIGN { 1299 return true 1300 } 1301 lhs, ok := assign.Lhs[0].(*ast.IndexExpr) 1302 if !ok { 1303 return true 1304 } 1305 rhs, ok := assign.Rhs[0].(*ast.IndexExpr) 1306 if !ok { 1307 return true 1308 } 1309 1310 bs1, ok := lhs.X.(*ast.Ident) 1311 if !ok { 1312 return true 1313 } 1314 bs2, ok := rhs.X.(*ast.Ident) 1315 if !ok { 1316 return true 1317 } 1318 obj1 := ObjectOf(j, bs1) 1319 obj2 := ObjectOf(j, bs2) 1320 if obj1 != obj2 { 1321 return true 1322 } 1323 if _, ok := obj1.Type().Underlying().(*types.Slice); !ok { 1324 return true 1325 } 1326 1327 index1, ok := lhs.Index.(*ast.Ident) 1328 if !ok || ObjectOf(j, index1) != ObjectOf(j, initvar) { 1329 return true 1330 } 1331 index2, ok := rhs.Index.(*ast.BinaryExpr) 1332 if !ok || index2.Op != token.ADD { 1333 return true 1334 } 1335 add1, ok := index2.X.(*ast.Ident) 1336 if !ok { 1337 return true 1338 } 1339 add2, ok := index2.Y.(*ast.Ident) 1340 if !ok || ObjectOf(j, add2) != ObjectOf(j, initvar) { 1341 return true 1342 } 1343 1344 j.Errorf(loop, "should use copy(%s[:%s], %s[%s:]) instead", Render(j, bs1), Render(j, biny), Render(j, bs1), Render(j, add1)) 1345 return true 1346 } 1347 for _, f := range j.Program.Files { 1348 ast.Inspect(f, fn) 1349 } 1350 } 1351 1352 func (c *Checker) LintMakeLenCap(j *lint.Job) { 1353 fn := func(node ast.Node) bool { 1354 call, ok := node.(*ast.CallExpr) 1355 if !ok { 1356 return true 1357 } 1358 if fn, ok := call.Fun.(*ast.Ident); !ok || fn.Name != "make" { 1359 // FIXME check whether make is indeed the built-in function 1360 return true 1361 } 1362 switch len(call.Args) { 1363 case 2: 1364 // make(T, len) 1365 if _, ok := TypeOf(j, call.Args[Arg("make.t")]).Underlying().(*types.Slice); ok { 1366 break 1367 } 1368 if IsZero(call.Args[Arg("make.size[0]")]) { 1369 j.Errorf(call.Args[Arg("make.size[0]")], "should use make(%s) instead", Render(j, call.Args[Arg("make.t")])) 1370 } 1371 case 3: 1372 // make(T, len, cap) 1373 if Render(j, call.Args[Arg("make.size[0]")]) == Render(j, call.Args[Arg("make.size[1]")]) { 1374 j.Errorf(call.Args[Arg("make.size[0]")], 1375 "should use make(%s, %s) instead", 1376 Render(j, call.Args[Arg("make.t")]), Render(j, call.Args[Arg("make.size[0]")])) 1377 } 1378 } 1379 return false 1380 } 1381 for _, f := range j.Program.Files { 1382 ast.Inspect(f, fn) 1383 } 1384 } 1385 1386 func (c *Checker) LintAssertNotNil(j *lint.Job) { 1387 isNilCheck := func(ident *ast.Ident, expr ast.Expr) bool { 1388 xbinop, ok := expr.(*ast.BinaryExpr) 1389 if !ok || xbinop.Op != token.NEQ { 1390 return false 1391 } 1392 xident, ok := xbinop.X.(*ast.Ident) 1393 if !ok || xident.Obj != ident.Obj { 1394 return false 1395 } 1396 if !IsNil(j, xbinop.Y) { 1397 return false 1398 } 1399 return true 1400 } 1401 isOKCheck := func(ident *ast.Ident, expr ast.Expr) bool { 1402 yident, ok := expr.(*ast.Ident) 1403 if !ok || yident.Obj != ident.Obj { 1404 return false 1405 } 1406 return true 1407 } 1408 fn1 := func(node ast.Node) bool { 1409 ifstmt, ok := node.(*ast.IfStmt) 1410 if !ok { 1411 return true 1412 } 1413 assign, ok := ifstmt.Init.(*ast.AssignStmt) 1414 if !ok || len(assign.Lhs) != 2 || len(assign.Rhs) != 1 || !IsBlank(assign.Lhs[0]) { 1415 return true 1416 } 1417 assert, ok := assign.Rhs[0].(*ast.TypeAssertExpr) 1418 if !ok { 1419 return true 1420 } 1421 binop, ok := ifstmt.Cond.(*ast.BinaryExpr) 1422 if !ok || binop.Op != token.LAND { 1423 return true 1424 } 1425 assertIdent, ok := assert.X.(*ast.Ident) 1426 if !ok { 1427 return true 1428 } 1429 assignIdent, ok := assign.Lhs[1].(*ast.Ident) 1430 if !ok { 1431 return true 1432 } 1433 if !(isNilCheck(assertIdent, binop.X) && isOKCheck(assignIdent, binop.Y)) && 1434 !(isNilCheck(assertIdent, binop.Y) && isOKCheck(assignIdent, binop.X)) { 1435 return true 1436 } 1437 j.Errorf(ifstmt, "when %s is true, %s can't be nil", Render(j, assignIdent), Render(j, assertIdent)) 1438 return true 1439 } 1440 fn2 := func(node ast.Node) bool { 1441 // Check that outer ifstmt is an 'if x != nil {}' 1442 ifstmt, ok := node.(*ast.IfStmt) 1443 if !ok { 1444 return true 1445 } 1446 if ifstmt.Init != nil { 1447 return true 1448 } 1449 if ifstmt.Else != nil { 1450 return true 1451 } 1452 if len(ifstmt.Body.List) != 1 { 1453 return true 1454 } 1455 binop, ok := ifstmt.Cond.(*ast.BinaryExpr) 1456 if !ok { 1457 return true 1458 } 1459 if binop.Op != token.NEQ { 1460 return true 1461 } 1462 lhs, ok := binop.X.(*ast.Ident) 1463 if !ok { 1464 return true 1465 } 1466 if !IsNil(j, binop.Y) { 1467 return true 1468 } 1469 1470 // Check that inner ifstmt is an `if _, ok := x.(T); ok {}` 1471 ifstmt, ok = ifstmt.Body.List[0].(*ast.IfStmt) 1472 if !ok { 1473 return true 1474 } 1475 assign, ok := ifstmt.Init.(*ast.AssignStmt) 1476 if !ok || len(assign.Lhs) != 2 || len(assign.Rhs) != 1 || !IsBlank(assign.Lhs[0]) { 1477 return true 1478 } 1479 assert, ok := assign.Rhs[0].(*ast.TypeAssertExpr) 1480 if !ok { 1481 return true 1482 } 1483 assertIdent, ok := assert.X.(*ast.Ident) 1484 if !ok { 1485 return true 1486 } 1487 if lhs.Obj != assertIdent.Obj { 1488 return true 1489 } 1490 assignIdent, ok := assign.Lhs[1].(*ast.Ident) 1491 if !ok { 1492 return true 1493 } 1494 if !isOKCheck(assignIdent, ifstmt.Cond) { 1495 return true 1496 } 1497 j.Errorf(ifstmt, "when %s is true, %s can't be nil", Render(j, assignIdent), Render(j, assertIdent)) 1498 return true 1499 } 1500 fn := func(node ast.Node) bool { 1501 b1 := fn1(node) 1502 b2 := fn2(node) 1503 return b1 || b2 1504 } 1505 for _, f := range j.Program.Files { 1506 ast.Inspect(f, fn) 1507 } 1508 } 1509 1510 func (c *Checker) LintDeclareAssign(j *lint.Job) { 1511 fn := func(node ast.Node) bool { 1512 block, ok := node.(*ast.BlockStmt) 1513 if !ok { 1514 return true 1515 } 1516 if len(block.List) < 2 { 1517 return true 1518 } 1519 for i, stmt := range block.List[:len(block.List)-1] { 1520 _ = i 1521 decl, ok := stmt.(*ast.DeclStmt) 1522 if !ok { 1523 continue 1524 } 1525 gdecl, ok := decl.Decl.(*ast.GenDecl) 1526 if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 { 1527 continue 1528 } 1529 vspec, ok := gdecl.Specs[0].(*ast.ValueSpec) 1530 if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 { 1531 continue 1532 } 1533 1534 assign, ok := block.List[i+1].(*ast.AssignStmt) 1535 if !ok || assign.Tok != token.ASSIGN { 1536 continue 1537 } 1538 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 { 1539 continue 1540 } 1541 ident, ok := assign.Lhs[0].(*ast.Ident) 1542 if !ok { 1543 continue 1544 } 1545 if vspec.Names[0].Obj != ident.Obj { 1546 continue 1547 } 1548 1549 if refersTo(j, assign.Rhs[0], ident) { 1550 continue 1551 } 1552 j.Errorf(decl, "should merge variable declaration with assignment on next line") 1553 } 1554 return true 1555 } 1556 for _, f := range j.Program.Files { 1557 ast.Inspect(f, fn) 1558 } 1559 } 1560 1561 func (c *Checker) LintRedundantBreak(j *lint.Job) { 1562 fn1 := func(node ast.Node) { 1563 clause, ok := node.(*ast.CaseClause) 1564 if !ok { 1565 return 1566 } 1567 if len(clause.Body) < 2 { 1568 return 1569 } 1570 branch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt) 1571 if !ok || branch.Tok != token.BREAK || branch.Label != nil { 1572 return 1573 } 1574 j.Errorf(branch, "redundant break statement") 1575 } 1576 fn2 := func(node ast.Node) { 1577 var ret *ast.FieldList 1578 var body *ast.BlockStmt 1579 switch x := node.(type) { 1580 case *ast.FuncDecl: 1581 ret = x.Type.Results 1582 body = x.Body 1583 case *ast.FuncLit: 1584 ret = x.Type.Results 1585 body = x.Body 1586 default: 1587 return 1588 } 1589 // if the func has results, a return can't be redundant. 1590 // similarly, if there are no statements, there can be 1591 // no return. 1592 if ret != nil || body == nil || len(body.List) < 1 { 1593 return 1594 } 1595 rst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt) 1596 if !ok { 1597 return 1598 } 1599 // we don't need to check rst.Results as we already 1600 // checked x.Type.Results to be nil. 1601 j.Errorf(rst, "redundant return statement") 1602 } 1603 fn := func(node ast.Node) bool { 1604 fn1(node) 1605 fn2(node) 1606 return true 1607 } 1608 for _, f := range j.Program.Files { 1609 ast.Inspect(f, fn) 1610 } 1611 } 1612 1613 func (c *Checker) Implements(j *lint.Job, typ types.Type, iface string) bool { 1614 // OPT(dh): we can cache the type lookup 1615 idx := strings.IndexRune(iface, '.') 1616 var scope *types.Scope 1617 var ifaceName string 1618 if idx == -1 { 1619 scope = types.Universe 1620 ifaceName = iface 1621 } else { 1622 pkgName := iface[:idx] 1623 pkg := j.Program.Package(pkgName) 1624 if pkg == nil { 1625 return false 1626 } 1627 scope = pkg.Types.Scope() 1628 ifaceName = iface[idx+1:] 1629 } 1630 1631 obj := scope.Lookup(ifaceName) 1632 if obj == nil { 1633 return false 1634 } 1635 i, ok := obj.Type().Underlying().(*types.Interface) 1636 if !ok { 1637 return false 1638 } 1639 return types.Implements(typ, i) 1640 } 1641 1642 func (c *Checker) LintRedundantSprintf(j *lint.Job) { 1643 fn := func(node ast.Node) bool { 1644 call, ok := node.(*ast.CallExpr) 1645 if !ok { 1646 return true 1647 } 1648 if !IsCallToAST(j, call, "fmt.Sprintf") { 1649 return true 1650 } 1651 if len(call.Args) != 2 { 1652 return true 1653 } 1654 if s, ok := ExprToString(j, call.Args[Arg("fmt.Sprintf.format")]); !ok || s != "%s" { 1655 return true 1656 } 1657 arg := call.Args[Arg("fmt.Sprintf.a[0]")] 1658 typ := TypeOf(j, arg) 1659 1660 if c.Implements(j, typ, "fmt.Stringer") { 1661 j.Errorf(call, "should use String() instead of fmt.Sprintf") 1662 return true 1663 } 1664 1665 if typ.Underlying() == types.Universe.Lookup("string").Type() { 1666 if typ == types.Universe.Lookup("string").Type() { 1667 j.Errorf(call, "the argument is already a string, there's no need to use fmt.Sprintf") 1668 } else { 1669 j.Errorf(call, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf") 1670 } 1671 } 1672 return true 1673 } 1674 for _, f := range j.Program.Files { 1675 ast.Inspect(f, fn) 1676 } 1677 } 1678 1679 func (c *Checker) LintErrorsNewSprintf(j *lint.Job) { 1680 fn := func(node ast.Node) bool { 1681 if !IsCallToAST(j, node, "errors.New") { 1682 return true 1683 } 1684 call := node.(*ast.CallExpr) 1685 if !IsCallToAST(j, call.Args[Arg("errors.New.text")], "fmt.Sprintf") { 1686 return true 1687 } 1688 j.Errorf(node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))") 1689 return true 1690 } 1691 for _, f := range j.Program.Files { 1692 ast.Inspect(f, fn) 1693 } 1694 } 1695 1696 func (c *Checker) LintRangeStringRunes(j *lint.Job) { 1697 sharedcheck.CheckRangeStringRunes(j) 1698 } 1699 1700 func (c *Checker) LintNilCheckAroundRange(j *lint.Job) { 1701 fn := func(node ast.Node) bool { 1702 ifstmt, ok := node.(*ast.IfStmt) 1703 if !ok { 1704 return true 1705 } 1706 1707 cond, ok := ifstmt.Cond.(*ast.BinaryExpr) 1708 if !ok { 1709 return true 1710 } 1711 1712 if cond.Op != token.NEQ || !IsNil(j, cond.Y) || len(ifstmt.Body.List) != 1 { 1713 return true 1714 } 1715 1716 loop, ok := ifstmt.Body.List[0].(*ast.RangeStmt) 1717 if !ok { 1718 return true 1719 } 1720 ifXIdent, ok := cond.X.(*ast.Ident) 1721 if !ok { 1722 return true 1723 } 1724 rangeXIdent, ok := loop.X.(*ast.Ident) 1725 if !ok { 1726 return true 1727 } 1728 if ifXIdent.Obj != rangeXIdent.Obj { 1729 return true 1730 } 1731 switch TypeOf(j, rangeXIdent).(type) { 1732 case *types.Slice, *types.Map: 1733 j.Errorf(node, "unnecessary nil check around range") 1734 } 1735 return true 1736 } 1737 for _, f := range j.Program.Files { 1738 ast.Inspect(f, fn) 1739 } 1740 } 1741 1742 func isPermissibleSort(j *lint.Job, node ast.Node) bool { 1743 call := node.(*ast.CallExpr) 1744 typeconv, ok := call.Args[0].(*ast.CallExpr) 1745 if !ok { 1746 return true 1747 } 1748 1749 sel, ok := typeconv.Fun.(*ast.SelectorExpr) 1750 if !ok { 1751 return true 1752 } 1753 name := SelectorName(j, sel) 1754 switch name { 1755 case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice": 1756 default: 1757 return true 1758 } 1759 1760 return false 1761 } 1762 1763 func (c *Checker) LintSortHelpers(j *lint.Job) { 1764 fnFuncs := func(node ast.Node) bool { 1765 var body *ast.BlockStmt 1766 switch node := node.(type) { 1767 case *ast.FuncLit: 1768 body = node.Body 1769 case *ast.FuncDecl: 1770 body = node.Body 1771 default: 1772 return true 1773 } 1774 if body == nil { 1775 return true 1776 } 1777 1778 type Error struct { 1779 node lint.Positioner 1780 msg string 1781 } 1782 var errors []Error 1783 permissible := false 1784 fnSorts := func(node ast.Node) bool { 1785 if permissible { 1786 return false 1787 } 1788 if !IsCallToAST(j, node, "sort.Sort") { 1789 return true 1790 } 1791 if isPermissibleSort(j, node) { 1792 permissible = true 1793 return false 1794 } 1795 call := node.(*ast.CallExpr) 1796 typeconv := call.Args[Arg("sort.Sort.data")].(*ast.CallExpr) 1797 sel := typeconv.Fun.(*ast.SelectorExpr) 1798 name := SelectorName(j, sel) 1799 1800 switch name { 1801 case "sort.IntSlice": 1802 errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"}) 1803 case "sort.Float64Slice": 1804 errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"}) 1805 case "sort.StringSlice": 1806 errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"}) 1807 } 1808 return true 1809 } 1810 ast.Inspect(body, fnSorts) 1811 1812 if permissible { 1813 return false 1814 } 1815 for _, err := range errors { 1816 j.Errorf(err.node, "%s", err.msg) 1817 } 1818 return false 1819 } 1820 1821 for _, f := range j.Program.Files { 1822 ast.Inspect(f, fnFuncs) 1823 } 1824 } 1825 1826 func (c *Checker) LintGuardedDelete(j *lint.Job) { 1827 isCommaOkMapIndex := func(stmt ast.Stmt) (b *ast.Ident, m ast.Expr, key ast.Expr, ok bool) { 1828 // Has to be of the form `_, <b:*ast.Ident> = <m:*types.Map>[<key>] 1829 1830 assign, ok := stmt.(*ast.AssignStmt) 1831 if !ok { 1832 return nil, nil, nil, false 1833 } 1834 if len(assign.Lhs) != 2 || len(assign.Rhs) != 1 { 1835 return nil, nil, nil, false 1836 } 1837 if !IsBlank(assign.Lhs[0]) { 1838 return nil, nil, nil, false 1839 } 1840 ident, ok := assign.Lhs[1].(*ast.Ident) 1841 if !ok { 1842 return nil, nil, nil, false 1843 } 1844 index, ok := assign.Rhs[0].(*ast.IndexExpr) 1845 if !ok { 1846 return nil, nil, nil, false 1847 } 1848 if _, ok := TypeOf(j, index.X).(*types.Map); !ok { 1849 return nil, nil, nil, false 1850 } 1851 key = index.Index 1852 return ident, index.X, key, true 1853 } 1854 fn := func(node ast.Node) bool { 1855 stmt, ok := node.(*ast.IfStmt) 1856 if !ok { 1857 return true 1858 } 1859 if len(stmt.Body.List) != 1 { 1860 return true 1861 } 1862 expr, ok := stmt.Body.List[0].(*ast.ExprStmt) 1863 if !ok { 1864 return true 1865 } 1866 call, ok := expr.X.(*ast.CallExpr) 1867 if !ok { 1868 return true 1869 } 1870 if !IsCallToAST(j, call, "delete") { 1871 return true 1872 } 1873 b, m, key, ok := isCommaOkMapIndex(stmt.Init) 1874 if !ok { 1875 return true 1876 } 1877 if cond, ok := stmt.Cond.(*ast.Ident); !ok || ObjectOf(j, cond) != ObjectOf(j, b) { 1878 return true 1879 } 1880 if Render(j, call.Args[0]) != Render(j, m) || Render(j, call.Args[1]) != Render(j, key) { 1881 return true 1882 } 1883 j.Errorf(stmt, "unnecessary guard around call to delete") 1884 return true 1885 } 1886 for _, f := range j.Program.Files { 1887 ast.Inspect(f, fn) 1888 } 1889 } 1890 1891 func (c *Checker) LintSimplifyTypeSwitch(j *lint.Job) { 1892 fn := func(node ast.Node) bool { 1893 stmt, ok := node.(*ast.TypeSwitchStmt) 1894 if !ok { 1895 return true 1896 } 1897 if stmt.Init != nil { 1898 // bailing out for now, can't anticipate how type switches with initializers are being used 1899 return true 1900 } 1901 expr, ok := stmt.Assign.(*ast.ExprStmt) 1902 if !ok { 1903 // the user is in fact assigning the result 1904 return true 1905 } 1906 assert := expr.X.(*ast.TypeAssertExpr) 1907 ident, ok := assert.X.(*ast.Ident) 1908 if !ok { 1909 return true 1910 } 1911 x := ObjectOf(j, ident) 1912 var allOffenders []ast.Node 1913 for _, clause := range stmt.Body.List { 1914 clause := clause.(*ast.CaseClause) 1915 if len(clause.List) != 1 { 1916 continue 1917 } 1918 hasUnrelatedAssertion := false 1919 var offenders []ast.Node 1920 ast.Inspect(clause, func(node ast.Node) bool { 1921 assert2, ok := node.(*ast.TypeAssertExpr) 1922 if !ok { 1923 return true 1924 } 1925 ident, ok := assert2.X.(*ast.Ident) 1926 if !ok { 1927 hasUnrelatedAssertion = true 1928 return false 1929 } 1930 if ObjectOf(j, ident) != x { 1931 hasUnrelatedAssertion = true 1932 return false 1933 } 1934 1935 if !types.Identical(TypeOf(j, clause.List[0]), TypeOf(j, assert2.Type)) { 1936 hasUnrelatedAssertion = true 1937 return false 1938 } 1939 offenders = append(offenders, assert2) 1940 return true 1941 }) 1942 if !hasUnrelatedAssertion { 1943 // don't flag cases that have other type assertions 1944 // unrelated to the one in the case clause. often 1945 // times, this is done for symmetry, when two 1946 // different values have to be asserted to the same 1947 // type. 1948 allOffenders = append(allOffenders, offenders...) 1949 } 1950 } 1951 if len(allOffenders) != 0 { 1952 at := "" 1953 for _, offender := range allOffenders { 1954 pos := j.Program.DisplayPosition(offender.Pos()) 1955 at += "\n\t" + pos.String() 1956 } 1957 j.Errorf(expr, "assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate the following type assertions:%s", Render(j, ident), Render(j, ident), at) 1958 } 1959 return true 1960 } 1961 for _, f := range j.Program.Files { 1962 ast.Inspect(f, fn) 1963 } 1964 }