Migrate "non-exhaustive patterns: type is non-empty" diagnostic

This commit is contained in:
TheOddGarlic 2022-08-26 16:38:21 +03:00 committed by mejrs
parent 71a9cb9b7e
commit c7bfd00719
4 changed files with 113 additions and 7 deletions

View file

@ -175,3 +175,11 @@ mir_build_unused_unsafe = unnecessary `unsafe` block
mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
.def_note = `{$peeled_ty}` defined here
.type_note = the matched value is of type `{$ty}`
.non_exhaustive_type_note = the matched value is of type `{$ty}`, which is marked as non-exhaustive
.reference_note = references are always considered inhabited
.suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
.help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern

View file

@ -1,4 +1,8 @@
use crate::thir::pattern::MatchCheckCtxt;
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
use rustc_middle::ty::{self, Ty};
use rustc_session::{parse::ParseSess, SessionDiagnostic};
use rustc_span::Span;
#[derive(LintDiagnostic)]
@ -336,3 +340,92 @@ pub enum UnusedUnsafeEnclosing {
span: Span,
},
}
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
pub cx: &'m MatchCheckCtxt<'p, 'tcx>,
pub expr_span: Span,
pub span: Span,
pub ty: Ty<'tcx>,
}
impl<'a> SessionDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
let mut diag = sess.span_diagnostic.struct_span_err_with_code(
self.span,
rustc_errors::fluent::mir_build::non_exhaustive_patterns_type_not_empty,
error_code!(E0004),
);
let peeled_ty = self.ty.peel_refs();
diag.set_arg("ty", self.ty);
diag.set_arg("peeled_ty", peeled_ty);
if let ty::Adt(def, _) = peeled_ty.kind() {
let def_span = self
.cx
.tcx
.hir()
.get_if_local(def.did())
.and_then(|node| node.ident())
.map(|ident| ident.span)
.unwrap_or_else(|| self.cx.tcx.def_span(def.did()));
// workaround to make test pass
let mut span: MultiSpan = def_span.into();
span.push_span_label(def_span, "");
diag.span_note(span, rustc_errors::fluent::mir_build::def_note);
}
let is_variant_list_non_exhaustive = match self.ty.kind() {
ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => {
true
}
_ => false,
};
if is_variant_list_non_exhaustive {
diag.note(rustc_errors::fluent::mir_build::non_exhaustive_type_note);
} else {
diag.note(rustc_errors::fluent::mir_build::type_note);
}
if let ty::Ref(_, sub_ty, _) = self.ty.kind() {
if self.cx.tcx.is_ty_uninhabited_from(self.cx.module, *sub_ty, self.cx.param_env) {
diag.note(rustc_errors::fluent::mir_build::reference_note);
}
}
let mut suggestion = None;
let sm = self.cx.tcx.sess.source_map();
if self.span.eq_ctxt(self.expr_span) {
// Get the span for the empty match body `{}`.
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
(format!("\n{}", snippet), " ")
} else {
(" ".to_string(), "")
};
suggestion = Some((
self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
format!(
" {{{indentation}{more}_ => todo!(),{indentation}}}",
indentation = indentation,
more = more,
),
));
}
if let Some((span, sugg)) = suggestion {
diag.span_suggestion_verbose(
span,
rustc_errors::fluent::mir_build::suggestion,
sugg,
Applicability::HasPlaceholders,
);
} else {
diag.help(rustc_errors::fluent::mir_build::help);
}
diag
}
}

View file

@ -4,6 +4,8 @@ use super::usefulness::{
};
use super::{PatCtxt, PatternError};
use crate::errors::NonExhaustivePatternsTypeNotEmpty;
use rustc_arena::TypedArena;
use rustc_ast::Mutability;
use rustc_errors::{
@ -760,15 +762,17 @@ fn non_exhaustive_match<'p, 'tcx>(
// informative.
let mut err;
let pattern;
let mut patterns_len = 0;
let patterns_len;
if is_empty_match && !non_empty_enum {
err = create_e0004(
cx.tcx.sess,
sp,
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
);
pattern = "_".to_string();
cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
cx,
expr_span,
span: sp,
ty: scrut_ty,
});
return;
} else {
// FIXME: migration of this diagnostic will require list support
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
err = create_e0004(
cx.tcx.sess,

View file

@ -6,6 +6,7 @@ mod deconstruct_pat;
mod usefulness;
pub(crate) use self::check_match::check_match;
pub(crate) use self::usefulness::MatchCheckCtxt;
use crate::thir::util::UserAnnotatedTyHelpers;