about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_mir_build/src/errors.rs3
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/migration.rs87
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs75
3 files changed, 104 insertions, 61 deletions
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index 07bdc59756a..9f8b52165ab 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -1113,9 +1113,6 @@ pub(crate) struct Rust2024IncompatiblePatSugg {
     pub(crate) suggestion: Vec<(Span, String)>,
     pub(crate) ref_pattern_count: usize,
     pub(crate) binding_mode_count: usize,
-    /// Internal state: the ref-mutability of the default binding mode at the subpattern being
-    /// lowered, with the span where it was introduced. `None` for a by-value default mode.
-    pub(crate) default_mode_span: Option<(Span, ty::Mutability)>,
     /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
     pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
 }
diff --git a/compiler/rustc_mir_build/src/thir/pattern/migration.rs b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
new file mode 100644
index 00000000000..be1ebd57d11
--- /dev/null
+++ b/compiler/rustc_mir_build/src/thir/pattern/migration.rs
@@ -0,0 +1,87 @@
+//! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.
+
+use rustc_data_structures::fx::FxIndexMap;
+use rustc_errors::MultiSpan;
+use rustc_hir::HirId;
+use rustc_lint as lint;
+use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
+use rustc_span::Span;
+
+use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
+use crate::fluent_generated as fluent;
+
+/// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
+/// a diagnostic suggestion.
+pub(super) struct PatMigration<'a> {
+    pub(super) suggestion: Vec<(Span, String)>,
+    pub(super) ref_pattern_count: usize,
+    pub(super) binding_mode_count: usize,
+    /// Internal state: the ref-mutability of the default binding mode at the subpattern being
+    /// lowered, with the span where it was introduced. `None` for a by-value default mode.
+    pub(super) default_mode_span: Option<(Span, ty::Mutability)>,
+    /// Labels for where incompatibility-causing by-ref default binding modes were introduced.
+    // FIXME(ref_pat_eat_one_layer_2024_structural): To track the default binding mode, we duplicate
+    // logic from HIR typeck (in order to avoid needing to store all changes to the dbm in
+    // TypeckResults). Since the default binding mode acts differently under this feature gate, the
+    // labels will be wrong.
+    pub(super) default_mode_labels: FxIndexMap<Span, Mutability>,
+    /// Information collected from typeck, including spans for subpatterns invalid in Rust 2024.
+    pub(super) info: &'a Rust2024IncompatiblePatInfo,
+}
+
+impl<'a> PatMigration<'a> {
+    pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self {
+        PatMigration {
+            suggestion: Vec::new(),
+            ref_pattern_count: 0,
+            binding_mode_count: 0,
+            default_mode_span: None,
+            default_mode_labels: Default::default(),
+            info,
+        }
+    }
+
+    /// On Rust 2024, this emits a hard error. On earlier Editions, this emits the
+    /// future-incompatibility lint `rust_2024_incompatible_pat`.
+    pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
+        let mut spans =
+            MultiSpan::from_spans(self.info.primary_labels.iter().map(|(span, _)| *span).collect());
+        for (span, label) in self.info.primary_labels.iter() {
+            spans.push_span_label(*span, label.clone());
+        }
+        let sugg = Rust2024IncompatiblePatSugg {
+            suggest_eliding_modes: self.info.suggest_eliding_modes,
+            suggestion: self.suggestion,
+            ref_pattern_count: self.ref_pattern_count,
+            binding_mode_count: self.binding_mode_count,
+            default_mode_labels: self.default_mode_labels,
+        };
+        // If a relevant span is from at least edition 2024, this is a hard error.
+        let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
+        if is_hard_error {
+            let mut err =
+                tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
+            if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
+                // provide the same reference link as the lint
+                err.note(format!("for more information, see {}", info.reference));
+            }
+            err.arg("bad_modifiers", self.info.bad_modifiers);
+            err.arg("bad_ref_pats", self.info.bad_ref_pats);
+            err.arg("is_hard_error", true);
+            err.subdiagnostic(sugg);
+            err.emit();
+        } else {
+            tcx.emit_node_span_lint(
+                lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
+                pat_id,
+                spans,
+                Rust2024IncompatiblePat {
+                    sugg,
+                    bad_modifiers: self.info.bad_modifiers,
+                    bad_ref_pats: self.info.bad_ref_pats,
+                    is_hard_error,
+                },
+            );
+        }
+    }
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 83fef7b0de6..5535ac9bd63 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -2,18 +2,17 @@
 
 mod check_match;
 mod const_to_pat;
+mod migration;
 
 use std::cmp::Ordering;
 use std::sync::Arc;
 
 use rustc_abi::{FieldIdx, Integer};
-use rustc_errors::MultiSpan;
 use rustc_errors::codes::*;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::pat_util::EnumerateAndAdjustIterator;
 use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
 use rustc_index::Idx;
-use rustc_lint as lint;
 use rustc_middle::mir::interpret::LitToConstInput;
 use rustc_middle::thir::{
     Ascription, FieldPat, LocalVarId, Pat, PatKind, PatRange, PatRangeBoundary,
@@ -26,8 +25,8 @@ use rustc_span::{ErrorGuaranteed, Span};
 use tracing::{debug, instrument};
 
 pub(crate) use self::check_match::check_match;
+use self::migration::PatMigration;
 use crate::errors::*;
-use crate::fluent_generated as fluent;
 
 struct PatCtxt<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -35,7 +34,7 @@ struct PatCtxt<'a, 'tcx> {
     typeck_results: &'a ty::TypeckResults<'tcx>,
 
     /// Used by the Rust 2024 migration lint.
-    rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
+    rust_2024_migration: Option<PatMigration<'a>>,
 }
 
 pub(super) fn pat_from_hir<'a, 'tcx>(
@@ -44,59 +43,19 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
     typeck_results: &'a ty::TypeckResults<'tcx>,
     pat: &'tcx hir::Pat<'tcx>,
 ) -> Box<Pat<'tcx>> {
-    let migration_info = typeck_results.rust_2024_migration_desugared_pats().get(pat.hir_id);
     let mut pcx = PatCtxt {
         tcx,
         typing_env,
         typeck_results,
-        rust_2024_migration_suggestion: migration_info.and_then(|info| {
-            Some(Rust2024IncompatiblePatSugg {
-                suggest_eliding_modes: info.suggest_eliding_modes,
-                suggestion: Vec::new(),
-                ref_pattern_count: 0,
-                binding_mode_count: 0,
-                default_mode_span: None,
-                default_mode_labels: Default::default(),
-            })
-        }),
+        rust_2024_migration: typeck_results
+            .rust_2024_migration_desugared_pats()
+            .get(pat.hir_id)
+            .map(PatMigration::new),
     };
     let result = pcx.lower_pattern(pat);
     debug!("pat_from_hir({:?}) = {:?}", pat, result);
-    if let Some(info) = migration_info
-        && let Some(sugg) = pcx.rust_2024_migration_suggestion
-    {
-        let mut spans =
-            MultiSpan::from_spans(info.primary_labels.iter().map(|(span, _)| *span).collect());
-        for (span, label) in &info.primary_labels {
-            spans.push_span_label(*span, label.clone());
-        }
-        // If a relevant span is from at least edition 2024, this is a hard error.
-        let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
-        if is_hard_error {
-            let mut err =
-                tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
-            if let Some(lint_info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
-                // provide the same reference link as the lint
-                err.note(format!("for more information, see {}", lint_info.reference));
-            }
-            err.arg("bad_modifiers", info.bad_modifiers);
-            err.arg("bad_ref_pats", info.bad_ref_pats);
-            err.arg("is_hard_error", true);
-            err.subdiagnostic(sugg);
-            err.emit();
-        } else {
-            tcx.emit_node_span_lint(
-                lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
-                pat.hir_id,
-                spans,
-                Rust2024IncompatiblePat {
-                    sugg,
-                    bad_modifiers: info.bad_modifiers,
-                    bad_ref_pats: info.bad_ref_pats,
-                    is_hard_error,
-                },
-            );
-        }
+    if let Some(m) = pcx.rust_2024_migration {
+        m.emit(tcx, pat.hir_id);
     }
     result
 }
@@ -107,7 +66,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
 
         let mut opt_old_mode_span = None;
-        if let Some(s) = &mut self.rust_2024_migration_suggestion
+        if let Some(s) = &mut self.rust_2024_migration
             && !adjustments.is_empty()
         {
             let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
@@ -117,7 +76,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                 mutbl
             });
 
-            if !s.suggest_eliding_modes {
+            if !s.info.suggest_eliding_modes {
                 let suggestion_str: String =
                     implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
                 s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
@@ -169,7 +128,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             })
         });
 
-        if let Some(s) = &mut self.rust_2024_migration_suggestion
+        if let Some(s) = &mut self.rust_2024_migration
             && let Some(old_mode_span) = opt_old_mode_span
         {
             s.default_mode_span = old_mode_span;
@@ -368,7 +327,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
             }
             hir::PatKind::Ref(subpattern, _) => {
                 // Track the default binding mode for the Rust 2024 migration suggestion.
-                let old_mode_span = self.rust_2024_migration_suggestion.as_mut().and_then(|s| {
+                let old_mode_span = self.rust_2024_migration.as_mut().and_then(|s| {
                     if let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span {
                         // If this eats a by-ref default binding mode, label the binding mode.
                         s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
@@ -376,7 +335,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     s.default_mode_span.take()
                 });
                 let subpattern = self.lower_pattern(subpattern);
-                if let Some(s) = &mut self.rust_2024_migration_suggestion {
+                if let Some(s) = &mut self.rust_2024_migration {
                     s.default_mode_span = old_mode_span;
                 }
                 PatKind::Deref { subpattern }
@@ -408,19 +367,19 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
                     .get(pat.hir_id)
                     .expect("missing binding mode");
 
-                if let Some(s) = &mut self.rust_2024_migration_suggestion {
+                if let Some(s) = &mut self.rust_2024_migration {
                     if explicit_ba != hir::BindingMode::NONE
                         && let Some((default_mode_span, default_ref_mutbl)) = s.default_mode_span
                     {
                         // If this overrides a by-ref default binding mode, label the binding mode.
                         s.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
                         // If our suggestion is to elide redundnt modes, this will be one of them.
-                        if s.suggest_eliding_modes {
+                        if s.info.suggest_eliding_modes {
                             s.suggestion.push((pat.span.with_hi(ident.span.lo()), String::new()));
                             s.binding_mode_count += 1;
                         }
                     }
-                    if !s.suggest_eliding_modes
+                    if !s.info.suggest_eliding_modes
                         && explicit_ba.0 == ByRef::No
                         && let ByRef::Yes(mutbl) = mode.0
                     {