Skip to content

Commit d63d2bd

Browse files
authored
Rollup merge of rust-lang#100238 - Bryysen:master, r=cjgillot
Further improve error message for E0081 Closes rust-lang#97533
2 parents 65cc68b + 74e71da commit d63d2bd

File tree

10 files changed

+173
-165
lines changed

10 files changed

+173
-165
lines changed

compiler/rustc_typeck/src/check/check.rs

+95-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,109 @@ 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+
/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
1503+
fn detect_discriminant_duplicate<'tcx>(
15521504
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}`)");
1505+
mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
1506+
vs: &'tcx [hir::Variant<'tcx>],
1507+
self_span: Span,
1508+
) {
1509+
// Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate.
1510+
// Here `idx` refers to the order of which the discriminant appears, and its index in `vs`
1511+
let report = |dis: Discr<'tcx>,
1512+
idx: usize,
1513+
err: &mut DiagnosticBuilder<'_, ErrorGuaranteed>| {
1514+
let var = &vs[idx]; // HIR for the duplicate discriminant
1515+
let (span, display_discr) = match var.disr_expr {
1516+
Some(ref expr) => {
1517+
// In the case the discriminant is both a duplicate and overflowed, let the user know
1518+
if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind
1519+
&& let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
1520+
&& *lit_value != dis.val
1521+
{
1522+
(tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
1523+
// Otherwise, format the value as-is
1524+
} else {
1525+
(tcx.hir().span(expr.hir_id), format!("`{dis}`"))
1526+
}
1527+
}
1528+
None => {
1529+
// At this point we know this discriminant is a duplicate, and was not explicitly
1530+
// assigned by the user. Here we iterate backwards to fetch the HIR for the last
1531+
// explictly assigned discriminant, and letting the user know that this was the
1532+
// increment startpoint, and how many steps from there leading to the duplicate
1533+
if let Some((n, hir::Variant { span, ident, .. })) =
1534+
vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
1535+
{
1536+
let ve_ident = var.ident;
1537+
let n = n + 1;
1538+
let sp = if n > 1 { "variants" } else { "variant" };
1539+
1540+
err.span_label(
1541+
*span,
1542+
format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"),
1543+
);
1544+
}
1545+
1546+
(vs[idx].span, format!("`{dis}`"))
1547+
}
1548+
};
1549+
1550+
err.span_label(span, format!("{display_discr} assigned here"));
1551+
};
1552+
1553+
// Here we loop through the discriminants, comparing each discriminant to another.
1554+
// When a duplicate is detected, we instatiate an error and point to both
1555+
// initial and duplicate value. The duplicate discriminant is then discarded by swapping
1556+
// it with the last element and decrementing the `vec.len` (which is why we have to evaluate
1557+
// `discrs.len()` anew every iteration, and why this could be tricky to do in a functional
1558+
// style as we are mutating `discrs` on the fly).
1559+
let mut i = 0;
1560+
while i < discrs.len() {
1561+
let hir_var_i_idx = discrs[i].0.index();
1562+
let mut error: Option<DiagnosticBuilder<'_, _>> = None;
1563+
1564+
let mut o = i + 1;
1565+
while o < discrs.len() {
1566+
let hir_var_o_idx = discrs[o].0.index();
1567+
1568+
if discrs[i].1.val == discrs[o].1.val {
1569+
let err = error.get_or_insert_with(|| {
1570+
let mut ret = struct_span_err!(
1571+
tcx.sess,
1572+
self_span,
1573+
E0081,
1574+
"discriminant value `{}` assigned more than once",
1575+
discrs[i].1,
1576+
);
1577+
1578+
report(discrs[i].1, hir_var_i_idx, &mut ret);
1579+
1580+
ret
1581+
});
1582+
1583+
report(discrs[o].1, hir_var_o_idx, err);
1584+
1585+
// Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty
1586+
discrs[o] = *discrs.last().unwrap();
1587+
discrs.pop();
1588+
} else {
1589+
o += 1;
1590+
}
15631591
}
1564-
}
15651592

1566-
format!("`{dis}`")
1593+
if let Some(mut e) = error {
1594+
e.emit();
1595+
}
1596+
1597+
i += 1;
1598+
}
15671599
}
15681600

15691601
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

+30-7
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,53 @@
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+
enum MultipleDuplicates {
31+
//~^ ERROR discriminant value `0` assigned more than once
32+
//~^^ ERROR discriminant value `-2` 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 variants later => `V7` = 0)
43+
//~^^ NOTE `-2` assigned here
44+
V6,
45+
V7,
46+
//~^ NOTE `0` assigned here
47+
V8 = -3,
48+
//~^ NOTE discriminant for `V9` incremented from this startpoint (`V8` + 1 variant later => `V9` = -2)
49+
V9,
50+
//~^ NOTE `-2` assigned here
2851
}
2952

3053
fn main() {

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

+44-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,50 @@ 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:30:1
42+
|
43+
LL | enum MultipleDuplicates {
44+
| ^^^^^^^^^^^^^^^^^^^^^^^
45+
...
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 variants later => `V7` = 0)
57+
...
58+
LL | V7,
59+
| -- `0` assigned here
60+
61+
error[E0081]: discriminant value `-2` assigned more than once
62+
--> $DIR/E0081.rs:30:1
63+
|
64+
LL | enum MultipleDuplicates {
65+
| ^^^^^^^^^^^^^^^^^^^^^^^
66+
...
67+
LL | V5 = -2,
68+
| -- `-2` assigned here
69+
...
70+
LL | V8 = -3,
71+
| ------- discriminant for `V9` incremented from this startpoint (`V8` + 1 variant later => `V9` = -2)
72+
LL |
73+
LL | V9,
74+
| -- `-2` assigned here
75+
76+
error: aborting due to 5 previous errors
4177

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

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

-16
This file was deleted.

0 commit comments

Comments
 (0)