src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

s1003.go (2555B)


      1 package s1003
      2 
      3 import (
      4 	"fmt"
      5 	"go/ast"
      6 	"go/token"
      7 
      8 	"honnef.co/go/tools/analysis/code"
      9 	"honnef.co/go/tools/analysis/edit"
     10 	"honnef.co/go/tools/analysis/facts/generated"
     11 	"honnef.co/go/tools/analysis/lint"
     12 	"honnef.co/go/tools/analysis/report"
     13 
     14 	"golang.org/x/tools/go/analysis"
     15 	"golang.org/x/tools/go/analysis/passes/inspect"
     16 )
     17 
     18 var SCAnalyzer = lint.InitializeAnalyzer(&lint.Analyzer{
     19 	Analyzer: &analysis.Analyzer{
     20 		Name:     "S1003",
     21 		Run:      run,
     22 		Requires: []*analysis.Analyzer{inspect.Analyzer, generated.Analyzer},
     23 	},
     24 	Doc: &lint.RawDocumentation{
     25 		Title:   `Replace call to \'strings.Index\' with \'strings.Contains\'`,
     26 		Before:  `if strings.Index(x, y) != -1 {}`,
     27 		After:   `if strings.Contains(x, y) {}`,
     28 		Since:   "2017.1",
     29 		MergeIf: lint.MergeIfAny,
     30 	},
     31 })
     32 
     33 var Analyzer = SCAnalyzer.Analyzer
     34 
     35 func run(pass *analysis.Pass) (any, error) {
     36 	// map of value to token to bool value
     37 	allowed := map[int64]map[token.Token]bool{
     38 		-1: {token.GTR: true, token.NEQ: true, token.EQL: false},
     39 		0:  {token.GEQ: true, token.LSS: false},
     40 	}
     41 	fn := func(node ast.Node) {
     42 		expr := node.(*ast.BinaryExpr)
     43 		switch expr.Op {
     44 		case token.GEQ, token.GTR, token.NEQ, token.LSS, token.EQL:
     45 		default:
     46 			return
     47 		}
     48 
     49 		value, ok := code.ExprToInt(pass, expr.Y)
     50 		if !ok {
     51 			return
     52 		}
     53 
     54 		allowedOps, ok := allowed[value]
     55 		if !ok {
     56 			return
     57 		}
     58 		b, ok := allowedOps[expr.Op]
     59 		if !ok {
     60 			return
     61 		}
     62 
     63 		call, ok := expr.X.(*ast.CallExpr)
     64 		if !ok {
     65 			return
     66 		}
     67 		sel, ok := call.Fun.(*ast.SelectorExpr)
     68 		if !ok {
     69 			return
     70 		}
     71 		pkgIdent, ok := sel.X.(*ast.Ident)
     72 		if !ok {
     73 			return
     74 		}
     75 		funIdent := sel.Sel
     76 		if pkgIdent.Name != "strings" && pkgIdent.Name != "bytes" {
     77 			return
     78 		}
     79 
     80 		var r ast.Expr
     81 		switch funIdent.Name {
     82 		case "IndexRune":
     83 			r = &ast.SelectorExpr{
     84 				X:   pkgIdent,
     85 				Sel: &ast.Ident{Name: "ContainsRune"},
     86 			}
     87 		case "IndexAny":
     88 			r = &ast.SelectorExpr{
     89 				X:   pkgIdent,
     90 				Sel: &ast.Ident{Name: "ContainsAny"},
     91 			}
     92 		case "Index":
     93 			r = &ast.SelectorExpr{
     94 				X:   pkgIdent,
     95 				Sel: &ast.Ident{Name: "Contains"},
     96 			}
     97 		default:
     98 			return
     99 		}
    100 
    101 		r = &ast.CallExpr{
    102 			Fun:  r,
    103 			Args: call.Args,
    104 		}
    105 		if !b {
    106 			r = &ast.UnaryExpr{
    107 				Op: token.NOT,
    108 				X:  r,
    109 			}
    110 		}
    111 
    112 		report.Report(pass, node, fmt.Sprintf("should use %s instead", report.Render(pass, r)),
    113 			report.FilterGenerated(),
    114 			report.Fixes(edit.Fix(fmt.Sprintf("Simplify use of %s", report.Render(pass, call.Fun)), edit.ReplaceWithNode(pass.Fset, node, r))))
    115 	}
    116 	code.Preorder(pass, fn, (*ast.BinaryExpr)(nil))
    117 	return nil, nil
    118 }