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/astcopy" 10 "github.com/go-toolsmith/astp" 11) 12 13func init() { 14 var info linter.CheckerInfo 15 info.Name = "yodaStyleExpr" 16 info.Tags = []string{"style", "experimental"} 17 info.Summary = "Detects Yoda style expressions and suggests to replace them" 18 info.Before = `return nil != ptr` 19 info.After = `return ptr != nil` 20 21 collection.AddChecker(&info, func(ctx *linter.CheckerContext) linter.FileWalker { 22 return astwalk.WalkerForLocalExpr(&yodaStyleExprChecker{ctx: ctx}) 23 }) 24} 25 26type yodaStyleExprChecker struct { 27 astwalk.WalkHandler 28 ctx *linter.CheckerContext 29} 30 31func (c *yodaStyleExprChecker) VisitLocalExpr(expr ast.Expr) { 32 binexpr, ok := expr.(*ast.BinaryExpr) 33 if !ok { 34 return 35 } 36 switch binexpr.Op { 37 case token.EQL, token.NEQ, token.LSS, token.LEQ, token.GEQ, token.GTR: 38 if c.isConstExpr(binexpr.X) && !c.isConstExpr(binexpr.Y) { 39 c.warn(binexpr) 40 } 41 } 42} 43 44func (c *yodaStyleExprChecker) isConstExpr(expr ast.Expr) bool { 45 return qualifiedName(expr) == "nil" || astp.IsBasicLit(expr) 46} 47 48func (c *yodaStyleExprChecker) invert(expr *ast.BinaryExpr) { 49 expr.X, expr.Y = expr.Y, expr.X 50 switch expr.Op { 51 case token.LSS: 52 expr.Op = token.GEQ 53 case token.LEQ: 54 expr.Op = token.GTR 55 case token.GEQ: 56 expr.Op = token.LSS 57 case token.GTR: 58 expr.Op = token.LEQ 59 } 60} 61 62func (c *yodaStyleExprChecker) warn(expr *ast.BinaryExpr) { 63 e := astcopy.BinaryExpr(expr) 64 c.invert(e) 65 c.ctx.Warn(expr, "consider to change order in expression to %s", e) 66} 67