How to use Check method of ast Package

Best Syzkaller code snippet using ast.Check

lint.go

Source:lint.go Github

copy

Full Screen

...32 (AssignStmt _ _ (UnaryExpr "<-" _)))33 _)))`)34 checkSingleCaseSelectQ2 = pattern.MustParse(`(SelectStmt (CommClause _ _))`)35)36func CheckSingleCaseSelect(pass *analysis.Pass) (interface{}, error) {37 seen := map[ast.Node]struct{}{}38 fn := func(node ast.Node) {39 if m, ok := Match(pass, checkSingleCaseSelectQ1, node); ok {40 seen[m.State["select"].(ast.Node)] = struct{}{}41 report.Report(pass, node, "should use for range instead of for { select {} }", report.FilterGenerated())42 } else if _, ok := Match(pass, checkSingleCaseSelectQ2, node); ok {43 if _, ok := seen[node]; !ok {44 report.Report(pass, node, "should use a simple channel send/receive instead of select with a single case",45 report.ShortRange(),46 report.FilterGenerated())47 }48 }49 }50 code.Preorder(pass, fn, (*ast.ForStmt)(nil), (*ast.SelectStmt)(nil))51 return nil, nil52}53var (54 checkLoopCopyQ = pattern.MustParse(`55 (Or56 (RangeStmt57 key value ":=" src@(Ident _)58 [(AssignStmt59 (IndexExpr dst@(Ident _) key)60 "="61 value)])62 (RangeStmt63 key nil ":=" src@(Ident _)64 [(AssignStmt65 (IndexExpr dst@(Ident _) key)66 "="67 (IndexExpr src key))]))`)68 checkLoopCopyR = pattern.MustParse(`(CallExpr (Ident "copy") [dst src])`)69)70func CheckLoopCopy(pass *analysis.Pass) (interface{}, error) {71 fn := func(node ast.Node) {72 m, edits, ok := MatchAndEdit(pass, checkLoopCopyQ, checkLoopCopyR, node)73 if !ok {74 return75 }76 t1 := pass.TypesInfo.TypeOf(m.State["src"].(*ast.Ident))77 t2 := pass.TypesInfo.TypeOf(m.State["dst"].(*ast.Ident))78 if _, ok := t1.Underlying().(*types.Slice); !ok {79 return80 }81 if !types.Identical(t1, t2) {82 return83 }84 tv, err := types.Eval(pass.Fset, pass.Pkg, node.Pos(), "copy")85 if err == nil && tv.IsBuiltin() {86 report.Report(pass, node,87 "should use copy() instead of a loop",88 report.ShortRange(),89 report.FilterGenerated(),90 report.Fixes(edit.Fix("replace loop with call to copy()", edits...)))91 } else {92 report.Report(pass, node, "should use copy() instead of a loop", report.FilterGenerated())93 }94 }95 code.Preorder(pass, fn, (*ast.RangeStmt)(nil))96 return nil, nil97}98func CheckIfBoolCmp(pass *analysis.Pass) (interface{}, error) {99 fn := func(node ast.Node) {100 if code.IsInTest(pass, node) {101 return102 }103 expr := node.(*ast.BinaryExpr)104 if expr.Op != token.EQL && expr.Op != token.NEQ {105 return106 }107 x := code.IsBoolConst(pass, expr.X)108 y := code.IsBoolConst(pass, expr.Y)109 if !x && !y {110 return111 }112 var other ast.Expr113 var val bool114 if x {115 val = code.BoolConst(pass, expr.X)116 other = expr.Y117 } else {118 val = code.BoolConst(pass, expr.Y)119 other = expr.X120 }121 basic, ok := pass.TypesInfo.TypeOf(other).Underlying().(*types.Basic)122 if !ok || basic.Kind() != types.Bool {123 return124 }125 op := ""126 if (expr.Op == token.EQL && !val) || (expr.Op == token.NEQ && val) {127 op = "!"128 }129 r := op + report.Render(pass, other)130 l1 := len(r)131 r = strings.TrimLeft(r, "!")132 if (l1-len(r))%2 == 1 {133 r = "!" + r134 }135 report.Report(pass, expr, fmt.Sprintf("should omit comparison to bool constant, can be simplified to %s", r),136 report.FilterGenerated(),137 report.Fixes(edit.Fix("simplify bool comparison", edit.ReplaceWithString(pass.Fset, expr, r))))138 }139 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))140 return nil, nil141}142var (143 checkBytesBufferConversionsQ = pattern.MustParse(`(CallExpr _ [(CallExpr sel@(SelectorExpr recv _) [])])`)144 checkBytesBufferConversionsRs = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident "String")) [])`)145 checkBytesBufferConversionsRb = pattern.MustParse(`(CallExpr (SelectorExpr recv (Ident "Bytes")) [])`)146)147func CheckBytesBufferConversions(pass *analysis.Pass) (interface{}, error) {148 if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" {149 // The bytes package can use itself however it wants150 return nil, nil151 }152 fn := func(node ast.Node) {153 m, ok := Match(pass, checkBytesBufferConversionsQ, node)154 if !ok {155 return156 }157 call := node.(*ast.CallExpr)158 sel := m.State["sel"].(*ast.SelectorExpr)159 typ := pass.TypesInfo.TypeOf(call.Fun)160 if typ == types.Universe.Lookup("string").Type() && code.IsCallToAST(pass, call.Args[0], "(*bytes.Buffer).Bytes") {161 report.Report(pass, call, fmt.Sprintf("should use %v.String() instead of %v", report.Render(pass, sel.X), report.Render(pass, call)),162 report.FilterGenerated(),163 report.Fixes(edit.Fix("simplify conversion", edit.ReplaceWithPattern(pass, checkBytesBufferConversionsRs, m.State, node))))164 } else if typ, ok := typ.(*types.Slice); ok && typ.Elem() == types.Universe.Lookup("byte").Type() && code.IsCallToAST(pass, call.Args[0], "(*bytes.Buffer).String") {165 report.Report(pass, call, fmt.Sprintf("should use %v.Bytes() instead of %v", report.Render(pass, sel.X), report.Render(pass, call)),166 report.FilterGenerated(),167 report.Fixes(edit.Fix("simplify conversion", edit.ReplaceWithPattern(pass, checkBytesBufferConversionsRb, m.State, node))))168 }169 }170 code.Preorder(pass, fn, (*ast.CallExpr)(nil))171 return nil, nil172}173func CheckStringsContains(pass *analysis.Pass) (interface{}, error) {174 // map of value to token to bool value175 allowed := map[int64]map[token.Token]bool{176 -1: {token.GTR: true, token.NEQ: true, token.EQL: false},177 0: {token.GEQ: true, token.LSS: false},178 }179 fn := func(node ast.Node) {180 expr := node.(*ast.BinaryExpr)181 switch expr.Op {182 case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:183 default:184 return185 }186 value, ok := code.ExprToInt(pass, expr.Y)187 if !ok {188 return189 }190 allowedOps, ok := allowed[value]191 if !ok {192 return193 }194 b, ok := allowedOps[expr.Op]195 if !ok {196 return197 }198 call, ok := expr.X.(*ast.CallExpr)199 if !ok {200 return201 }202 sel, ok := call.Fun.(*ast.SelectorExpr)203 if !ok {204 return205 }206 pkgIdent, ok := sel.X.(*ast.Ident)207 if !ok {208 return209 }210 funIdent := sel.Sel211 if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" {212 return213 }214 var r ast.Expr215 switch funIdent.Name {216 case "IndexRune":217 r = &ast.SelectorExpr{218 X: pkgIdent,219 Sel: &ast.Ident{Name: "ContainsRune"},220 }221 case "IndexAny":222 r = &ast.SelectorExpr{223 X: pkgIdent,224 Sel: &ast.Ident{Name: "ContainsAny"},225 }226 case "Index":227 r = &ast.SelectorExpr{228 X: pkgIdent,229 Sel: &ast.Ident{Name: "Contains"},230 }231 default:232 return233 }234 r = &ast.CallExpr{235 Fun: r,236 Args: call.Args,237 }238 if !b {239 r = &ast.UnaryExpr{240 Op: token.NOT,241 X: r,242 }243 }244 report.Report(pass, node, fmt.Sprintf("should use %s instead", report.Render(pass, r)),245 report.FilterGenerated(),246 report.Fixes(edit.Fix(fmt.Sprintf("simplify use of %s", report.Render(pass, call.Fun)), edit.ReplaceWithNode(pass.Fset, node, r))))247 }248 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))249 return nil, nil250}251var (252 checkBytesCompareQ = pattern.MustParse(`(BinaryExpr (CallExpr (Function "bytes.Compare") args) op@(Or "==" "!=") (BasicLit "INT" "0"))`)253 checkBytesCompareRe = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args)`)254 checkBytesCompareRn = pattern.MustParse(`(UnaryExpr "!" (CallExpr (SelectorExpr (Ident "bytes") (Ident "Equal")) args))`)255)256func CheckBytesCompare(pass *analysis.Pass) (interface{}, error) {257 if pass.Pkg.Path() == "bytes" || pass.Pkg.Path() == "bytes_test" {258 // the bytes package is free to use bytes.Compare as it sees fit259 return nil, nil260 }261 fn := func(node ast.Node) {262 m, ok := Match(pass, checkBytesCompareQ, node)263 if !ok {264 return265 }266 args := report.RenderArgs(pass, m.State["args"].([]ast.Expr))267 prefix := ""268 if m.State["op"].(token.Token) == token.NEQ {269 prefix = "!"270 }271 var fix analysis.SuggestedFix272 switch tok := m.State["op"].(token.Token); tok {273 case token.EQL:274 fix = edit.Fix("simplify use of bytes.Compare", edit.ReplaceWithPattern(pass, checkBytesCompareRe, m.State, node))275 case token.NEQ:276 fix = edit.Fix("simplify use of bytes.Compare", edit.ReplaceWithPattern(pass, checkBytesCompareRn, m.State, node))277 default:278 panic(fmt.Sprintf("unexpected token %v", tok))279 }280 report.Report(pass, node, fmt.Sprintf("should use %sbytes.Equal(%s) instead", prefix, args), report.FilterGenerated(), report.Fixes(fix))281 }282 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))283 return nil, nil284}285func CheckForTrue(pass *analysis.Pass) (interface{}, error) {286 fn := func(node ast.Node) {287 loop := node.(*ast.ForStmt)288 if loop.Init != nil || loop.Post != nil {289 return290 }291 if !code.IsBoolConst(pass, loop.Cond) || !code.BoolConst(pass, loop.Cond) {292 return293 }294 report.Report(pass, loop, "should use for {} instead of for true {}",295 report.ShortRange(),296 report.FilterGenerated())297 }298 code.Preorder(pass, fn, (*ast.ForStmt)(nil))299 return nil, nil300}301func CheckRegexpRaw(pass *analysis.Pass) (interface{}, error) {302 fn := func(node ast.Node) {303 call := node.(*ast.CallExpr)304 if !code.IsCallToAnyAST(pass, call, "regexp.MustCompile", "regexp.Compile") {305 return306 }307 sel, ok := call.Fun.(*ast.SelectorExpr)308 if !ok {309 return310 }311 lit, ok := call.Args[Arg("regexp.Compile.expr")].(*ast.BasicLit)312 if !ok {313 // TODO(dominikh): support string concat, maybe support constants314 return315 }316 if lit.Kind != token.STRING {317 // invalid function call318 return319 }320 if lit.Value[0] != '"' {321 // already a raw string322 return323 }324 val := lit.Value325 if !strings.Contains(val, `\\`) {326 return327 }328 if strings.Contains(val, "`") {329 return330 }331 bs := false332 for _, c := range val {333 if !bs && c == '\\' {334 bs = true335 continue336 }337 if bs && c == '\\' {338 bs = false339 continue340 }341 if bs {342 // backslash followed by non-backslash -> escape sequence343 return344 }345 }346 report.Report(pass, call, fmt.Sprintf("should use raw string (`...`) with regexp.%s to avoid having to escape twice", sel.Sel.Name), report.FilterGenerated())347 }348 code.Preorder(pass, fn, (*ast.CallExpr)(nil))349 return nil, nil350}351var (352 checkIfReturnQIf = pattern.MustParse(`(IfStmt nil cond [(ReturnStmt [ret@(Ident _)])] nil)`)353 checkIfReturnQRet = pattern.MustParse(`(ReturnStmt [ret@(Ident _)])`)354)355func CheckIfReturn(pass *analysis.Pass) (interface{}, error) {356 fn := func(node ast.Node) {357 block := node.(*ast.BlockStmt)358 l := len(block.List)359 if l < 2 {360 return361 }362 n1, n2 := block.List[l-2], block.List[l-1]363 if len(block.List) >= 3 {364 if _, ok := block.List[l-3].(*ast.IfStmt); ok {365 // Do not flag a series of if statements366 return367 }368 }369 m1, ok := Match(pass, checkIfReturnQIf, n1)370 if !ok {371 return372 }373 m2, ok := Match(pass, checkIfReturnQRet, n2)374 if !ok {375 return376 }377 if op, ok := m1.State["cond"].(*ast.BinaryExpr); ok {378 switch op.Op {379 case token.EQL, token.LSS, token.GTR, token.NEQ, token.LEQ, token.GEQ:380 default:381 return382 }383 }384 ret1 := m1.State["ret"].(*ast.Ident)385 if !code.IsBoolConst(pass, ret1) {386 return387 }388 ret2 := m2.State["ret"].(*ast.Ident)389 if !code.IsBoolConst(pass, ret2) {390 return391 }392 if ret1.Name == ret2.Name {393 // we want the function to return true and false, not the394 // same value both times.395 return396 }397 cond := m1.State["cond"].(ast.Expr)398 origCond := cond399 if ret1.Name == "false" {400 cond = negate(cond)401 }402 report.Report(pass, n1,403 fmt.Sprintf("should use 'return %s' instead of 'if %s { return %s }; return %s'",404 report.Render(pass, cond),405 report.Render(pass, origCond), report.Render(pass, ret1), report.Render(pass, ret2)),406 report.FilterGenerated())407 }408 code.Preorder(pass, fn, (*ast.BlockStmt)(nil))409 return nil, nil410}411func negate(expr ast.Expr) ast.Expr {412 switch expr := expr.(type) {413 case *ast.BinaryExpr:414 out := *expr415 switch expr.Op {416 case token.EQL:417 out.Op = token.NEQ418 case token.LSS:419 out.Op = token.GEQ420 case token.GTR:421 out.Op = token.LEQ422 case token.NEQ:423 out.Op = token.EQL424 case token.LEQ:425 out.Op = token.GTR426 case token.GEQ:427 out.Op = token.LEQ428 }429 return &out430 case *ast.Ident, *ast.CallExpr, *ast.IndexExpr:431 return &ast.UnaryExpr{432 Op: token.NOT,433 X: expr,434 }435 default:436 return &ast.UnaryExpr{437 Op: token.NOT,438 X: &ast.ParenExpr{439 X: expr,440 },441 }442 }443}444// CheckRedundantNilCheckWithLen checks for the following redundant nil-checks:445//446// if x == nil || len(x) == 0 {}447// if x != nil && len(x) != 0 {}448// if x != nil && len(x) == N {} (where N != 0)449// if x != nil && len(x) > N {}450// if x != nil && len(x) >= N {} (where N != 0)451//452func CheckRedundantNilCheckWithLen(pass *analysis.Pass) (interface{}, error) {453 isConstZero := func(expr ast.Expr) (isConst bool, isZero bool) {454 _, ok := expr.(*ast.BasicLit)455 if ok {456 return true, code.IsIntLiteral(expr, "0")457 }458 id, ok := expr.(*ast.Ident)459 if !ok {460 return false, false461 }462 c, ok := pass.TypesInfo.ObjectOf(id).(*types.Const)463 if !ok {464 return false, false465 }466 return true, c.Val().Kind() == constant.Int && c.Val().String() == "0"467 }468 fn := func(node ast.Node) {469 // check that expr is "x || y" or "x && y"470 expr := node.(*ast.BinaryExpr)471 if expr.Op != token.LOR && expr.Op != token.LAND {472 return473 }474 eqNil := expr.Op == token.LOR475 // check that x is "xx == nil" or "xx != nil"476 x, ok := expr.X.(*ast.BinaryExpr)477 if !ok {478 return479 }480 if eqNil && x.Op != token.EQL {481 return482 }483 if !eqNil && x.Op != token.NEQ {484 return485 }486 xx, ok := x.X.(*ast.Ident)487 if !ok {488 return489 }490 if !code.IsNil(pass, x.Y) {491 return492 }493 // check that y is "len(xx) == 0" or "len(xx) ... "494 y, ok := expr.Y.(*ast.BinaryExpr)495 if !ok {496 return497 }498 if eqNil && y.Op != token.EQL { // must be len(xx) *==* 0499 return500 }501 yx, ok := y.X.(*ast.CallExpr)502 if !ok {503 return504 }505 yxFun, ok := yx.Fun.(*ast.Ident)506 if !ok || yxFun.Name != "len" || len(yx.Args) != 1 {507 return508 }509 yxArg, ok := yx.Args[Arg("len.v")].(*ast.Ident)510 if !ok {511 return512 }513 if yxArg.Name != xx.Name {514 return515 }516 if eqNil && !code.IsIntLiteral(y.Y, "0") { // must be len(x) == *0*517 return518 }519 if !eqNil {520 isConst, isZero := isConstZero(y.Y)521 if !isConst {522 return523 }524 switch y.Op {525 case token.EQL:526 // avoid false positive for "xx != nil && len(xx) == 0"527 if isZero {528 return529 }530 case token.GEQ:531 // avoid false positive for "xx != nil && len(xx) >= 0"532 if isZero {533 return534 }535 case token.NEQ:536 // avoid false positive for "xx != nil && len(xx) != <non-zero>"537 if !isZero {538 return539 }540 case token.GTR:541 // ok542 default:543 return544 }545 }546 // finally check that xx type is one of array, slice, map or chan547 // this is to prevent false positive in case if xx is a pointer to an array548 var nilType string549 switch pass.TypesInfo.TypeOf(xx).(type) {550 case *types.Slice:551 nilType = "nil slices"552 case *types.Map:553 nilType = "nil maps"554 case *types.Chan:555 nilType = "nil channels"556 default:557 return558 }559 report.Report(pass, expr, fmt.Sprintf("should omit nil check; len() for %s is defined as zero", nilType), report.FilterGenerated())560 }561 code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))562 return nil, nil563}564var checkSlicingQ = pattern.MustParse(`(SliceExpr x@(Object _) low (CallExpr (Builtin "len") [x]) nil)`)565func CheckSlicing(pass *analysis.Pass) (interface{}, error) {566 fn := func(node ast.Node) {567 if _, ok := Match(pass, checkSlicingQ, node); ok {568 expr := node.(*ast.SliceExpr)569 report.Report(pass, expr.High,570 "should omit second index in slice, s[a:len(s)] is identical to s[a:]",571 report.FilterGenerated(),572 report.Fixes(edit.Fix("simplify slice expression", edit.Delete(expr.High))))573 }574 }575 code.Preorder(pass, fn, (*ast.SliceExpr)(nil))576 return nil, nil577}578func refersTo(pass *analysis.Pass, expr ast.Expr, ident types.Object) bool {579 found := false580 fn := func(node ast.Node) bool {581 ident2, ok := node.(*ast.Ident)582 if !ok {583 return true584 }585 if ident == pass.TypesInfo.ObjectOf(ident2) {586 found = true587 return false588 }589 return true590 }591 ast.Inspect(expr, fn)592 return found593}594var checkLoopAppendQ = pattern.MustParse(`595 (RangeStmt596 (Ident "_")597 val@(Object _)598 _599 x600 [(AssignStmt [lhs] "=" [(CallExpr (Builtin "append") [lhs val])])]) `)601func CheckLoopAppend(pass *analysis.Pass) (interface{}, error) {602 fn := func(node ast.Node) {603 m, ok := Match(pass, checkLoopAppendQ, node)604 if !ok {605 return606 }607 val := m.State["val"].(types.Object)608 if refersTo(pass, m.State["lhs"].(ast.Expr), val) {609 return610 }611 src := pass.TypesInfo.TypeOf(m.State["x"].(ast.Expr))612 dst := pass.TypesInfo.TypeOf(m.State["lhs"].(ast.Expr))613 if !types.Identical(src, dst) {614 return615 }616 r := &ast.AssignStmt{617 Lhs: []ast.Expr{m.State["lhs"].(ast.Expr)},618 Tok: token.ASSIGN,619 Rhs: []ast.Expr{620 &ast.CallExpr{621 Fun: &ast.Ident{Name: "append"},622 Args: []ast.Expr{623 m.State["lhs"].(ast.Expr),624 m.State["x"].(ast.Expr),625 },626 Ellipsis: 1,627 },628 },629 }630 report.Report(pass, node, fmt.Sprintf("should replace loop with %s", report.Render(pass, r)),631 report.ShortRange(),632 report.FilterGenerated(),633 report.Fixes(edit.Fix("replace loop with call to append", edit.ReplaceWithNode(pass.Fset, node, r))))634 }635 code.Preorder(pass, fn, (*ast.RangeStmt)(nil))636 return nil, nil637}638var (639 checkTimeSinceQ = pattern.MustParse(`(CallExpr (SelectorExpr (CallExpr (Function "time.Now") []) (Function "(time.Time).Sub")) [arg])`)640 checkTimeSinceR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Since")) [arg])`)641)642func CheckTimeSince(pass *analysis.Pass) (interface{}, error) {643 fn := func(node ast.Node) {644 if _, edits, ok := MatchAndEdit(pass, checkTimeSinceQ, checkTimeSinceR, node); ok {645 report.Report(pass, node, "should use time.Since instead of time.Now().Sub",646 report.FilterGenerated(),647 report.Fixes(edit.Fix("replace with call to time.Since", edits...)))648 }649 }650 code.Preorder(pass, fn, (*ast.CallExpr)(nil))651 return nil, nil652}653var (654 checkTimeUntilQ = pattern.MustParse(`(CallExpr (Function "(time.Time).Sub") [(CallExpr (Function "time.Now") [])])`)655 checkTimeUntilR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Until")) [arg])`)656)657func CheckTimeUntil(pass *analysis.Pass) (interface{}, error) {658 if !code.IsGoVersion(pass, 8) {659 return nil, nil660 }661 fn := func(node ast.Node) {662 if _, ok := Match(pass, checkTimeUntilQ, node); ok {663 if sel, ok := node.(*ast.CallExpr).Fun.(*ast.SelectorExpr); ok {664 r := pattern.NodeToAST(checkTimeUntilR.Root, map[string]interface{}{"arg": sel.X}).(ast.Node)665 report.Report(pass, node, "should use time.Until instead of t.Sub(time.Now())",666 report.FilterGenerated(),667 report.Fixes(edit.Fix("replace with call to time.Until", edit.ReplaceWithNode(pass.Fset, node, r))))668 } else {669 report.Report(pass, node, "should use time.Until instead of t.Sub(time.Now())", report.FilterGenerated())670 }671 }672 }673 code.Preorder(pass, fn, (*ast.CallExpr)(nil))674 return nil, nil675}676var (677 checkUnnecessaryBlankQ1 = pattern.MustParse(`678 (AssignStmt679 [_ (Ident "_")]680 _681 (Or682 (IndexExpr _ _)683 (UnaryExpr "<-" _))) `)684 checkUnnecessaryBlankQ2 = pattern.MustParse(`685 (AssignStmt686 (Ident "_") _ recv@(UnaryExpr "<-" _))`)687)688func CheckUnnecessaryBlank(pass *analysis.Pass) (interface{}, error) {689 fn1 := func(node ast.Node) {690 if _, ok := Match(pass, checkUnnecessaryBlankQ1, node); ok {691 r := *node.(*ast.AssignStmt)692 r.Lhs = r.Lhs[0:1]693 report.Report(pass, node, "unnecessary assignment to the blank identifier",694 report.FilterGenerated(),695 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.ReplaceWithNode(pass.Fset, node, &r))))696 } else if m, ok := Match(pass, checkUnnecessaryBlankQ2, node); ok {697 report.Report(pass, node, "unnecessary assignment to the blank identifier",698 report.FilterGenerated(),699 report.Fixes(edit.Fix("simplify channel receive operation", edit.ReplaceWithNode(pass.Fset, node, m.State["recv"].(ast.Node)))))700 }701 }702 fn3 := func(node ast.Node) {703 rs := node.(*ast.RangeStmt)704 // for _705 if rs.Value == nil && code.IsBlank(rs.Key) {706 report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier",707 report.FilterGenerated(),708 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))709 }710 // for _, _711 if code.IsBlank(rs.Key) && code.IsBlank(rs.Value) {712 // FIXME we should mark both key and value713 report.Report(pass, rs.Key, "unnecessary assignment to the blank identifier",714 report.FilterGenerated(),715 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.Pos(), rs.TokPos + 1}))))716 }717 // for x, _718 if !code.IsBlank(rs.Key) && code.IsBlank(rs.Value) {719 report.Report(pass, rs.Value, "unnecessary assignment to the blank identifier",720 report.FilterGenerated(),721 report.Fixes(edit.Fix("remove assignment to blank identifier", edit.Delete(edit.Range{rs.Key.End(), rs.Value.End()}))))722 }723 }724 code.Preorder(pass, fn1, (*ast.AssignStmt)(nil))725 if code.IsGoVersion(pass, 4) {726 code.Preorder(pass, fn3, (*ast.RangeStmt)(nil))727 }728 return nil, nil729}730func CheckSimplerStructConversion(pass *analysis.Pass) (interface{}, error) {731 var skip ast.Node732 fn := func(node ast.Node) {733 // Do not suggest type conversion between pointers734 if unary, ok := node.(*ast.UnaryExpr); ok && unary.Op == token.AND {735 if lit, ok := unary.X.(*ast.CompositeLit); ok {736 skip = lit737 }738 return739 }740 if node == skip {741 return742 }743 lit, ok := node.(*ast.CompositeLit)744 if !ok {745 return746 }747 typ1, _ := pass.TypesInfo.TypeOf(lit.Type).(*types.Named)748 if typ1 == nil {749 return750 }751 s1, ok := typ1.Underlying().(*types.Struct)752 if !ok {753 return754 }755 var typ2 *types.Named756 var ident *ast.Ident757 getSelType := func(expr ast.Expr) (types.Type, *ast.Ident, bool) {758 sel, ok := expr.(*ast.SelectorExpr)759 if !ok {760 return nil, nil, false761 }762 ident, ok := sel.X.(*ast.Ident)763 if !ok {764 return nil, nil, false765 }766 typ := pass.TypesInfo.TypeOf(sel.X)767 return typ, ident, typ != nil768 }769 if len(lit.Elts) == 0 {770 return771 }772 if s1.NumFields() != len(lit.Elts) {773 return774 }775 for i, elt := range lit.Elts {776 var t types.Type777 var id *ast.Ident778 var ok bool779 switch elt := elt.(type) {780 case *ast.SelectorExpr:781 t, id, ok = getSelType(elt)782 if !ok {783 return784 }785 if i >= s1.NumFields() || s1.Field(i).Name() != elt.Sel.Name {786 return787 }788 case *ast.KeyValueExpr:789 var sel *ast.SelectorExpr790 sel, ok = elt.Value.(*ast.SelectorExpr)791 if !ok {792 return793 }794 if elt.Key.(*ast.Ident).Name != sel.Sel.Name {795 return796 }797 t, id, ok = getSelType(elt.Value)798 }799 if !ok {800 return801 }802 // All fields must be initialized from the same object803 if ident != nil && ident.Obj != id.Obj {804 return805 }806 typ2, _ = t.(*types.Named)807 if typ2 == nil {808 return809 }810 ident = id811 }812 if typ2 == nil {813 return814 }815 if typ1.Obj().Pkg() != typ2.Obj().Pkg() {816 // Do not suggest type conversions between different817 // packages. Types in different packages might only match818 // by coincidence. Furthermore, if the dependency ever819 // adds more fields to its type, it could break the code820 // that relies on the type conversion to work.821 return822 }823 s2, ok := typ2.Underlying().(*types.Struct)824 if !ok {825 return826 }827 if typ1 == typ2 {828 return829 }830 if code.IsGoVersion(pass, 8) {831 if !types.IdenticalIgnoreTags(s1, s2) {832 return833 }834 } else {835 if !types.Identical(s1, s2) {836 return837 }838 }839 r := &ast.CallExpr{840 Fun: lit.Type,841 Args: []ast.Expr{ident},842 }843 report.Report(pass, node,844 fmt.Sprintf("should convert %s (type %s) to %s instead of using struct literal", ident.Name, typ2.Obj().Name(), typ1.Obj().Name()),845 report.FilterGenerated(),846 report.Fixes(edit.Fix("use type conversion", edit.ReplaceWithNode(pass.Fset, node, r))))847 }848 code.Preorder(pass, fn, (*ast.UnaryExpr)(nil), (*ast.CompositeLit)(nil))849 return nil, nil850}851func CheckTrim(pass *analysis.Pass) (interface{}, error) {852 sameNonDynamic := func(node1, node2 ast.Node) bool {853 if reflect.TypeOf(node1) != reflect.TypeOf(node2) {854 return false855 }856 switch node1 := node1.(type) {857 case *ast.Ident:858 return node1.Obj == node2.(*ast.Ident).Obj859 case *ast.SelectorExpr:860 return report.Render(pass, node1) == report.Render(pass, node2)861 case *ast.IndexExpr:862 return report.Render(pass, node1) == report.Render(pass, node2)863 }864 return false865 }866 isLenOnIdent := func(fn ast.Expr, ident ast.Expr) bool {867 call, ok := fn.(*ast.CallExpr)868 if !ok {869 return false870 }871 if fn, ok := call.Fun.(*ast.Ident); !ok || fn.Name != "len" {872 return false873 }874 if len(call.Args) != 1 {875 return false876 }877 return sameNonDynamic(call.Args[Arg("len.v")], ident)878 }879 fn := func(node ast.Node) {880 var pkg string881 var fun string882 ifstmt := node.(*ast.IfStmt)883 if ifstmt.Init != nil {884 return885 }886 if ifstmt.Else != nil {887 return888 }889 if len(ifstmt.Body.List) != 1 {890 return891 }892 condCall, ok := ifstmt.Cond.(*ast.CallExpr)893 if !ok {894 return895 }896 condCallName := code.CallNameAST(pass, condCall)897 switch condCallName {898 case "strings.HasPrefix":899 pkg = "strings"900 fun = "HasPrefix"901 case "strings.HasSuffix":902 pkg = "strings"903 fun = "HasSuffix"904 case "strings.Contains":905 pkg = "strings"906 fun = "Contains"907 case "bytes.HasPrefix":908 pkg = "bytes"909 fun = "HasPrefix"910 case "bytes.HasSuffix":911 pkg = "bytes"912 fun = "HasSuffix"913 case "bytes.Contains":914 pkg = "bytes"915 fun = "Contains"916 default:917 return918 }919 assign, ok := ifstmt.Body.List[0].(*ast.AssignStmt)920 if !ok {921 return922 }923 if assign.Tok != token.ASSIGN {924 return925 }926 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {927 return928 }929 if !sameNonDynamic(condCall.Args[0], assign.Lhs[0]) {930 return931 }932 switch rhs := assign.Rhs[0].(type) {933 case *ast.CallExpr:934 if len(rhs.Args) < 2 || !sameNonDynamic(condCall.Args[0], rhs.Args[0]) || !sameNonDynamic(condCall.Args[1], rhs.Args[1]) {935 return936 }937 rhsName := code.CallNameAST(pass, rhs)938 if condCallName == "strings.HasPrefix" && rhsName == "strings.TrimPrefix" ||939 condCallName == "strings.HasSuffix" && rhsName == "strings.TrimSuffix" ||940 condCallName == "strings.Contains" && rhsName == "strings.Replace" ||941 condCallName == "bytes.HasPrefix" && rhsName == "bytes.TrimPrefix" ||942 condCallName == "bytes.HasSuffix" && rhsName == "bytes.TrimSuffix" ||943 condCallName == "bytes.Contains" && rhsName == "bytes.Replace" {944 report.Report(pass, ifstmt, fmt.Sprintf("should replace this if statement with an unconditional %s", rhsName), report.FilterGenerated())945 }946 return947 case *ast.SliceExpr:948 slice := rhs949 if !ok {950 return951 }952 if slice.Slice3 {953 return954 }955 if !sameNonDynamic(slice.X, condCall.Args[0]) {956 return957 }958 var index ast.Expr959 switch fun {960 case "HasPrefix":961 // TODO(dh) We could detect a High that is len(s), but another962 // rule will already flag that, anyway.963 if slice.High != nil {964 return965 }966 index = slice.Low967 case "HasSuffix":968 if slice.Low != nil {969 n, ok := code.ExprToInt(pass, slice.Low)970 if !ok || n != 0 {971 return972 }973 }974 index = slice.High975 }976 switch index := index.(type) {977 case *ast.CallExpr:978 if fun != "HasPrefix" {979 return980 }981 if fn, ok := index.Fun.(*ast.Ident); !ok || fn.Name != "len" {982 return983 }984 if len(index.Args) != 1 {985 return986 }987 id3 := index.Args[Arg("len.v")]988 switch oid3 := condCall.Args[1].(type) {989 case *ast.BasicLit:990 if pkg != "strings" {991 return992 }993 lit, ok := id3.(*ast.BasicLit)994 if !ok {995 return996 }997 s1, ok1 := code.ExprToString(pass, lit)998 s2, ok2 := code.ExprToString(pass, condCall.Args[1])999 if !ok1 || !ok2 || s1 != s2 {1000 return1001 }1002 default:1003 if !sameNonDynamic(id3, oid3) {1004 return1005 }1006 }1007 case *ast.BasicLit, *ast.Ident:1008 if fun != "HasPrefix" {1009 return1010 }1011 if pkg != "strings" {1012 return1013 }1014 string, ok1 := code.ExprToString(pass, condCall.Args[1])1015 int, ok2 := code.ExprToInt(pass, slice.Low)1016 if !ok1 || !ok2 || int != int64(len(string)) {1017 return1018 }1019 case *ast.BinaryExpr:1020 if fun != "HasSuffix" {1021 return1022 }1023 if index.Op != token.SUB {1024 return1025 }1026 if !isLenOnIdent(index.X, condCall.Args[0]) ||1027 !isLenOnIdent(index.Y, condCall.Args[1]) {1028 return1029 }1030 default:1031 return1032 }1033 var replacement string1034 switch fun {1035 case "HasPrefix":1036 replacement = "TrimPrefix"1037 case "HasSuffix":1038 replacement = "TrimSuffix"1039 }1040 report.Report(pass, ifstmt, fmt.Sprintf("should replace this if statement with an unconditional %s.%s", pkg, replacement),1041 report.ShortRange(),1042 report.FilterGenerated())1043 }1044 }1045 code.Preorder(pass, fn, (*ast.IfStmt)(nil))1046 return nil, nil1047}1048var (1049 checkLoopSlideQ = pattern.MustParse(`1050 (ForStmt1051 (AssignStmt initvar@(Ident _) _ (BasicLit "INT" "0"))1052 (BinaryExpr initvar "<" limit@(Ident _))1053 (IncDecStmt initvar "++")1054 [(AssignStmt1055 (IndexExpr slice@(Ident _) initvar)1056 "="1057 (IndexExpr slice (BinaryExpr offset@(Ident _) "+" initvar)))])`)1058 checkLoopSlideR = pattern.MustParse(`1059 (CallExpr1060 (Ident "copy")1061 [(SliceExpr slice nil limit nil)1062 (SliceExpr slice offset nil nil)])`)1063)1064func CheckLoopSlide(pass *analysis.Pass) (interface{}, error) {1065 // TODO(dh): detect bs[i+offset] in addition to bs[offset+i]1066 // TODO(dh): consider merging this function with LintLoopCopy1067 // TODO(dh): detect length that is an expression, not a variable name1068 // TODO(dh): support sliding to a different offset than the beginning of the slice1069 fn := func(node ast.Node) {1070 loop := node.(*ast.ForStmt)1071 m, edits, ok := MatchAndEdit(pass, checkLoopSlideQ, checkLoopSlideR, loop)1072 if !ok {1073 return1074 }1075 if _, ok := pass.TypesInfo.TypeOf(m.State["slice"].(*ast.Ident)).Underlying().(*types.Slice); !ok {1076 return1077 }1078 report.Report(pass, loop, "should use copy() instead of loop for sliding slice elements",1079 report.ShortRange(),1080 report.FilterGenerated(),1081 report.Fixes(edit.Fix("use copy() instead of loop", edits...)))1082 }1083 code.Preorder(pass, fn, (*ast.ForStmt)(nil))1084 return nil, nil1085}1086var (1087 checkMakeLenCapQ1 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size@(BasicLit "INT" "0")])`)1088 checkMakeLenCapQ2 = pattern.MustParse(`(CallExpr (Builtin "make") [typ size size])`)1089)1090func CheckMakeLenCap(pass *analysis.Pass) (interface{}, error) {1091 fn := func(node ast.Node) {1092 if pass.Pkg.Path() == "runtime_test" && filepath.Base(pass.Fset.Position(node.Pos()).Filename) == "map_test.go" {1093 // special case of runtime tests testing map creation1094 return1095 }1096 if m, ok := Match(pass, checkMakeLenCapQ1, node); ok {1097 T := m.State["typ"].(ast.Expr)1098 size := m.State["size"].(ast.Node)1099 if _, ok := pass.TypesInfo.TypeOf(T).Underlying().(*types.Slice); ok {1100 return1101 }1102 report.Report(pass, size, fmt.Sprintf("should use make(%s) instead", report.Render(pass, T)), report.FilterGenerated())1103 } else if m, ok := Match(pass, checkMakeLenCapQ2, node); ok {1104 // TODO(dh): don't consider sizes identical if they're1105 // dynamic. for example: make(T, <-ch, <-ch).1106 T := m.State["typ"].(ast.Expr)1107 size := m.State["size"].(ast.Node)1108 report.Report(pass, size,1109 fmt.Sprintf("should use make(%s, %s) instead", report.Render(pass, T), report.Render(pass, size)),1110 report.FilterGenerated())1111 }1112 }1113 code.Preorder(pass, fn, (*ast.CallExpr)(nil))1114 return nil, nil1115}1116var (1117 checkAssertNotNilFn1Q = pattern.MustParse(`1118 (IfStmt1119 (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr assert@(Object _) _)])1120 (Or1121 (BinaryExpr ok "&&" (BinaryExpr assert "!=" (Builtin "nil")))1122 (BinaryExpr (BinaryExpr assert "!=" (Builtin "nil")) "&&" ok))1123 _1124 _)`)1125 checkAssertNotNilFn2Q = pattern.MustParse(`1126 (IfStmt1127 nil1128 (BinaryExpr lhs@(Object _) "!=" (Builtin "nil"))1129 [1130 ifstmt@(IfStmt1131 (AssignStmt [(Ident "_") ok@(Object _)] _ [(TypeAssertExpr lhs _)])1132 ok1133 _1134 _)1135 ]1136 nil)`)1137)1138func CheckAssertNotNil(pass *analysis.Pass) (interface{}, error) {1139 fn1 := func(node ast.Node) {1140 m, ok := Match(pass, checkAssertNotNilFn1Q, node)1141 if !ok {1142 return1143 }1144 assert := m.State["assert"].(types.Object)1145 assign := m.State["ok"].(types.Object)1146 report.Report(pass, node, fmt.Sprintf("when %s is true, %s can't be nil", assign.Name(), assert.Name()),1147 report.ShortRange(),1148 report.FilterGenerated())1149 }1150 fn2 := func(node ast.Node) {1151 m, ok := Match(pass, checkAssertNotNilFn2Q, node)1152 if !ok {1153 return1154 }1155 ifstmt := m.State["ifstmt"].(*ast.IfStmt)1156 lhs := m.State["lhs"].(types.Object)1157 assignIdent := m.State["ok"].(types.Object)1158 report.Report(pass, ifstmt, fmt.Sprintf("when %s is true, %s can't be nil", assignIdent.Name(), lhs.Name()),1159 report.ShortRange(),1160 report.FilterGenerated())1161 }1162 code.Preorder(pass, fn1, (*ast.IfStmt)(nil))1163 code.Preorder(pass, fn2, (*ast.IfStmt)(nil))1164 return nil, nil1165}1166func CheckDeclareAssign(pass *analysis.Pass) (interface{}, error) {1167 hasMultipleAssignments := func(root ast.Node, ident *ast.Ident) bool {1168 num := 01169 ast.Inspect(root, func(node ast.Node) bool {1170 if num >= 2 {1171 return false1172 }1173 assign, ok := node.(*ast.AssignStmt)1174 if !ok {1175 return true1176 }1177 for _, lhs := range assign.Lhs {1178 if oident, ok := lhs.(*ast.Ident); ok {1179 if oident.Obj == ident.Obj {1180 num++1181 }1182 }1183 }1184 return true1185 })1186 return num >= 21187 }1188 fn := func(node ast.Node) {1189 block := node.(*ast.BlockStmt)1190 if len(block.List) < 2 {1191 return1192 }1193 for i, stmt := range block.List[:len(block.List)-1] {1194 _ = i1195 decl, ok := stmt.(*ast.DeclStmt)1196 if !ok {1197 continue1198 }1199 gdecl, ok := decl.Decl.(*ast.GenDecl)1200 if !ok || gdecl.Tok != token.VAR || len(gdecl.Specs) != 1 {1201 continue1202 }1203 vspec, ok := gdecl.Specs[0].(*ast.ValueSpec)1204 if !ok || len(vspec.Names) != 1 || len(vspec.Values) != 0 {1205 continue1206 }1207 assign, ok := block.List[i+1].(*ast.AssignStmt)1208 if !ok || assign.Tok != token.ASSIGN {1209 continue1210 }1211 if len(assign.Lhs) != 1 || len(assign.Rhs) != 1 {1212 continue1213 }1214 ident, ok := assign.Lhs[0].(*ast.Ident)1215 if !ok {1216 continue1217 }1218 if vspec.Names[0].Obj != ident.Obj {1219 continue1220 }1221 if refersTo(pass, assign.Rhs[0], pass.TypesInfo.ObjectOf(ident)) {1222 continue1223 }1224 if hasMultipleAssignments(block, ident) {1225 continue1226 }1227 r := &ast.GenDecl{1228 Specs: []ast.Spec{1229 &ast.ValueSpec{1230 Names: vspec.Names,1231 Values: []ast.Expr{assign.Rhs[0]},1232 Type: vspec.Type,1233 },1234 },1235 Tok: gdecl.Tok,1236 }1237 report.Report(pass, decl, "should merge variable declaration with assignment on next line",1238 report.FilterGenerated(),1239 report.Fixes(edit.Fix("merge declaration with assignment", edit.ReplaceWithNode(pass.Fset, edit.Range{decl.Pos(), assign.End()}, r))))1240 }1241 }1242 code.Preorder(pass, fn, (*ast.BlockStmt)(nil))1243 return nil, nil1244}1245func CheckRedundantBreak(pass *analysis.Pass) (interface{}, error) {1246 fn1 := func(node ast.Node) {1247 clause := node.(*ast.CaseClause)1248 if len(clause.Body) < 2 {1249 return1250 }1251 branch, ok := clause.Body[len(clause.Body)-1].(*ast.BranchStmt)1252 if !ok || branch.Tok != token.BREAK || branch.Label != nil {1253 return1254 }1255 report.Report(pass, branch, "redundant break statement", report.FilterGenerated())1256 }1257 fn2 := func(node ast.Node) {1258 var ret *ast.FieldList1259 var body *ast.BlockStmt1260 switch x := node.(type) {1261 case *ast.FuncDecl:1262 ret = x.Type.Results1263 body = x.Body1264 case *ast.FuncLit:1265 ret = x.Type.Results1266 body = x.Body1267 default:1268 ExhaustiveTypeSwitch(node)1269 }1270 // if the func has results, a return can't be redundant.1271 // similarly, if there are no statements, there can be1272 // no return.1273 if ret != nil || body == nil || len(body.List) < 1 {1274 return1275 }1276 rst, ok := body.List[len(body.List)-1].(*ast.ReturnStmt)1277 if !ok {1278 return1279 }1280 // we don't need to check rst.Results as we already1281 // checked x.Type.Results to be nil.1282 report.Report(pass, rst, "redundant return statement", report.FilterGenerated())1283 }1284 code.Preorder(pass, fn1, (*ast.CaseClause)(nil))1285 code.Preorder(pass, fn2, (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil))1286 return nil, nil1287}1288func isStringer(T types.Type, msCache *typeutil.MethodSetCache) bool {1289 ms := msCache.MethodSet(T)1290 sel := ms.Lookup(nil, "String")1291 if sel == nil {1292 return false1293 }1294 fn, ok := sel.Obj().(*types.Func)1295 if !ok {1296 // should be unreachable1297 return false1298 }1299 sig := fn.Type().(*types.Signature)1300 if sig.Params().Len() != 0 {1301 return false1302 }1303 if sig.Results().Len() != 1 {1304 return false1305 }1306 if !code.IsType(sig.Results().At(0).Type(), "string") {1307 return false1308 }1309 return true1310}1311var checkRedundantSprintfQ = pattern.MustParse(`(CallExpr (Function "fmt.Sprintf") [format arg])`)1312func CheckRedundantSprintf(pass *analysis.Pass) (interface{}, error) {1313 fn := func(node ast.Node) {1314 m, ok := Match(pass, checkRedundantSprintfQ, node)1315 if !ok {1316 return1317 }1318 format := m.State["format"].(ast.Expr)1319 arg := m.State["arg"].(ast.Expr)1320 if s, ok := code.ExprToString(pass, format); !ok || s != "%s" {1321 return1322 }1323 typ := pass.TypesInfo.TypeOf(arg)1324 irpkg := pass.ResultOf[buildir.Analyzer].(*buildir.IR).Pkg1325 if types.TypeString(typ, nil) != "reflect.Value" && isStringer(typ, &irpkg.Prog.MethodSets) {1326 replacement := &ast.CallExpr{1327 Fun: &ast.SelectorExpr{1328 X: arg,1329 Sel: &ast.Ident{Name: "String"},1330 },1331 }1332 report.Report(pass, node, "should use String() instead of fmt.Sprintf",1333 report.Fixes(edit.Fix("replace with call to String method", edit.ReplaceWithNode(pass.Fset, node, replacement))))1334 return1335 }1336 if typ.Underlying() == types.Universe.Lookup("string").Type() {1337 if typ == types.Universe.Lookup("string").Type() {1338 report.Report(pass, node, "the argument is already a string, there's no need to use fmt.Sprintf",1339 report.FilterGenerated(),1340 report.Fixes(edit.Fix("remove unnecessary call to fmt.Sprintf", edit.ReplaceWithNode(pass.Fset, node, arg))))1341 } else {1342 replacement := &ast.CallExpr{1343 Fun: &ast.Ident{Name: "string"},1344 Args: []ast.Expr{arg},1345 }1346 report.Report(pass, node, "the argument's underlying type is a string, should use a simple conversion instead of fmt.Sprintf",1347 report.FilterGenerated(),1348 report.Fixes(edit.Fix("replace with conversion to string", edit.ReplaceWithNode(pass.Fset, node, replacement))))1349 }1350 }1351 }1352 code.Preorder(pass, fn, (*ast.CallExpr)(nil))1353 return nil, nil1354}1355var (1356 checkErrorsNewSprintfQ = pattern.MustParse(`(CallExpr (Function "errors.New") [(CallExpr (Function "fmt.Sprintf") args)])`)1357 checkErrorsNewSprintfR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "fmt") (Ident "Errorf")) args)`)1358)1359func CheckErrorsNewSprintf(pass *analysis.Pass) (interface{}, error) {1360 fn := func(node ast.Node) {1361 if _, edits, ok := MatchAndEdit(pass, checkErrorsNewSprintfQ, checkErrorsNewSprintfR, node); ok {1362 // TODO(dh): the suggested fix may leave an unused import behind1363 report.Report(pass, node, "should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...))",1364 report.FilterGenerated(),1365 report.Fixes(edit.Fix("use fmt.Errorf", edits...)))1366 }1367 }1368 code.Preorder(pass, fn, (*ast.CallExpr)(nil))1369 return nil, nil1370}1371func CheckRangeStringRunes(pass *analysis.Pass) (interface{}, error) {1372 return sharedcheck.CheckRangeStringRunes(pass)1373}1374var checkNilCheckAroundRangeQ = pattern.MustParse(`1375 (IfStmt1376 nil1377 (BinaryExpr x@(Object _) "!=" (Builtin "nil"))1378 [(RangeStmt _ _ _ x _)]1379 nil)`)1380func CheckNilCheckAroundRange(pass *analysis.Pass) (interface{}, error) {1381 fn := func(node ast.Node) {1382 m, ok := Match(pass, checkNilCheckAroundRangeQ, node)1383 if !ok {1384 return1385 }1386 switch m.State["x"].(types.Object).Type().Underlying().(type) {1387 case *types.Slice, *types.Map:1388 report.Report(pass, node, "unnecessary nil check around range",1389 report.ShortRange(),1390 report.FilterGenerated())1391 }1392 }1393 code.Preorder(pass, fn, (*ast.IfStmt)(nil))1394 return nil, nil1395}1396func isPermissibleSort(pass *analysis.Pass, node ast.Node) bool {1397 call := node.(*ast.CallExpr)1398 typeconv, ok := call.Args[0].(*ast.CallExpr)1399 if !ok {1400 return true1401 }1402 sel, ok := typeconv.Fun.(*ast.SelectorExpr)1403 if !ok {1404 return true1405 }1406 name := code.SelectorName(pass, sel)1407 switch name {1408 case "sort.IntSlice", "sort.Float64Slice", "sort.StringSlice":1409 default:1410 return true1411 }1412 return false1413}1414func CheckSortHelpers(pass *analysis.Pass) (interface{}, error) {1415 type Error struct {1416 node ast.Node1417 msg string1418 }1419 var allErrors []Error1420 fn := func(node ast.Node) {1421 var body *ast.BlockStmt1422 switch node := node.(type) {1423 case *ast.FuncLit:1424 body = node.Body1425 case *ast.FuncDecl:1426 body = node.Body1427 default:1428 ExhaustiveTypeSwitch(node)1429 }1430 if body == nil {1431 return1432 }1433 var errors []Error1434 permissible := false1435 fnSorts := func(node ast.Node) bool {1436 if permissible {1437 return false1438 }1439 if !code.IsCallToAST(pass, node, "sort.Sort") {1440 return true1441 }1442 if isPermissibleSort(pass, node) {1443 permissible = true1444 return false1445 }1446 call := node.(*ast.CallExpr)1447 typeconv := call.Args[Arg("sort.Sort.data")].(*ast.CallExpr)1448 sel := typeconv.Fun.(*ast.SelectorExpr)1449 name := code.SelectorName(pass, sel)1450 switch name {1451 case "sort.IntSlice":1452 errors = append(errors, Error{node, "should use sort.Ints(...) instead of sort.Sort(sort.IntSlice(...))"})1453 case "sort.Float64Slice":1454 errors = append(errors, Error{node, "should use sort.Float64s(...) instead of sort.Sort(sort.Float64Slice(...))"})1455 case "sort.StringSlice":1456 errors = append(errors, Error{node, "should use sort.Strings(...) instead of sort.Sort(sort.StringSlice(...))"})1457 }1458 return true1459 }1460 ast.Inspect(body, fnSorts)1461 if permissible {1462 return1463 }1464 allErrors = append(allErrors, errors...)1465 }1466 code.Preorder(pass, fn, (*ast.FuncLit)(nil), (*ast.FuncDecl)(nil))1467 sort.Slice(allErrors, func(i, j int) bool {1468 return allErrors[i].node.Pos() < allErrors[j].node.Pos()1469 })1470 var prev token.Pos1471 for _, err := range allErrors {1472 if err.node.Pos() == prev {1473 continue1474 }1475 prev = err.node.Pos()1476 report.Report(pass, err.node, err.msg, report.FilterGenerated())1477 }1478 return nil, nil1479}1480var checkGuardedDeleteQ = pattern.MustParse(`1481 (IfStmt1482 (AssignStmt1483 [(Ident "_") ok@(Ident _)]1484 ":="1485 (IndexExpr m key))1486 ok1487 [call@(CallExpr (Builtin "delete") [m key])]1488 nil)`)1489func CheckGuardedDelete(pass *analysis.Pass) (interface{}, error) {1490 fn := func(node ast.Node) {1491 if m, ok := Match(pass, checkGuardedDeleteQ, node); ok {1492 report.Report(pass, node, "unnecessary guard around call to delete",1493 report.ShortRange(),1494 report.FilterGenerated(),1495 report.Fixes(edit.Fix("remove guard", edit.ReplaceWithNode(pass.Fset, node, m.State["call"].(ast.Node)))))1496 }1497 }1498 code.Preorder(pass, fn, (*ast.IfStmt)(nil))1499 return nil, nil1500}1501var (1502 checkSimplifyTypeSwitchQ = pattern.MustParse(`1503 (TypeSwitchStmt1504 nil1505 expr@(TypeAssertExpr ident@(Ident _) _)1506 body)`)1507 checkSimplifyTypeSwitchR = pattern.MustParse(`(AssignStmt ident ":=" expr)`)1508)1509func CheckSimplifyTypeSwitch(pass *analysis.Pass) (interface{}, error) {1510 fn := func(node ast.Node) {1511 m, ok := Match(pass, checkSimplifyTypeSwitchQ, node)1512 if !ok {1513 return1514 }1515 stmt := node.(*ast.TypeSwitchStmt)1516 expr := m.State["expr"].(ast.Node)1517 ident := m.State["ident"].(*ast.Ident)1518 x := pass.TypesInfo.ObjectOf(ident)1519 var allOffenders []*ast.TypeAssertExpr1520 canSuggestFix := true1521 for _, clause := range stmt.Body.List {1522 clause := clause.(*ast.CaseClause)1523 if len(clause.List) != 1 {1524 continue1525 }1526 hasUnrelatedAssertion := false1527 var offenders []*ast.TypeAssertExpr1528 ast.Inspect(clause, func(node ast.Node) bool {1529 assert2, ok := node.(*ast.TypeAssertExpr)1530 if !ok {1531 return true1532 }1533 ident, ok := assert2.X.(*ast.Ident)1534 if !ok {1535 hasUnrelatedAssertion = true1536 return false1537 }1538 if pass.TypesInfo.ObjectOf(ident) != x {1539 hasUnrelatedAssertion = true1540 return false1541 }1542 if !types.Identical(pass.TypesInfo.TypeOf(clause.List[0]), pass.TypesInfo.TypeOf(assert2.Type)) {1543 hasUnrelatedAssertion = true1544 return false1545 }1546 offenders = append(offenders, assert2)1547 return true1548 })1549 if !hasUnrelatedAssertion {1550 // don't flag cases that have other type assertions1551 // unrelated to the one in the case clause. often1552 // times, this is done for symmetry, when two1553 // different values have to be asserted to the same1554 // type.1555 allOffenders = append(allOffenders, offenders...)1556 }1557 canSuggestFix = canSuggestFix && !hasUnrelatedAssertion1558 }1559 if len(allOffenders) != 0 {1560 var opts []report.Option1561 for _, offender := range allOffenders {1562 opts = append(opts, report.Related(offender, "could eliminate this type assertion"))1563 }1564 opts = append(opts, report.FilterGenerated())1565 msg := fmt.Sprintf("assigning the result of this type assertion to a variable (switch %s := %s.(type)) could eliminate type assertions in switch cases",1566 report.Render(pass, ident), report.Render(pass, ident))1567 if canSuggestFix {1568 var edits []analysis.TextEdit1569 edits = append(edits, edit.ReplaceWithPattern(pass, checkSimplifyTypeSwitchR, m.State, expr))1570 for _, offender := range allOffenders {1571 edits = append(edits, edit.ReplaceWithNode(pass.Fset, offender, offender.X))1572 }1573 opts = append(opts, report.Fixes(edit.Fix("simplify type switch", edits...)))1574 report.Report(pass, expr, msg, opts...)1575 } else {1576 report.Report(pass, expr, msg, opts...)1577 }1578 }1579 }1580 code.Preorder(pass, fn, (*ast.TypeSwitchStmt)(nil))1581 return nil, nil1582}1583func CheckRedundantCanonicalHeaderKey(pass *analysis.Pass) (interface{}, error) {1584 fn := func(node ast.Node) {1585 call := node.(*ast.CallExpr)1586 callName := code.CallNameAST(pass, call)1587 switch callName {1588 case "(net/http.Header).Add", "(net/http.Header).Del", "(net/http.Header).Get", "(net/http.Header).Set":1589 default:1590 return1591 }1592 if !code.IsCallToAST(pass, call.Args[0], "net/http.CanonicalHeaderKey") {1593 return1594 }1595 report.Report(pass, call,1596 fmt.Sprintf("calling net/http.CanonicalHeaderKey on the 'key' argument of %s is redundant", callName),1597 report.FilterGenerated(),1598 report.Fixes(edit.Fix("remove call to CanonicalHeaderKey", edit.ReplaceWithNode(pass.Fset, call.Args[0], call.Args[0].(*ast.CallExpr).Args[0]))))1599 }1600 code.Preorder(pass, fn, (*ast.CallExpr)(nil))1601 return nil, nil1602}1603var checkUnnecessaryGuardQ = pattern.MustParse(`1604 (Or1605 (IfStmt1606 (AssignStmt [(Ident "_") ok@(Ident _)] ":=" indexexpr@(IndexExpr _ _))1607 ok1608 set@(AssignStmt indexexpr "=" (CallExpr (Builtin "append") indexexpr:values))1609 (AssignStmt indexexpr "=" (CompositeLit _ values)))1610 (IfStmt1611 (AssignStmt [(Ident "_") ok] ":=" indexexpr@(IndexExpr _ _))1612 ok1613 set@(AssignStmt indexexpr "+=" value)1614 (AssignStmt indexexpr "=" value))1615 (IfStmt1616 (AssignStmt [(Ident "_") ok] ":=" indexexpr@(IndexExpr _ _))1617 ok1618 set@(IncDecStmt indexexpr "++")1619 (AssignStmt indexexpr "=" (BasicLit "INT" "1"))))`)1620func CheckUnnecessaryGuard(pass *analysis.Pass) (interface{}, error) {1621 fn := func(node ast.Node) {1622 if m, ok := Match(pass, checkUnnecessaryGuardQ, node); ok {1623 if code.MayHaveSideEffects(pass, m.State["indexexpr"].(ast.Expr), nil) {1624 return1625 }1626 report.Report(pass, node, "unnecessary guard around map access",1627 report.ShortRange(),1628 report.Fixes(edit.Fix("simplify map access", edit.ReplaceWithNode(pass.Fset, node, m.State["set"].(ast.Node)))))1629 }1630 }1631 code.Preorder(pass, fn, (*ast.IfStmt)(nil))1632 return nil, nil1633}1634var (1635 checkElaborateSleepQ = pattern.MustParse(`(SelectStmt (CommClause (UnaryExpr "<-" (CallExpr (Function "time.After") [arg])) body))`)1636 checkElaborateSleepR = pattern.MustParse(`(CallExpr (SelectorExpr (Ident "time") (Ident "Sleep")) [arg])`)1637)1638func CheckElaborateSleep(pass *analysis.Pass) (interface{}, error) {1639 fn := func(node ast.Node) {1640 if m, ok := Match(pass, checkElaborateSleepQ, node); ok {1641 if body, ok := m.State["body"].([]ast.Stmt); ok && len(body) == 0 {1642 report.Report(pass, node, "should use time.Sleep instead of elaborate way of sleeping",1643 report.ShortRange(),1644 report.FilterGenerated(),1645 report.Fixes(edit.Fix("Use time.Sleep", edit.ReplaceWithPattern(pass, checkElaborateSleepR, m.State, node))))1646 } else {1647 // TODO(dh): we could make a suggested fix if the body1648 // doesn't declare or shadow any identifiers1649 report.Report(pass, node, "should use time.Sleep instead of elaborate way of sleeping",1650 report.ShortRange(),1651 report.FilterGenerated())1652 }1653 }1654 }1655 code.Preorder(pass, fn, (*ast.SelectStmt)(nil))1656 return nil, nil1657}1658var checkPrintSprintQ = pattern.MustParse(`1659 (Or1660 (CallExpr1661 fn@(Or1662 (Function "fmt.Print")1663 (Function "fmt.Sprint")1664 (Function "fmt.Println")1665 (Function "fmt.Sprintln"))1666 [(CallExpr (Function "fmt.Sprintf") f:_)])1667 (CallExpr1668 fn@(Or1669 (Function "fmt.Fprint")1670 (Function "fmt.Fprintln"))1671 [_ (CallExpr (Function "fmt.Sprintf") f:_)]))`)1672func CheckPrintSprintf(pass *analysis.Pass) (interface{}, error) {1673 fn := func(node ast.Node) {1674 m, ok := Match(pass, checkPrintSprintQ, node)1675 if !ok {1676 return1677 }1678 name := m.State["fn"].(*types.Func).Name()1679 var msg string1680 switch name {1681 case "Print", "Fprint", "Sprint":1682 newname := name + "f"1683 msg = fmt.Sprintf("should use fmt.%s instead of fmt.%s(fmt.Sprintf(...))", newname, name)1684 case "Println", "Fprintln", "Sprintln":1685 if _, ok := m.State["f"].(*ast.BasicLit); !ok {1686 // This may be an instance of1687 // fmt.Println(fmt.Sprintf(arg, ...)) where arg is an1688 // externally provided format string and the caller1689 // cannot guarantee that the format string ends with a1690 // newline.1691 return1692 }1693 newname := name[:len(name)-2] + "f"1694 msg = fmt.Sprintf("should use fmt.%s instead of fmt.%s(fmt.Sprintf(...)) (but don't forget the newline)", newname, name)1695 }1696 report.Report(pass, node, msg,1697 report.FilterGenerated())1698 }1699 code.Preorder(pass, fn, (*ast.CallExpr)(nil))1700 return nil, nil1701}1702var checkSprintLiteralQ = pattern.MustParse(`1703 (CallExpr1704 fn@(Or1705 (Function "fmt.Sprint")1706 (Function "fmt.Sprintf"))1707 [lit@(BasicLit "STRING" _)])`)1708func CheckSprintLiteral(pass *analysis.Pass) (interface{}, error) {1709 // We only flag calls with string literals, not expressions of1710 // type string, because some people use fmt.Sprint(s) as a pattern1711 // for copying strings, which may be useful when extracing a small1712 // substring from a large string.1713 fn := func(node ast.Node) {1714 m, ok := Match(pass, checkSprintLiteralQ, node)1715 if !ok {1716 return1717 }1718 callee := m.State["fn"].(*types.Func)1719 lit := m.State["lit"].(*ast.BasicLit)1720 if callee.Name() == "Sprintf" {1721 if strings.ContainsRune(lit.Value, '%') {1722 // This might be a format string...

Full Screen

Full Screen

check.go

Source:check.go Github

copy

Full Screen

1// Copyright 2011 The Go Authors. All rights reserved.2// Use of this source code is governed by a BSD-style3// license that can be found in the LICENSE file.4// This file implements the Check function, which typechecks a package.5package types6import (7 "fmt"8 "go/ast"9 "go/token"10)11// enable for debugging12const trace = false13type checker struct {14 ctxt *Context15 fset *token.FileSet16 files []*ast.File17 // lazily initialized18 pkg *Package // current package...

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 f, err := parser.ParseFile(fset, "1.go", nil, 0)4 if err != nil {5 fmt.Println(err)6 }7 v = &ast.Checker{}8 ast.Walk(v, f)9}10import "fmt"11func main() {12 fmt.Println("Hello, playground")13}141.go:5:2: expected 'package', found 'import'151.go:6:2: expected 'package', found 'import'161.go:8:2: expected 'package', found '}'171.go:9:2: expected 'package', found '}'18import (19func main() {20 _, err := b.Import("path/to/package", "", build.FindOnly)21 if err != nil {22 fmt.Println(err)23 }24}25import (26func main() {27 _, err := b.Import("path/to/package", "", build.FindOnly)28 if err != nil {29 fmt.Println(err)30 }31}

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 f, err := parser.ParseFile(fset, "1.go", nil, parser.ParseComments)4 if err != nil {5 fmt.Println(err)6 }7 ast.Print(fset, f)8 for _, s := range f.Imports {9 fmt.Println(s.Path.Value)10 }11 for _, c := range f.Comments {12 fmt.Println(c.Text())13 }

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 f, err := parser.ParseFile(fset, "2.go", nil, parser.ParseComments)4 if err != nil {5 fmt.Println(err)6 }7 for _, s := range f.Imports {8 fmt.Println(s.Path.Value)9 }10}11import (12func main() {13 f, err := parser.ParseFile(fset, "3.go", nil, parser.ParseComments)14 if err != nil {15 fmt.Println(err)16 }17 for _, s := range f.Imports {18 fmt.Println(s.Path.Value)19 }20}21import (22func main() {23 f, err := parser.ParseFile(fset, "4.go", nil, parser.ParseComments)24 if err != nil {25 fmt.Println(err)26 }27 for _, s := range f.Imports {28 fmt.Println(s.Path.Value)29 }30}

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 f, err := parser.ParseFile(fset, "2.go", nil, parser.ParseComments)4 if err != nil {5 fmt.Println(err)6 }7 for _, s := range f.Imports {8 fmt.Println(s.Path.Value)9 }10}

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 node, err := parser.ParseFile(fset, "1.go", nil, 0)4 if err != nil {5 fmt.Println(err)6 }7 ast.Print(fset, node)8 if err := ast.Check(fset, node); err != nil {9 fmt.Println(err)10 }11}12import "fmt"13func main() {14 fmt.Println("Hello, playground")15}16import "fmt"17func main() {18 fmt.Println("Hello, playground")19}

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 f, err := parser.ParseFile(fset, "1.go", nil, parser.ParseComments)4 if err != nil {5 fmt.Println(err)6 }7 ast.Print(fset, f)8 for _, s := range f.Imports {9 fmt.Println(s.Path.Value)10 }11 if err := ast.Check(fset, f); err != nil {12 fmt.Println(err)13 }14 ast.Print(fset, f)15 ast.Fprint(os.Stdout, fset, f, nil)16}

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 src, err := parser.ParseFile(fset, "", "package p; import \"fmt\"; func f() { fmt.Println(1) }", parser.ImportsOnly)4 if err != nil {5 }6 ast.Print(fset, src)7 fmt.Println("Imports:")8 for _, s := range src.Imports {9 fmt.Println(s.Path.Value)10 }11}12 0: ()13 ExprStmt 0: fmt.Println(1)14 CallExpr 0: fmt.Println(1)15 1: ()

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1import (2func main() {3 expr, err := parser.ParseExpr("x + y")4 if err != nil {5 fmt.Println(err)6 }7 ast.Inspect(expr, func(n ast.Node) bool {8 switch x := n.(type) {9 fmt.Println("BinaryExpr")10 fmt.Println("Ident")11 fmt.Println("BasicLit")12 }13 })14}

Full Screen

Full Screen

Check

Using AI Code Generation

copy

Full Screen

1func main() {2 astObj.Check()3}4func main() {5 astObj.Check()6}7func main() {8 astObj.Check()9}10func main() {11 astObj.Check()12}13func main() {14 astObj.Check()15}16func main() {17 astObj.Check()18}19func main() {20 astObj.Check()21}22func main() {23 astObj.Check()24}25func main() {26 astObj.Check()27}28func main() {29 astObj.Check()30}31func main() {32 astObj.Check()33}34func main() {35 astObj.Check()36}37func main() {38 astObj.Check()39}40func main() {41 astObj.Check()42}43func main() {

Full Screen

Full Screen

Automation Testing Tutorials

Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.

LambdaTest Learning Hubs:

YouTube

You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.

Try LambdaTest Now !!

Get 100 minutes of automation test minutes FREE!!

Next-Gen App & Browser Testing Cloud

Was this article helpful?

Helpful

NotHelpful