Skip to content

Commit 5ba0e3a

Browse files
authored
Parenthesize AssocOp::Cast in suggestion when replacement operator is < to avoid parse error (#16848)
Fix int_plus_one suggestion causing parse error when LHS is an `AssocOp::Cast` When int_plus_one rewrites an expression like `x as usize + 1 <= y`, it suggests `x as usize < y`. This doesn't compile because the parser interprets `usize<y>` as the start of generic arguments rather than a lt comparison. The fix parenthesizes the LHS if it's an `AssocOp::Cast` and the replacement operator is `<`: `(x as usize) < y`. --- changelog: [int_plus_one]: Parenthesize `AssocOp::Cast` in suggestion when replacement operator is < to avoid parse error
2 parents c0adb72 + 6995867 commit 5ba0e3a

4 files changed

Lines changed: 74 additions & 2 deletions

File tree

clippy_lints/src/int_plus_one.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
22
use clippy_utils::sugg;
33
use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
4+
use rustc_ast::util::parser::AssocOp;
45
use rustc_data_structures::packed::Pu128;
56
use rustc_errors::Applicability;
67
use rustc_lint::{EarlyContext, EarlyLintPass};
@@ -134,12 +135,19 @@ impl IntPlusOne {
134135
|diag| {
135136
let mut app = Applicability::MachineApplicable;
136137
let ctxt = expr.span.ctxt();
137-
let new_lhs = sugg::Sugg::ast(cx, new_lhs, "_", ctxt, &mut app);
138+
let mut new_lhs = sugg::Sugg::ast(cx, new_lhs, "_", ctxt, &mut app);
138139
let new_rhs = sugg::Sugg::ast(cx, new_rhs, "_", ctxt, &mut app);
139140
let new_binop = match le_or_ge {
140141
LeOrGe::Ge => BinOpKind::Gt,
141142
LeOrGe::Le => BinOpKind::Lt,
142143
};
144+
// When the replacement operator is `<`, an `as` cast on the LHS
145+
// must be parenthesized. Otherwise, the parser interprets the `<`
146+
// as the start of generic arguments on the cast type
147+
// (e.g., `x as usize < y` is parsed as `x as usize<y>`).
148+
if matches!(new_lhs, sugg::Sugg::BinOp(AssocOp::Cast, ..)) && new_binop == BinOpKind::Lt {
149+
new_lhs = new_lhs.maybe_paren();
150+
}
143151
let rec = sugg::make_binop(new_binop, &new_lhs, &new_rhs);
144152
diag.span_suggestion(expr.span, "change it to", rec, app);
145153
},

tests/ui/int_plus_one.fixed

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,18 @@ fn main() {
1616

1717
let _ = x > y; // should be ok
1818
let _ = y < x; // should be ok
19+
20+
// When the suggestion replaces `<=`/`>=` with `<`, an `as` cast on
21+
// the LHS must be parenthesized to avoid parser ambiguity
22+
// (e.g., `x as usize < y` is parsed as `x as usize<y>`).
23+
let z = 0usize;
24+
let _ = (x as usize) < z; //~ int_plus_one
25+
let _ = z > x as usize; //~ int_plus_one
26+
// No parentheses needed when the replacement operator is `>`.
27+
let _ = x as usize > z; //~ int_plus_one
28+
let _ = z < x as usize; //~ int_plus_one
29+
30+
// Nested and parenthesized casts on the LHS.
31+
let _ = ((x as usize) as u8) < 5u8; //~ int_plus_one
32+
let _ = (x as usize) < z; //~ int_plus_one
1933
}

tests/ui/int_plus_one.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,18 @@ fn main() {
1616

1717
let _ = x > y; // should be ok
1818
let _ = y < x; // should be ok
19+
20+
// When the suggestion replaces `<=`/`>=` with `<`, an `as` cast on
21+
// the LHS must be parenthesized to avoid parser ambiguity
22+
// (e.g., `x as usize < y` is parsed as `x as usize<y>`).
23+
let z = 0usize;
24+
let _ = x as usize + 1 <= z; //~ int_plus_one
25+
let _ = z >= x as usize + 1; //~ int_plus_one
26+
// No parentheses needed when the replacement operator is `>`.
27+
let _ = x as usize - 1 >= z; //~ int_plus_one
28+
let _ = z <= x as usize - 1; //~ int_plus_one
29+
30+
// Nested and parenthesized casts on the LHS.
31+
let _ = ((x as usize) as u8) + 1 <= 5u8; //~ int_plus_one
32+
let _ = (x as usize) + 1 <= z; //~ int_plus_one
1933
}

tests/ui/int_plus_one.stderr

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,41 @@ error: unnecessary `>= y + 1` or `x - 1 >=`
4949
LL | let _ = y <= -1 + x;
5050
| ^^^^^^^^^^^ help: change it to: `y < x`
5151

52-
error: aborting due to 8 previous errors
52+
error: unnecessary `>= y + 1` or `x - 1 >=`
53+
--> tests/ui/int_plus_one.rs:24:13
54+
|
55+
LL | let _ = x as usize + 1 <= z;
56+
| ^^^^^^^^^^^^^^^^^^^ help: change it to: `(x as usize) < z`
57+
58+
error: unnecessary `>= y + 1` or `x - 1 >=`
59+
--> tests/ui/int_plus_one.rs:25:13
60+
|
61+
LL | let _ = z >= x as usize + 1;
62+
| ^^^^^^^^^^^^^^^^^^^ help: change it to: `z > x as usize`
63+
64+
error: unnecessary `>= y + 1` or `x - 1 >=`
65+
--> tests/ui/int_plus_one.rs:27:13
66+
|
67+
LL | let _ = x as usize - 1 >= z;
68+
| ^^^^^^^^^^^^^^^^^^^ help: change it to: `x as usize > z`
69+
70+
error: unnecessary `>= y + 1` or `x - 1 >=`
71+
--> tests/ui/int_plus_one.rs:28:13
72+
|
73+
LL | let _ = z <= x as usize - 1;
74+
| ^^^^^^^^^^^^^^^^^^^ help: change it to: `z < x as usize`
75+
76+
error: unnecessary `>= y + 1` or `x - 1 >=`
77+
--> tests/ui/int_plus_one.rs:31:13
78+
|
79+
LL | let _ = ((x as usize) as u8) + 1 <= 5u8;
80+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: change it to: `((x as usize) as u8) < 5u8`
81+
82+
error: unnecessary `>= y + 1` or `x - 1 >=`
83+
--> tests/ui/int_plus_one.rs:32:13
84+
|
85+
LL | let _ = (x as usize) + 1 <= z;
86+
| ^^^^^^^^^^^^^^^^^^^^^ help: change it to: `(x as usize) < z`
87+
88+
error: aborting due to 14 previous errors
5389

0 commit comments

Comments
 (0)