@@ -32,7 +32,6 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
3232use rustc_trait_selection:: traits:: { self , ObligationCtxt } ;
3333use rustc_ty_utils:: representability:: { self , Representability } ;
3434
35- use std:: iter;
3635use std:: ops:: ControlFlow ;
3736
3837pub ( 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
15691588pub ( super ) fn check_type_params_are_used < ' tcx > (
0 commit comments