11use std:: cell:: { Cell , RefCell } ;
22use std:: char;
3+ use std:: collections:: HashMap ;
34use std:: str:: Chars ;
45
56use ast:: OperationKind ;
@@ -1399,28 +1400,18 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum {
13991400 return string_enum ( self , program, js_name, generate_typescript, comments) ;
14001401 }
14011402
1402- let has_discriminant = self . variants [ 0 ] . discriminant . is_some ( ) ;
1403-
14041403 match self . vis {
14051404 syn:: Visibility :: Public ( _) => { }
14061405 _ => bail_span ! ( self , "only public enums are allowed with #[wasm_bindgen]" ) ,
14071406 }
14081407
1408+ let mut last_discriminant: Option < u32 > = None ;
1409+ let mut discriminate_map: HashMap < u32 , & syn:: Variant > = HashMap :: new ( ) ;
1410+
14091411 let variants = self
14101412 . variants
14111413 . iter ( )
1412- . enumerate ( )
1413- . map ( |( i, v) | {
1414- // Require that everything either has a discriminant or doesn't.
1415- // We don't really want to get in the business of emulating how
1416- // rustc assigns values to enums.
1417- if v. discriminant . is_some ( ) != has_discriminant {
1418- bail_span ! (
1419- v,
1420- "must either annotate discriminant of all variants or none"
1421- ) ;
1422- }
1423-
1414+ . map ( |v| {
14241415 let value = match & v. discriminant {
14251416 Some ( ( _, expr) ) => match get_expr ( expr) {
14261417 syn:: Expr :: Lit ( syn:: ExprLit {
@@ -1442,8 +1433,33 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum {
14421433 number literal values",
14431434 ) ,
14441435 } ,
1445- None => i as u32 ,
1436+ None => {
1437+ // Use the same algorithm as rustc to determine the next discriminant
1438+ // https://doc.rust-lang.org/reference/items/enumerations.html#implicit-discriminants
1439+ if let Some ( last) = last_discriminant {
1440+ if let Some ( value) = last. checked_add ( 1 ) {
1441+ value
1442+ } else {
1443+ bail_span ! (
1444+ v,
1445+ "the discriminants of C-style enums with #[wasm_bindgen] must be representable as u32"
1446+ ) ;
1447+ }
1448+ } else {
1449+ 0
1450+ }
1451+ }
14461452 } ;
1453+ last_discriminant = Some ( value) ;
1454+
1455+ if let Some ( old) = discriminate_map. insert ( value, v) {
1456+ bail_span ! (
1457+ v,
1458+ "discriminant value `{}` is already used by {} in this enum" ,
1459+ value,
1460+ old. ident
1461+ ) ;
1462+ }
14471463
14481464 let comments = extract_doc_comments ( & v. attrs ) ;
14491465 Ok ( ast:: Variant {
@@ -1454,21 +1470,9 @@ impl<'a> MacroParse<(&'a mut TokenStream, BindgenAttrs)> for syn::ItemEnum {
14541470 } )
14551471 . collect :: < Result < Vec < _ > , Diagnostic > > ( ) ?;
14561472
1457- let mut values = variants. iter ( ) . map ( |v| v. value ) . collect :: < Vec < _ > > ( ) ;
1458- values. sort ( ) ;
1459- let hole = values
1460- . windows ( 2 )
1461- . find_map ( |window| {
1462- if window[ 0 ] + 1 != window[ 1 ] {
1463- Some ( window[ 0 ] + 1 )
1464- } else {
1465- None
1466- }
1467- } )
1468- . unwrap_or ( * values. last ( ) . unwrap ( ) + 1 ) ;
1469- for value in values {
1470- assert ! ( hole != value) ;
1471- }
1473+ let hole = ( 0 ..=u32:: MAX )
1474+ . find ( |v| !discriminate_map. contains_key ( v) )
1475+ . unwrap ( ) ;
14721476
14731477 self . to_tokens ( tokens) ;
14741478
0 commit comments