Skip to content

Commit 193a28d

Browse files
authored
Rollup merge of #144006 - dianne:match-ergonomics-jargon, r=Nadrieril
clarify wording of match ergonomics diagnostics (`rust_2024_incompatible_pat` lint and error) Partially addresses #143557: - Uses different wording than the Edition Guide chapter, to hopefully stand alone a bit better. Instead of referring to the "default binding mode", it now talks about what can't be written "within elided reference patterns". I ended up going with "elided" instead of "implicit" in hope that it reads bit less like it should behave the same as an explicit reference pattern, but I'm not totally happy with that wording. - The explanatory note still points to where the default binding mode was introduced, but only refers to its effect, not what we call it. How that relates to the rest of the diagnostic may still be a bit of a puzzle, but hopefully it isn't too much of one? It also doesn't make sense anymore for the case of `&` written under a by-ref binding mode, so I've left the note out in that case (but kept the label). It's more cramped, but talking about binding modes would feel like a non-sequitur for the error about `&` patterns without further explanation. - Links to the stable version of the Edition Guide instead of the nightly version. It looks like almost every link to the Edition Guide in diagnostics is to the nightly version, presumably for the same reason as here: the diagnostics were added before the new Edition was stabilized, then never updated. I'll make a separate PR to clean up the others. This only changes the diagnostic messages, not the code suggestion or the Edition Guide. r? `@Nadrieril` or reassign
2 parents 82224f6 + ec99e3e commit 193a28d

19 files changed

+629
-548
lines changed

compiler/rustc_hir_typeck/src/pat.rs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3115,20 +3115,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31153115
// binding mode. This keeps it from making those suggestions, as doing so could panic.
31163116
let info = table.entry(pat_id).or_insert_with(|| ty::Rust2024IncompatiblePatInfo {
31173117
primary_labels: Vec::new(),
3118-
bad_modifiers: false,
3118+
bad_ref_modifiers: false,
3119+
bad_mut_modifiers: false,
31193120
bad_ref_pats: false,
31203121
suggest_eliding_modes: !self.tcx.features().ref_pat_eat_one_layer_2024()
31213122
&& !self.tcx.features().ref_pat_eat_one_layer_2024_structural(),
31223123
});
31233124

31243125
let pat_kind = if let PatKind::Binding(user_bind_annot, _, _, _) = subpat.kind {
3125-
info.bad_modifiers = true;
31263126
// If the user-provided binding modifier doesn't match the default binding mode, we'll
31273127
// need to suggest reference patterns, which can affect other bindings.
31283128
// For simplicity, we opt to suggest making the pattern fully explicit.
31293129
info.suggest_eliding_modes &=
31303130
user_bind_annot == BindingMode(ByRef::Yes(def_br_mutbl), Mutability::Not);
3131-
"binding modifier"
3131+
if user_bind_annot == BindingMode(ByRef::No, Mutability::Mut) {
3132+
info.bad_mut_modifiers = true;
3133+
"`mut` binding modifier"
3134+
} else {
3135+
info.bad_ref_modifiers = true;
3136+
match user_bind_annot.1 {
3137+
Mutability::Not => "explicit `ref` binding modifier",
3138+
Mutability::Mut => "explicit `ref mut` binding modifier",
3139+
}
3140+
}
31323141
} else {
31333142
info.bad_ref_pats = true;
31343143
// For simplicity, we don't try to suggest eliding reference patterns. Thus, we'll
@@ -3147,11 +3156,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
31473156
// so, we may want to inspect the span's source callee or macro backtrace.
31483157
"occurs within macro expansion".to_owned()
31493158
} else {
3150-
let dbm_str = match def_br_mutbl {
3151-
Mutability::Not => "ref",
3152-
Mutability::Mut => "ref mut",
3153-
};
3154-
format!("{pat_kind} not allowed under `{dbm_str}` default binding mode")
3159+
format!("{pat_kind} not allowed when implicitly borrowing")
31553160
};
31563161
info.primary_labels.push((trimmed_span, primary_label));
31573162
}

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,7 @@ declare_lint! {
16011601
"detects patterns whose meaning will change in Rust 2024",
16021602
@future_incompatible = FutureIncompatibleInfo {
16031603
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
1604-
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>",
1604+
reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/match-ergonomics.html>",
16051605
};
16061606
}
16071607

compiler/rustc_middle/src/ty/typeck_results.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,10 @@ impl<'tcx> std::fmt::Display for UserTypeKind<'tcx> {
858858
pub struct Rust2024IncompatiblePatInfo {
859859
/// Labeled spans for `&`s, `&mut`s, and binding modifiers incompatible with Rust 2024.
860860
pub primary_labels: Vec<(Span, String)>,
861-
/// Whether any binding modifiers occur under a non-`move` default binding mode.
862-
pub bad_modifiers: bool,
861+
/// Whether any `mut` binding modifiers occur under a non-`move` default binding mode.
862+
pub bad_mut_modifiers: bool,
863+
/// Whether any `ref`/`ref mut` binding modifiers occur under a non-`move` default binding mode.
864+
pub bad_ref_modifiers: bool,
863865
/// Whether any `&` or `&mut` patterns occur under a non-`move` default binding mode.
864866
pub bad_ref_pats: bool,
865867
/// If `true`, we can give a simpler suggestion solely by eliding explicit binding modifiers.

compiler/rustc_mir_build/messages.ftl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -322,17 +322,6 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
322322
323323
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
324324
325-
mir_build_rust_2024_incompatible_pat = {$bad_modifiers ->
326-
*[true] binding modifiers{$bad_ref_pats ->
327-
*[true] {" "}and reference patterns
328-
[false] {""}
329-
}
330-
[false] reference patterns
331-
} may only be written when the default binding mode is `move`{$is_hard_error ->
332-
*[true] {""}
333-
[false] {" "}in Rust 2024
334-
}
335-
336325
mir_build_static_in_pattern = statics cannot be referenced in patterns
337326
.label = can't be used in patterns
338327
mir_build_static_in_pattern_def = `static` defined here

compiler/rustc_mir_build/src/errors.rs

Lines changed: 1 addition & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
use rustc_data_structures::fx::FxIndexMap;
21
use rustc_errors::codes::*;
32
use rustc_errors::{
43
Applicability, Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level,
5-
MultiSpan, Subdiagnostic, pluralize,
4+
MultiSpan, Subdiagnostic,
65
};
76
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
87
use rustc_middle::ty::{self, Ty};
@@ -1087,69 +1086,6 @@ pub(crate) enum MiscPatternSuggestion {
10871086
},
10881087
}
10891088

1090-
#[derive(LintDiagnostic)]
1091-
#[diag(mir_build_rust_2024_incompatible_pat)]
1092-
pub(crate) struct Rust2024IncompatiblePat {
1093-
#[subdiagnostic]
1094-
pub(crate) sugg: Rust2024IncompatiblePatSugg,
1095-
pub(crate) bad_modifiers: bool,
1096-
pub(crate) bad_ref_pats: bool,
1097-
pub(crate) is_hard_error: bool,
1098-
}
1099-
1100-
pub(crate) struct Rust2024IncompatiblePatSugg {
1101-
/// If true, our suggestion is to elide explicit binding modifiers.
1102-
/// If false, our suggestion is to make the pattern fully explicit.
1103-
pub(crate) suggest_eliding_modes: bool,
1104-
pub(crate) suggestion: Vec<(Span, String)>,
1105-
pub(crate) ref_pattern_count: usize,
1106-
pub(crate) binding_mode_count: usize,
1107-
/// Labels for where incompatibility-causing by-ref default binding modes were introduced.
1108-
pub(crate) default_mode_labels: FxIndexMap<Span, ty::Mutability>,
1109-
}
1110-
1111-
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
1112-
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
1113-
// Format and emit explanatory notes about default binding modes. Reversing the spans' order
1114-
// means if we have nested spans, the innermost ones will be visited first.
1115-
for (span, def_br_mutbl) in self.default_mode_labels.into_iter().rev() {
1116-
// Don't point to a macro call site.
1117-
if !span.from_expansion() {
1118-
let note_msg = "matching on a reference type with a non-reference pattern changes the default binding mode";
1119-
let label_msg =
1120-
format!("this matches on type `{}_`", def_br_mutbl.ref_prefix_str());
1121-
let mut label = MultiSpan::from(span);
1122-
label.push_span_label(span, label_msg);
1123-
diag.span_note(label, note_msg);
1124-
}
1125-
}
1126-
1127-
// Format and emit the suggestion.
1128-
let applicability =
1129-
if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {
1130-
Applicability::MachineApplicable
1131-
} else {
1132-
Applicability::MaybeIncorrect
1133-
};
1134-
let msg = if self.suggest_eliding_modes {
1135-
let plural_modes = pluralize!(self.binding_mode_count);
1136-
format!("remove the unnecessary binding modifier{plural_modes}")
1137-
} else {
1138-
let plural_derefs = pluralize!(self.ref_pattern_count);
1139-
let and_modes = if self.binding_mode_count > 0 {
1140-
format!(" and variable binding mode{}", pluralize!(self.binding_mode_count))
1141-
} else {
1142-
String::new()
1143-
};
1144-
format!("make the implied reference pattern{plural_derefs}{and_modes} explicit")
1145-
};
1146-
// FIXME(dianne): for peace of mind, don't risk emitting a 0-part suggestion (that panics!)
1147-
if !self.suggestion.is_empty() {
1148-
diag.multipart_suggestion_verbose(msg, self.suggestion, applicability);
1149-
}
1150-
}
1151-
}
1152-
11531089
#[derive(Diagnostic)]
11541090
#[diag(mir_build_loop_match_invalid_update)]
11551091
pub(crate) struct LoopMatchInvalidUpdate {

compiler/rustc_mir_build/src/thir/pattern/migration.rs

Lines changed: 80 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
//! Automatic migration of Rust 2021 patterns to a form valid in both Editions 2021 and 2024.
22
33
use rustc_data_structures::fx::FxIndexMap;
4-
use rustc_errors::MultiSpan;
4+
use rustc_errors::{Applicability, Diag, EmissionGuarantee, MultiSpan, pluralize};
55
use rustc_hir::{BindingMode, ByRef, HirId, Mutability};
66
use rustc_lint as lint;
77
use rustc_middle::ty::{self, Rust2024IncompatiblePatInfo, TyCtxt};
88
use rustc_span::{Ident, Span};
99

10-
use crate::errors::{Rust2024IncompatiblePat, Rust2024IncompatiblePatSugg};
11-
use crate::fluent_generated as fluent;
12-
1310
/// For patterns flagged for migration during HIR typeck, this handles constructing and emitting
1411
/// a diagnostic suggestion.
1512
pub(super) struct PatMigration<'a> {
@@ -49,39 +46,90 @@ impl<'a> PatMigration<'a> {
4946
for (span, label) in self.info.primary_labels.iter() {
5047
spans.push_span_label(*span, label.clone());
5148
}
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-
};
5949
// If a relevant span is from at least edition 2024, this is a hard error.
6050
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);
6152
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);
7256
err.emit();
7357
} 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);
85133
}
86134
}
87135

tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2021.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,20 +59,20 @@ LL | let [&bind_ref_mut!(x)] = &mut [0];
5959
| |
6060
| help: replace this `&` with `&mut`: `&mut`
6161

62-
error: binding modifiers may only be written when the default binding mode is `move`
62+
error: cannot explicitly borrow within an implicitly-borrowing pattern
6363
--> $DIR/mixed-editions.rs:30:10
6464
|
6565
LL | let [bind_ref!(y)] = &[0];
6666
| ^^^^^^^^^^^^ occurs within macro expansion
6767
|
68-
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
69-
note: matching on a reference type with a non-reference pattern changes the default binding mode
68+
= note: for more information, see <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
69+
note: matching on a reference type with a non-reference pattern implicitly borrows the contents
7070
--> $DIR/mixed-editions.rs:30:9
7171
|
7272
LL | let [bind_ref!(y)] = &[0];
73-
| ^^^^^^^^^^^^^^ this matches on type `&_`
73+
| ^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_`
7474
= note: this error originates in the macro `bind_ref` (in Nightly builds, run with -Z macro-backtrace for more info)
75-
help: make the implied reference pattern explicit
75+
help: match on the reference with a reference pattern to avoid implicitly borrowing
7676
|
7777
LL | let &[bind_ref!(y)] = &[0];
7878
| +

tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.classic2024.stderr

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,14 @@ LL | let [&bind_ref_mut!(x)] = &mut [0];
5858
| |
5959
| help: replace this `&` with `&mut`: `&mut`
6060

61-
error: binding modifiers may only be written when the default binding mode is `move`
61+
error: cannot explicitly borrow within an implicitly-borrowing pattern
6262
--> $DIR/mixed-editions.rs:26:21
6363
|
6464
LL | let match_ctor!(ref x) = &[0];
65-
| ^^^ binding modifier not allowed under `ref` default binding mode
65+
| ^^^ explicit `ref` binding modifier not allowed when implicitly borrowing
6666
|
67-
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
68-
help: make the implied reference pattern explicit
67+
= note: for more information, see <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
68+
help: match on the reference with a reference pattern to avoid implicitly borrowing
6969
--> $DIR/auxiliary/mixed-editions-macros.rs:11:9
7070
|
7171
LL | &[$p]

tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ fn assert_type_eq<T, U: Eq<T>>(_: T, _: U) {}
2424
/// only when the binding is from edition 2024.
2525
fn ref_binding_tests() {
2626
let match_ctor!(ref x) = &[0];
27-
//[classic2024,structural2024]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
27+
//[classic2024,structural2024]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern
2828
#[cfg(any(classic2021, structural2021))] assert_type_eq(x, &0u32);
2929

3030
let [bind_ref!(y)] = &[0];
31-
//[classic2021,structural2021]~^ ERROR: binding modifiers may only be written when the default binding mode is `move`
31+
//[classic2021,structural2021]~^ ERROR: cannot explicitly borrow within an implicitly-borrowing pattern
3232
#[cfg(any(classic2024, structural2024))] assert_type_eq(y, &0u32);
3333
}
3434

tests/ui/pattern/rfc-3627-match-ergonomics-2024/experimental/mixed-editions.structural2021.stderr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,20 @@ LL | let [&bind_ref_mut!(x)] = &mut [0];
3737
| |
3838
| help: replace this `&` with `&mut`: `&mut`
3939

40-
error: binding modifiers may only be written when the default binding mode is `move`
40+
error: cannot explicitly borrow within an implicitly-borrowing pattern
4141
--> $DIR/mixed-editions.rs:30:10
4242
|
4343
LL | let [bind_ref!(y)] = &[0];
4444
| ^^^^^^^^^^^^ occurs within macro expansion
4545
|
46-
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/match-ergonomics.html>
47-
note: matching on a reference type with a non-reference pattern changes the default binding mode
46+
= note: for more information, see <https://doc.rust-lang.org/reference/patterns.html#binding-modes>
47+
note: matching on a reference type with a non-reference pattern implicitly borrows the contents
4848
--> $DIR/mixed-editions.rs:30:9
4949
|
5050
LL | let [bind_ref!(y)] = &[0];
51-
| ^^^^^^^^^^^^^^ this matches on type `&_`
51+
| ^^^^^^^^^^^^^^ this non-reference pattern matches on a reference type `&_`
5252
= note: this error originates in the macro `bind_ref` (in Nightly builds, run with -Z macro-backtrace for more info)
53-
help: make the implied reference pattern explicit
53+
help: match on the reference with a reference pattern to avoid implicitly borrowing
5454
|
5555
LL | let &[bind_ref!(y)] = &[0];
5656
| +

0 commit comments

Comments
 (0)