Skip to content
/ rust Public
forked from rust-lang/rust

Commit ef00278

Browse files
committed
Auto merge of rust-lang#124639 - Jules-Bertholet:match-ergonomics-2024-migration-lint, r=Nadrieril
Match ergonomics 2024: migration lint Depends on rust-lang#124567 r? `@Nadrieril` cc rust-lang#123076 `@rustbot` label A-edition-2024 A-patterns
2 parents 852a78e + 9d92a7f commit ef00278

17 files changed

+409
-116
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4369,6 +4369,7 @@ dependencies = [
43694369
"rustc_hir",
43704370
"rustc_index",
43714371
"rustc_infer",
4372+
"rustc_lint",
43724373
"rustc_macros",
43734374
"rustc_middle",
43744375
"rustc_pattern_analysis",

compiler/rustc_hir_typeck/messages.ftl

-4
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,6 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
4646
4747
hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
4848
49-
hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding
50-
.label = `mut` dereferences the type of this binding
51-
.help = this will change in edition 2024
52-
5349
hir_typeck_expected_default_return_type = expected `()` because of default return type
5450
5551
hir_typeck_expected_return_type = expected `{$expected}` because of return type

compiler/rustc_hir_typeck/src/errors.rs

-7
Original file line numberDiff line numberDiff line change
@@ -651,10 +651,3 @@ pub enum SuggestBoxingForReturnImplTrait {
651651
ends: Vec<Span>,
652652
},
653653
}
654-
#[derive(LintDiagnostic)]
655-
#[diag(hir_typeck_dereferencing_mut_binding)]
656-
pub struct DereferencingMutBinding {
657-
#[label]
658-
#[help]
659-
pub span: Span,
660-
}

compiler/rustc_hir_typeck/src/pat.rs

+29-20
Original file line numberDiff line numberDiff line change
@@ -73,21 +73,23 @@ struct TopInfo<'tcx> {
7373
/// found type `std::result::Result<_, _>`
7474
/// ```
7575
span: Option<Span>,
76+
/// The [`HirId`] of the top-level pattern.
77+
hir_id: HirId,
7678
}
7779

7880
#[derive(Copy, Clone)]
7981
struct PatInfo<'tcx, 'a> {
8082
binding_mode: ByRef,
8183
max_ref_mutbl: MutblCap,
82-
top_info: TopInfo<'tcx>,
83-
decl_origin: Option<DeclOrigin<'a>>,
84+
top_info: &'a TopInfo<'tcx>,
85+
decl_origin: Option<DeclOrigin<'tcx>>,
8486

8587
/// The depth of current pattern
8688
current_depth: u32,
8789
}
8890

8991
impl<'tcx> FnCtxt<'_, 'tcx> {
90-
fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
92+
fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
9193
let code = ObligationCauseCode::Pattern {
9294
span: ti.span,
9395
root_ty: ti.expected,
@@ -101,7 +103,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
101103
cause_span: Span,
102104
expected: Ty<'tcx>,
103105
actual: Ty<'tcx>,
104-
ti: TopInfo<'tcx>,
106+
ti: &TopInfo<'tcx>,
105107
) -> Option<Diag<'tcx>> {
106108
let mut diag =
107109
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
@@ -118,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
118120
cause_span: Span,
119121
expected: Ty<'tcx>,
120122
actual: Ty<'tcx>,
121-
ti: TopInfo<'tcx>,
123+
ti: &TopInfo<'tcx>,
122124
) {
123125
if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) {
124126
err.emit();
@@ -199,11 +201,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
199201
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
200202
decl_origin: Option<DeclOrigin<'tcx>>,
201203
) {
202-
let info = TopInfo { expected, origin_expr, span };
204+
let info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id };
203205
let pat_info = PatInfo {
204206
binding_mode: ByRef::No,
205207
max_ref_mutbl: MutblCap::Mut,
206-
top_info: info,
208+
top_info: &info,
207209
decl_origin,
208210
current_depth: 0,
209211
};
@@ -463,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
463465
span: Span,
464466
lt: &hir::Expr<'tcx>,
465467
expected: Ty<'tcx>,
466-
ti: TopInfo<'tcx>,
468+
ti: &TopInfo<'tcx>,
467469
) -> Ty<'tcx> {
468470
// We've already computed the type above (when checking for a non-ref pat),
469471
// so avoid computing it again.
@@ -533,7 +535,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
533535
lhs: Option<&'tcx hir::Expr<'tcx>>,
534536
rhs: Option<&'tcx hir::Expr<'tcx>>,
535537
expected: Ty<'tcx>,
536-
ti: TopInfo<'tcx>,
538+
ti: &TopInfo<'tcx>,
537539
) -> Ty<'tcx> {
538540
let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
539541
None => None,
@@ -671,18 +673,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
671673

672674
// Determine the binding mode...
673675
let bm = match user_bind_annot {
676+
// `mut` resets binding mode on edition <= 2021
674677
BindingMode(ByRef::No, Mutability::Mut)
675678
if !(pat.span.at_least_rust_2024()
676679
&& self.tcx.features().mut_preserve_binding_mode_2024)
677680
&& matches!(def_br, ByRef::Yes(_)) =>
678681
{
679-
// `mut x` resets the binding mode in edition <= 2021.
680-
self.tcx.emit_node_span_lint(
681-
rustc_lint::builtin::DEREFERENCING_MUT_BINDING,
682-
pat.hir_id,
683-
pat.span,
684-
errors::DereferencingMutBinding { span: pat.span },
685-
);
682+
self.typeck_results
683+
.borrow_mut()
684+
.rust_2024_migration_desugared_pats_mut()
685+
.insert(pat_info.top_info.hir_id);
686686
BindingMode(ByRef::No, Mutability::Mut)
687687
}
688688
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
@@ -754,7 +754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
754754
span: Span,
755755
var_id: HirId,
756756
ty: Ty<'tcx>,
757-
ti: TopInfo<'tcx>,
757+
ti: &TopInfo<'tcx>,
758758
) {
759759
let var_ty = self.local_ty(span, var_id);
760760
if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) {
@@ -996,7 +996,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
996996
qpath: &hir::QPath<'_>,
997997
path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
998998
expected: Ty<'tcx>,
999-
ti: TopInfo<'tcx>,
999+
ti: &TopInfo<'tcx>,
10001000
) -> Ty<'tcx> {
10011001
let tcx = self.tcx;
10021002

@@ -2178,8 +2178,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21782178
}
21792179
} else {
21802180
// Reset binding mode on old editions
2181-
pat_info.binding_mode = ByRef::No;
2182-
pat_info.max_ref_mutbl = MutblCap::Mut
2181+
2182+
if pat_info.binding_mode != ByRef::No {
2183+
pat_info.binding_mode = ByRef::No;
2184+
2185+
self.typeck_results
2186+
.borrow_mut()
2187+
.rust_2024_migration_desugared_pats_mut()
2188+
.insert(pat_info.top_info.hir_id);
2189+
}
2190+
2191+
pat_info.max_ref_mutbl = MutblCap::Mut;
21832192
}
21842193

21852194
let tcx = self.tcx;

compiler/rustc_hir_typeck/src/writeback.rs

+17
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
346346
_ => {}
347347
};
348348

349+
self.visit_rust_2024_migration_desugared_pats(p.hir_id);
349350
self.visit_skipped_ref_pats(p.hir_id);
350351
self.visit_pat_adjustments(p.span, p.hir_id);
351352

@@ -655,6 +656,22 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
655656
}
656657
}
657658

659+
#[instrument(skip(self), level = "debug")]
660+
fn visit_rust_2024_migration_desugared_pats(&mut self, hir_id: hir::HirId) {
661+
if self
662+
.fcx
663+
.typeck_results
664+
.borrow_mut()
665+
.rust_2024_migration_desugared_pats_mut()
666+
.remove(hir_id)
667+
{
668+
debug!(
669+
"node is a pat whose match ergonomics are desugared by the Rust 2024 migration lint"
670+
);
671+
self.typeck_results.rust_2024_migration_desugared_pats_mut().insert(hir_id);
672+
}
673+
}
674+
658675
#[instrument(skip(self, span), level = "debug")]
659676
fn visit_pat_adjustments(&mut self, span: Span, hir_id: HirId) {
660677
let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id);

compiler/rustc_lint_defs/src/builtin.rs

+19-19
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ declare_lint_pass! {
3838
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
3939
DEPRECATED_IN_FUTURE,
4040
DEPRECATED_WHERE_CLAUSE_LOCATION,
41-
DEREFERENCING_MUT_BINDING,
4241
DUPLICATE_MACRO_ATTRIBUTES,
4342
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4443
ELIDED_LIFETIMES_IN_PATHS,
@@ -90,6 +89,7 @@ declare_lint_pass! {
9089
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
9190
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
9291
RUST_2021_PRELUDE_COLLISIONS,
92+
RUST_2024_INCOMPATIBLE_PAT,
9393
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
9494
SINGLE_USE_LIFETIMES,
9595
SOFT_UNSTABLE,
@@ -1630,34 +1630,34 @@ declare_lint! {
16301630
}
16311631

16321632
declare_lint! {
1633-
/// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode,
1634-
/// as this behavior will change in rust 2024.
1633+
/// The `rust_2024_incompatible_pat` lint
1634+
/// detects patterns whose meaning will change in the Rust 2024 edition.
16351635
///
16361636
/// ### Example
16371637
///
1638-
/// ```rust
1639-
/// # #![warn(dereferencing_mut_binding)]
1640-
/// let x = Some(123u32);
1641-
/// let _y = match &x {
1642-
/// Some(mut x) => {
1643-
/// x += 1;
1644-
/// x
1645-
/// }
1646-
/// None => 0,
1647-
/// };
1638+
/// ```rust,edition2021
1639+
/// #![feature(ref_pat_eat_one_layer_2024)]
1640+
/// #![warn(rust_2024_incompatible_pat)]
1641+
///
1642+
/// if let Some(&a) = &Some(&0u8) {
1643+
/// let _: u8 = a;
1644+
/// }
1645+
/// if let Some(mut _a) = &mut Some(0u8) {
1646+
/// _a = 7u8;
1647+
/// }
16481648
/// ```
16491649
///
16501650
/// {{produces}}
16511651
///
16521652
/// ### Explanation
16531653
///
1654-
/// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type
1655-
/// `u32`, which was deemed surprising. After edition 2024, adding `mut` will not change the
1656-
/// type of `x`. This lint warns users of editions before 2024 to update their code.
1657-
pub DEREFERENCING_MUT_BINDING,
1654+
/// In Rust 2024 and above, the `mut` keyword does not reset the pattern binding mode,
1655+
/// and nor do `&` or `&mut` patterns. The lint will suggest code that
1656+
/// has the same meaning in all editions.
1657+
pub RUST_2024_INCOMPATIBLE_PAT,
16581658
Allow,
1659-
"detects `mut x` bindings that change the type of `x`",
1660-
@feature_gate = sym::mut_preserve_binding_mode_2024;
1659+
"detects patterns whose meaning will change in Rust 2024",
1660+
@feature_gate = sym::ref_pat_eat_one_layer_2024;
16611661
// FIXME uncomment below upon stabilization
16621662
/*@future_incompatible = FutureIncompatibleInfo {
16631663
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),

compiler/rustc_middle/src/ty/typeck_results.rs

+19
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ pub struct TypeckResults<'tcx> {
7979
/// Stores the actual binding mode for all instances of [`BindingMode`].
8080
pat_binding_modes: ItemLocalMap<BindingMode>,
8181

82+
/// Top-level patterns whose match ergonomics need to be desugared
83+
/// by the Rust 2021 -> 2024 migration lint.
84+
rust_2024_migration_desugared_pats: ItemLocalSet,
85+
8286
/// Stores the types which were implicitly dereferenced in pattern binding modes
8387
/// for later usage in THIR lowering. For example,
8488
///
@@ -229,6 +233,7 @@ impl<'tcx> TypeckResults<'tcx> {
229233
adjustments: Default::default(),
230234
pat_binding_modes: Default::default(),
231235
pat_adjustments: Default::default(),
236+
rust_2024_migration_desugared_pats: Default::default(),
232237
skipped_ref_pats: Default::default(),
233238
closure_kind_origins: Default::default(),
234239
liberated_fn_sigs: Default::default(),
@@ -432,6 +437,20 @@ impl<'tcx> TypeckResults<'tcx> {
432437
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
433438
}
434439

440+
pub fn rust_2024_migration_desugared_pats(&self) -> LocalSetInContext<'_> {
441+
LocalSetInContext {
442+
hir_owner: self.hir_owner,
443+
data: &self.rust_2024_migration_desugared_pats,
444+
}
445+
}
446+
447+
pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalSetInContextMut<'_> {
448+
LocalSetInContextMut {
449+
hir_owner: self.hir_owner,
450+
data: &mut self.rust_2024_migration_desugared_pats,
451+
}
452+
}
453+
435454
pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> {
436455
LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats }
437456
}

compiler/rustc_mir_build/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
1616
rustc_hir = { path = "../rustc_hir" }
1717
rustc_index = { path = "../rustc_index" }
1818
rustc_infer = { path = "../rustc_infer" }
19+
rustc_lint = { path = "../rustc_lint" }
1920
rustc_macros = { path = "../rustc_macros" }
2021
rustc_middle = { path = "../rustc_middle" }
2122
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }

compiler/rustc_mir_build/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
267267
268268
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
269269
270+
mir_build_rust_2024_incompatible_pat = the semantics of this pattern will change in edition 2024
271+
270272
mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
271273
.attributes = no other attributes may be applied
272274
.not_box = `#[rustc_box]` may only be applied to a `Box::new()` call

compiler/rustc_mir_build/src/errors.rs

+27
Original file line numberDiff line numberDiff line change
@@ -950,3 +950,30 @@ pub enum RustcBoxAttrReason {
950950
#[note(mir_build_missing_box)]
951951
MissingBox,
952952
}
953+
954+
#[derive(LintDiagnostic)]
955+
#[diag(mir_build_rust_2024_incompatible_pat)]
956+
pub struct Rust2024IncompatiblePat {
957+
#[subdiagnostic]
958+
pub sugg: Rust2024IncompatiblePatSugg,
959+
}
960+
961+
pub struct Rust2024IncompatiblePatSugg {
962+
pub suggestion: Vec<(Span, String)>,
963+
}
964+
965+
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
966+
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
967+
self,
968+
diag: &mut Diag<'_, G>,
969+
_f: &F,
970+
) {
971+
let applicability =
972+
if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {
973+
Applicability::MachineApplicable
974+
} else {
975+
Applicability::MaybeIncorrect
976+
};
977+
diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability);
978+
}
979+
}

0 commit comments

Comments
 (0)