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