Skip to content

Commit dc1ccc5

Browse files
authored
Rollup merge of rust-lang#131035 - dingxiangfei2009:tweak-if-let-rescope-lint, r=jieyouxu
Preserve brackets around if-lets and skip while-lets r? `@jieyouxu` Tracked by rust-lang#124085 Fresh out of rust-lang#129466, we have discovered 9 crates that the lint did not successfully migrate because the span of `if let` includes the surrounding brackets `(..)` like the following, which surprised me a bit. ```rust if (if let .. { .. } else { .. }) { // ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // the span somehow includes the surrounding brackets } ``` There is one crate that failed the migration because some suggestion spans cross the macro expansion boundaries. Surely there is no way to patch them with `match` rewrite. To handle this case, we will instead require all spans to be tested for admissibility as suggestion spans. Besides, there are 4 false negative cases discovered with desugared-`while let`. We don't need to lint them, because the `else` branch surely contains exactly one statement because the drop order is not changed whatsoever in this case. ```rust while let Some(value) = droppy().get() { .. } // is desugared into loop { if let Some(value) = droppy().get() { .. } else { break; // here can be nothing observable in this block } } ``` I believe this is the one and only false positive that I have found. I think we have finally nailed all the corner cases this time.
2 parents b6b43af + ed5443f commit dc1ccc5

File tree

4 files changed

+145
-7
lines changed

4 files changed

+145
-7
lines changed

Diff for: compiler/rustc_lint/src/if_let_rescope.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,11 @@ impl IfLetRescope {
122122
}
123123
let tcx = cx.tcx;
124124
let source_map = tcx.sess.source_map();
125-
let expr_end = expr.span.shrink_to_hi();
125+
let expr_end = match expr.kind {
126+
hir::ExprKind::If(_cond, conseq, None) => conseq.span.shrink_to_hi(),
127+
hir::ExprKind::If(_cond, _conseq, Some(alt)) => alt.span.shrink_to_hi(),
128+
_ => return,
129+
};
126130
let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr);
127131
let mut significant_droppers = vec![];
128132
let mut lifetime_ends = vec![];
@@ -145,7 +149,10 @@ impl IfLetRescope {
145149
recovered: Recovered::No,
146150
}) = cond.kind
147151
{
148-
let if_let_pat = expr.span.shrink_to_lo().between(init.span);
152+
// Peel off round braces
153+
let if_let_pat = source_map
154+
.span_take_while(expr.span, |&ch| ch == '(' || ch.is_whitespace())
155+
.between(init.span);
149156
// The consequent fragment is always a block.
150157
let before_conseq = conseq.span.shrink_to_lo();
151158
let lifetime_end = source_map.end_point(conseq.span);
@@ -159,6 +166,8 @@ impl IfLetRescope {
159166
if ty_ascription.is_some()
160167
|| !expr.span.can_be_used_for_suggestions()
161168
|| !pat.span.can_be_used_for_suggestions()
169+
|| !if_let_pat.can_be_used_for_suggestions()
170+
|| !before_conseq.can_be_used_for_suggestions()
162171
{
163172
// Our `match` rewrites does not support type ascription,
164173
// so we just bail.
@@ -240,6 +249,23 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope {
240249
if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) {
241250
return;
242251
}
252+
if let hir::ExprKind::Loop(block, _label, hir::LoopSource::While, _span) = expr.kind
253+
&& let Some(value) = block.expr
254+
&& let hir::ExprKind::If(cond, _conseq, _alt) = value.kind
255+
&& let hir::ExprKind::Let(..) = cond.kind
256+
{
257+
// Recall that `while let` is lowered into this:
258+
// ```
259+
// loop {
260+
// if let .. { body } else { break; }
261+
// }
262+
// ```
263+
// There is no observable change in drop order on the overall `if let` expression
264+
// given that the `{ break; }` block is trivial so the edition change
265+
// means nothing substantial to this `while` statement.
266+
self.skip.insert(value.hir_id);
267+
return;
268+
}
243269
if expr_parent_is_stmt(cx.tcx, expr.hir_id)
244270
&& matches!(expr.kind, hir::ExprKind::If(_cond, _conseq, None))
245271
{

Diff for: tests/ui/drop/lint-if-let-rescope.fixed

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//@ run-rustfix
22

33
#![deny(if_let_rescope)]
4-
#![feature(if_let_rescope)]
5-
#![allow(irrefutable_let_patterns)]
4+
#![feature(if_let_rescope, stmt_expr_attributes)]
5+
#![allow(irrefutable_let_patterns, unused_parens)]
66

77
fn droppy() -> Droppy {
88
Droppy
@@ -68,4 +68,30 @@ fn main() {
6868
//~| HELP: the value is now dropped here in Edition 2024
6969
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
7070
}
71+
72+
#[rustfmt::skip]
73+
if (match droppy().get() { Some(_value) => { true } _ => { false }}) {
74+
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
75+
//~| WARN: this changes meaning in Rust 2024
76+
//~| HELP: the value is now dropped here in Edition 2024
77+
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
78+
// do something
79+
} else if (((match droppy().get() { Some(_value) => { true } _ => { false }}))) {
80+
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
81+
//~| WARN: this changes meaning in Rust 2024
82+
//~| HELP: the value is now dropped here in Edition 2024
83+
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
84+
}
85+
86+
while let Some(_value) = droppy().get() {
87+
// Should not lint
88+
break;
89+
}
90+
91+
while (match droppy().get() { Some(_value) => { false } _ => { true }}) {
92+
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
93+
//~| WARN: this changes meaning in Rust 2024
94+
//~| HELP: the value is now dropped here in Edition 2024
95+
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
96+
}
7197
}

Diff for: tests/ui/drop/lint-if-let-rescope.rs

+28-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//@ run-rustfix
22

33
#![deny(if_let_rescope)]
4-
#![feature(if_let_rescope)]
5-
#![allow(irrefutable_let_patterns)]
4+
#![feature(if_let_rescope, stmt_expr_attributes)]
5+
#![allow(irrefutable_let_patterns, unused_parens)]
66

77
fn droppy() -> Droppy {
88
Droppy
@@ -68,4 +68,30 @@ fn main() {
6868
//~| HELP: the value is now dropped here in Edition 2024
6969
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
7070
}
71+
72+
#[rustfmt::skip]
73+
if (if let Some(_value) = droppy().get() { true } else { false }) {
74+
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
75+
//~| WARN: this changes meaning in Rust 2024
76+
//~| HELP: the value is now dropped here in Edition 2024
77+
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
78+
// do something
79+
} else if (((if let Some(_value) = droppy().get() { true } else { false }))) {
80+
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
81+
//~| WARN: this changes meaning in Rust 2024
82+
//~| HELP: the value is now dropped here in Edition 2024
83+
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
84+
}
85+
86+
while let Some(_value) = droppy().get() {
87+
// Should not lint
88+
break;
89+
}
90+
91+
while (if let Some(_value) = droppy().get() { false } else { true }) {
92+
//~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024
93+
//~| WARN: this changes meaning in Rust 2024
94+
//~| HELP: the value is now dropped here in Edition 2024
95+
//~| HELP: a `match` with a single arm can preserve the drop order up to Edition 2021
96+
}
7197
}

Diff for: tests/ui/drop/lint-if-let-rescope.stderr

+61-1
Original file line numberDiff line numberDiff line change
@@ -131,5 +131,65 @@ help: a `match` with a single arm can preserve the drop order up to Edition 2021
131131
LL | if let () = { match Droppy.get() { Some(_value) => {} _ => {}} } {
132132
| ~~~~~ +++++++++++++++++ ++++++++
133133

134-
error: aborting due to 5 previous errors
134+
error: `if let` assigns a shorter lifetime since Edition 2024
135+
--> $DIR/lint-if-let-rescope.rs:73:12
136+
|
137+
LL | if (if let Some(_value) = droppy().get() { true } else { false }) {
138+
| ^^^^^^^^^^^^^^^^^^^--------^^^^^^
139+
| |
140+
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
141+
|
142+
= warning: this changes meaning in Rust 2024
143+
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
144+
help: the value is now dropped here in Edition 2024
145+
--> $DIR/lint-if-let-rescope.rs:73:53
146+
|
147+
LL | if (if let Some(_value) = droppy().get() { true } else { false }) {
148+
| ^
149+
help: a `match` with a single arm can preserve the drop order up to Edition 2021
150+
|
151+
LL | if (match droppy().get() { Some(_value) => { true } _ => { false }}) {
152+
| ~~~~~ +++++++++++++++++ ~~~~ +
153+
154+
error: `if let` assigns a shorter lifetime since Edition 2024
155+
--> $DIR/lint-if-let-rescope.rs:79:21
156+
|
157+
LL | } else if (((if let Some(_value) = droppy().get() { true } else { false }))) {
158+
| ^^^^^^^^^^^^^^^^^^^--------^^^^^^
159+
| |
160+
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
161+
|
162+
= warning: this changes meaning in Rust 2024
163+
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
164+
help: the value is now dropped here in Edition 2024
165+
--> $DIR/lint-if-let-rescope.rs:79:62
166+
|
167+
LL | } else if (((if let Some(_value) = droppy().get() { true } else { false }))) {
168+
| ^
169+
help: a `match` with a single arm can preserve the drop order up to Edition 2021
170+
|
171+
LL | } else if (((match droppy().get() { Some(_value) => { true } _ => { false }}))) {
172+
| ~~~~~ +++++++++++++++++ ~~~~ +
173+
174+
error: `if let` assigns a shorter lifetime since Edition 2024
175+
--> $DIR/lint-if-let-rescope.rs:91:15
176+
|
177+
LL | while (if let Some(_value) = droppy().get() { false } else { true }) {
178+
| ^^^^^^^^^^^^^^^^^^^--------^^^^^^
179+
| |
180+
| this value has a significant drop implementation which may observe a major change in drop order and requires your discretion
181+
|
182+
= warning: this changes meaning in Rust 2024
183+
= note: for more information, see issue #124085 <https://github.com/rust-lang/rust/issues/124085>
184+
help: the value is now dropped here in Edition 2024
185+
--> $DIR/lint-if-let-rescope.rs:91:57
186+
|
187+
LL | while (if let Some(_value) = droppy().get() { false } else { true }) {
188+
| ^
189+
help: a `match` with a single arm can preserve the drop order up to Edition 2021
190+
|
191+
LL | while (match droppy().get() { Some(_value) => { false } _ => { true }}) {
192+
| ~~~~~ +++++++++++++++++ ~~~~ +
193+
194+
error: aborting due to 8 previous errors
135195

0 commit comments

Comments
 (0)