1
Fork 0

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:
Jules Bertholet 2024-05-02 19:55:03 -04:00
parent 686bfc4c42
commit 9d92a7f355
No known key found for this signature in database
GPG key ID: 32034DAFC38C1BFC
17 changed files with 409 additions and 116 deletions

View file

@ -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" }

View file

@ -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

View file

@ -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);
}
}

View file

@ -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);