• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

facet-rs / facet / 19992197531

06 Dec 2025 05:58PM UTC coverage: 58.742% (-0.005%) from 58.747%
19992197531

Pull #1118

github

web-flow
Merge 9a6a630f8 into 45a8cb1c3
Pull Request #1118: Reduce/cordon bloat in facet, introduce bloatbench

1142 of 3107 new or added lines in 61 files covered. (36.76%)

533 existing lines in 29 files now uncovered.

24225 of 41240 relevant lines covered (58.74%)

502.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.87
/facet-macros-impl/src/process_enum.rs
1
use super::*;
2
use crate::{
3
    parsed::{IdentOrLiteral, PRepr, PVariantKind, PrimitiveRepr},
4
    process_struct::{TraitSources, gen_field_from_pfield, gen_trait_bounds, gen_vtable},
5
};
6
use quote::{format_ident, quote, quote_spanned};
7

8
/// Generate a Variant using VariantBuilder for more compact output.
9
///
10
/// NOTE: This function generates code that uses short aliases from the 𝟋 prelude.
11
/// It MUST be called within a context where `use #facet_crate::𝟋::*` has been emitted.
12
fn gen_variant(
668✔
13
    name: impl quote::ToTokens,
668✔
14
    discriminant: impl quote::ToTokens,
668✔
15
    attributes: impl quote::ToTokens,
668✔
16
    struct_kind: impl quote::ToTokens,
668✔
17
    fields: impl quote::ToTokens,
668✔
18
    doc: impl quote::ToTokens,
668✔
19
) -> TokenStream {
668✔
20
    quote! {
668✔
21
        𝟋VarB::new(
22
            #name,
23
            𝟋STyB::new(#struct_kind, #fields).build()
24
        )
25
        .discriminant(Some(#discriminant as i64))
26
        .attributes(#attributes)
27
        .doc(#doc)
28
        .build()
29
    }
30
}
668✔
31

32
/// Processes an enum to implement Facet
33
pub(crate) fn process_enum(parsed: Enum) -> TokenStream {
276✔
34
    // Use already-parsed PEnum, including container/variant/field attributes and rename rules
35
    let pe = PEnum::parse(&parsed);
276✔
36

37
    // Emit any collected errors as compile_error! with proper spans
38
    if !pe.container.attrs.errors.is_empty() {
276✔
39
        let errors = pe.container.attrs.errors.iter().map(|e| {
×
40
            let msg = &e.message;
×
41
            let span = e.span;
×
42
            quote_spanned! { span => compile_error!(#msg); }
×
43
        });
×
44
        return quote! { #(#errors)* };
×
45
    }
276✔
46

47
    let enum_name = &pe.container.name;
276✔
48
    let enum_name_str = enum_name.to_string();
276✔
49

50
    let opaque = pe
276✔
51
        .container
276✔
52
        .attrs
276✔
53
        .facet
276✔
54
        .iter()
276✔
55
        .any(|a| a.is_builtin() && a.key_str() == "opaque");
276✔
56

57
    // Get the facet crate path (custom or default ::facet)
58
    let facet_crate = pe.container.attrs.facet_crate();
276✔
59

60
    let type_name_fn =
276✔
61
        generate_type_name_fn(enum_name, parsed.generics.as_ref(), opaque, &facet_crate);
276✔
62

63
    // Determine trait sources and generate vtable accordingly
64
    let trait_sources = TraitSources::from_attrs(&pe.container.attrs);
276✔
65
    let vtable_code = gen_vtable(&facet_crate, &type_name_fn, &trait_sources);
276✔
66
    let vtable_init = quote! { const { #vtable_code } };
276✔
67

68
    let bgp = pe.container.bgp.clone();
276✔
69
    // Use the AST directly for where clauses and generics, as PContainer/PEnum doesn't store them
70
    let where_clauses_tokens = build_where_clauses(
276✔
71
        parsed.clauses.as_ref(),
276✔
72
        parsed.generics.as_ref(),
276✔
73
        opaque,
276✔
74
        &facet_crate,
276✔
75
    );
76
    let type_params = build_type_params(parsed.generics.as_ref(), opaque, &facet_crate);
276✔
77

78
    // Container-level docs from PAttrs - returns value for struct literal
79
    let doc_field = match &pe.container.attrs.doc[..] {
276✔
80
        [] => quote! { &[] },
276✔
81
        doc_lines => quote! { &[#(#doc_lines),*] },
9✔
82
    };
83

84
    // Container attributes - returns value for struct literal
85
    let attributes_field = {
276✔
86
        let mut attribute_tokens: Vec<TokenStream> = Vec::new();
276✔
87
        for attr in &pe.container.attrs.facet {
276✔
88
            // These attributes are handled specially and not emitted to runtime:
89
            // - crate: sets the facet crate path
90
            // - traits: compile-time directive for vtable generation
91
            // - auto_traits: compile-time directive for vtable generation
92
            if attr.is_builtin() {
42✔
93
                let key = attr.key_str();
41✔
94
                if matches!(key.as_str(), "crate" | "traits" | "auto_traits") {
41✔
95
                    continue;
4✔
96
                }
37✔
97
            }
1✔
98
            // All attributes go through grammar dispatch
99
            let ext_attr = emit_attr(attr, &facet_crate);
38✔
100
            attribute_tokens.push(quote! { #ext_attr });
38✔
101
        }
102

103
        if attribute_tokens.is_empty() {
276✔
104
            quote! { &[] }
246✔
105
        } else {
106
            quote! { &const {[#(#attribute_tokens),*]} }
30✔
107
        }
108
    };
109

110
    // Type tag - returns value for struct literal
111
    let type_tag_field = {
276✔
112
        if let Some(type_tag) = pe.container.attrs.get_builtin_args("type_tag") {
276✔
NEW
113
            quote! { Some(#type_tag) }
×
114
        } else {
115
            quote! { None }
276✔
116
        }
117
    };
118

119
    // Determine enum repr (already resolved by PEnum::parse())
120
    let valid_repr = &pe.repr;
276✔
121

122
    // Are these relevant for enums? Or is it always `repr(C)` if a `PrimitiveRepr` is present?
123
    let repr = match &valid_repr {
276✔
124
        PRepr::Transparent => unreachable!("this should be caught by PRepr::parse"),
×
125
        PRepr::Rust(_) => quote! { #facet_crate::Repr::default() },
235✔
126
        PRepr::C(_) => quote! { #facet_crate::Repr::c() },
41✔
127
        PRepr::RustcWillCatch => {
128
            // rustc will emit the error - return empty TokenStream
129
            return quote! {};
×
130
        }
131
    };
132

133
    // Helper for EnumRepr TS (token stream) generation for primitives
134
    let enum_repr_ts_from_primitive = |primitive_repr: PrimitiveRepr| -> TokenStream {
276✔
135
        let type_name_str = primitive_repr.type_name().to_string();
237✔
136
        let enum_repr_variant_ident = format_ident!("{}", type_name_str.to_uppercase());
237✔
137
        quote! { #facet_crate::EnumRepr::#enum_repr_variant_ident }
237✔
138
    };
237✔
139

140
    // --- Processing code for shadow struct/fields/variant_expressions ---
141
    // A. C-style enums have shadow-discriminant, shadow-union, shadow-struct
142
    // B. Primitive enums have simpler layout.
143
    let (shadow_struct_defs, variant_expressions, enum_repr_type_tokenstream) = match valid_repr {
276✔
144
        PRepr::C(prim_opt) => {
41✔
145
            // Shadow discriminant
146
            let shadow_discriminant_name =
41✔
147
                quote::format_ident!("__Shadow_CRepr_Discriminant_for_{}", enum_name_str);
41✔
148
            let all_variant_names: Vec<Ident> = pe
41✔
149
                .variants
41✔
150
                .iter()
41✔
151
                .map(|pv| match &pv.name.raw {
102✔
152
                    IdentOrLiteral::Ident(id) => id.clone(),
102✔
153
                    IdentOrLiteral::Literal(n) => format_ident!("_{}", n), // Should not happen for enums
×
154
                })
102✔
155
                .collect();
41✔
156

157
            let repr_attr_content = match prim_opt {
41✔
158
                Some(p) => p.type_name(),
2✔
159
                None => quote! { C },
39✔
160
            };
161
            let mut shadow_defs = vec![quote! {
41✔
162
                #[repr(#repr_attr_content)]
163
                #[allow(dead_code)]
164
                enum #shadow_discriminant_name { #(#all_variant_names),* }
165
            }];
166

167
            // Shadow union
168
            let shadow_union_name =
41✔
169
                quote::format_ident!("__Shadow_CRepr_Fields_Union_for_{}", enum_name_str);
41✔
170
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
41✔
171
            let bgp_with_bounds = facet_bgp.display_with_bounds();
41✔
172
            let bgp_without_bounds = facet_bgp.display_without_bounds();
41✔
173
            let phantom_data = facet_bgp.display_as_phantom_data();
41✔
174
            let all_union_fields: Vec<TokenStream> = pe.variants.iter().map(|pv| {
102✔
175
                // Each field is named after the variant, struct for its fields.
176
                let variant_ident = match &pv.name.raw {
102✔
177
                    IdentOrLiteral::Ident(id) => id.clone(),
102✔
178
                     IdentOrLiteral::Literal(idx) => format_ident!("_{}", idx), // Should not happen
×
179
                };
180
                let shadow_field_name_ident = quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, variant_ident);
102✔
181
                quote! {
102✔
182
                    #variant_ident: ::core::mem::ManuallyDrop<#shadow_field_name_ident #bgp_without_bounds>
183
                }
184
            }).collect();
102✔
185

186
            shadow_defs.push(quote! {
41✔
187
                #[repr(C)]
188
                #[allow(non_snake_case, dead_code)]
189
                union #shadow_union_name #bgp_with_bounds #where_clauses_tokens { #(#all_union_fields),* }
190
            });
191

192
            // Shadow repr struct for enum as a whole
193
            let shadow_repr_name =
41✔
194
                quote::format_ident!("__Shadow_CRepr_Struct_for_{}", enum_name_str);
41✔
195
            shadow_defs.push(quote! {
41✔
196
                #[repr(C)]
197
                #[allow(non_snake_case)]
198
                #[allow(dead_code)]
199
                struct #shadow_repr_name #bgp_with_bounds #where_clauses_tokens {
200
                    _discriminant: #shadow_discriminant_name,
201
                    _phantom: #phantom_data,
202
                    _fields: #shadow_union_name #bgp_without_bounds,
203
                }
204
            });
205

206
            // Generate variant_expressions
207
            let mut discriminant: Option<&TokenStream> = None;
41✔
208
            let mut discriminant_offset: i64 = 0;
41✔
209
            let mut exprs = Vec::new();
41✔
210

211
            for pv in pe.variants.iter() {
102✔
212
                if let Some(dis) = &pv.discriminant {
102✔
213
                    discriminant = Some(dis);
×
214
                    discriminant_offset = 0;
×
215
                }
102✔
216

217
                let discriminant_ts = if let Some(discriminant) = discriminant {
102✔
218
                    if discriminant_offset > 0 {
×
219
                        quote! { #discriminant + #discriminant_offset }
×
220
                    } else {
221
                        quote! { #discriminant }
×
222
                    }
223
                } else {
224
                    quote! { #discriminant_offset }
102✔
225
                };
226

227
                let display_name = pv.name.effective.clone();
102✔
228
                let name_token = TokenTree::Literal(Literal::string(&display_name));
102✔
229
                let variant_attributes = {
102✔
230
                    if pv.attrs.facet.is_empty() {
102✔
231
                        quote! { &[] }
93✔
232
                    } else {
233
                        let attrs_list: Vec<TokenStream> = pv
9✔
234
                            .attrs
9✔
235
                            .facet
9✔
236
                            .iter()
9✔
237
                            .map(|attr| {
9✔
238
                                let ext_attr = emit_attr(attr, &facet_crate);
9✔
239
                                quote! { #ext_attr }
9✔
240
                            })
9✔
241
                            .collect();
9✔
242
                        quote! { &const {[#(#attrs_list),*]} }
9✔
243
                    }
244
                };
245

246
                let variant_doc = match &pv.attrs.doc[..] {
102✔
247
                    [] => quote! { &[] },
102✔
NEW
248
                    doc_lines => quote! { &[#(#doc_lines),*] },
×
249
                };
250

251
                let shadow_struct_name = match &pv.name.raw {
102✔
252
                    IdentOrLiteral::Ident(id) => {
102✔
253
                        // Use the same naming convention as in the union definition
254
                        quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, id)
102✔
255
                    }
256
                    IdentOrLiteral::Literal(idx) => {
×
257
                        // Use the same naming convention as in the union definition
258
                        quote::format_ident!(
×
259
                            "__Shadow_CRepr_Field{}_{}",
260
                            enum_name_str,
261
                            format_ident!("_{}", idx) // Should not happen
×
262
                        )
263
                    }
264
                };
265

266
                let variant_offset = quote! {
102✔
267
                    ::core::mem::offset_of!(#shadow_repr_name #bgp_without_bounds, _fields)
268
                };
269

270
                // Determine field structure for the variant
271
                match &pv.kind {
102✔
272
                    PVariantKind::Unit => {
15✔
273
                        // Generate unit shadow struct for the variant
15✔
274
                        shadow_defs.push(quote! {
15✔
275
                            #[repr(C)]
15✔
276
                            #[allow(non_snake_case, dead_code)]
15✔
277
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens { _phantom: #phantom_data }
15✔
278
                        });
15✔
279
                        let kind = quote! { 𝟋Sk::Unit };
15✔
280
                        let variant = gen_variant(
15✔
281
                            &name_token,
15✔
282
                            &discriminant_ts,
15✔
283
                            &variant_attributes,
15✔
284
                            &kind,
15✔
285
                            &quote! { &[] },
15✔
286
                            &variant_doc,
15✔
287
                        );
15✔
288
                        exprs.push(variant);
15✔
289
                    }
15✔
290
                    PVariantKind::Tuple { fields } => {
47✔
291
                        // Tuple shadow struct
292
                        let fields_with_types: Vec<TokenStream> = fields
47✔
293
                            .iter()
47✔
294
                            .enumerate()
47✔
295
                            .map(|(idx, pf)| {
59✔
296
                                let field_ident = format_ident!("_{}", idx);
59✔
297
                                let typ = &pf.ty;
59✔
298
                                quote! { #field_ident: #typ }
59✔
299
                            })
59✔
300
                            .collect();
47✔
301
                        shadow_defs.push(quote! {
47✔
302
                            #[repr(C)]
303
                            #[allow(non_snake_case, dead_code)]
304
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
305
                                #(#fields_with_types),* ,
306
                                _phantom: #phantom_data
307
                            }
308
                        });
309
                        let field_defs: Vec<TokenStream> = fields
47✔
310
                            .iter()
47✔
311
                            .enumerate()
47✔
312
                            .map(|(idx, pf)| {
59✔
313
                                let mut pfield = pf.clone();
59✔
314
                                let field_ident = format_ident!("_{}", idx);
59✔
315
                                pfield.name.raw = IdentOrLiteral::Ident(field_ident);
59✔
316
                                gen_field_from_pfield(
59✔
317
                                    &pfield,
59✔
318
                                    &shadow_struct_name,
59✔
319
                                    &facet_bgp,
59✔
320
                                    Some(variant_offset.clone()),
59✔
321
                                    &facet_crate,
59✔
322
                                )
323
                            })
59✔
324
                            .collect();
47✔
325
                        let kind = quote! { 𝟋Sk::Tuple };
47✔
326
                        let variant = gen_variant(
47✔
327
                            &name_token,
47✔
328
                            &discriminant_ts,
47✔
329
                            &variant_attributes,
47✔
330
                            &kind,
47✔
331
                            &quote! { fields },
47✔
332
                            &variant_doc,
47✔
333
                        );
334
                        exprs.push(quote! {{
47✔
335
                            let fields: &'static [𝟋Fld] = &const {[
336
                                #(#field_defs),*
337
                            ]};
338
                            #variant
339
                        }});
340
                    }
341
                    PVariantKind::Struct { fields } => {
40✔
342
                        let fields_with_types: Vec<TokenStream> = fields
40✔
343
                            .iter()
40✔
344
                            .map(|pf| {
68✔
345
                                // Use raw name for struct field definition
346
                                let field_name = match &pf.name.raw {
68✔
347
                                    IdentOrLiteral::Ident(id) => quote! { #id },
68✔
348
                                    IdentOrLiteral::Literal(_) => {
349
                                        panic!("Struct variant cannot have literal field names")
×
350
                                    }
351
                                };
352
                                let typ = &pf.ty;
68✔
353
                                quote! { #field_name: #typ }
68✔
354
                            })
68✔
355
                            .collect();
40✔
356

357
                        // Handle empty fields case explicitly
358
                        let struct_fields = if fields_with_types.is_empty() {
40✔
359
                            // Only add phantom data for empty struct variants
360
                            quote! { _phantom: #phantom_data }
×
361
                        } else {
362
                            // Add fields plus phantom data for non-empty struct variants
363
                            quote! { #(#fields_with_types),*, _phantom: #phantom_data }
40✔
364
                        };
365
                        shadow_defs.push(quote! {
40✔
366
                            #[repr(C)]
367
                            #[allow(non_snake_case, dead_code)]
368
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
369
                                #struct_fields
370
                            }
371
                        });
372

373
                        let field_defs: Vec<TokenStream> = fields
40✔
374
                            .iter()
40✔
375
                            .map(|pf| {
68✔
376
                                gen_field_from_pfield(
68✔
377
                                    pf,
68✔
378
                                    &shadow_struct_name,
68✔
379
                                    &facet_bgp,
68✔
380
                                    Some(variant_offset.clone()),
68✔
381
                                    &facet_crate,
68✔
382
                                )
383
                            })
68✔
384
                            .collect();
40✔
385

386
                        let kind = quote! { 𝟋Sk::Struct };
40✔
387
                        let variant = gen_variant(
40✔
388
                            &name_token,
40✔
389
                            &discriminant_ts,
40✔
390
                            &variant_attributes,
40✔
391
                            &kind,
40✔
392
                            &quote! { fields },
40✔
393
                            &variant_doc,
40✔
394
                        );
395
                        exprs.push(quote! {{
40✔
396
                            let fields: &'static [𝟋Fld] = &const {[
397
                                #(#field_defs),*
398
                            ]};
399
                            #variant
400
                        }});
401
                    }
402
                };
403

404
                // C-style enums increment discriminant unless explicitly set
405
                discriminant_offset += 1;
102✔
406
            }
407

408
            // Generate the EnumRepr token stream
409
            let repr_type_ts = match prim_opt {
41✔
410
                None => {
411
                    quote! { #facet_crate::EnumRepr::from_discriminant_size::<#shadow_discriminant_name>() }
39✔
412
                }
413
                Some(p) => enum_repr_ts_from_primitive(*p),
2✔
414
            };
415

416
            (shadow_defs, exprs, repr_type_ts)
41✔
417
        }
418
        PRepr::Rust(Some(prim)) => {
235✔
419
            // Treat as primitive repr
420
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
235✔
421
            let bgp_with_bounds = facet_bgp.display_with_bounds();
235✔
422
            let phantom_data = facet_bgp.display_as_phantom_data();
235✔
423
            let discriminant_rust_type = prim.type_name();
235✔
424
            let mut shadow_defs = Vec::new();
235✔
425

426
            // Generate variant_expressions
427
            let mut discriminant: Option<&TokenStream> = None;
235✔
428
            let mut discriminant_offset: i64 = 0;
235✔
429

430
            let mut exprs = Vec::new();
235✔
431

432
            for pv in pe.variants.iter() {
566✔
433
                if let Some(dis) = &pv.discriminant {
566✔
434
                    discriminant = Some(dis);
26✔
435
                    discriminant_offset = 0;
26✔
436
                }
540✔
437

438
                let discriminant_ts = if let Some(discriminant) = discriminant {
566✔
439
                    if discriminant_offset > 0 {
26✔
440
                        quote! { #discriminant + #discriminant_offset }
×
441
                    } else {
442
                        quote! { #discriminant }
26✔
443
                    }
444
                } else {
445
                    quote! { #discriminant_offset }
540✔
446
                };
447

448
                let display_name = pv.name.effective.clone();
566✔
449
                let name_token = TokenTree::Literal(Literal::string(&display_name));
566✔
450
                let variant_attributes = {
566✔
451
                    if pv.attrs.facet.is_empty() {
566✔
452
                        quote! { &[] }
551✔
453
                    } else {
454
                        let attrs_list: Vec<TokenStream> = pv
15✔
455
                            .attrs
15✔
456
                            .facet
15✔
457
                            .iter()
15✔
458
                            .map(|attr| {
15✔
459
                                let ext_attr = emit_attr(attr, &facet_crate);
15✔
460
                                quote! { #ext_attr }
15✔
461
                            })
15✔
462
                            .collect();
15✔
463
                        quote! { &const {[#(#attrs_list),*]} }
15✔
464
                    }
465
                };
466

467
                let variant_doc = match &pv.attrs.doc[..] {
566✔
468
                    [] => quote! { &[] },
566✔
469
                    doc_lines => quote! { &[#(#doc_lines),*] },
27✔
470
                };
471

472
                match &pv.kind {
566✔
473
                    PVariantKind::Unit => {
191✔
474
                        let kind = quote! { 𝟋Sk::Unit };
191✔
475
                        let variant = gen_variant(
191✔
476
                            &name_token,
191✔
477
                            &discriminant_ts,
191✔
478
                            &variant_attributes,
191✔
479
                            &kind,
191✔
480
                            &quote! { &[] },
191✔
481
                            &variant_doc,
191✔
482
                        );
191✔
483
                        exprs.push(variant);
191✔
484
                    }
191✔
485
                    PVariantKind::Tuple { fields } => {
232✔
486
                        let shadow_struct_name = match &pv.name.raw {
232✔
487
                            IdentOrLiteral::Ident(id) => {
232✔
488
                                quote::format_ident!(
232✔
489
                                    "__Shadow_RustRepr_Tuple_for_{}_{}",
490
                                    enum_name_str,
491
                                    id
492
                                )
493
                            }
494
                            IdentOrLiteral::Literal(_) => {
495
                                panic!(
×
496
                                    "Enum variant names cannot be literals for tuple variants in #[repr(Rust)]"
497
                                )
498
                            }
499
                        };
500
                        let fields_with_types: Vec<TokenStream> = fields
232✔
501
                            .iter()
232✔
502
                            .enumerate()
232✔
503
                            .map(|(idx, pf)| {
256✔
504
                                let field_ident = format_ident!("_{}", idx);
256✔
505
                                let typ = &pf.ty;
256✔
506
                                quote! { #field_ident: #typ }
256✔
507
                            })
256✔
508
                            .collect();
232✔
509
                        shadow_defs.push(quote! {
232✔
510
                            #[repr(C)] // Layout variants like C structs
511
                            #[allow(non_snake_case, dead_code)]
512
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
513
                                _discriminant: #discriminant_rust_type,
514
                                _phantom: #phantom_data,
515
                                #(#fields_with_types),*
516
                            }
517
                        });
518
                        let field_defs: Vec<TokenStream> = fields
232✔
519
                            .iter()
232✔
520
                            .enumerate()
232✔
521
                            .map(|(idx, pf)| {
256✔
522
                                let mut pf = pf.clone();
256✔
523
                                let field_ident = format_ident!("_{}", idx);
256✔
524
                                pf.name.raw = IdentOrLiteral::Ident(field_ident);
256✔
525
                                gen_field_from_pfield(
256✔
526
                                    &pf,
256✔
527
                                    &shadow_struct_name,
256✔
528
                                    &facet_bgp,
256✔
529
                                    None,
256✔
530
                                    &facet_crate,
256✔
531
                                )
532
                            })
256✔
533
                            .collect();
232✔
534
                        let kind = quote! { 𝟋Sk::Tuple };
232✔
535
                        let variant = gen_variant(
232✔
536
                            &name_token,
232✔
537
                            &discriminant_ts,
232✔
538
                            &variant_attributes,
232✔
539
                            &kind,
232✔
540
                            &quote! { fields },
232✔
541
                            &variant_doc,
232✔
542
                        );
543
                        exprs.push(quote! {{
232✔
544
                            let fields: &'static [𝟋Fld] = &const {[
545
                                #(#field_defs),*
546
                            ]};
547
                            #variant
548
                        }});
549
                    }
550
                    PVariantKind::Struct { fields } => {
143✔
551
                        let shadow_struct_name = match &pv.name.raw {
143✔
552
                            IdentOrLiteral::Ident(id) => {
143✔
553
                                // Use a more descriptive name, similar to the Tuple variant case
554
                                quote::format_ident!(
143✔
555
                                    "__Shadow_RustRepr_Struct_for_{}_{}",
556
                                    enum_name_str,
557
                                    id
558
                                )
559
                            }
560
                            IdentOrLiteral::Literal(_) => {
561
                                // This case should ideally not happen for named struct variants
562
                                panic!(
×
563
                                    "Enum variant names cannot be literals for struct variants in #[repr(Rust)]"
564
                                )
565
                            }
566
                        };
567
                        let fields_with_types: Vec<TokenStream> = fields
143✔
568
                            .iter()
143✔
569
                            .map(|pf| {
211✔
570
                                let field_name = match &pf.name.raw {
211✔
571
                                    IdentOrLiteral::Ident(id) => quote! { #id },
211✔
572
                                    IdentOrLiteral::Literal(_) => {
573
                                        panic!("Struct variant cannot have literal field names")
×
574
                                    }
575
                                };
576
                                let typ = &pf.ty;
211✔
577
                                quote! { #field_name: #typ }
211✔
578
                            })
211✔
579
                            .collect();
143✔
580
                        shadow_defs.push(quote! {
143✔
581
                            #[repr(C)] // Layout variants like C structs
582
                            #[allow(non_snake_case, dead_code)]
583
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
584
                                _discriminant: #discriminant_rust_type,
585
                                _phantom: #phantom_data,
586
                                #(#fields_with_types),*
587
                            }
588
                        });
589
                        let field_defs: Vec<TokenStream> = fields
143✔
590
                            .iter()
143✔
591
                            .map(|pf| {
211✔
592
                                gen_field_from_pfield(
211✔
593
                                    pf,
211✔
594
                                    &shadow_struct_name,
211✔
595
                                    &facet_bgp,
211✔
596
                                    None,
211✔
597
                                    &facet_crate,
211✔
598
                                )
599
                            })
211✔
600
                            .collect();
143✔
601
                        let kind = quote! { 𝟋Sk::Struct };
143✔
602
                        let variant = gen_variant(
143✔
603
                            &name_token,
143✔
604
                            &discriminant_ts,
143✔
605
                            &variant_attributes,
143✔
606
                            &kind,
143✔
607
                            &quote! { fields },
143✔
608
                            &variant_doc,
143✔
609
                        );
610
                        exprs.push(quote! {{
143✔
611
                            let fields: &'static [𝟋Fld] = &const {[
612
                                #(#field_defs),*
613
                            ]};
614
                            #variant
615
                        }});
616
                    }
617
                }
618
                // Rust-style enums increment discriminant unless explicitly set
619
                discriminant_offset += 1;
566✔
620
            }
621
            let repr_type_ts = enum_repr_ts_from_primitive(*prim);
235✔
622
            (shadow_defs, exprs, repr_type_ts)
235✔
623
        }
624
        PRepr::Transparent => {
625
            return quote! {
×
626
                compile_error!("#[repr(transparent)] is not supported on enums by Facet");
627
            };
628
        }
629
        PRepr::Rust(None) => {
630
            return quote! {
×
631
                compile_error!("Facet requires enums to have an explicit representation (e.g., #[repr(C)], #[repr(u8)])");
632
            };
633
        }
634
        PRepr::RustcWillCatch => {
635
            // rustc will emit an error for the invalid repr (e.g., conflicting hints).
636
            // Return empty TokenStream so we don't add misleading errors.
637
            return quote! {};
×
638
        }
639
    };
640

641
    // Only make static_decl for non-generic enums
642
    let static_decl = if parsed.generics.is_none() {
276✔
643
        generate_static_decl(enum_name, &facet_crate)
266✔
644
    } else {
645
        quote! {}
10✔
646
    };
647

648
    // Set up generics for impl blocks
649
    let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
276✔
650
    let bgp_def = facet_bgp.display_with_bounds();
276✔
651
    let bgp_without_bounds = bgp.display_without_bounds();
276✔
652

653
    let (ty_field, fields) = if opaque {
276✔
654
        (
×
655
            quote! {
×
NEW
656
                𝟋Ty::User(𝟋UTy::Opaque)
×
657
            },
×
658
            quote! {},
×
659
        )
×
660
    } else {
661
        // Inline the const block directly into the builder call
662
        (
663
            quote! {
276✔
664
                𝟋Ty::User(𝟋UTy::Enum(
665
                    𝟋ETyB::new(#enum_repr_type_tokenstream, &const {[
666
                        #(#variant_expressions),*
667
                    ]})
668
                        .repr(#repr)
669
                        .build()
670
                ))
671
            },
672
            quote! {},
276✔
673
        )
674
    };
675

676
    // Generate constructor expressions to suppress dead_code warnings on enum variants.
677
    // When variants are constructed via reflection (e.g., facet_args::from_std_args()),
678
    // the compiler doesn't see them being used and warns about dead code.
679
    // This ensures all variants are "constructed" from the compiler's perspective.
680
    // We use explicit type annotations to help inference with const generics and
681
    // unused type parameters.
682
    let variant_constructors: Vec<TokenStream> = pe
276✔
683
        .variants
276✔
684
        .iter()
276✔
685
        .map(|pv| {
668✔
686
            let variant_ident = match &pv.name.raw {
668✔
687
                IdentOrLiteral::Ident(id) => id.clone(),
668✔
688
                IdentOrLiteral::Literal(n) => format_ident!("_{}", n),
×
689
            };
690
            match &pv.kind {
668✔
691
                PVariantKind::Unit => quote! { let _: #enum_name #bgp_without_bounds = #enum_name::#variant_ident },
206✔
692
                PVariantKind::Tuple { fields } => {
279✔
693
                    let todos = fields.iter().map(|_| quote! { todo!() });
315✔
694
                    quote! { let _: #enum_name #bgp_without_bounds = #enum_name::#variant_ident(#(#todos),*) }
279✔
695
                }
696
                PVariantKind::Struct { fields } => {
183✔
697
                    let field_inits: Vec<TokenStream> = fields
183✔
698
                        .iter()
183✔
699
                        .map(|pf| {
279✔
700
                            let field_name = match &pf.name.raw {
279✔
701
                                IdentOrLiteral::Ident(id) => id.clone(),
279✔
702
                                IdentOrLiteral::Literal(n) => format_ident!("_{}", n),
×
703
                            };
704
                            quote! { #field_name: todo!() }
279✔
705
                        })
279✔
706
                        .collect();
183✔
707
                    quote! { let _: #enum_name #bgp_without_bounds = #enum_name::#variant_ident { #(#field_inits),* } }
183✔
708
                }
709
            }
710
        })
668✔
711
        .collect();
276✔
712

713
    // Generate static assertions for declared traits (catches lies at compile time)
714
    // We put this in a generic function outside the const block so it can reference generic parameters
715
    let facet_default = pe.container.attrs.has_builtin("default");
276✔
716
    let trait_assertion_fn = if let Some(bounds) =
276✔
717
        gen_trait_bounds(pe.container.attrs.declared_traits.as_ref(), facet_default)
276✔
718
    {
719
        // Note: where_clauses_tokens already includes "where" keyword if non-empty
720
        // We need to add the trait bounds as an additional constraint
NEW
721
        quote! {
×
722
            const _: () = {
723
                #[allow(dead_code, clippy::multiple_bound_locations)]
724
                fn __facet_assert_traits #bgp_def (_: &#enum_name #bgp_without_bounds)
725
                where
726
                    #enum_name #bgp_without_bounds: #bounds
727
                {}
728
            };
729
        }
730
    } else {
731
        quote! {}
276✔
732
    };
733

734
    // Generate the impl
735
    quote! {
276✔
736
        #static_decl
737

738
        // Suppress dead_code warnings for enum variants constructed via reflection.
739
        // See: https://github.com/facet-rs/facet/issues/996
740
        const _: () = {
741
            #[allow(dead_code, unreachable_code, clippy::multiple_bound_locations, clippy::diverging_sub_expression)]
742
            fn __facet_construct_all_variants #bgp_def () -> #enum_name #bgp_without_bounds #where_clauses_tokens {
743
                loop {
744
                    #(#variant_constructors;)*
745
                }
746
            }
747
        };
748

749
        #trait_assertion_fn
750

751
        #[automatically_derived]
752
        #[allow(non_camel_case_types)]
753
        unsafe impl #bgp_def #facet_crate::Facet<'__facet> for #enum_name #bgp_without_bounds #where_clauses_tokens {
754
            const SHAPE: &'static #facet_crate::Shape = &const {
755
                use #facet_crate::𝟋::*;
756
                #(#shadow_struct_defs)*
757
                #fields
758
                𝟋Shp {
759
                    id: 𝟋Shp::id_of::<Self>(),
760
                    layout: 𝟋Shp::layout_of::<Self>(),
761
                    vtable: #vtable_init,
762
                    ty: #ty_field,
763
                    def: 𝟋Def::Undefined,
764
                    type_identifier: #enum_name_str,
765
                    type_params: #type_params,
766
                    doc: #doc_field,
767
                    attributes: #attributes_field,
768
                    type_tag: #type_tag_field,
769
                    inner: None,
770
                }
771
            };
772
        }
773
    }
774
}
276✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc