@@ -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,96 @@ 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
+ fn detect_discriminant_duplicate < ' tcx > (
1552
1503
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
+ }
1563
1578
}
1564
- }
1565
1579
1566
- format ! ( "`{dis}`" )
1580
+ if let Some ( mut e) = error {
1581
+ e. emit ( ) ;
1582
+ }
1583
+
1584
+ i += 1 ;
1585
+ }
1567
1586
}
1568
1587
1569
1588
pub ( super ) fn check_type_params_are_used < ' tcx > (
0 commit comments