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