diff options
| author | Matthias Krüger <matthias.krueger@famsik.de> | 2024-09-30 18:25:14 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-30 18:25:14 +0200 |
| commit | dc1ccc526435c55c28664576270ab558ac86e506 (patch) | |
| tree | 3decb543e0466b76e959740b416fc2ac2a9f9aa3 /compiler | |
| parent | b6b43af68d9cbe6725717e169b5b878a9b688649 (diff) | |
| parent | ed5443fcdf3b4d02a89aec929cd62aa97586096f (diff) | |
| download | rust-dc1ccc526435c55c28664576270ab558ac86e506.tar.gz rust-dc1ccc526435c55c28664576270ab558ac86e506.zip | |
Rollup merge of #131035 - dingxiangfei2009:tweak-if-let-rescope-lint, r=jieyouxu
Preserve brackets around if-lets and skip while-lets
r? `@jieyouxu`
Tracked by #124085
Fresh out of #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.
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_lint/src/if_let_rescope.rs | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index c6218fe1e74..cdd0e80c458 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -122,7 +122,11 @@ impl IfLetRescope { } let tcx = cx.tcx; let source_map = tcx.sess.source_map(); - let expr_end = expr.span.shrink_to_hi(); + let expr_end = match expr.kind { + hir::ExprKind::If(_cond, conseq, None) => conseq.span.shrink_to_hi(), + hir::ExprKind::If(_cond, _conseq, Some(alt)) => alt.span.shrink_to_hi(), + _ => return, + }; let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr); let mut significant_droppers = vec![]; let mut lifetime_ends = vec![]; @@ -145,7 +149,10 @@ impl IfLetRescope { recovered: Recovered::No, }) = cond.kind { - let if_let_pat = expr.span.shrink_to_lo().between(init.span); + // Peel off round braces + let if_let_pat = source_map + .span_take_while(expr.span, |&ch| ch == '(' || ch.is_whitespace()) + .between(init.span); // The consequent fragment is always a block. let before_conseq = conseq.span.shrink_to_lo(); let lifetime_end = source_map.end_point(conseq.span); @@ -159,6 +166,8 @@ impl IfLetRescope { if ty_ascription.is_some() || !expr.span.can_be_used_for_suggestions() || !pat.span.can_be_used_for_suggestions() + || !if_let_pat.can_be_used_for_suggestions() + || !before_conseq.can_be_used_for_suggestions() { // Our `match` rewrites does not support type ascription, // so we just bail. @@ -240,6 +249,23 @@ impl<'tcx> LateLintPass<'tcx> for IfLetRescope { if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { return; } + if let hir::ExprKind::Loop(block, _label, hir::LoopSource::While, _span) = expr.kind + && let Some(value) = block.expr + && let hir::ExprKind::If(cond, _conseq, _alt) = value.kind + && let hir::ExprKind::Let(..) = cond.kind + { + // Recall that `while let` is lowered into this: + // ``` + // loop { + // if let .. { body } else { break; } + // } + // ``` + // There is no observable change in drop order on the overall `if let` expression + // given that the `{ break; }` block is trivial so the edition change + // means nothing substantial to this `while` statement. + self.skip.insert(value.hir_id); + return; + } if expr_parent_is_stmt(cx.tcx, expr.hir_id) && matches!(expr.kind, hir::ExprKind::If(_cond, _conseq, None)) { |
