From 4c0ff4db80d2c6cb4dc204f65f34ce2246e27bfe Mon Sep 17 00:00:00 2001
From: Maarten de Vries <maarten@de-vri.es>
Date: Thu, 15 Jul 2021 21:25:11 +0200
Subject: [PATCH] Show discriminant before overflow in diagnostic.

---
 compiler/rustc_typeck/src/check/check.rs      | 27 ++++++++++++++++---
 src/test/ui/enum/enum-discrim-autosizing.rs   |  6 +++--
 .../ui/enum/enum-discrim-autosizing.stderr    |  2 +-
 src/test/ui/error-codes/E0081.rs              | 11 ++++++++
 src/test/ui/error-codes/E0081.stderr          | 14 ++++++++--
 5 files changed, 52 insertions(+), 8 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs
index b5db3331d0447..9b555df4b92de 100644
--- a/compiler/rustc_typeck/src/check/check.rs
+++ b/compiler/rustc_typeck/src/check/check.rs
@@ -1472,15 +1472,17 @@ fn check_enum<'tcx>(
                 Some(ref expr) => tcx.hir().span(expr.hir_id),
                 None => v.span,
             };
+            let display_discr = display_discriminant_value(tcx, v, discr.val);
+            let display_discr_i = display_discriminant_value(tcx, variant_i, disr_vals[i].val);
             struct_span_err!(
                 tcx.sess,
                 span,
                 E0081,
                 "discriminant value `{}` already exists",
-                disr_vals[i]
+                discr.val,
             )
-            .span_label(i_span, format!("first use of `{}`", disr_vals[i]))
-            .span_label(span, format!("enum already has `{}`", disr_vals[i]))
+            .span_label(i_span, format!("first use of {}", display_discr_i))
+            .span_label(span, format!("enum already has {}", display_discr))
             .emit();
         }
         disr_vals.push(discr);
@@ -1490,6 +1492,25 @@ fn check_enum<'tcx>(
     check_transparent(tcx, sp, def);
 }
 
+/// Format an enum discriminant value for use in a diagnostic message.
+fn display_discriminant_value<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    variant: &hir::Variant<'_>,
+    evaluated: u128,
+) -> String {
+    if let Some(expr) = &variant.disr_expr {
+        let body = &tcx.hir().body(expr.body).value;
+        if let hir::ExprKind::Lit(lit) = &body.kind {
+            if let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node {
+                if evaluated != *lit_value {
+                    return format!("`{}` (overflowed from `{}`)", evaluated, lit_value);
+                }
+            }
+        }
+    }
+    format!("`{}`", evaluated)
+}
+
 pub(super) fn check_type_params_are_used<'tcx>(
     tcx: TyCtxt<'tcx>,
     generics: &ty::Generics,
diff --git a/src/test/ui/enum/enum-discrim-autosizing.rs b/src/test/ui/enum/enum-discrim-autosizing.rs
index e9bb927597100..473a0ad6f7a13 100644
--- a/src/test/ui/enum/enum-discrim-autosizing.rs
+++ b/src/test/ui/enum/enum-discrim-autosizing.rs
@@ -4,8 +4,10 @@
 // so force the repr.
 #[cfg_attr(not(target_pointer_width = "32"), repr(i32))]
 enum Eu64 {
-    Au64 = 0,
-    Bu64 = 0x8000_0000_0000_0000 //~ERROR already exists
+    Au64 = 0, //~NOTE first use of `0`
+    Bu64 = 0x8000_0000_0000_0000
+    //~^ ERROR discriminant value `0` already exists
+    //~| NOTE enum already has `0` (overflowed from `9223372036854775808`)
 }
 
 fn main() {}
diff --git a/src/test/ui/enum/enum-discrim-autosizing.stderr b/src/test/ui/enum/enum-discrim-autosizing.stderr
index 8848f984cfb7d..a0f39098a2e98 100644
--- a/src/test/ui/enum/enum-discrim-autosizing.stderr
+++ b/src/test/ui/enum/enum-discrim-autosizing.stderr
@@ -4,7 +4,7 @@ error[E0081]: discriminant value `0` already exists
 LL |     Au64 = 0,
    |            - first use of `0`
 LL |     Bu64 = 0x8000_0000_0000_0000
-   |            ^^^^^^^^^^^^^^^^^^^^^ enum already has `0`
+   |            ^^^^^^^^^^^^^^^^^^^^^ enum already has `0` (overflowed from `9223372036854775808`)
 
 error: aborting due to previous error
 
diff --git a/src/test/ui/error-codes/E0081.rs b/src/test/ui/error-codes/E0081.rs
index 33c8c14306bd1..255e05ced19f7 100644
--- a/src/test/ui/error-codes/E0081.rs
+++ b/src/test/ui/error-codes/E0081.rs
@@ -1,9 +1,20 @@
 enum Enum {
     P = 3,
+    //~^ NOTE first use of `3`
     X = 3,
     //~^ ERROR discriminant value `3` already exists
+    //~| NOTE enum already has `3`
     Y = 5
 }
 
+#[repr(u8)]
+enum EnumOverflowRepr {
+    P = 257,
+    //~^ NOTE first use of `1` (overflowed from `257`)
+    X = 513,
+    //~^ ERROR discriminant value `1` already exists
+    //~| NOTE enum already has `1` (overflowed from `513`)
+}
+
 fn main() {
 }
diff --git a/src/test/ui/error-codes/E0081.stderr b/src/test/ui/error-codes/E0081.stderr
index 0b3d351e1ac9a..9b279bb0214c6 100644
--- a/src/test/ui/error-codes/E0081.stderr
+++ b/src/test/ui/error-codes/E0081.stderr
@@ -1,11 +1,21 @@
 error[E0081]: discriminant value `3` already exists
-  --> $DIR/E0081.rs:3:9
+  --> $DIR/E0081.rs:4:9
    |
 LL |     P = 3,
    |         - first use of `3`
+LL |
 LL |     X = 3,
    |         ^ enum already has `3`
 
-error: aborting due to previous error
+error[E0081]: discriminant value `1` already exists
+  --> $DIR/E0081.rs:14:9
+   |
+LL |     P = 257,
+   |         --- first use of `1` (overflowed from `257`)
+LL |
+LL |     X = 513,
+   |         ^^^ enum already has `1` (overflowed from `513`)
+
+error: aborting due to 2 previous errors
 
 For more information about this error, try `rustc --explain E0081`.