Skip to content

Commit 59c0b8d

Browse files
committed
Migrate "non-exhaustive patterns: type is non-empty" diagnostic
1 parent a0777d3 commit 59c0b8d

File tree

4 files changed

+113
-7
lines changed

4 files changed

+113
-7
lines changed

compiler/rustc_error_messages/locales/en-US/mir_build.ftl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,11 @@ mir_build_unused_unsafe = unnecessary `unsafe` block
175175
176176
mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `unsafe` block
177177
mir_build_unused_unsafe_enclosing_fn_label = because it's nested under this `unsafe` fn
178+
179+
mir_build_non_exhaustive_patterns_type_not_empty = non-exhaustive patterns: type `{$ty}` is non-empty
180+
.def_note = `{$peeled_ty}` defined here
181+
.type_note = the matched value is of type `{$ty}`
182+
.non_exhaustive_type_note = the matched value is of type `{$ty}`, which is marked as non-exhaustive
183+
.reference_note = references are always considered inhabited
184+
.suggestion = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern as shown
185+
.help = ensure that all possible cases are being handled by adding a match arm with a wildcard pattern

compiler/rustc_mir_build/src/errors.rs

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
use crate::thir::pattern::MatchCheckCtxt;
2+
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, MultiSpan};
13
use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic};
4+
use rustc_middle::ty::{self, Ty};
5+
use rustc_session::{parse::ParseSess, SessionDiagnostic};
26
use rustc_span::Span;
37

48
#[derive(LintDiagnostic)]
@@ -336,3 +340,92 @@ pub enum UnusedUnsafeEnclosing {
336340
span: Span,
337341
},
338342
}
343+
344+
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
345+
pub cx: &'m MatchCheckCtxt<'p, 'tcx>,
346+
pub expr_span: Span,
347+
pub span: Span,
348+
pub ty: Ty<'tcx>,
349+
}
350+
351+
impl<'a> SessionDiagnostic<'a> for NonExhaustivePatternsTypeNotEmpty<'_, '_, '_> {
352+
fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
353+
let mut diag = sess.span_diagnostic.struct_span_err_with_code(
354+
self.span,
355+
rustc_errors::fluent::mir_build::non_exhaustive_patterns_type_not_empty,
356+
error_code!(E0004),
357+
);
358+
359+
let peeled_ty = self.ty.peel_refs();
360+
diag.set_arg("ty", self.ty);
361+
diag.set_arg("peeled_ty", peeled_ty);
362+
363+
if let ty::Adt(def, _) = peeled_ty.kind() {
364+
let def_span = self
365+
.cx
366+
.tcx
367+
.hir()
368+
.get_if_local(def.did())
369+
.and_then(|node| node.ident())
370+
.map(|ident| ident.span)
371+
.unwrap_or_else(|| self.cx.tcx.def_span(def.did()));
372+
373+
// workaround to make test pass
374+
let mut span: MultiSpan = def_span.into();
375+
span.push_span_label(def_span, "");
376+
377+
diag.span_note(span, rustc_errors::fluent::mir_build::def_note);
378+
}
379+
380+
let is_variant_list_non_exhaustive = match self.ty.kind() {
381+
ty::Adt(def, _) if def.is_variant_list_non_exhaustive() && !def.did().is_local() => {
382+
true
383+
}
384+
_ => false,
385+
};
386+
387+
if is_variant_list_non_exhaustive {
388+
diag.note(rustc_errors::fluent::mir_build::non_exhaustive_type_note);
389+
} else {
390+
diag.note(rustc_errors::fluent::mir_build::type_note);
391+
}
392+
393+
if let ty::Ref(_, sub_ty, _) = self.ty.kind() {
394+
if self.cx.tcx.is_ty_uninhabited_from(self.cx.module, *sub_ty, self.cx.param_env) {
395+
diag.note(rustc_errors::fluent::mir_build::reference_note);
396+
}
397+
}
398+
399+
let mut suggestion = None;
400+
let sm = self.cx.tcx.sess.source_map();
401+
if self.span.eq_ctxt(self.expr_span) {
402+
// Get the span for the empty match body `{}`.
403+
let (indentation, more) = if let Some(snippet) = sm.indentation_before(self.span) {
404+
(format!("\n{}", snippet), " ")
405+
} else {
406+
(" ".to_string(), "")
407+
};
408+
suggestion = Some((
409+
self.span.shrink_to_hi().with_hi(self.expr_span.hi()),
410+
format!(
411+
" {{{indentation}{more}_ => todo!(),{indentation}}}",
412+
indentation = indentation,
413+
more = more,
414+
),
415+
));
416+
}
417+
418+
if let Some((span, sugg)) = suggestion {
419+
diag.span_suggestion_verbose(
420+
span,
421+
rustc_errors::fluent::mir_build::suggestion,
422+
sugg,
423+
Applicability::HasPlaceholders,
424+
);
425+
} else {
426+
diag.help(rustc_errors::fluent::mir_build::help);
427+
}
428+
429+
diag
430+
}
431+
}

compiler/rustc_mir_build/src/thir/pattern/check_match.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use super::usefulness::{
44
};
55
use super::{PatCtxt, PatternError};
66

7+
use crate::errors::NonExhaustivePatternsTypeNotEmpty;
8+
79
use rustc_arena::TypedArena;
810
use rustc_ast::Mutability;
911
use rustc_errors::{
@@ -743,15 +745,17 @@ fn non_exhaustive_match<'p, 'tcx>(
743745
// informative.
744746
let mut err;
745747
let pattern;
746-
let mut patterns_len = 0;
748+
let patterns_len;
747749
if is_empty_match && !non_empty_enum {
748-
err = create_e0004(
749-
cx.tcx.sess,
750-
sp,
751-
format!("non-exhaustive patterns: type `{}` is non-empty", scrut_ty),
752-
);
753-
pattern = "_".to_string();
750+
cx.tcx.sess.emit_err(NonExhaustivePatternsTypeNotEmpty {
751+
cx,
752+
expr_span,
753+
span: sp,
754+
ty: scrut_ty,
755+
});
756+
return;
754757
} else {
758+
// FIXME: migration of this diagnostic will require list support
755759
let joined_patterns = joined_uncovered_patterns(cx, &witnesses);
756760
err = create_e0004(
757761
cx.tcx.sess,

compiler/rustc_mir_build/src/thir/pattern/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ mod deconstruct_pat;
66
mod usefulness;
77

88
pub(crate) use self::check_match::check_match;
9+
pub(crate) use self::usefulness::MatchCheckCtxt;
910

1011
use crate::thir::util::UserAnnotatedTyHelpers;
1112

0 commit comments

Comments
 (0)