about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTheOddGarlic <umutinanerdogan@pm.me>2022-08-26 16:38:21 +0300
committermejrs <>2022-12-17 19:08:24 +0100
commitc7bfd007194a0f4062a8809996b60f2700dd4652 (patch)
tree213451b2fa2b392ddd0621fbc4a9baa24ff71e6f
parent71a9cb9b7ebc1a4d56f8d837ea8b4b696fae9e89 (diff)
downloadrust-c7bfd007194a0f4062a8809996b60f2700dd4652.tar.gz
rust-c7bfd007194a0f4062a8809996b60f2700dd4652.zip
Migrate "non-exhaustive patterns: type is non-empty" diagnostic
-rw-r--r--compiler/rustc_error_messages/locales/en-US/mir_build.ftl8
-rw-r--r--compiler/rustc_mir_build/src/errors.rs93
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/check_match.rs18
-rw-r--r--compiler/rustc_mir_build/src/thir/pattern/mod.rs1
4 files changed, 113 insertions, 7 deletions
diff --git a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
index 9dab6f8e883..5fb89dcccae 100644
--- a/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
+++ b/compiler/rustc_error_messages/locales/en-US/mir_build.ftl
@@ -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
diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs
index a3e87151226..9e63141d716 100644
--- a/compiler/rustc_mir_build/src/errors.rs
+++ b/compiler/rustc_mir_build/src/errors.rs
@@ -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
+    }
+}
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index e369dba5524..c818a2a3a89 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -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,
diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
index 48a231a6cd6..5aaf6c907e3 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs
@@ -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;