wowlab_tidy/languages/rust/rules/style/
yoda_conditions.rs1use ra_ap_syntax::ast::{self, BinaryOp, CmpOp};
2
3use crate::{AstCtx, Example, Violation};
4
5#[rustfmt::skip]
6const EXAMPLES: &[Example] = &[
7 Example {
8 label: "literal on left eq",
9 code: "fn f(x: i32) { if 0 == x {} }",
10 pass: false,
11 },
12 Example {
13 label: "literal on right",
14 code: "fn f(x: i32) { if x == 0 {} }",
15 pass: true,
16 },
17 Example {
18 label: "literal on left ne",
19 code: "fn f(y: i32) { if 1 != y {} }",
20 pass: false,
21 },
22 Example {
23 label: "both literals",
24 code: r#"fn f() { if "a" == "b" {} }"#,
25 pass: true,
26 },
27 Example {
28 label: "non-comparison",
29 code: "fn f(x: i32) { let _ = 1 + x; }",
30 pass: true,
31 },
32 Example {
33 label: "yoda in test module",
34 code: "#[cfg(test)]\nmod tests {\n fn t(x: i32) { if 0 == x {} }\n}",
35 pass: true,
36 },
37];
38
39crate::ast_rule!(
40 yoda_conditions,
41 "Flag reversed comparisons like `0 == x` — prefer `x == 0`.",
42 "In Rust there is no accidental assignment in conditions, so the C-style 0 == x guard is unnecessary and harder to read.",
43);
44
45fn check_yoda_conditions(ctx: &AstCtx<'_>) -> Vec<Violation> {
46 ctx.nodes::<ast::BinExpr>()
47 .filter(|expr| {
48 !ctx.is_in_test(expr)
49 && matches!(expr.op_kind(), Some(BinaryOp::CmpOp(CmpOp::Eq { .. })))
50 })
51 .filter_map(|expr| {
52 let (left, right) = expr.sub_exprs();
53 let (left, right) = (left?, right?);
54
55 (matches!(left, ast::Expr::Literal(_)) && !matches!(right, ast::Expr::Literal(_)))
56 .then(|| ctx.violation(&left, "Yoda condition — put the literal on the right side"))
57 })
58 .collect()
59}
60
61crate::tidy_ast_test!(check_yoda_conditions, {
62 crate::example_tests!(EXAMPLES, check_yoda_conditions);
63});