From e2120a7c3853226e486988b40196ff87b5eacb42 Mon Sep 17 00:00:00 2001 From: Ding Xiang Fei Date: Fri, 6 Sep 2024 03:03:10 +0800 Subject: [PATCH] coalesce lint suggestions that can intersect --- compiler/rustc_lint/messages.ftl | 4 +- compiler/rustc_lint/src/if_let_rescope.rs | 323 ++++++++++++------ compiler/rustc_lint/src/lib.rs | 2 +- tests/ui/drop/lint-if-let-rescope-gated.rs | 5 +- ...let-rescope-gated.with_feature_gate.stderr | 44 ++- tests/ui/drop/lint-if-let-rescope.fixed | 69 +++- tests/ui/drop/lint-if-let-rescope.rs | 63 +++- tests/ui/drop/lint-if-let-rescope.stderr | 223 ++++++++++-- 8 files changed, 572 insertions(+), 161 deletions(-) diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 1f8dca337fc..2d062465bcf 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -337,7 +337,9 @@ lint_identifier_uncommon_codepoints = identifier contains {$codepoints_len -> lint_if_let_rescope = `if let` assigns a shorter lifetime since Edition 2024 .label = this value has a significant drop implementation which may observe a major change in drop order and requires your discretion .help = the value is now dropped here in Edition 2024 - .suggestion = rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021 + +lint_if_let_rescope_suggestion = a `match` with a single arm can preserve the drop order up to Edition 2021 + .suggestion = rewrite this `if let` into `match` lint_ignored_unless_crate_specified = {$level}({$name}) is ignored unless specified at crate level diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 143965ae58f..e5bf192d23e 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -1,11 +1,16 @@ +use std::iter::repeat; use std::ops::ControlFlow; use hir::intravisit::Visitor; use rustc_ast::Recovered; -use rustc_hir as hir; +use rustc_errors::{ + Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle, +}; +use rustc_hir::{self as hir, HirIdSet}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; -use rustc_session::lint::FutureIncompatibilityReason; -use rustc_session::{declare_lint, declare_lint_pass}; +use rustc_middle::ty::TyCtxt; +use rustc_session::lint::{FutureIncompatibilityReason, Level}; +use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::edition::Edition; use rustc_span::Span; @@ -84,138 +89,236 @@ }; } -declare_lint_pass!( - /// Lint for potential change in program semantics of `if let`s +/// Lint for potential change in program semantics of `if let`s +#[derive(Default)] +pub(crate) struct IfLetRescope { + skip: HirIdSet, +} + +fn expr_parent_is_else(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { + let Some((_, hir::Node::Expr(expr))) = tcx.hir().parent_iter(hir_id).next() else { + return false; + }; + let hir::ExprKind::If(_cond, _conseq, Some(alt)) = expr.kind else { return false }; + alt.hir_id == hir_id +} + +fn expr_parent_is_stmt(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool { + let Some((_, hir::Node::Stmt(stmt))) = tcx.hir().parent_iter(hir_id).next() else { + return false; + }; + let (hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr)) = stmt.kind else { return false }; + expr.hir_id == hir_id +} + +fn match_head_needs_bracket(tcx: TyCtxt<'_>, expr: &hir::Expr<'_>) -> bool { + expr_parent_is_else(tcx, expr.hir_id) && matches!(expr.kind, hir::ExprKind::If(..)) +} + +impl IfLetRescope { + fn probe_if_cascade<'tcx>(&mut self, cx: &LateContext<'tcx>, mut expr: &'tcx hir::Expr<'tcx>) { + if self.skip.contains(&expr.hir_id) { + return; + } + let tcx = cx.tcx; + let source_map = tcx.sess.source_map(); + let expr_end = expr.span.shrink_to_hi(); + let mut add_bracket_to_match_head = match_head_needs_bracket(tcx, expr); + let mut closing_brackets = 0; + let mut alt_heads = vec![]; + let mut match_heads = vec![]; + let mut consequent_heads = vec![]; + let mut first_if_to_rewrite = None; + let mut empty_alt = false; + while let hir::ExprKind::If(cond, conseq, alt) = expr.kind { + self.skip.insert(expr.hir_id); + let hir::ExprKind::Let(&hir::LetExpr { + span, + pat, + init, + ty: ty_ascription, + recovered: Recovered::No, + }) = cond.kind + else { + if let Some(alt) = alt { + add_bracket_to_match_head = matches!(alt.kind, hir::ExprKind::If(..)); + expr = alt; + continue; + } else { + // finalize and emit span + break; + } + }; + let if_let_pat = expr.span.shrink_to_lo().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); + + if let ControlFlow::Break(significant_dropper) = + (FindSignificantDropper { cx }).visit_expr(init) + { + tcx.emit_node_span_lint( + IF_LET_RESCOPE, + expr.hir_id, + span, + IfLetRescopeLint { significant_dropper, lifetime_end }, + ); + if ty_ascription.is_some() + || !expr.span.can_be_used_for_suggestions() + || !pat.span.can_be_used_for_suggestions() + { + // Our `match` rewrites does not support type ascription, + // so we just bail. + // Alternatively when the span comes from proc macro expansion, + // we will also bail. + // FIXME(#101728): change this when type ascription syntax is stabilized again + } else if let Ok(pat) = source_map.span_to_snippet(pat.span) { + let emit_suggestion = || { + first_if_to_rewrite = + first_if_to_rewrite.or_else(|| Some((expr.span, expr.hir_id))); + if add_bracket_to_match_head { + closing_brackets += 2; + match_heads.push(SingleArmMatchBegin::WithOpenBracket(if_let_pat)); + } else { + // It has to be a block + closing_brackets += 1; + match_heads.push(SingleArmMatchBegin::WithoutOpenBracket(if_let_pat)); + } + consequent_heads.push(ConsequentRewrite { span: before_conseq, pat }); + }; + if let Some(alt) = alt { + let alt_head = conseq.span.between(alt.span); + if alt_head.can_be_used_for_suggestions() { + // lint + emit_suggestion(); + alt_heads.push(AltHead(alt_head)); + } + } else { + emit_suggestion(); + empty_alt = true; + break; + } + } + } + if let Some(alt) = alt { + add_bracket_to_match_head = matches!(alt.kind, hir::ExprKind::If(..)); + expr = alt; + } else { + break; + } + } + if let Some((span, hir_id)) = first_if_to_rewrite { + tcx.emit_node_span_lint( + IF_LET_RESCOPE, + hir_id, + span, + IfLetRescopeRewrite { + match_heads, + consequent_heads, + closing_brackets: ClosingBrackets { + span: expr_end, + count: closing_brackets, + empty_alt, + }, + alt_heads, + }, + ); + } + } +} + +impl_lint_pass!( IfLetRescope => [IF_LET_RESCOPE] ); impl<'tcx> LateLintPass<'tcx> for IfLetRescope { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - if !expr.span.edition().at_least_rust_2021() || !cx.tcx.features().if_let_rescope { + if expr.span.edition().at_least_rust_2024() || !cx.tcx.features().if_let_rescope { return; } - let hir::ExprKind::If(cond, conseq, alt) = expr.kind else { return }; - let hir::ExprKind::Let(&hir::LetExpr { - span, - pat, - init, - ty: ty_ascription, - recovered: Recovered::No, - }) = cond.kind - else { + if let (Level::Allow, _) = cx.tcx.lint_level_at_node(IF_LET_RESCOPE, expr.hir_id) { return; - }; - let source_map = cx.tcx.sess.source_map(); - let expr_end = expr.span.shrink_to_hi(); - let if_let_pat = expr.span.shrink_to_lo().between(init.span); - let before_conseq = conseq.span.shrink_to_lo(); - let lifetime_end = source_map.end_point(conseq.span); - - if let ControlFlow::Break(significant_dropper) = - (FindSignificantDropper { cx }).visit_expr(init) + } + if expr_parent_is_stmt(cx.tcx, expr.hir_id) + && matches!(expr.kind, hir::ExprKind::If(_cond, _conseq, None)) { - let lint_without_suggestion = || { - cx.tcx.emit_node_span_lint( - IF_LET_RESCOPE, - expr.hir_id, - span, - IfLetRescopeRewrite { significant_dropper, lifetime_end, sugg: None }, - ) - }; - if ty_ascription.is_some() - || !expr.span.can_be_used_for_suggestions() - || !pat.span.can_be_used_for_suggestions() - { - // Our `match` rewrites does not support type ascription, - // so we just bail. - // Alternatively when the span comes from proc macro expansion, - // we will also bail. - // FIXME(#101728): change this when type ascription syntax is stabilized again - lint_without_suggestion(); - } else { - let Ok(pat) = source_map.span_to_snippet(pat.span) else { - lint_without_suggestion(); - return; - }; - if let Some(alt) = alt { - let alt_start = conseq.span.between(alt.span); - if !alt_start.can_be_used_for_suggestions() { - lint_without_suggestion(); - return; - } - cx.tcx.emit_node_span_lint( - IF_LET_RESCOPE, - expr.hir_id, - span, - IfLetRescopeRewrite { - significant_dropper, - lifetime_end, - sugg: Some(IfLetRescopeRewriteSuggestion::WithElse { - if_let_pat, - before_conseq, - pat, - expr_end, - alt_start, - }), - }, - ); - } else { - cx.tcx.emit_node_span_lint( - IF_LET_RESCOPE, - expr.hir_id, - span, - IfLetRescopeRewrite { - significant_dropper, - lifetime_end, - sugg: Some(IfLetRescopeRewriteSuggestion::WithoutElse { - if_let_pat, - before_conseq, - pat, - expr_end, - }), - }, - ); - } - } + // `if let` statement without an `else` branch has no observable change + // so we can skip linting it + return; } + self.probe_if_cascade(cx, expr); } } #[derive(LintDiagnostic)] #[diag(lint_if_let_rescope)] -struct IfLetRescopeRewrite { +struct IfLetRescopeLint { #[label] significant_dropper: Span, #[help] lifetime_end: Span, +} + +#[derive(LintDiagnostic)] +#[diag(lint_if_let_rescope_suggestion)] +struct IfLetRescopeRewrite { #[subdiagnostic] - sugg: Option, + match_heads: Vec, + #[subdiagnostic] + consequent_heads: Vec, + #[subdiagnostic] + closing_brackets: ClosingBrackets, + #[subdiagnostic] + alt_heads: Vec, } #[derive(Subdiagnostic)] -enum IfLetRescopeRewriteSuggestion { +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +struct AltHead(#[suggestion_part(code = " _ => ")] Span); + +#[derive(Subdiagnostic)] +#[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] +struct ConsequentRewrite { + #[suggestion_part(code = "{{ {pat} => ")] + span: Span, + pat: String, +} + +struct ClosingBrackets { + span: Span, + count: usize, + empty_alt: bool, +} + +impl Subdiagnostic for ClosingBrackets { + fn add_to_diag_with>( + self, + diag: &mut Diag<'_, G>, + f: &F, + ) { + let code: String = self + .empty_alt + .then_some(" _ => {}".chars()) + .into_iter() + .flatten() + .chain(repeat('}').take(self.count)) + .collect(); + let msg = f(diag, crate::fluent_generated::lint_suggestion.into()); + diag.multipart_suggestion_with_style( + msg, + vec![(self.span, code)], + Applicability::MachineApplicable, + SuggestionStyle::ShowCode, + ); + } +} + +#[derive(Subdiagnostic)] +enum SingleArmMatchBegin { #[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] - WithElse { - #[suggestion_part(code = "match ")] - if_let_pat: Span, - #[suggestion_part(code = " {{ {pat} => ")] - before_conseq: Span, - pat: String, - #[suggestion_part(code = "}}")] - expr_end: Span, - #[suggestion_part(code = " _ => ")] - alt_start: Span, - }, + WithOpenBracket(#[suggestion_part(code = "{{ match ")] Span), #[multipart_suggestion(lint_suggestion, applicability = "machine-applicable")] - WithoutElse { - #[suggestion_part(code = "match ")] - if_let_pat: Span, - #[suggestion_part(code = " {{ {pat} => ")] - before_conseq: Span, - pat: String, - #[suggestion_part(code = " _ => {{}} }}")] - expr_end: Span, - }, + WithoutOpenBracket(#[suggestion_part(code = "match ")] Span), } struct FindSignificantDropper<'tcx, 'a> { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 58b51240a90..0c32157758c 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -245,7 +245,7 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { NonLocalDefinitions: NonLocalDefinitions::default(), ImplTraitOvercaptures: ImplTraitOvercaptures, TailExprDropOrder: TailExprDropOrder, - IfLetRescope: IfLetRescope, + IfLetRescope: IfLetRescope::default(), ] ] ); diff --git a/tests/ui/drop/lint-if-let-rescope-gated.rs b/tests/ui/drop/lint-if-let-rescope-gated.rs index f8844a09f7c..08ded67a512 100644 --- a/tests/ui/drop/lint-if-let-rescope-gated.rs +++ b/tests/ui/drop/lint-if-let-rescope-gated.rs @@ -26,6 +26,9 @@ fn get(&self) -> Option { fn main() { if let Some(_value) = Droppy.get() { //[with_feature_gate]~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //[with_feature_gate]~| ERROR: a `match` with a single arm can preserve the drop order up to Edition 2021 //[with_feature_gate]~| WARN: this changes meaning in Rust 2024 - }; + //[with_feature_gate]~| WARN: this changes meaning in Rust 2024 + } else { + } } diff --git a/tests/ui/drop/lint-if-let-rescope-gated.with_feature_gate.stderr b/tests/ui/drop/lint-if-let-rescope-gated.with_feature_gate.stderr index 7c295b7f9d8..e0f2bebdbf1 100644 --- a/tests/ui/drop/lint-if-let-rescope-gated.with_feature_gate.stderr +++ b/tests/ui/drop/lint-if-let-rescope-gated.with_feature_gate.stderr @@ -9,22 +9,46 @@ LL | if let Some(_value) = Droppy.get() { = warning: this changes meaning in Rust 2024 = note: for more information, see issue #124085 help: the value is now dropped here in Edition 2024 - --> $DIR/lint-if-let-rescope-gated.rs:30:5 + --> $DIR/lint-if-let-rescope-gated.rs:32:5 | -LL | }; +LL | } else { | ^ note: the lint level is defined here --> $DIR/lint-if-let-rescope-gated.rs:11:9 | LL | #![deny(if_let_rescope)] | ^^^^^^^^^^^^^^ -help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021 - | -LL ~ match Droppy.get() { Some(_value) => { -LL | -LL | -LL ~ } _ => {} }; - | -error: aborting due to 1 previous error +error: a `match` with a single arm can preserve the drop order up to Edition 2021 + --> $DIR/lint-if-let-rescope-gated.rs:27:5 + | +LL | / if let Some(_value) = Droppy.get() { +LL | | +LL | | +LL | | +LL | | +LL | | } else { +LL | | } + | |_____^ + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: rewrite this `if let` into `match` + | +LL | match Droppy.get() { + | ~~~~~ +help: rewrite this `if let` into `match` + | +LL | if let Some(_value) = Droppy.get() { Some(_value) => { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | }} + | + +help: rewrite this `if let` into `match` + | +LL | } _ => { + | ~~~~ + +error: aborting due to 2 previous errors diff --git a/tests/ui/drop/lint-if-let-rescope.fixed b/tests/ui/drop/lint-if-let-rescope.fixed index 7ed7e4b279c..85ffa320796 100644 --- a/tests/ui/drop/lint-if-let-rescope.fixed +++ b/tests/ui/drop/lint-if-let-rescope.fixed @@ -1,9 +1,7 @@ -//@ edition:2024 -//@ compile-flags: -Z validate-mir -Zunstable-options //@ run-rustfix -#![feature(if_let_rescope)] #![deny(if_let_rescope)] +#![feature(if_let_rescope)] #![allow(irrefutable_let_patterns)] fn droppy() -> Droppy { @@ -22,27 +20,80 @@ impl Droppy { } fn main() { - match droppy().get() { Some(_value) => { + if let Some(_value) = droppy().get() { + // Should not lint + } + + match droppy().get() { Some(_value) => { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: rewrite this `if let` into a `match` + //~| WARN: this changes meaning in Rust 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` // do something } _ => { //~^ HELP: the value is now dropped here in Edition 2024 + //~| HELP: rewrite this `if let` into `match` // do something else }} + //~^ HELP: rewrite this `if let` into `match` - if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } { + match droppy().get() { Some(_value) => { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: rewrite this `if let` into a `match` + //~| WARN: this changes meaning in Rust 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + // do something + } _ => { match droppy().get() { Some(_value) => { + //~^ HELP: the value is now dropped here in Edition 2024 + //~| ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + // do something else + } _ => {}}}} + //~^ HELP: rewrite this `if let` into `match` + //~| HELP: the value is now dropped here in Edition 2024 + + if droppy().get().is_some() { + // Should not lint + } else { match droppy().get() { Some(_value) => { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| WARN: this changes meaning in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + } _ => if droppy().get().is_none() { + //~^ HELP: the value is now dropped here in Edition 2024 + //~| HELP: rewrite this `if let` into `match` + }}} + //~^ HELP: rewrite this `if let` into `match` + + if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| WARN: this changes meaning in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` //~| HELP: the value is now dropped here in Edition 2024 } - if let () = { match Droppy.get() { Some(_value) => {} _ => {} } } { + if let () = { match Droppy.get() { Some(_value) => {} _ => {}} } { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: rewrite this `if let` into a `match` + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` //~| HELP: the value is now dropped here in Edition 2024 } } diff --git a/tests/ui/drop/lint-if-let-rescope.rs b/tests/ui/drop/lint-if-let-rescope.rs index d282cd2626b..776b34fdbea 100644 --- a/tests/ui/drop/lint-if-let-rescope.rs +++ b/tests/ui/drop/lint-if-let-rescope.rs @@ -1,9 +1,7 @@ -//@ edition:2024 -//@ compile-flags: -Z validate-mir -Zunstable-options //@ run-rustfix -#![feature(if_let_rescope)] #![deny(if_let_rescope)] +#![feature(if_let_rescope)] #![allow(irrefutable_let_patterns)] fn droppy() -> Droppy { @@ -22,27 +20,80 @@ fn get(&self) -> Option { } fn main() { + if let Some(_value) = droppy().get() { + // Should not lint + } + if let Some(_value) = droppy().get() { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: rewrite this `if let` into a `match` + //~| WARN: this changes meaning in Rust 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` // do something } else { //~^ HELP: the value is now dropped here in Edition 2024 + //~| HELP: rewrite this `if let` into `match` // do something else } + //~^ HELP: rewrite this `if let` into `match` + + if let Some(_value) = droppy().get() { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + // do something + } else if let Some(_value) = droppy().get() { + //~^ HELP: the value is now dropped here in Edition 2024 + //~| ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + // do something else + } + //~^ HELP: rewrite this `if let` into `match` + //~| HELP: the value is now dropped here in Edition 2024 + + if droppy().get().is_some() { + // Should not lint + } else if let Some(_value) = droppy().get() { + //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 + //~| WARN: this changes meaning in Rust 2024 + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + } else if droppy().get().is_none() { + //~^ HELP: the value is now dropped here in Edition 2024 + //~| HELP: rewrite this `if let` into `match` + } + //~^ HELP: rewrite this `if let` into `match` if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: rewrite this `if let` into a `match` + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` //~| HELP: the value is now dropped here in Edition 2024 } if let () = { if let Some(_value) = Droppy.get() {} } { //~^ ERROR: `if let` assigns a shorter lifetime since Edition 2024 + //~| ERROR a `match` with a single arm can preserve the drop order up to Edition 2021 //~| WARN: this changes meaning in Rust 2024 - //~| HELP: rewrite this `if let` into a `match` + //~| WARN: this changes meaning in Rust 2024 + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` + //~| HELP: rewrite this `if let` into `match` //~| HELP: the value is now dropped here in Edition 2024 } } diff --git a/tests/ui/drop/lint-if-let-rescope.stderr b/tests/ui/drop/lint-if-let-rescope.stderr index 832e73b898c..a08d95e1138 100644 --- a/tests/ui/drop/lint-if-let-rescope.stderr +++ b/tests/ui/drop/lint-if-let-rescope.stderr @@ -1,5 +1,5 @@ error: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/lint-if-let-rescope.rs:25:8 + --> $DIR/lint-if-let-rescope.rs:27:8 | LL | if let Some(_value) = droppy().get() { | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ @@ -9,29 +9,168 @@ LL | if let Some(_value) = droppy().get() { = warning: this changes meaning in Rust 2024 = note: for more information, see issue #124085 help: the value is now dropped here in Edition 2024 - --> $DIR/lint-if-let-rescope.rs:30:5 + --> $DIR/lint-if-let-rescope.rs:35:5 | LL | } else { | ^ note: the lint level is defined here - --> $DIR/lint-if-let-rescope.rs:6:9 + --> $DIR/lint-if-let-rescope.rs:3:9 | LL | #![deny(if_let_rescope)] | ^^^^^^^^^^^^^^ -help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021 + +error: a `match` with a single arm can preserve the drop order up to Edition 2021 + --> $DIR/lint-if-let-rescope.rs:27:5 | -LL ~ match droppy().get() { Some(_value) => { -LL | -... -LL | // do something -LL ~ } _ => { -LL | -LL | // do something else -LL ~ }} +LL | / if let Some(_value) = droppy().get() { +LL | | +LL | | +LL | | +... | +LL | | // do something else +LL | | } + | |_____^ | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: rewrite this `if let` into `match` + | +LL | match droppy().get() { + | ~~~~~ +help: rewrite this `if let` into `match` + | +LL | if let Some(_value) = droppy().get() { Some(_value) => { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | }} + | + +help: rewrite this `if let` into `match` + | +LL | } _ => { + | ~~~~ error: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/lint-if-let-rescope.rs:35:27 + --> $DIR/lint-if-let-rescope.rs:42:8 + | +LL | if let Some(_value) = droppy().get() { + | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:50:5 + | +LL | } else if let Some(_value) = droppy().get() { + | ^ + +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:50:15 + | +LL | } else if let Some(_value) = droppy().get() { + | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:58:5 + | +LL | } + | ^ + +error: a `match` with a single arm can preserve the drop order up to Edition 2021 + --> $DIR/lint-if-let-rescope.rs:42:5 + | +LL | / if let Some(_value) = droppy().get() { +LL | | +LL | | +LL | | +... | +LL | | // do something else +LL | | } + | |_____^ + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: rewrite this `if let` into `match` + | +LL | match droppy().get() { + | ~~~~~ +help: rewrite this `if let` into `match` + | +LL | } else { match droppy().get() { + | ~~~~~~~ +help: rewrite this `if let` into `match` + | +LL | if let Some(_value) = droppy().get() { Some(_value) => { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | } else if let Some(_value) = droppy().get() { Some(_value) => { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | } _ => {}}}} + | ++++++++++ +help: rewrite this `if let` into `match` + | +LL | } _ => if let Some(_value) = droppy().get() { + | ~~~~ + +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:64:15 + | +LL | } else if let Some(_value) = droppy().get() { + | ^^^^^^^^^^^^^^^^^^^--------^^^^^^ + | | + | this value has a significant drop implementation which may observe a major change in drop order and requires your discretion + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: the value is now dropped here in Edition 2024 + --> $DIR/lint-if-let-rescope.rs:71:5 + | +LL | } else if droppy().get().is_none() { + | ^ + +error: a `match` with a single arm can preserve the drop order up to Edition 2021 + --> $DIR/lint-if-let-rescope.rs:64:12 + | +LL | } else if let Some(_value) = droppy().get() { + | ____________^ +LL | | +LL | | +LL | | +... | +LL | | +LL | | } + | |_____^ + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: rewrite this `if let` into `match` + | +LL | } else { match droppy().get() { + | ~~~~~~~ +help: rewrite this `if let` into `match` + | +LL | } else if let Some(_value) = droppy().get() { Some(_value) => { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | }}} + | ++ +help: rewrite this `if let` into `match` + | +LL | } _ => if droppy().get().is_none() { + | ~~~~ + +error: `if let` assigns a shorter lifetime since Edition 2024 + --> $DIR/lint-if-let-rescope.rs:77:27 | LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } { | ^^^^^^^^^^^^^^^^^^^------^^^^^^ @@ -41,17 +180,38 @@ LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else = warning: this changes meaning in Rust 2024 = note: for more information, see issue #124085 help: the value is now dropped here in Edition 2024 - --> $DIR/lint-if-let-rescope.rs:35:69 + --> $DIR/lint-if-let-rescope.rs:77:69 | LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } { | ^ -help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021 + +error: a `match` with a single arm can preserve the drop order up to Edition 2021 + --> $DIR/lint-if-let-rescope.rs:77:24 | -LL | if let Some(1) = { match Droppy.get() { Some(_value) => { Some(1) } _ => { None }} } { - | ~~~~~ +++++++++++++++++ ~~~~ + +LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None } } { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: rewrite this `if let` into `match` + | +LL | if let Some(1) = { match Droppy.get() { Some(1) } else { None } } { + | ~~~~~ +help: rewrite this `if let` into `match` + | +LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(_value) => { Some(1) } else { None } } { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } else { None }} } { + | + +help: rewrite this `if let` into `match` + | +LL | if let Some(1) = { if let Some(_value) = Droppy.get() { Some(1) } _ => { None } } { + | ~~~~ error: `if let` assigns a shorter lifetime since Edition 2024 - --> $DIR/lint-if-let-rescope.rs:42:22 + --> $DIR/lint-if-let-rescope.rs:89:22 | LL | if let () = { if let Some(_value) = Droppy.get() {} } { | ^^^^^^^^^^^^^^^^^^^------^^^^^^ @@ -61,14 +221,31 @@ LL | if let () = { if let Some(_value) = Droppy.get() {} } { = warning: this changes meaning in Rust 2024 = note: for more information, see issue #124085 help: the value is now dropped here in Edition 2024 - --> $DIR/lint-if-let-rescope.rs:42:55 + --> $DIR/lint-if-let-rescope.rs:89:55 | LL | if let () = { if let Some(_value) = Droppy.get() {} } { | ^ -help: rewrite this `if let` into a `match` with a single arm to preserve the drop order up to Edition 2021 + +error: a `match` with a single arm can preserve the drop order up to Edition 2021 + --> $DIR/lint-if-let-rescope.rs:89:19 | -LL | if let () = { match Droppy.get() { Some(_value) => {} _ => {} } } { - | ~~~~~ +++++++++++++++++ +++++++++ +LL | if let () = { if let Some(_value) = Droppy.get() {} } { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this changes meaning in Rust 2024 + = note: for more information, see issue #124085 +help: rewrite this `if let` into `match` + | +LL | if let () = { match Droppy.get() {} } { + | ~~~~~ +help: rewrite this `if let` into `match` + | +LL | if let () = { if let Some(_value) = Droppy.get() { Some(_value) => {} } { + | +++++++++++++++++ +help: rewrite this `if let` into `match` + | +LL | if let () = { if let Some(_value) = Droppy.get() {} _ => {}} } { + | ++++++++ -error: aborting due to 3 previous errors +error: aborting due to 11 previous errors