1
Fork 0

Rollup merge of #106097 - mejrs:mir_build2, r=oli-obk

Migrate mir_build diagnostics 2 of 3

The first three commits are fairly boring, however I've made some changes to the output of the match checking diagnostics.
This commit is contained in:
Michael Goulet 2023-01-11 22:25:47 -08:00 committed by GitHub
commit 83d3b76ac2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
39 changed files with 568 additions and 488 deletions

View file

@ -1,9 +1,13 @@
use crate::thir::pattern::deconstruct_pat::DeconstructedPat;
use crate::thir::pattern::MatchCheckCtxt;
use rustc_errors::Handler;
use rustc_errors::{
error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, MultiSpan,
error_code, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
IntoDiagnostic, MultiSpan, SubdiagnosticMessage,
};
use rustc_hir::def::Res;
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::thir::Pat;
use rustc_middle::ty::{self, Ty};
use rustc_span::{symbol::Ident, Span};
@ -624,3 +628,223 @@ pub enum MultipleMutBorrowOccurence {
name_moved: Ident,
},
}
#[derive(Diagnostic)]
#[diag(mir_build_union_pattern)]
pub struct UnionPattern {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(mir_build_type_not_structural)]
pub struct TypeNotStructural<'tcx> {
#[primary_span]
pub span: Span,
pub non_sm_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(mir_build_invalid_pattern)]
pub struct InvalidPattern<'tcx> {
#[primary_span]
pub span: Span,
pub non_sm_ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(mir_build_unsized_pattern)]
pub struct UnsizedPattern<'tcx> {
#[primary_span]
pub span: Span,
pub non_sm_ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_float_pattern)]
pub struct FloatPattern;
#[derive(LintDiagnostic)]
#[diag(mir_build_pointer_pattern)]
pub struct PointerPattern;
#[derive(LintDiagnostic)]
#[diag(mir_build_indirect_structural_match)]
pub struct IndirectStructuralMatch<'tcx> {
pub non_sm_ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_nontrivial_structural_match)]
pub struct NontrivialStructuralMatch<'tcx> {
pub non_sm_ty: Ty<'tcx>,
}
#[derive(LintDiagnostic)]
#[diag(mir_build_overlapping_range_endpoints)]
#[note]
pub struct OverlappingRangeEndpoints<'tcx> {
#[label(range)]
pub range: Span,
#[subdiagnostic]
pub overlap: Vec<Overlap<'tcx>>,
}
pub struct Overlap<'tcx> {
pub span: Span,
pub range: Pat<'tcx>,
}
impl<'tcx> AddToDiagnostic for Overlap<'tcx> {
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
where
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
{
let Overlap { span, range } = self;
// FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]`
// does not support `#[subdiagnostic(eager)]`...
let message = format!("this range overlaps on `{range}`...");
diag.span_label(span, message);
}
}
#[derive(LintDiagnostic)]
#[diag(mir_build_non_exhaustive_omitted_pattern)]
#[help]
#[note]
pub(crate) struct NonExhaustiveOmittedPattern<'tcx> {
pub scrut_ty: Ty<'tcx>,
#[subdiagnostic]
pub uncovered: Uncovered<'tcx>,
}
#[derive(Subdiagnostic)]
#[label(mir_build_uncovered)]
pub(crate) struct Uncovered<'tcx> {
#[primary_span]
span: Span,
count: usize,
witness_1: Pat<'tcx>,
witness_2: Pat<'tcx>,
witness_3: Pat<'tcx>,
remainder: usize,
}
impl<'tcx> Uncovered<'tcx> {
pub fn new<'p>(
span: Span,
cx: &MatchCheckCtxt<'p, 'tcx>,
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
) -> Self {
let witness_1 = witnesses.get(0).unwrap().to_pat(cx);
Self {
span,
count: witnesses.len(),
// Substitute dummy values if witnesses is smaller than 3. These will never be read.
witness_2: witnesses.get(1).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()),
witness_3: witnesses.get(2).map(|w| w.to_pat(cx)).unwrap_or_else(|| witness_1.clone()),
witness_1,
remainder: witnesses.len().saturating_sub(3),
}
}
}
#[derive(Diagnostic)]
#[diag(mir_build_pattern_not_covered, code = "E0005")]
pub(crate) struct PatternNotCovered<'s, 'tcx> {
#[primary_span]
pub span: Span,
pub origin: &'s str,
#[subdiagnostic]
pub uncovered: Uncovered<'tcx>,
#[subdiagnostic]
pub inform: Option<Inform>,
#[subdiagnostic]
pub interpreted_as_const: Option<InterpretedAsConst>,
#[subdiagnostic]
pub adt_defined_here: Option<AdtDefinedHere<'tcx>>,
#[note(pattern_ty)]
pub _p: (),
pub pattern_ty: Ty<'tcx>,
#[subdiagnostic]
pub let_suggestion: Option<SuggestLet>,
#[subdiagnostic]
pub res_defined_here: Option<ResDefinedHere>,
}
#[derive(Subdiagnostic)]
#[note(mir_build_inform_irrefutable)]
#[note(mir_build_more_information)]
pub struct Inform;
pub struct AdtDefinedHere<'tcx> {
pub adt_def_span: Span,
pub ty: Ty<'tcx>,
pub variants: Vec<Variant>,
}
pub struct Variant {
pub span: Span,
}
impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> {
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F)
where
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
{
diag.set_arg("ty", self.ty);
let mut spans = MultiSpan::from(self.adt_def_span);
for Variant { span } in self.variants {
spans.push_span_label(span, rustc_errors::fluent::mir_build_variant_defined_here);
}
diag.span_note(spans, rustc_errors::fluent::mir_build_adt_defined_here);
}
}
#[derive(Subdiagnostic)]
#[label(mir_build_res_defined_here)]
pub struct ResDefinedHere {
#[primary_span]
pub def_span: Span,
pub res: Res,
}
#[derive(Subdiagnostic)]
#[suggestion(
mir_build_interpreted_as_const,
code = "{variable}_var",
applicability = "maybe-incorrect"
)]
#[label(mir_build_confused)]
pub struct InterpretedAsConst {
#[primary_span]
pub span: Span,
pub article: &'static str,
pub variable: String,
pub res: Res,
}
#[derive(Subdiagnostic)]
pub enum SuggestLet {
#[multipart_suggestion(mir_build_suggest_if_let, applicability = "has-placeholders")]
If {
#[suggestion_part(code = "if ")]
start_span: Span,
#[suggestion_part(code = " {{ todo!() }}")]
semi_span: Span,
count: usize,
},
#[suggestion(
mir_build_suggest_let_else,
code = " else {{ todo!() }}",
applicability = "has-placeholders"
)]
Else {
#[primary_span]
end_span: Span,
count: usize,
},
}

View file

@ -10,6 +10,7 @@
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(once_cell)]
#![feature(try_blocks)]
#![recursion_limit = "256"]
#[macro_use]

View file

@ -9,8 +9,7 @@ use crate::errors::*;
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_errors::{
pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
MultiSpan,
struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan,
};
use rustc_hir as hir;
use rustc_hir::def::*;
@ -378,8 +377,8 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
let pattern = self.lower_pattern(&mut cx, pat, &mut false);
let pattern_ty = pattern.ty();
let arms = vec![MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false }];
let report = compute_match_usefulness(&cx, &arms, pat.hir_id, pattern_ty);
let arm = MatchArm { pat: pattern, hir_id: pat.hir_id, has_guard: false };
let report = compute_match_usefulness(&cx, &[arm], pat.hir_id, pattern_ty);
// Note: we ignore whether the pattern is unreachable (i.e. whether the type is empty). We
// only care about exhaustiveness here.
@ -390,145 +389,73 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> {
return;
}
let joined_patterns = joined_uncovered_patterns(&cx, &witnesses);
let mut bindings = vec![];
let mut err = struct_span_err!(
self.tcx.sess,
pat.span,
E0005,
"refutable pattern in {}: {} not covered",
origin,
joined_patterns
);
let suggest_if_let = match &pat.kind {
hir::PatKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].args.is_none() =>
let (inform, interpreted_as_const, res_defined_here,let_suggestion) =
if let hir::PatKind::Path(hir::QPath::Resolved(
None,
hir::Path {
segments: &[hir::PathSegment { args: None, res, ident, .. }],
..
},
)) = &pat.kind
{
const_not_var(&mut err, cx.tcx, pat, path);
false
}
_ => {
pat.walk(&mut |pat: &hir::Pat<'_>| {
match pat.kind {
hir::PatKind::Binding(_, _, ident, _) => {
bindings.push(ident);
(
None,
Some(InterpretedAsConst {
span: pat.span,
article: res.article(),
variable: ident.to_string().to_lowercase(),
res,
}),
try {
ResDefinedHere {
def_span: cx.tcx.hir().res_span(res)?,
res,
}
_ => {}
},
None,
)
} else if let Some(span) = sp && self.tcx.sess.source_map().is_span_accessible(span) {
let mut bindings = vec![];
pat.walk_always(&mut |pat: &hir::Pat<'_>| {
if let hir::PatKind::Binding(_, _, ident, _) = pat.kind {
bindings.push(ident);
}
true
});
err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns));
true
}
};
if let (Some(span), true) = (sp, suggest_if_let) {
err.note(
"`let` bindings require an \"irrefutable pattern\", like a `struct` or \
an `enum` with only one variant",
);
if self.tcx.sess.source_map().is_span_accessible(span) {
let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1));
let start_span = span.shrink_to_lo();
let end_span = semi_span.shrink_to_lo();
err.multipart_suggestion(
&format!(
"you might want to use `if let` to ignore the variant{} that {} matched",
pluralize!(witnesses.len()),
match witnesses.len() {
1 => "isn't",
_ => "aren't",
},
),
vec![
match &bindings[..] {
[] => (start_span, "if ".to_string()),
[binding] => (start_span, format!("let {} = if ", binding)),
bindings => (
start_span,
format!(
"let ({}) = if ",
bindings
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ")
),
),
},
match &bindings[..] {
[] => (semi_span, " { todo!() }".to_string()),
[binding] => {
(end_span, format!(" {{ {} }} else {{ todo!() }}", binding))
}
bindings => (
end_span,
format!(
" {{ ({}) }} else {{ todo!() }}",
bindings
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>()
.join(", ")
),
),
},
],
Applicability::HasPlaceholders,
);
if !bindings.is_empty() {
err.span_suggestion_verbose(
semi_span.shrink_to_lo(),
&format!(
"alternatively, you might want to use \
let else to handle the variant{} that {} matched",
pluralize!(witnesses.len()),
match witnesses.len() {
1 => "isn't",
_ => "aren't",
},
),
" else { todo!() }",
Applicability::HasPlaceholders,
);
}
let count = witnesses.len();
let let_suggestion = if bindings.is_empty() {SuggestLet::If{start_span, semi_span, count}} else{ SuggestLet::Else{end_span, count }};
(sp.map(|_|Inform), None, None, Some(let_suggestion))
} else{
(sp.map(|_|Inform), None, None, None)
};
let adt_defined_here = try {
let ty = pattern_ty.peel_refs();
let ty::Adt(def, _) = ty.kind() else { None? };
let adt_def_span = cx.tcx.hir().get_if_local(def.did())?.ident()?.span;
let mut variants = vec![];
for span in maybe_point_at_variant(&cx, *def, witnesses.iter().take(5)) {
variants.push(Variant { span });
}
err.note(
"for more information, visit \
https://doc.rust-lang.org/book/ch18-02-refutability.html",
);
}
AdtDefinedHere { adt_def_span, ty, variants }
};
adt_defined_here(&cx, &mut err, pattern_ty, &witnesses);
err.note(&format!("the matched value is of type `{}`", pattern_ty));
err.emit();
}
}
/// A path pattern was interpreted as a constant, not a new variable.
/// This caused an irrefutable match failure in e.g. `let`.
fn const_not_var(err: &mut Diagnostic, tcx: TyCtxt<'_>, pat: &Pat<'_>, path: &hir::Path<'_>) {
let descr = path.res.descr();
err.span_label(
pat.span,
format!("interpreted as {} {} pattern, not a new variable", path.res.article(), descr,),
);
err.span_suggestion(
pat.span,
"introduce a variable instead",
format!("{}_var", path.segments[0].ident).to_lowercase(),
// Cannot use `MachineApplicable` as it's not really *always* correct
// because there may be such an identifier in scope or the user maybe
// really wanted to match against the constant. This is quite unlikely however.
Applicability::MaybeIncorrect,
);
if let Some(span) = tcx.hir().res_span(path.res) {
err.span_label(span, format!("{} defined here", descr));
self.tcx.sess.emit_err(PatternNotCovered {
span: pat.span,
origin,
uncovered: Uncovered::new(pat.span, &cx, witnesses),
inform,
interpreted_as_const,
_p: (),
pattern_ty,
let_suggestion,
res_defined_here,
adt_defined_here,
});
}
}

View file

@ -1,11 +1,9 @@
use rustc_errors::DelayDm;
use rustc_hir as hir;
use rustc_index::vec::Idx;
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
use rustc_middle::mir::{self, Field};
use rustc_middle::thir::{FieldPat, Pat, PatKind};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_session::lint;
use rustc_span::Span;
use rustc_trait_selection::traits::predicate_for_trait_def;
@ -15,6 +13,10 @@ use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
use std::cell::Cell;
use super::PatCtxt;
use crate::errors::{
FloatPattern, IndirectStructuralMatch, InvalidPattern, NontrivialStructuralMatch,
PointerPattern, TypeNotStructural, UnionPattern, UnsizedPattern,
};
impl<'a, 'tcx> PatCtxt<'a, 'tcx> {
/// Converts an evaluated constant to a pattern (if possible).
@ -105,47 +107,6 @@ impl<'tcx> ConstToPat<'tcx> {
self.infcx.tcx
}
fn adt_derive_msg(&self, adt_def: AdtDef<'tcx>) -> String {
let path = self.tcx().def_path_str(adt_def.did());
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
)
}
fn search_for_structural_match_violation(&self, ty: Ty<'tcx>) -> Option<String> {
traits::search_for_structural_match_violation(self.span, self.tcx(), ty).map(|non_sm_ty| {
with_no_trimmed_paths!(match non_sm_ty.kind() {
ty::Adt(adt, _) => self.adt_derive_msg(*adt),
ty::Dynamic(..) => {
"trait objects cannot be used in patterns".to_string()
}
ty::Alias(ty::Opaque, ..) => {
"opaque types cannot be used in patterns".to_string()
}
ty::Closure(..) => {
"closures cannot be used in patterns".to_string()
}
ty::Generator(..) | ty::GeneratorWitness(..) => {
"generators cannot be used in patterns".to_string()
}
ty::Float(..) => {
"floating-point numbers cannot be used in patterns".to_string()
}
ty::FnPtr(..) => {
"function pointers cannot be used in patterns".to_string()
}
ty::RawPtr(..) => {
"raw pointers cannot be used in patterns".to_string()
}
_ => {
bug!("use of a value of `{non_sm_ty}` inside a pattern")
}
})
})
}
fn type_marked_structural(&self, ty: Ty<'tcx>) -> bool {
ty.is_structural_eq_shallow(self.infcx.tcx)
}
@ -176,7 +137,8 @@ impl<'tcx> ConstToPat<'tcx> {
// If we were able to successfully convert the const to some pat,
// double-check that all types in the const implement `Structural`.
let structural = self.search_for_structural_match_violation(cv.ty());
let structural =
traits::search_for_structural_match_violation(self.span, self.tcx(), cv.ty());
debug!(
"search_for_structural_match_violation cv.ty: {:?} returned: {:?}",
cv.ty(),
@ -194,17 +156,18 @@ impl<'tcx> ConstToPat<'tcx> {
return inlined_const_as_pat;
}
if let Some(msg) = structural {
if let Some(non_sm_ty) = structural {
if !self.type_may_have_partial_eq_impl(cv.ty()) {
// span_fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx().sess.span_fatal(self.span, &msg);
// fatal avoids ICE from resolution of non-existent method (rare case).
self.tcx()
.sess
.emit_fatal(TypeNotStructural { span: self.span, non_sm_ty: non_sm_ty });
} else if mir_structural_match_violation && !self.saw_const_match_lint.get() {
self.tcx().struct_span_lint_hir(
self.tcx().emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
msg,
|lint| lint,
IndirectStructuralMatch { non_sm_ty },
);
} else {
debug!(
@ -278,12 +241,11 @@ impl<'tcx> ConstToPat<'tcx> {
let kind = match cv.ty().kind() {
ty::Float(_) => {
if self.include_lint_checks {
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
lint::builtin::ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
id,
span,
"floating-point types cannot be used in patterns",
|lint| lint,
FloatPattern,
);
}
PatKind::Constant { value: cv }
@ -291,29 +253,22 @@ impl<'tcx> ConstToPat<'tcx> {
ty::Adt(adt_def, _) if adt_def.is_union() => {
// Matching on union fields is unsafe, we can't hide it in constants
self.saw_const_match_error.set(true);
let msg = "cannot use unions in constant patterns";
if self.include_lint_checks {
tcx.sess.span_err(span, msg);
} else {
tcx.sess.delay_span_bug(span, msg);
}
let err = UnionPattern { span };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
ty::Adt(..)
if !self.type_may_have_partial_eq_impl(cv.ty())
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
&& self.search_for_structural_match_violation(cv.ty()).is_some() =>
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty()) =>
{
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
let msg = self.search_for_structural_match_violation(cv.ty()).unwrap();
self.saw_const_match_error.set(true);
if self.include_lint_checks {
tcx.sess.span_err(self.span, &msg);
} else {
tcx.sess.delay_span_bug(self.span, &msg);
}
let err = TypeNotStructural { span, non_sm_ty };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
// If the type is not structurally comparable, just emit the constant directly,
@ -331,19 +286,11 @@ impl<'tcx> ConstToPat<'tcx> {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
id,
span,
DelayDm(|| {
format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
cv.ty(),
cv.ty(),
)
}),
|lint| lint,
IndirectStructuralMatch { non_sm_ty: cv.ty() },
);
}
// Since we are behind a reference, we can just bubble the error up so we get a
@ -357,18 +304,9 @@ impl<'tcx> ConstToPat<'tcx> {
adt_def,
cv.ty()
);
let path = tcx.def_path_str(adt_def.did());
let msg = format!(
"to use a constant of type `{}` in a pattern, \
`{}` must be annotated with `#[derive(PartialEq, Eq)]`",
path, path,
);
self.saw_const_match_error.set(true);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = TypeNotStructural { span, non_sm_ty: cv.ty() };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
ty::Adt(adt_def, substs) if adt_def.is_enum() => {
@ -401,12 +339,8 @@ impl<'tcx> ConstToPat<'tcx> {
// These are not allowed and will error elsewhere anyway.
ty::Dynamic(..) => {
self.saw_const_match_error.set(true);
let msg = format!("`{}` cannot be used in patterns", cv.ty());
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = InvalidPattern { span, non_sm_ty: cv.ty() };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
// `&str` is represented as `ConstValue::Slice`, let's keep using this
@ -471,32 +405,26 @@ impl<'tcx> ConstToPat<'tcx> {
// this pattern to a `PartialEq::eq` comparison and `PartialEq::eq` takes a
// reference. This makes the rest of the matching logic simpler as it doesn't have
// to figure out how to get a reference again.
ty::Adt(adt_def, _) if !self.type_marked_structural(*pointee_ty) => {
ty::Adt(_, _) if !self.type_marked_structural(*pointee_ty) => {
if self.behind_reference.get() {
if self.include_lint_checks
&& !self.saw_const_match_error.get()
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
let msg = self.adt_derive_msg(adt_def);
self.tcx().struct_span_lint_hir(
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::INDIRECT_STRUCTURAL_MATCH,
self.id,
self.span,
msg,
|lint| lint,
span,
IndirectStructuralMatch { non_sm_ty: *pointee_ty },
);
}
PatKind::Constant { value: cv }
} else {
if !self.saw_const_match_error.get() {
self.saw_const_match_error.set(true);
let msg = self.adt_derive_msg(adt_def);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = TypeNotStructural { span, non_sm_ty: *pointee_ty };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
}
PatKind::Wild
}
@ -508,12 +436,10 @@ impl<'tcx> ConstToPat<'tcx> {
if !pointee_ty.is_sized(tcx, param_env) {
// `tcx.deref_mir_constant()` below will ICE with an unsized type
// (except slices, which are handled in a separate arm above).
let msg = format!("cannot use unsized non-slice type `{}` in constant patterns", pointee_ty);
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = UnsizedPattern { span, non_sm_ty: *pointee_ty };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
} else {
let old = self.behind_reference.replace(true);
@ -545,27 +471,19 @@ impl<'tcx> ConstToPat<'tcx> {
&& !self.saw_const_match_lint.get()
{
self.saw_const_match_lint.set(true);
let msg = "function pointers and unsized pointers in patterns behave \
unpredictably and should not be relied upon. \
See https://github.com/rust-lang/rust/issues/70861 for details.";
tcx.struct_span_lint_hir(
tcx.emit_spanned_lint(
lint::builtin::POINTER_STRUCTURAL_MATCH,
id,
span,
msg,
|lint| lint,
PointerPattern
);
}
PatKind::Constant { value: cv }
}
_ => {
self.saw_const_match_error.set(true);
let msg = format!("`{}` cannot be used in patterns", cv.ty());
if self.include_lint_checks {
tcx.sess.span_err(span, &msg);
} else {
tcx.sess.delay_span_bug(span, &msg);
}
let err = InvalidPattern { span, non_sm_ty: cv.ty() };
tcx.sess.create_err(err).emit_unless(!self.include_lint_checks);
PatKind::Wild
}
};
@ -576,21 +494,17 @@ impl<'tcx> ConstToPat<'tcx> {
&& mir_structural_match_violation
// FIXME(#73448): Find a way to bring const qualification into parity with
// `search_for_structural_match_violation` and then remove this condition.
&& self.search_for_structural_match_violation(cv.ty()).is_some()
{
self.saw_const_match_lint.set(true);
// Obtain the actual type that isn't annotated. If we just looked at `cv.ty` we
// could get `Option<NonStructEq>`, even though `Option` is annotated with derive.
let msg = self.search_for_structural_match_violation(cv.ty()).unwrap().replace(
"in a pattern,",
"in a pattern, the constant's initializer must be trivial or",
);
tcx.struct_span_lint_hir(
&& let Some(non_sm_ty) = traits::search_for_structural_match_violation(span, tcx, cv.ty())
{
self.saw_const_match_lint.set(true);
tcx.emit_spanned_lint(
lint::builtin::NONTRIVIAL_STRUCTURAL_MATCH,
id,
span,
msg,
|lint| lint,
NontrivialStructuralMatch {non_sm_ty}
);
}

View file

@ -67,6 +67,7 @@ use self::SliceKind::*;
use super::compare_const_vals;
use super::usefulness::{MatchCheckCtxt, PatCtxt};
use crate::errors::{Overlap, OverlappingRangeEndpoints};
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
@ -96,7 +97,7 @@ fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
/// `IntRange` is never used to encode an empty range or a "range" that wraps
/// around the (offset) space: i.e., `range.lo <= range.hi`.
#[derive(Clone, PartialEq, Eq)]
pub(super) struct IntRange {
pub(crate) struct IntRange {
range: RangeInclusive<u128>,
/// Keeps the bias used for encoding the range. It depends on the type of the range and
/// possibly the pointer size of the current architecture. The algorithm ensures we never
@ -284,32 +285,21 @@ impl IntRange {
return;
}
let overlaps: Vec<_> = pats
let overlap: Vec<_> = pats
.filter_map(|pat| Some((pat.ctor().as_int_range()?, pat.span())))
.filter(|(range, _)| self.suspicious_intersection(range))
.map(|(range, span)| (self.intersection(&range).unwrap(), span))
.map(|(range, span)| Overlap {
range: self.intersection(&range).unwrap().to_pat(pcx.cx.tcx, pcx.ty),
span,
})
.collect();
if !overlaps.is_empty() {
pcx.cx.tcx.struct_span_lint_hir(
if !overlap.is_empty() {
pcx.cx.tcx.emit_spanned_lint(
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
hir_id,
pcx.span,
"multiple patterns overlap on their endpoints",
|lint| {
for (int_range, span) in overlaps {
lint.span_label(
span,
&format!(
"this range overlaps on `{}`...",
int_range.to_pat(pcx.cx.tcx, pcx.ty)
),
);
}
lint.span_label(pcx.span, "... with this range");
lint.note("you likely meant to write mutually exclusive ranges");
lint
},
OverlappingRangeEndpoints { overlap, range: pcx.span },
);
}
}

View file

@ -2,7 +2,7 @@
mod check_match;
mod const_to_pat;
mod deconstruct_pat;
pub(crate) mod deconstruct_pat;
mod usefulness;
pub(crate) use self::check_match::check_match;

View file

@ -291,9 +291,8 @@
use self::ArmType::*;
use self::Usefulness::*;
use super::check_match::{joined_uncovered_patterns, pattern_not_covered_label};
use super::deconstruct_pat::{Constructor, DeconstructedPat, Fields, SplitWildcard};
use crate::errors::{NonExhaustiveOmittedPattern, Uncovered};
use rustc_data_structures::captures::Captures;
@ -743,31 +742,6 @@ impl<'p, 'tcx> Witness<'p, 'tcx> {
}
}
/// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
/// is not exhaustive enough.
///
/// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
fn lint_non_exhaustive_omitted_patterns<'p, 'tcx>(
cx: &MatchCheckCtxt<'p, 'tcx>,
scrut_ty: Ty<'tcx>,
sp: Span,
hir_id: HirId,
witnesses: Vec<DeconstructedPat<'p, 'tcx>>,
) {
cx.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, hir_id, sp, "some variants are not matched explicitly", |lint| {
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
lint.span_label(sp, pattern_not_covered_label(&witnesses, &joined_patterns));
lint.help(
"ensure that all variants are matched explicitly by adding the suggested match arms",
);
lint.note(&format!(
"the matched value is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found",
scrut_ty,
));
lint
});
}
/// Algorithm from <http://moscova.inria.fr/~maranget/papers/warn/index.html>.
/// The algorithm from the paper has been modified to correctly handle empty
/// types. The changes are:
@ -913,7 +887,19 @@ fn is_useful<'p, 'tcx>(
.collect::<Vec<_>>()
};
lint_non_exhaustive_omitted_patterns(pcx.cx, pcx.ty, pcx.span, hir_id, patterns);
// Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns`
// is not exhaustive enough.
//
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
cx.tcx.emit_spanned_lint(
NON_EXHAUSTIVE_OMITTED_PATTERNS,
hir_id,
pcx.span,
NonExhaustiveOmittedPattern {
scrut_ty: pcx.ty,
uncovered: Uncovered::new(pcx.span, pcx.cx, patterns),
},
);
}
ret.extend(usefulness);