rustc_mir_build/thir/pattern/
migration.rs1use rustc_data_structures::fx::FxIndexMap;
4use rustc_errors::MultiSpan;
5use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
6use rustc_lint as lint;
7use rustc_middle::span_bug;
8use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, Ty, TyCtxt};
9use rustc_span::{Ident, Span};
10
11use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
12use crate::fluent_generated as fluent;
13
14pub(super) struct PatMigration<'a> {
17 suggestion: Vec<(Span, String)>,
18 ref_pattern_count: usize,
19 binding_mode_count: usize,
20 default_mode_span: Option<(Span, ty::Mutability)>,
23 default_mode_labels: FxIndexMap<Span, Mutability>,
29 info: &'a Rust2024IncompatiblePatInfo,
31}
32
33impl<'a> PatMigration<'a> {
34 pub(super) fn new(info: &'a Rust2024IncompatiblePatInfo) -> Self {
35 PatMigration {
36 suggestion: Vec::new(),
37 ref_pattern_count: 0,
38 binding_mode_count: 0,
39 default_mode_span: None,
40 default_mode_labels: Default::default(),
41 info,
42 }
43 }
44
45 pub(super) fn emit<'tcx>(self, tcx: TyCtxt<'tcx>, pat_id: HirId) {
48 let mut spans =
49 MultiSpan::from_spans(self.info.primary_labels.iter().map(|(span, _)| *span).collect());
50 for (span, label) in self.info.primary_labels.iter() {
51 spans.push_span_label(*span, label.clone());
52 }
53 let sugg = Rust2024IncompatiblePatSugg {
54 suggest_eliding_modes: self.info.suggest_eliding_modes,
55 suggestion: self.suggestion,
56 ref_pattern_count: self.ref_pattern_count,
57 binding_mode_count: self.binding_mode_count,
58 default_mode_labels: self.default_mode_labels,
59 };
60 let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());
62 if is_hard_error {
63 let mut err =
64 tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);
65 if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {
66 err.note(format!("for more information, see {}", info.reference));
68 }
69 err.arg("bad_modifiers", self.info.bad_modifiers);
70 err.arg("bad_ref_pats", self.info.bad_ref_pats);
71 err.arg("is_hard_error", true);
72 err.subdiagnostic(sugg);
73 err.emit();
74 } else {
75 tcx.emit_node_span_lint(
76 lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
77 pat_id,
78 spans,
79 Rust2024IncompatiblePat {
80 sugg,
81 bad_modifiers: self.info.bad_modifiers,
82 bad_ref_pats: self.info.bad_ref_pats,
83 is_hard_error,
84 },
85 );
86 }
87 }
88
89 pub(super) fn visit_implicit_derefs<'tcx>(
94 &mut self,
95 pat_span: Span,
96 adjustments: &[Ty<'tcx>],
97 ) -> Option<(Span, Mutability)> {
98 let implicit_deref_mutbls = adjustments.iter().map(|ref_ty| {
99 let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
100 span_bug!(pat_span, "pattern implicitly dereferences a non-ref type");
101 };
102 mutbl
103 });
104
105 if !self.info.suggest_eliding_modes {
106 let suggestion_str: String =
109 implicit_deref_mutbls.clone().map(|mutbl| mutbl.ref_prefix_str()).collect();
110 self.suggestion.push((pat_span.shrink_to_lo(), suggestion_str));
111 self.ref_pattern_count += adjustments.len();
112 }
113
114 let min_mutbl = implicit_deref_mutbls.min().unwrap();
116 if self.default_mode_span.is_none_or(|(_, old_mutbl)| min_mutbl < old_mutbl) {
117 self.default_mode_span.replace((pat_span, min_mutbl))
120 } else {
121 self.default_mode_span
123 }
124 }
125
126 pub(super) fn visit_explicit_deref(&mut self) -> Option<(Span, Mutability)> {
130 if let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span {
131 self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
133 }
134 self.default_mode_span.take()
137 }
138
139 pub(super) fn leave_ref(&mut self, old_mode_span: Option<(Span, Mutability)>) {
143 self.default_mode_span = old_mode_span
144 }
145
146 pub(super) fn visit_binding(
150 &mut self,
151 pat_span: Span,
152 mode: BindingMode,
153 explicit_ba: BindingMode,
154 ident: Ident,
155 ) {
156 if explicit_ba != BindingMode::NONE
157 && let Some((default_mode_span, default_ref_mutbl)) = self.default_mode_span
158 {
159 self.default_mode_labels.insert(default_mode_span, default_ref_mutbl);
161 if self.info.suggest_eliding_modes {
163 self.suggestion.push((pat_span.with_hi(ident.span.lo()), String::new()));
164 self.binding_mode_count += 1;
165 }
166 }
167 if !self.info.suggest_eliding_modes
168 && explicit_ba.0 == ByRef::No
169 && let ByRef::Yes(mutbl) = mode.0
170 {
171 let sugg_str = match mutbl {
174 Mutability::Not => "ref ",
175 Mutability::Mut => "ref mut ",
176 };
177 self.suggestion
178 .push((pat_span.with_lo(ident.span.lo()).shrink_to_lo(), sugg_str.to_owned()));
179 self.binding_mode_count += 1;
180 }
181 }
182}