1package checkers 2 3import ( 4 "go/ast" 5 "go/token" 6 7 "github.com/go-critic/go-critic/checkers/internal/astwalk" 8 "github.com/go-critic/go-critic/framework/linter" 9 "github.com/go-toolsmith/astcast" 10 "github.com/go-toolsmith/astcopy" 11 "github.com/go-toolsmith/typep" 12) 13 14func init() { 15 var info linter.CheckerInfo 16 info.Name = "methodExprCall" 17 info.Tags = []string{"style", "experimental"} 18 info.Summary = "Detects method expression call that can be replaced with a method call" 19 info.Before = `f := foo{} 20foo.bar(f)` 21 info.After = `f := foo{} 22f.bar()` 23 24 collection.AddChecker(&info, func(ctx *linter.CheckerContext) (linter.FileWalker, error) { 25 return astwalk.WalkerForExpr(&methodExprCallChecker{ctx: ctx}), nil 26 }) 27} 28 29type methodExprCallChecker struct { 30 astwalk.WalkHandler 31 ctx *linter.CheckerContext 32} 33 34func (c *methodExprCallChecker) VisitExpr(x ast.Expr) { 35 call := astcast.ToCallExpr(x) 36 s := astcast.ToSelectorExpr(call.Fun) 37 38 if len(call.Args) < 1 || astcast.ToIdent(call.Args[0]).Name == "nil" { 39 return 40 } 41 42 if typep.IsTypeExpr(c.ctx.TypesInfo, s.X) { 43 c.warn(call, s) 44 } 45} 46 47func (c *methodExprCallChecker) warn(cause *ast.CallExpr, s *ast.SelectorExpr) { 48 selector := astcopy.SelectorExpr(s) 49 selector.X = cause.Args[0] 50 51 // Remove "&" from the receiver (if any). 52 if u, ok := selector.X.(*ast.UnaryExpr); ok && u.Op == token.AND { 53 selector.X = u.X 54 } 55 56 c.ctx.Warn(cause, "consider to change `%s` to `%s`", cause.Fun, selector) 57} 58