Skip to content

Commit 4ee2fe3

Browse files
committed
Further improve error message for E0081
Multiple duplicate assignments of the same discriminant are now reported in the samme error. We now point out the incrementation start point for discriminants that are not explicitly assigned that are also duplicates. Removed old test related to E0081 that is now covered by error-codes/E0081.rs. Also refactored parts of the `check_enum` function.
1 parent 5651759 commit 4ee2fe3

File tree

9 files changed

+142
-141
lines changed

9 files changed

+142
-141
lines changed

compiler/rustc_typeck/src/check/check.rs

+82-63
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
3232
use rustc_trait_selection::traits::{self, ObligationCtxt};
3333
use rustc_ty_utils::representability::{self, Representability};
3434

35-
use std::iter;
3635
use std::ops::ControlFlow;
3736

3837
pub(super) fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
@@ -1494,76 +1493,96 @@ fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: L
14941493
}
14951494
}
14961495

1497-
let mut disr_vals: Vec<Discr<'tcx>> = Vec::with_capacity(vs.len());
1498-
// This tracks the previous variant span (in the loop) incase we need it for diagnostics
1499-
let mut prev_variant_span: Span = DUMMY_SP;
1500-
for ((_, discr), v) in iter::zip(def.discriminants(tcx), vs) {
1501-
// Check for duplicate discriminant values
1502-
if let Some(i) = disr_vals.iter().position(|&x| x.val == discr.val) {
1503-
let variant_did = def.variant(VariantIdx::new(i)).def_id;
1504-
let variant_i_hir_id = tcx.hir().local_def_id_to_hir_id(variant_did.expect_local());
1505-
let variant_i = tcx.hir().expect_variant(variant_i_hir_id);
1506-
let i_span = match variant_i.disr_expr {
1507-
Some(ref expr) => tcx.hir().span(expr.hir_id),
1508-
None => tcx.def_span(variant_did),
1509-
};
1510-
let span = match v.disr_expr {
1511-
Some(ref expr) => tcx.hir().span(expr.hir_id),
1512-
None => v.span,
1513-
};
1514-
let display_discr = format_discriminant_overflow(tcx, v, discr);
1515-
let display_discr_i = format_discriminant_overflow(tcx, variant_i, disr_vals[i]);
1516-
let no_disr = v.disr_expr.is_none();
1517-
let mut err = struct_span_err!(
1518-
tcx.sess,
1519-
sp,
1520-
E0081,
1521-
"discriminant value `{}` assigned more than once",
1522-
discr,
1523-
);
1524-
1525-
err.span_label(i_span, format!("first assignment of {display_discr_i}"));
1526-
err.span_label(span, format!("second assignment of {display_discr}"));
1527-
1528-
if no_disr {
1529-
err.span_label(
1530-
prev_variant_span,
1531-
format!(
1532-
"assigned discriminant for `{}` was incremented from this discriminant",
1533-
v.ident
1534-
),
1535-
);
1536-
}
1537-
err.emit();
1538-
}
1539-
1540-
disr_vals.push(discr);
1541-
prev_variant_span = v.span;
1542-
}
1496+
detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
15431497

15441498
check_representable(tcx, sp, def_id);
15451499
check_transparent(tcx, sp, def);
15461500
}
15471501

1548-
/// In the case that a discriminant is both a duplicate and an overflowing literal,
1549-
/// we insert both the assigned discriminant and the literal it overflowed from into the formatted
1550-
/// output. Otherwise we format the discriminant normally.
1551-
fn format_discriminant_overflow<'tcx>(
1502+
fn detect_discriminant_duplicate<'tcx>(
15521503
tcx: TyCtxt<'tcx>,
1553-
variant: &hir::Variant<'_>,
1554-
dis: Discr<'tcx>,
1555-
) -> String {
1556-
if let Some(expr) = &variant.disr_expr {
1557-
let body = &tcx.hir().body(expr.body).value;
1558-
if let hir::ExprKind::Lit(lit) = &body.kind
1559-
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
1560-
&& dis.val != *lit_value
1561-
{
1562-
return format!("`{dis}` (overflowed from `{lit_value}`)");
1504+
mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
1505+
vs: &'tcx [hir::Variant<'tcx>],
1506+
self_span: Span,
1507+
) {
1508+
let report = |var: &hir::Variant<'_>,
1509+
dis: Discr<'tcx>,
1510+
idx: usize,
1511+
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>| {
1512+
let (span, display_discr) = match var.disr_expr {
1513+
Some(ref expr) => {
1514+
// In the case the discriminant is both a duplicate and overflowed, let the user know
1515+
if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind
1516+
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
1517+
&& *lit_value != dis.val
1518+
{
1519+
(tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
1520+
} else {
1521+
(tcx.hir().span(expr.hir_id), format!("`{dis}`"))
1522+
}
1523+
}
1524+
None => {
1525+
if let Some((n, hir::Variant { span, ident, .. })) =
1526+
vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
1527+
{
1528+
let ve_ident = var.ident;
1529+
let sp = if n > 1 { "variants" } else { "variant" };
1530+
let n = n + 1;
1531+
1532+
err.span_label(
1533+
*span,
1534+
format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"),
1535+
);
1536+
}
1537+
1538+
(vs[idx].span, format!("`{dis}`"))
1539+
}
1540+
};
1541+
1542+
err.span_label(span, format!("{display_discr} assigned here"));
1543+
};
1544+
1545+
let mut i = 0;
1546+
while i < discrs.len() {
1547+
let hir_var_i_idx = discrs[i].0.index();
1548+
let hir_var_i = &vs[hir_var_i_idx];
1549+
let mut error: Option<DiagnosticBuilder<'_, _>> = None;
1550+
1551+
let mut o = i + 1;
1552+
while o < discrs.len() {
1553+
let hir_var_o_idx = discrs[o].0.index();
1554+
let hir_var_o = &vs[hir_var_o_idx];
1555+
1556+
if discrs[i].1.val == discrs[o].1.val {
1557+
let err = error.get_or_insert_with(|| {
1558+
let mut ret = struct_span_err!(
1559+
tcx.sess,
1560+
self_span,
1561+
E0081,
1562+
"discriminant value `{}` assigned more than once",
1563+
discrs[i].1,
1564+
);
1565+
1566+
report(hir_var_i, discrs[i].1, hir_var_i_idx, &mut ret);
1567+
1568+
ret
1569+
});
1570+
1571+
report(hir_var_o, discrs[o].1, hir_var_o_idx, err);
1572+
1573+
discrs[o] = *discrs.last().unwrap();
1574+
discrs.pop();
1575+
} else {
1576+
o += 1;
1577+
}
15631578
}
1564-
}
15651579

1566-
format!("`{dis}`")
1580+
if let Some(mut e) = error {
1581+
e.emit();
1582+
}
1583+
1584+
i += 1;
1585+
}
15671586
}
15681587

15691588
pub(super) fn check_type_params_are_used<'tcx>(

compiler/rustc_typeck/src/check/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ use rustc_hir::def_id::{DefId, LocalDefId};
112112
use rustc_hir::intravisit::Visitor;
113113
use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
114114
use rustc_index::bit_set::BitSet;
115-
use rustc_index::vec::Idx;
116115
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
117116
use rustc_middle::ty::query::Providers;
118117
use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef};

src/test/ui/enum/enum-discrim-autosizing.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
enum Eu64 {
77
//~^ ERROR discriminant value `0` assigned more than once
88
Au64 = 0,
9-
//~^NOTE first assignment of `0`
9+
//~^NOTE `0` assigned here
1010
Bu64 = 0x8000_0000_0000_0000
11-
//~^NOTE second assignment of `0` (overflowed from `9223372036854775808`)
11+
//~^NOTE `0` (overflowed from `9223372036854775808`) assigned here
1212
}
1313

1414
fn main() {}

src/test/ui/enum/enum-discrim-autosizing.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ LL | enum Eu64 {
55
| ^^^^^^^^^
66
LL |
77
LL | Au64 = 0,
8-
| - first assignment of `0`
8+
| - `0` assigned here
99
LL |
1010
LL | Bu64 = 0x8000_0000_0000_0000
11-
| --------------------- second assignment of `0` (overflowed from `9223372036854775808`)
11+
| --------------------- `0` (overflowed from `9223372036854775808`) assigned here
1212

1313
error: aborting due to previous error
1414

src/test/ui/error-codes/E0081.rs

+25-7
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,48 @@
11
enum Enum {
22
//~^ ERROR discriminant value `3` assigned more than once
33
P = 3,
4-
//~^ NOTE first assignment of `3`
4+
//~^ NOTE `3` assigned here
55
X = 3,
6-
//~^ NOTE second assignment of `3`
6+
//~^ NOTE `3` assigned here
77
Y = 5
88
}
99

1010
#[repr(u8)]
1111
enum EnumOverflowRepr {
1212
//~^ ERROR discriminant value `1` assigned more than once
1313
P = 257,
14-
//~^ NOTE first assignment of `1` (overflowed from `257`)
14+
//~^ NOTE `1` (overflowed from `257`) assigned here
1515
X = 513,
16-
//~^ NOTE second assignment of `1` (overflowed from `513`)
16+
//~^ NOTE `1` (overflowed from `513`) assigned here
1717
}
1818

1919
#[repr(i8)]
2020
enum NegDisEnum {
2121
//~^ ERROR discriminant value `-1` assigned more than once
2222
First = -1,
23-
//~^ NOTE first assignment of `-1`
23+
//~^ NOTE `-1` assigned here
2424
Second = -2,
25-
//~^ NOTE assigned discriminant for `Last` was incremented from this discriminant
25+
//~^ NOTE discriminant for `Last` incremented from this startpoint (`Second` + 1 variant later => `Last` = -1)
2626
Last,
27-
//~^ NOTE second assignment of `-1`
27+
//~^ NOTE `-1` assigned here
28+
}
29+
30+
#[repr(i32)]
31+
enum MultipleDuplicates {
32+
//~^ ERROR discriminant value `0` assigned more than once
33+
V0,
34+
//~^ NOTE `0` assigned here
35+
V1 = 0,
36+
//~^ NOTE `0` assigned here
37+
V2,
38+
V3,
39+
V4 = 0,
40+
//~^ NOTE `0` assigned here
41+
V5 = -2,
42+
//~^ NOTE discriminant for `V7` incremented from this startpoint (`V5` + 2 variant later => `V7` = 0)
43+
V6,
44+
V7,
45+
//~^ NOTE `0` assigned here
2846
}
2947

3048
fn main() {

src/test/ui/error-codes/E0081.stderr

+29-8
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ LL | enum Enum {
55
| ^^^^^^^^^
66
LL |
77
LL | P = 3,
8-
| - first assignment of `3`
8+
| - `3` assigned here
99
LL |
1010
LL | X = 3,
11-
| - second assignment of `3`
11+
| - `3` assigned here
1212

1313
error[E0081]: discriminant value `1` assigned more than once
1414
--> $DIR/E0081.rs:11:1
@@ -17,10 +17,10 @@ LL | enum EnumOverflowRepr {
1717
| ^^^^^^^^^^^^^^^^^^^^^
1818
LL |
1919
LL | P = 257,
20-
| --- first assignment of `1` (overflowed from `257`)
20+
| --- `1` (overflowed from `257`) assigned here
2121
LL |
2222
LL | X = 513,
23-
| --- second assignment of `1` (overflowed from `513`)
23+
| --- `1` (overflowed from `513`) assigned here
2424

2525
error[E0081]: discriminant value `-1` assigned more than once
2626
--> $DIR/E0081.rs:20:1
@@ -29,14 +29,35 @@ LL | enum NegDisEnum {
2929
| ^^^^^^^^^^^^^^^
3030
LL |
3131
LL | First = -1,
32-
| -- first assignment of `-1`
32+
| -- `-1` assigned here
3333
LL |
3434
LL | Second = -2,
35-
| ----------- assigned discriminant for `Last` was incremented from this discriminant
35+
| ----------- discriminant for `Last` incremented from this startpoint (`Second` + 1 variant later => `Last` = -1)
3636
LL |
3737
LL | Last,
38-
| ---- second assignment of `-1`
38+
| ---- `-1` assigned here
3939

40-
error: aborting due to 3 previous errors
40+
error[E0081]: discriminant value `0` assigned more than once
41+
--> $DIR/E0081.rs:31:1
42+
|
43+
LL | enum MultipleDuplicates {
44+
| ^^^^^^^^^^^^^^^^^^^^^^^
45+
LL |
46+
LL | V0,
47+
| -- `0` assigned here
48+
LL |
49+
LL | V1 = 0,
50+
| - `0` assigned here
51+
...
52+
LL | V4 = 0,
53+
| - `0` assigned here
54+
LL |
55+
LL | V5 = -2,
56+
| ------- discriminant for `V7` incremented from this startpoint (`V5` + 2 variant later => `V7` = 0)
57+
...
58+
LL | V7,
59+
| -- `0` assigned here
60+
61+
error: aborting due to 4 previous errors
4162

4263
For more information about this error, try `rustc --explain E0081`.

src/test/ui/issues/issue-15524.rs

-16
This file was deleted.

src/test/ui/issues/issue-15524.stderr

-40
This file was deleted.

src/test/ui/tag-variant-disr-dup.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ LL | enum Color {
55
| ^^^^^^^^^^
66
...
77
LL | Black = 0x000000,
8-
| -------- first assignment of `0`
8+
| -------- `0` assigned here
99
LL | White = 0x000000,
10-
| -------- second assignment of `0`
10+
| -------- `0` assigned here
1111

1212
error: aborting due to previous error
1313

0 commit comments

Comments
 (0)