From 69958674d514629c6d4f66007c06b3dd2f20fa3b Mon Sep 17 00:00:00 2001 From: zvkemp Date: Mon, 13 Apr 2026 09:47:39 -0400 Subject: [PATCH] conditionally wrap LHS of `int_plus_one` error to avoid parser ambiguity --- clippy_lints/src/int_plus_one.rs | 10 ++++++++- tests/ui/int_plus_one.fixed | 14 ++++++++++++ tests/ui/int_plus_one.rs | 14 ++++++++++++ tests/ui/int_plus_one.stderr | 38 +++++++++++++++++++++++++++++++- 4 files changed, 74 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs index f8184b30f400..524cb6bf7536 100644 --- a/clippy_lints/src/int_plus_one.rs +++ b/clippy_lints/src/int_plus_one.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::sugg; use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp}; +use rustc_ast::util::parser::AssocOp; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -134,12 +135,19 @@ fn emit_warning(cx: &EarlyContext<'_>, expr: &Expr, new_lhs: &Expr, le_or_ge: Le |diag| { let mut app = Applicability::MachineApplicable; let ctxt = expr.span.ctxt(); - let new_lhs = sugg::Sugg::ast(cx, new_lhs, "_", ctxt, &mut app); + let mut new_lhs = sugg::Sugg::ast(cx, new_lhs, "_", ctxt, &mut app); let new_rhs = sugg::Sugg::ast(cx, new_rhs, "_", ctxt, &mut app); let new_binop = match le_or_ge { LeOrGe::Ge => BinOpKind::Gt, LeOrGe::Le => BinOpKind::Lt, }; + // When the replacement operator is `<`, an `as` cast on the LHS + // must be parenthesized. Otherwise, the parser interprets the `<` + // as the start of generic arguments on the cast type + // (e.g., `x as usize < y` is parsed as `x as usize`). + if matches!(new_lhs, sugg::Sugg::BinOp(AssocOp::Cast, ..)) && new_binop == BinOpKind::Lt { + new_lhs = new_lhs.maybe_paren(); + } let rec = sugg::make_binop(new_binop, &new_lhs, &new_rhs); diag.span_suggestion(expr.span, "change it to", rec, app); }, diff --git a/tests/ui/int_plus_one.fixed b/tests/ui/int_plus_one.fixed index cdd19515e9a7..02087a0720e7 100644 --- a/tests/ui/int_plus_one.fixed +++ b/tests/ui/int_plus_one.fixed @@ -16,4 +16,18 @@ fn main() { let _ = x > y; // should be ok let _ = y < x; // should be ok + + // When the suggestion replaces `<=`/`>=` with `<`, an `as` cast on + // the LHS must be parenthesized to avoid parser ambiguity + // (e.g., `x as usize < y` is parsed as `x as usize`). + let z = 0usize; + let _ = (x as usize) < z; //~ int_plus_one + let _ = z > x as usize; //~ int_plus_one + // No parentheses needed when the replacement operator is `>`. + let _ = x as usize > z; //~ int_plus_one + let _ = z < x as usize; //~ int_plus_one + + // Nested and parenthesized casts on the LHS. + let _ = ((x as usize) as u8) < 5u8; //~ int_plus_one + let _ = (x as usize) < z; //~ int_plus_one } diff --git a/tests/ui/int_plus_one.rs b/tests/ui/int_plus_one.rs index 8d7d2e8982d8..235175e63ee0 100644 --- a/tests/ui/int_plus_one.rs +++ b/tests/ui/int_plus_one.rs @@ -16,4 +16,18 @@ fn main() { let _ = x > y; // should be ok let _ = y < x; // should be ok + + // When the suggestion replaces `<=`/`>=` with `<`, an `as` cast on + // the LHS must be parenthesized to avoid parser ambiguity + // (e.g., `x as usize < y` is parsed as `x as usize`). + let z = 0usize; + let _ = x as usize + 1 <= z; //~ int_plus_one + let _ = z >= x as usize + 1; //~ int_plus_one + // No parentheses needed when the replacement operator is `>`. + let _ = x as usize - 1 >= z; //~ int_plus_one + let _ = z <= x as usize - 1; //~ int_plus_one + + // Nested and parenthesized casts on the LHS. + let _ = ((x as usize) as u8) + 1 <= 5u8; //~ int_plus_one + let _ = (x as usize) + 1 <= z; //~ int_plus_one } diff --git a/tests/ui/int_plus_one.stderr b/tests/ui/int_plus_one.stderr index 8bdff5680bdc..4081b7a76367 100644 --- a/tests/ui/int_plus_one.stderr +++ b/tests/ui/int_plus_one.stderr @@ -49,5 +49,41 @@ error: unnecessary `>= y + 1` or `x - 1 >=` LL | let _ = y <= -1 + x; | ^^^^^^^^^^^ help: change it to: `y < x` -error: aborting due to 8 previous errors +error: unnecessary `>= y + 1` or `x - 1 >=` + --> tests/ui/int_plus_one.rs:24:13 + | +LL | let _ = x as usize + 1 <= z; + | ^^^^^^^^^^^^^^^^^^^ help: change it to: `(x as usize) < z` + +error: unnecessary `>= y + 1` or `x - 1 >=` + --> tests/ui/int_plus_one.rs:25:13 + | +LL | let _ = z >= x as usize + 1; + | ^^^^^^^^^^^^^^^^^^^ help: change it to: `z > x as usize` + +error: unnecessary `>= y + 1` or `x - 1 >=` + --> tests/ui/int_plus_one.rs:27:13 + | +LL | let _ = x as usize - 1 >= z; + | ^^^^^^^^^^^^^^^^^^^ help: change it to: `x as usize > z` + +error: unnecessary `>= y + 1` or `x - 1 >=` + --> tests/ui/int_plus_one.rs:28:13 + | +LL | let _ = z <= x as usize - 1; + | ^^^^^^^^^^^^^^^^^^^ help: change it to: `z < x as usize` + +error: unnecessary `>= y + 1` or `x - 1 >=` + --> tests/ui/int_plus_one.rs:31:13 + | +LL | let _ = ((x as usize) as u8) + 1 <= 5u8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: change it to: `((x as usize) as u8) < 5u8` + +error: unnecessary `>= y + 1` or `x - 1 >=` + --> tests/ui/int_plus_one.rs:32:13 + | +LL | let _ = (x as usize) + 1 <= z; + | ^^^^^^^^^^^^^^^^^^^^^ help: change it to: `(x as usize) < z` + +error: aborting due to 14 previous errors