@@ -32,7 +32,6 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
32
32
use rustc_trait_selection:: traits:: { self , ObligationCtxt } ;
33
33
use rustc_ty_utils:: representability:: { self , Representability } ;
34
34
35
- use std:: iter;
36
35
use std:: ops:: ControlFlow ;
37
36
38
37
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
1494
1493
}
1495
1494
}
1496
1495
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) ;
1543
1497
1544
1498
check_representable ( tcx, sp, def_id) ;
1545
1499
check_transparent ( tcx, sp, def) ;
1546
1500
}
1547
1501
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 > (
1552
1504
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
+ }
1563
1591
}
1564
- }
1565
1592
1566
- format ! ( "`{dis}`" )
1593
+ if let Some ( mut e) = error {
1594
+ e. emit ( ) ;
1595
+ }
1596
+
1597
+ i += 1 ;
1598
+ }
1567
1599
}
1568
1600
1569
1601
pub ( super ) fn check_type_params_are_used < ' tcx > (
0 commit comments