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
17 changed files with 409 additions and 116 deletions
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue