| 
1 | 1 | //! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.  | 
2 | 2 | 
  | 
3 | 3 | use rustc_data_structures::fx::FxIndexMap;  | 
4 |  | -use rustc_errors::MultiSpan;  | 
 | 4 | +use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, pluralize};  | 
5 | 5 | use rustc_hir::{BindingMode, ByRef, HirId, Mutability};  | 
6 | 6 | use rustc_lint as lint;  | 
7 | 7 | use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};  | 
8 | 8 | use rustc_span::{Ident, Span};  | 
9 | 9 | 
 
  | 
10 |  | -use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};  | 
11 |  | -use crate::fluent_generated as fluent;  | 
12 |  | - | 
13 | 10 | /// For patterns flagged for migration during HIR typeck, this handles constructing and emitting  | 
14 | 11 | /// a diagnostic suggestion.  | 
15 | 12 | pub(super) struct PatMigration<'a> {  | 
@@ -49,39 +46,90 @@ impl<'a> PatMigration<'a> {  | 
49 | 46 |         for (span, label) in self.info.primary_labels.iter() {  | 
50 | 47 |             spans.push_span_label(*span, label.clone());  | 
51 | 48 |         }  | 
52 |  | -        let sugg = Rust2024IncompatiblePatSugg {  | 
53 |  | -            suggest_eliding_modes: self.info.suggest_eliding_modes,  | 
54 |  | -            suggestion: self.suggestion,  | 
55 |  | -            ref_pattern_count: self.ref_pattern_count,  | 
56 |  | -            binding_mode_count: self.binding_mode_count,  | 
57 |  | -            default_mode_labels: self.default_mode_labels,  | 
58 |  | -        };  | 
59 | 49 |         // If a relevant span is from at least edition 2024, this is a hard error.  | 
60 | 50 |         let is_hard_error = spans.primary_spans().iter().any(|span| span.at_least_rust_2024());  | 
 | 51 | +        let primary_message = self.primary_message(is_hard_error);  | 
61 | 52 |         if is_hard_error {  | 
62 |  | -            let mut err =  | 
63 |  | -                tcx.dcx().struct_span_err(spans, fluent::mir_build_rust_2024_incompatible_pat);  | 
64 |  | -            if let Some(info) = lint::builtin::RUST_2024_INCOMPATIBLE_PAT.future_incompatible {  | 
65 |  | -                // provide the same reference link as the lint  | 
66 |  | -                err.note(format!("for more information, see {}", info.reference));  | 
67 |  | -            }  | 
68 |  | -            err.arg("bad_modifiers", self.info.bad_modifiers);  | 
69 |  | -            err.arg("bad_ref_pats", self.info.bad_ref_pats);  | 
70 |  | -            err.arg("is_hard_error", true);  | 
71 |  | -            err.subdiagnostic(sugg);  | 
 | 53 | +            let mut err = tcx.dcx().struct_span_err(spans, primary_message);  | 
 | 54 | +            err.note("for more information, see <https://doc.rust-lang.org/reference/patterns.html#binding-modes>");  | 
 | 55 | +            self.format_subdiagnostics(&mut err);  | 
72 | 56 |             err.emit();  | 
73 | 57 |         } else {  | 
74 |  | -            tcx.emit_node_span_lint(  | 
75 |  | -                lint::builtin::RUST_2024_INCOMPATIBLE_PAT,  | 
76 |  | -                pat_id,  | 
77 |  | -                spans,  | 
78 |  | -                Rust2024IncompatiblePat {  | 
79 |  | -                    sugg,  | 
80 |  | -                    bad_modifiers: self.info.bad_modifiers,  | 
81 |  | -                    bad_ref_pats: self.info.bad_ref_pats,  | 
82 |  | -                    is_hard_error,  | 
83 |  | -                },  | 
84 |  | -            );  | 
 | 58 | +            tcx.node_span_lint(lint::builtin::RUST_2024_INCOMPATIBLE_PAT, pat_id, spans, |diag| {  | 
 | 59 | +                diag.primary_message(primary_message);  | 
 | 60 | +                self.format_subdiagnostics(diag);  | 
 | 61 | +            });  | 
 | 62 | +        }  | 
 | 63 | +    }  | 
 | 64 | + | 
 | 65 | +    fn primary_message(&self, is_hard_error: bool) -> String {  | 
 | 66 | +        let verb1 = match (self.info.bad_mut_modifiers, self.info.bad_ref_modifiers) {  | 
 | 67 | +            (true, true) => "write explicit binding modifiers",  | 
 | 68 | +            (true, false) => "mutably bind by value",  | 
 | 69 | +            (false, true) => "explicitly borrow",  | 
 | 70 | +            (false, false) => "explicitly dereference",  | 
 | 71 | +        };  | 
 | 72 | +        let or_verb2 = match (  | 
 | 73 | +            self.info.bad_mut_modifiers,  | 
 | 74 | +            self.info.bad_ref_modifiers,  | 
 | 75 | +            self.info.bad_ref_pats,  | 
 | 76 | +        ) {  | 
 | 77 | +            // We only need two verb phrases if mentioning both modifiers and reference patterns.  | 
 | 78 | +            (false, false, _) | (_, _, false) => "",  | 
 | 79 | +            // If mentioning `mut`, we don't have an "explicitly" yet.  | 
 | 80 | +            (true, _, true) => " or explicitly dereference",  | 
 | 81 | +            // If mentioning `ref`/`ref mut` but not `mut`, we already have an "explicitly".  | 
 | 82 | +            (false, true, true) => " or dereference",  | 
 | 83 | +        };  | 
 | 84 | +        let in_rust_2024 = if is_hard_error { "" } else { " in Rust 2024" };  | 
 | 85 | +        format!("cannot {verb1}{or_verb2} within an implicitly-borrowing pattern{in_rust_2024}")  | 
 | 86 | +    }  | 
 | 87 | + | 
 | 88 | +    fn format_subdiagnostics(self, diag: &mut Diag<'_, impl EmissionGuarantee>) {  | 
 | 89 | +        // Format and emit explanatory notes about default binding modes. Reversing the spans' order  | 
 | 90 | +        // means if we have nested spans, the innermost ones will be visited first.  | 
 | 91 | +        for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() {  | 
 | 92 | +            // Don't point to a macro call site.  | 
 | 93 | +            if !span.from_expansion() {  | 
 | 94 | +                let note_msg = "matching on a reference type with a non-reference pattern implicitly borrows the contents";  | 
 | 95 | +                let label_msg = format!(  | 
 | 96 | +                    "this non-reference pattern matches on a reference type `{}_`",  | 
 | 97 | +                    def_br_mutbl.ref_prefix_str()  | 
 | 98 | +                );  | 
 | 99 | +                let mut label = MultiSpan::from(span);  | 
 | 100 | +                label.push_span_label(span, label_msg);  | 
 | 101 | +                diag.span_note(label, note_msg);  | 
 | 102 | +            }  | 
 | 103 | +        }  | 
 | 104 | + | 
 | 105 | +        // Format and emit the suggestion.  | 
 | 106 | +        let applicability =  | 
 | 107 | +            if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {  | 
 | 108 | +                Applicability::MachineApplicable  | 
 | 109 | +            } else {  | 
 | 110 | +                Applicability::MaybeIncorrect  | 
 | 111 | +            };  | 
 | 112 | +        let plural_modes = pluralize!(self.binding_mode_count);  | 
 | 113 | +        let msg = if self.info.suggest_eliding_modes {  | 
 | 114 | +            format!("remove the unnecessary binding modifier{plural_modes}")  | 
 | 115 | +        } else {  | 
 | 116 | +            let match_on_these_references = if self.ref_pattern_count == 1 {  | 
 | 117 | +                "match on the reference with a reference pattern"  | 
 | 118 | +            } else {  | 
 | 119 | +                "match on these references with reference patterns"  | 
 | 120 | +            };  | 
 | 121 | +            let and_explain_modes = if self.binding_mode_count > 0 {  | 
 | 122 | +                let a = if self.binding_mode_count == 1 { "a " } else { "" };  | 
 | 123 | +                format!(" and borrow explicitly using {a}variable binding mode{plural_modes}")  | 
 | 124 | +            } else {  | 
 | 125 | +                " to avoid implicitly borrowing".to_owned()  | 
 | 126 | +            };  | 
 | 127 | +            format!("{match_on_these_references}{and_explain_modes}")  | 
 | 128 | +        };  | 
 | 129 | +        // FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!)  | 
 | 130 | +        debug_assert!(!self.suggestion.is_empty());  | 
 | 131 | +        if !self.suggestion.is_empty() {  | 
 | 132 | +            diag.multipart_suggestion_verbose(msg, self.suggestion, applicability);  | 
85 | 133 |         }  | 
86 | 134 |     }  | 
87 | 135 | 
 
  | 
 | 
0 commit comments