mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Match ergonomics 2024: migration lint
Unfortunately, we can't always offer a machine-applicable suggestion when there are subpatterns from macro expansion. Co-Authored-By: Guillaume Boisseau <Nadrieril@users.noreply.github.com>
This commit is contained in:
parent
686bfc4c42
commit
9d92a7f355
@ -4367,6 +4367,7 @@ dependencies = [
|
||||
"rustc_hir",
|
||||
"rustc_index",
|
||||
"rustc_infer",
|
||||
"rustc_lint",
|
||||
"rustc_macros",
|
||||
"rustc_middle",
|
||||
"rustc_pattern_analysis",
|
||||
|
@ -46,10 +46,6 @@ hir_typeck_ctor_is_private = tuple struct constructor `{$def}` is private
|
||||
|
||||
hir_typeck_deref_is_empty = this expression `Deref`s to `{$deref_ty}` which implements `is_empty`
|
||||
|
||||
hir_typeck_dereferencing_mut_binding = dereferencing `mut` binding
|
||||
.label = `mut` dereferences the type of this binding
|
||||
.help = this will change in edition 2024
|
||||
|
||||
hir_typeck_expected_default_return_type = expected `()` because of default return type
|
||||
|
||||
hir_typeck_expected_return_type = expected `{$expected}` because of return type
|
||||
|
@ -651,10 +651,3 @@ pub enum SuggestBoxingForReturnImplTrait {
|
||||
ends: Vec<Span>,
|
||||
},
|
||||
}
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(hir_typeck_dereferencing_mut_binding)]
|
||||
pub struct DereferencingMutBinding {
|
||||
#[label]
|
||||
#[help]
|
||||
pub span: Span,
|
||||
}
|
||||
|
@ -73,21 +73,23 @@ struct TopInfo<'tcx> {
|
||||
/// found type `std::result::Result<_, _>`
|
||||
/// ```
|
||||
span: Option<Span>,
|
||||
/// The [`HirId`] of the top-level pattern.
|
||||
hir_id: HirId,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
struct PatInfo<'tcx, 'a> {
|
||||
binding_mode: ByRef,
|
||||
max_ref_mutbl: MutblCap,
|
||||
top_info: TopInfo<'tcx>,
|
||||
decl_origin: Option<DeclOrigin<'a>>,
|
||||
top_info: &'a TopInfo<'tcx>,
|
||||
decl_origin: Option<DeclOrigin<'tcx>>,
|
||||
|
||||
/// The depth of current pattern
|
||||
current_depth: u32,
|
||||
}
|
||||
|
||||
impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
|
||||
fn pattern_cause(&self, ti: &TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> {
|
||||
let code = ObligationCauseCode::Pattern {
|
||||
span: ti.span,
|
||||
root_ty: ti.expected,
|
||||
@ -101,7 +103,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
cause_span: Span,
|
||||
expected: Ty<'tcx>,
|
||||
actual: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
ti: &TopInfo<'tcx>,
|
||||
) -> Option<Diag<'tcx>> {
|
||||
let mut diag =
|
||||
self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual)?;
|
||||
@ -118,7 +120,7 @@ impl<'tcx> FnCtxt<'_, 'tcx> {
|
||||
cause_span: Span,
|
||||
expected: Ty<'tcx>,
|
||||
actual: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
ti: &TopInfo<'tcx>,
|
||||
) {
|
||||
if let Some(err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) {
|
||||
err.emit();
|
||||
@ -199,11 +201,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
origin_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
decl_origin: Option<DeclOrigin<'tcx>>,
|
||||
) {
|
||||
let info = TopInfo { expected, origin_expr, span };
|
||||
let info = TopInfo { expected, origin_expr, span, hir_id: pat.hir_id };
|
||||
let pat_info = PatInfo {
|
||||
binding_mode: ByRef::No,
|
||||
max_ref_mutbl: MutblCap::Mut,
|
||||
top_info: info,
|
||||
top_info: &info,
|
||||
decl_origin,
|
||||
current_depth: 0,
|
||||
};
|
||||
@ -463,7 +465,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
lt: &hir::Expr<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
ti: &TopInfo<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
// We've already computed the type above (when checking for a non-ref pat),
|
||||
// so avoid computing it again.
|
||||
@ -533,7 +535,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
lhs: Option<&'tcx hir::Expr<'tcx>>,
|
||||
rhs: Option<&'tcx hir::Expr<'tcx>>,
|
||||
expected: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
ti: &TopInfo<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr {
|
||||
None => None,
|
||||
@ -671,18 +673,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
// Determine the binding mode...
|
||||
let bm = match user_bind_annot {
|
||||
// `mut` resets binding mode on edition <= 2021
|
||||
BindingMode(ByRef::No, Mutability::Mut)
|
||||
if !(pat.span.at_least_rust_2024()
|
||||
&& self.tcx.features().mut_preserve_binding_mode_2024)
|
||||
&& matches!(def_br, ByRef::Yes(_)) =>
|
||||
{
|
||||
// `mut x` resets the binding mode in edition <= 2021.
|
||||
self.tcx.emit_node_span_lint(
|
||||
rustc_lint::builtin::DEREFERENCING_MUT_BINDING,
|
||||
pat.hir_id,
|
||||
pat.span,
|
||||
errors::DereferencingMutBinding { span: pat.span },
|
||||
);
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.insert(pat_info.top_info.hir_id);
|
||||
BindingMode(ByRef::No, Mutability::Mut)
|
||||
}
|
||||
BindingMode(ByRef::No, mutbl) => BindingMode(def_br, mutbl),
|
||||
@ -754,7 +754,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
var_id: HirId,
|
||||
ty: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
ti: &TopInfo<'tcx>,
|
||||
) {
|
||||
let var_ty = self.local_ty(span, var_id);
|
||||
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> {
|
||||
qpath: &hir::QPath<'_>,
|
||||
path_resolution: (Res, Option<LoweredTy<'tcx>>, &'tcx [hir::PathSegment<'tcx>]),
|
||||
expected: Ty<'tcx>,
|
||||
ti: TopInfo<'tcx>,
|
||||
ti: &TopInfo<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
@ -2178,8 +2178,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
} else {
|
||||
// Reset binding mode on old editions
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
pat_info.max_ref_mutbl = MutblCap::Mut
|
||||
|
||||
if pat_info.binding_mode != ByRef::No {
|
||||
pat_info.binding_mode = ByRef::No;
|
||||
|
||||
self.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.insert(pat_info.top_info.hir_id);
|
||||
}
|
||||
|
||||
pat_info.max_ref_mutbl = MutblCap::Mut;
|
||||
}
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
@ -346,6 +346,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
|
||||
_ => {}
|
||||
};
|
||||
|
||||
self.visit_rust_2024_migration_desugared_pats(p.hir_id);
|
||||
self.visit_skipped_ref_pats(p.hir_id);
|
||||
self.visit_pat_adjustments(p.span, p.hir_id);
|
||||
|
||||
@ -655,6 +656,22 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn visit_rust_2024_migration_desugared_pats(&mut self, hir_id: hir::HirId) {
|
||||
if self
|
||||
.fcx
|
||||
.typeck_results
|
||||
.borrow_mut()
|
||||
.rust_2024_migration_desugared_pats_mut()
|
||||
.remove(hir_id)
|
||||
{
|
||||
debug!(
|
||||
"node is a pat whose match ergonomics are desugared by the Rust 2024 migration lint"
|
||||
);
|
||||
self.typeck_results.rust_2024_migration_desugared_pats_mut().insert(hir_id);
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, span), level = "debug")]
|
||||
fn visit_pat_adjustments(&mut self, span: Span, hir_id: HirId) {
|
||||
let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id);
|
||||
|
@ -38,7 +38,6 @@ declare_lint_pass! {
|
||||
DEPRECATED_CFG_ATTR_CRATE_TYPE_NAME,
|
||||
DEPRECATED_IN_FUTURE,
|
||||
DEPRECATED_WHERE_CLAUSE_LOCATION,
|
||||
DEREFERENCING_MUT_BINDING,
|
||||
DUPLICATE_MACRO_ATTRIBUTES,
|
||||
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
|
||||
ELIDED_LIFETIMES_IN_PATHS,
|
||||
@ -90,6 +89,7 @@ declare_lint_pass! {
|
||||
RUST_2021_INCOMPATIBLE_OR_PATTERNS,
|
||||
RUST_2021_PREFIXES_INCOMPATIBLE_SYNTAX,
|
||||
RUST_2021_PRELUDE_COLLISIONS,
|
||||
RUST_2024_INCOMPATIBLE_PAT,
|
||||
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
|
||||
SINGLE_USE_LIFETIMES,
|
||||
SOFT_UNSTABLE,
|
||||
@ -1630,34 +1630,34 @@ declare_lint! {
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `dereferencing_mut_binding` lint detects a `mut x` pattern that resets the binding mode,
|
||||
/// as this behavior will change in rust 2024.
|
||||
/// The `rust_2024_incompatible_pat` lint
|
||||
/// detects patterns whose meaning will change in the Rust 2024 edition.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![warn(dereferencing_mut_binding)]
|
||||
/// let x = Some(123u32);
|
||||
/// let _y = match &x {
|
||||
/// Some(mut x) => {
|
||||
/// x += 1;
|
||||
/// x
|
||||
/// }
|
||||
/// None => 0,
|
||||
/// };
|
||||
/// ```rust,edition2021
|
||||
/// #![feature(ref_pat_eat_one_layer_2024)]
|
||||
/// #![warn(rust_2024_incompatible_pat)]
|
||||
///
|
||||
/// if let Some(&a) = &Some(&0u8) {
|
||||
/// let _: u8 = a;
|
||||
/// }
|
||||
/// if let Some(mut _a) = &mut Some(0u8) {
|
||||
/// _a = 7u8;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// Without the `mut`, `x` would have type `&u32`. Pre-2024, adding `mut` makes `x` have type
|
||||
/// `u32`, which was deemed surprising. After edition 2024, adding `mut` will not change the
|
||||
/// type of `x`. This lint warns users of editions before 2024 to update their code.
|
||||
pub DEREFERENCING_MUT_BINDING,
|
||||
/// In Rust 2024 and above, the `mut` keyword does not reset the pattern binding mode,
|
||||
/// and nor do `&` or `&mut` patterns. The lint will suggest code that
|
||||
/// has the same meaning in all editions.
|
||||
pub RUST_2024_INCOMPATIBLE_PAT,
|
||||
Allow,
|
||||
"detects `mut x` bindings that change the type of `x`",
|
||||
@feature_gate = sym::mut_preserve_binding_mode_2024;
|
||||
"detects patterns whose meaning will change in Rust 2024",
|
||||
@feature_gate = sym::ref_pat_eat_one_layer_2024;
|
||||
// FIXME uncomment below upon stabilization
|
||||
/*@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::EditionSemanticsChange(Edition::Edition2024),
|
||||
|
@ -79,6 +79,10 @@ pub struct TypeckResults<'tcx> {
|
||||
/// Stores the actual binding mode for all instances of [`BindingMode`].
|
||||
pat_binding_modes: ItemLocalMap<BindingMode>,
|
||||
|
||||
/// Top-level patterns whose match ergonomics need to be desugared
|
||||
/// by the Rust 2021 -> 2024 migration lint.
|
||||
rust_2024_migration_desugared_pats: ItemLocalSet,
|
||||
|
||||
/// Stores the types which were implicitly dereferenced in pattern binding modes
|
||||
/// for later usage in THIR lowering. For example,
|
||||
///
|
||||
@ -229,6 +233,7 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||
adjustments: Default::default(),
|
||||
pat_binding_modes: Default::default(),
|
||||
pat_adjustments: Default::default(),
|
||||
rust_2024_migration_desugared_pats: Default::default(),
|
||||
skipped_ref_pats: Default::default(),
|
||||
closure_kind_origins: Default::default(),
|
||||
liberated_fn_sigs: Default::default(),
|
||||
@ -432,6 +437,20 @@ impl<'tcx> TypeckResults<'tcx> {
|
||||
LocalTableInContextMut { hir_owner: self.hir_owner, data: &mut self.pat_adjustments }
|
||||
}
|
||||
|
||||
pub fn rust_2024_migration_desugared_pats(&self) -> LocalSetInContext<'_> {
|
||||
LocalSetInContext {
|
||||
hir_owner: self.hir_owner,
|
||||
data: &self.rust_2024_migration_desugared_pats,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rust_2024_migration_desugared_pats_mut(&mut self) -> LocalSetInContextMut<'_> {
|
||||
LocalSetInContextMut {
|
||||
hir_owner: self.hir_owner,
|
||||
data: &mut self.rust_2024_migration_desugared_pats,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skipped_ref_pats(&self) -> LocalSetInContext<'_> {
|
||||
LocalSetInContext { hir_owner: self.hir_owner, data: &self.skipped_ref_pats }
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_infer = { path = "../rustc_infer" }
|
||||
rustc_lint = { path = "../rustc_lint" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_pattern_analysis = { path = "../rustc_pattern_analysis" }
|
||||
|
@ -267,6 +267,8 @@ mir_build_pointer_pattern = function pointers and raw pointers not derived from
|
||||
|
||||
mir_build_privately_uninhabited = pattern `{$witness_1}` is currently uninhabited, but this variant contains private fields which may become inhabited in the future
|
||||
|
||||
mir_build_rust_2024_incompatible_pat = the semantics of this pattern will change in edition 2024
|
||||
|
||||
mir_build_rustc_box_attribute_error = `#[rustc_box]` attribute used incorrectly
|
||||
.attributes = no other attributes may be applied
|
||||
.not_box = `#[rustc_box]` may only be applied to a `Box::new()` call
|
||||
|
@ -950,3 +950,30 @@ pub enum RustcBoxAttrReason {
|
||||
#[note(mir_build_missing_box)]
|
||||
MissingBox,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(mir_build_rust_2024_incompatible_pat)]
|
||||
pub struct Rust2024IncompatiblePat {
|
||||
#[subdiagnostic]
|
||||
pub sugg: Rust2024IncompatiblePatSugg,
|
||||
}
|
||||
|
||||
pub struct Rust2024IncompatiblePatSugg {
|
||||
pub suggestion: Vec<(Span, String)>,
|
||||
}
|
||||
|
||||
impl Subdiagnostic for Rust2024IncompatiblePatSugg {
|
||||
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
|
||||
self,
|
||||
diag: &mut Diag<'_, G>,
|
||||
_f: &F,
|
||||
) {
|
||||
let applicability =
|
||||
if self.suggestion.iter().all(|(span, _)| span.can_be_used_for_suggestions()) {
|
||||
Applicability::MachineApplicable
|
||||
} else {
|
||||
Applicability::MaybeIncorrect
|
||||
};
|
||||
diag.multipart_suggestion("desugar the match ergonomics", self.suggestion, applicability);
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,9 @@ use crate::thir::util::UserAnnotatedTyHelpers;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::pat_util::EnumerateAndAdjustIterator;
|
||||
use rustc_hir::{self as hir, RangeEnd};
|
||||
use rustc_hir::{self as hir, ByRef, Mutability, RangeEnd};
|
||||
use rustc_index::Idx;
|
||||
use rustc_lint as lint;
|
||||
use rustc_middle::mir::interpret::{ErrorHandled, GlobalId, LitToConstError, LitToConstInput};
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{
|
||||
@ -30,6 +31,9 @@ struct PatCtxt<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
|
||||
/// Used by the Rust 2024 migration lint.
|
||||
rust_2024_migration_suggestion: Option<Rust2024IncompatiblePatSugg>,
|
||||
}
|
||||
|
||||
pub(super) fn pat_from_hir<'a, 'tcx>(
|
||||
@ -38,9 +42,25 @@ pub(super) fn pat_from_hir<'a, 'tcx>(
|
||||
typeck_results: &'a ty::TypeckResults<'tcx>,
|
||||
pat: &'tcx hir::Pat<'tcx>,
|
||||
) -> Box<Pat<'tcx>> {
|
||||
let mut pcx = PatCtxt { tcx, param_env, typeck_results };
|
||||
let mut pcx = PatCtxt {
|
||||
tcx,
|
||||
param_env,
|
||||
typeck_results,
|
||||
rust_2024_migration_suggestion: typeck_results
|
||||
.rust_2024_migration_desugared_pats()
|
||||
.contains(pat.hir_id)
|
||||
.then_some(Rust2024IncompatiblePatSugg { suggestion: Vec::new() }),
|
||||
};
|
||||
let result = pcx.lower_pattern(pat);
|
||||
debug!("pat_from_hir({:?}) = {:?}", pat, result);
|
||||
if let Some(sugg) = pcx.rust_2024_migration_suggestion {
|
||||
tcx.emit_node_span_lint(
|
||||
lint::builtin::RUST_2024_INCOMPATIBLE_PAT,
|
||||
pat.hir_id,
|
||||
pat.span,
|
||||
Rust2024IncompatiblePat { sugg },
|
||||
);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@ -73,17 +93,38 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
}
|
||||
_ => self.lower_pattern_unadjusted(pat),
|
||||
};
|
||||
self.typeck_results.pat_adjustments().get(pat.hir_id).unwrap_or(&vec![]).iter().rev().fold(
|
||||
unadjusted_pat,
|
||||
|pat: Box<_>, ref_ty| {
|
||||
debug!("{:?}: wrapping pattern with type {:?}", pat, ref_ty);
|
||||
Box::new(Pat {
|
||||
span: pat.span,
|
||||
ty: *ref_ty,
|
||||
kind: PatKind::Deref { subpattern: pat },
|
||||
|
||||
let adjustments: &[Ty<'tcx>] =
|
||||
self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(&[], |v| &**v);
|
||||
let adjusted_pat = adjustments.iter().rev().fold(unadjusted_pat, |thir_pat, ref_ty| {
|
||||
debug!("{:?}: wrapping pattern with type {:?}", thir_pat, ref_ty);
|
||||
Box::new(Pat {
|
||||
span: thir_pat.span,
|
||||
ty: *ref_ty,
|
||||
kind: PatKind::Deref { subpattern: thir_pat },
|
||||
})
|
||||
});
|
||||
|
||||
if let Some(s) = &mut self.rust_2024_migration_suggestion
|
||||
&& !adjustments.is_empty()
|
||||
{
|
||||
let suggestion_str: String = adjustments
|
||||
.iter()
|
||||
.map(|ref_ty| {
|
||||
let &ty::Ref(_, _, mutbl) = ref_ty.kind() else {
|
||||
span_bug!(pat.span, "pattern implicitly dereferences a non-ref type");
|
||||
};
|
||||
|
||||
match mutbl {
|
||||
ty::Mutability::Not => "&",
|
||||
ty::Mutability::Mut => "&mut ",
|
||||
}
|
||||
})
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
s.suggestion.push((pat.span.shrink_to_lo(), suggestion_str));
|
||||
};
|
||||
|
||||
adjusted_pat
|
||||
}
|
||||
|
||||
fn lower_pattern_range_endpoint(
|
||||
@ -272,7 +313,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
PatKind::Deref { subpattern: self.lower_pattern(subpattern) }
|
||||
}
|
||||
|
||||
hir::PatKind::Slice(prefix, ref slice, suffix) => {
|
||||
hir::PatKind::Slice(prefix, slice, suffix) => {
|
||||
self.slice_or_array_pattern(pat.span, ty, prefix, slice, suffix)
|
||||
}
|
||||
|
||||
@ -284,7 +325,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
|
||||
hir::PatKind::Binding(_, id, ident, ref sub) => {
|
||||
hir::PatKind::Binding(explicit_ba, id, ident, sub) => {
|
||||
if let Some(ident_span) = ident.span.find_ancestor_inside(span) {
|
||||
span = span.with_hi(ident_span.hi());
|
||||
}
|
||||
@ -295,6 +336,20 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
.get(pat.hir_id)
|
||||
.expect("missing binding mode");
|
||||
|
||||
if let Some(s) = &mut self.rust_2024_migration_suggestion
|
||||
&& explicit_ba.0 == ByRef::No
|
||||
&& let ByRef::Yes(mutbl) = mode.0
|
||||
{
|
||||
let sugg_str = match mutbl {
|
||||
Mutability::Not => "ref ",
|
||||
Mutability::Mut => "ref mut ",
|
||||
};
|
||||
s.suggestion.push((
|
||||
pat.span.with_lo(ident.span.lo()).shrink_to_lo(),
|
||||
sugg_str.to_owned(),
|
||||
))
|
||||
}
|
||||
|
||||
// A ref x pattern is the same node used for x, and as such it has
|
||||
// x's type, which is &T, where we want T (the type being matched).
|
||||
let var_ty = ty;
|
||||
@ -366,10 +421,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
pats.iter().map(|p| self.lower_pattern(p)).collect()
|
||||
}
|
||||
|
||||
fn lower_opt_pattern(
|
||||
&mut self,
|
||||
pat: &'tcx Option<&'tcx hir::Pat<'tcx>>,
|
||||
) -> Option<Box<Pat<'tcx>>> {
|
||||
fn lower_opt_pattern(&mut self, pat: Option<&'tcx hir::Pat<'tcx>>) -> Option<Box<Pat<'tcx>>> {
|
||||
pat.map(|p| self.lower_pattern(p))
|
||||
}
|
||||
|
||||
@ -378,7 +430,7 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
|
||||
span: Span,
|
||||
ty: Ty<'tcx>,
|
||||
prefix: &'tcx [hir::Pat<'tcx>],
|
||||
slice: &'tcx Option<&'tcx hir::Pat<'tcx>>,
|
||||
slice: Option<&'tcx hir::Pat<'tcx>>,
|
||||
suffix: &'tcx [hir::Pat<'tcx>],
|
||||
) -> PatKind<'tcx> {
|
||||
let prefix = self.lower_patterns(prefix);
|
||||
|
12
tests/ui/pattern/auxiliary/match_ergonomics_2024_macros.rs
Normal file
12
tests/ui/pattern/auxiliary/match_ergonomics_2024_macros.rs
Normal file
@ -0,0 +1,12 @@
|
||||
//@ edition: 2024
|
||||
//@ compile-flags: -Z unstable-options
|
||||
|
||||
// This contains a binding in edition 2024, so if matched with a reference binding mode it will end
|
||||
// up with a `mut ref mut` binding mode. We use this to test the migration lint on patterns with
|
||||
// mixed editions.
|
||||
#[macro_export]
|
||||
macro_rules! mixed_edition_pat {
|
||||
($foo:ident) => {
|
||||
Some(mut $foo)
|
||||
};
|
||||
}
|
57
tests/ui/pattern/match_ergonomics_2024.fixed
Normal file
57
tests/ui/pattern/match_ergonomics_2024.fixed
Normal file
@ -0,0 +1,57 @@
|
||||
//@ edition: 2021
|
||||
//@ run-rustfix
|
||||
//@ rustfix-only-machine-applicable
|
||||
//@ aux-build:match_ergonomics_2024_macros.rs
|
||||
#![feature(mut_preserve_binding_mode_2024, ref_pat_eat_one_layer_2024)]
|
||||
#![allow(incomplete_features, unused)]
|
||||
#![deny(rust_2024_incompatible_pat)]
|
||||
|
||||
extern crate match_ergonomics_2024_macros;
|
||||
|
||||
struct Foo(u8);
|
||||
|
||||
fn main() {
|
||||
let &Foo(mut a) = &Foo(0);
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
a = 42;
|
||||
|
||||
let &mut Foo(mut a) = &mut Foo(0);
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
a = 42;
|
||||
|
||||
if let &&&&&Some(&_) = &&&&&Some(&0u8) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let &&&&&Some(&mut _) = &&&&&Some(&mut 0u8) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let &&&&&mut Some(&_) = &&&&&mut Some(&0u8) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let &mut Some(&mut Some(&mut Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let &mut Some(&mut Some(&mut Some(ref mut _a))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
struct Struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
c: u32,
|
||||
}
|
||||
let s = Struct { a: 0, b: 0, c: 0 };
|
||||
let &Struct { ref a, mut b, ref c } = &s;
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
#[warn(rust_2024_incompatible_pat)]
|
||||
match &(Some(0), Some(0)) {
|
||||
// The two patterns are the same syntactically, but because they're defined in different
|
||||
// editions they don't mean the same thing.
|
||||
(Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(_y)) => {
|
||||
//~^ WARN: the semantics of this pattern will change in edition 2024
|
||||
_x = 4;
|
||||
_y = &7;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
57
tests/ui/pattern/match_ergonomics_2024.rs
Normal file
57
tests/ui/pattern/match_ergonomics_2024.rs
Normal file
@ -0,0 +1,57 @@
|
||||
//@ edition: 2021
|
||||
//@ run-rustfix
|
||||
//@ rustfix-only-machine-applicable
|
||||
//@ aux-build:match_ergonomics_2024_macros.rs
|
||||
#![feature(mut_preserve_binding_mode_2024, ref_pat_eat_one_layer_2024)]
|
||||
#![allow(incomplete_features, unused)]
|
||||
#![deny(rust_2024_incompatible_pat)]
|
||||
|
||||
extern crate match_ergonomics_2024_macros;
|
||||
|
||||
struct Foo(u8);
|
||||
|
||||
fn main() {
|
||||
let Foo(mut a) = &Foo(0);
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
a = 42;
|
||||
|
||||
let Foo(mut a) = &mut Foo(0);
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
a = 42;
|
||||
|
||||
if let Some(&_) = &&&&&Some(&0u8) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let Some(&mut _) = &&&&&Some(&mut 0u8) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let Some(&_) = &&&&&mut Some(&0u8) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let Some(&mut Some(Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
if let Some(&mut Some(Some(_a))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
struct Struct {
|
||||
a: u32,
|
||||
b: u32,
|
||||
c: u32,
|
||||
}
|
||||
let s = Struct { a: 0, b: 0, c: 0 };
|
||||
let Struct { a, mut b, c } = &s;
|
||||
//~^ ERROR: the semantics of this pattern will change in edition 2024
|
||||
|
||||
#[warn(rust_2024_incompatible_pat)]
|
||||
match &(Some(0), Some(0)) {
|
||||
// The two patterns are the same syntactically, but because they're defined in different
|
||||
// editions they don't mean the same thing.
|
||||
(Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(_y)) => {
|
||||
//~^ WARN: the semantics of this pattern will change in edition 2024
|
||||
_x = 4;
|
||||
_y = &7;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
97
tests/ui/pattern/match_ergonomics_2024.stderr
Normal file
97
tests/ui/pattern/match_ergonomics_2024.stderr
Normal file
@ -0,0 +1,97 @@
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:14:9
|
||||
|
|
||||
LL | let Foo(mut a) = &Foo(0);
|
||||
| -^^^^^^^^^
|
||||
| |
|
||||
| help: desugar the match ergonomics: `&`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/match_ergonomics_2024.rs:7:9
|
||||
|
|
||||
LL | #![deny(rust_2024_incompatible_pat)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:18:9
|
||||
|
|
||||
LL | let Foo(mut a) = &mut Foo(0);
|
||||
| -^^^^^^^^^
|
||||
| |
|
||||
| help: desugar the match ergonomics: `&mut`
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:22:12
|
||||
|
|
||||
LL | if let Some(&_) = &&&&&Some(&0u8) {}
|
||||
| -^^^^^^^
|
||||
| |
|
||||
| help: desugar the match ergonomics: `&&&&&`
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:25:12
|
||||
|
|
||||
LL | if let Some(&mut _) = &&&&&Some(&mut 0u8) {}
|
||||
| -^^^^^^^^^^^
|
||||
| |
|
||||
| help: desugar the match ergonomics: `&&&&&`
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:28:12
|
||||
|
|
||||
LL | if let Some(&_) = &&&&&mut Some(&0u8) {}
|
||||
| -^^^^^^^
|
||||
| |
|
||||
| help: desugar the match ergonomics: `&&&&&mut`
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:31:12
|
||||
|
|
||||
LL | if let Some(&mut Some(Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: desugar the match ergonomics
|
||||
|
|
||||
LL | if let &mut Some(&mut Some(&mut Some(_))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
| ++++ ++++
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:34:12
|
||||
|
|
||||
LL | if let Some(&mut Some(Some(_a))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: desugar the match ergonomics
|
||||
|
|
||||
LL | if let &mut Some(&mut Some(&mut Some(ref mut _a))) = &mut Some(&mut Some(&mut Some(0u8))) {}
|
||||
| ++++ ++++ +++++++
|
||||
|
||||
error: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:43:9
|
||||
|
|
||||
LL | let Struct { a, mut b, c } = &s;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
help: desugar the match ergonomics
|
||||
|
|
||||
LL | let &Struct { ref a, mut b, ref c } = &s;
|
||||
| + +++ +++
|
||||
|
||||
warning: the semantics of this pattern will change in edition 2024
|
||||
--> $DIR/match_ergonomics_2024.rs:50:9
|
||||
|
|
||||
LL | (Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(_y)) => {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/match_ergonomics_2024.rs:46:12
|
||||
|
|
||||
LL | #[warn(rust_2024_incompatible_pat)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: desugar the match ergonomics
|
||||
|
|
||||
LL | &(Some(mut _x), match_ergonomics_2024_macros::mixed_edition_pat!(ref _y)) => {
|
||||
| + +++
|
||||
|
||||
error: aborting due to 8 previous errors; 1 warning emitted
|
||||
|
@ -1,16 +0,0 @@
|
||||
//@ edition: 2021
|
||||
#![feature(mut_preserve_binding_mode_2024)]
|
||||
#![allow(incomplete_features, unused)]
|
||||
#![forbid(dereferencing_mut_binding)]
|
||||
|
||||
struct Foo(u8);
|
||||
|
||||
fn main() {
|
||||
let Foo(mut a) = &Foo(0);
|
||||
//~^ ERROR: dereferencing `mut` binding
|
||||
a = 42;
|
||||
|
||||
let Foo(mut a) = &mut Foo(0);
|
||||
//~^ ERROR: dereferencing `mut` binding
|
||||
a = 42;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
error: dereferencing `mut` binding
|
||||
--> $DIR/mut_preserve_binding_mode_2024_lint.rs:9:13
|
||||
|
|
||||
LL | let Foo(mut a) = &Foo(0);
|
||||
| ^^^^^ `mut` dereferences the type of this binding
|
||||
|
|
||||
help: this will change in edition 2024
|
||||
--> $DIR/mut_preserve_binding_mode_2024_lint.rs:9:13
|
||||
|
|
||||
LL | let Foo(mut a) = &Foo(0);
|
||||
| ^^^^^
|
||||
note: the lint level is defined here
|
||||
--> $DIR/mut_preserve_binding_mode_2024_lint.rs:4:11
|
||||
|
|
||||
LL | #![forbid(dereferencing_mut_binding)]
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: dereferencing `mut` binding
|
||||
--> $DIR/mut_preserve_binding_mode_2024_lint.rs:13:13
|
||||
|
|
||||
LL | let Foo(mut a) = &mut Foo(0);
|
||||
| ^^^^^ `mut` dereferences the type of this binding
|
||||
|
|
||||
help: this will change in edition 2024
|
||||
--> $DIR/mut_preserve_binding_mode_2024_lint.rs:13:13
|
||||
|
|
||||
LL | let Foo(mut a) = &mut Foo(0);
|
||||
| ^^^^^
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
Loading…
Reference in New Issue
Block a user