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

facet-rs / facet / 19993677870

06 Dec 2025 08:12PM UTC coverage: 58.752% (+0.05%) from 58.705%
19993677870

push

github

fasterthanlime
Reduce/cordon bloat in facet, introduce bloatbench

Looks into codegen size, compile times etc. - again :) after adding a buuuunch of features.

- [x] Disable miette's default features (derive, which depends on syn) (`b.miette`)
- [x] Don't enable miette fancy by default in facet-json (`b.miette-fancy`)
- [x] Flatten closures in facet-json to avoid generic instantiations (`b.flatten-closures`)
- [x] Get rid of shape_of in Shape derive, use `#field_type` directly (`b.shape-of`)
- [x] Detect known derives in facet-macros, eschew impls! for them (`b.detect-derives`)
- [x] Gate nonzero impls, net impls (`b.gate-impls`)
- [x] Gate doc-comments, vtable-fmt, vtable-cmp, vtable-hash (`b.gate-vtable`)
- [x] Make 'auto trait detection' opt-in (`b.auto-traits-optin`)
- [x] Detect traits from `derive` and from `#[facet(default)]` (`b.derive-traits`)
- [x] Allow manual trait specification via `#[facet(traits(...))]` (`b.manual-traits`)
- [x] Go back to builders (they expand to less code) (`b.builders`)
- [x] VTable restructured with sub-vtables (`format`, `cmp`, `hash`, `markers`) (`b.vtable-substruct`)
- [x] Convert manual impls to ShapeBuilder (`scalar.rs`, `fn_ptr.rs`, `impls_num_complex.rs`) (`b.shapebuilder`)
- [x] **Confirmed**: `#![allow(uncommon_codepoints, nonstandard_style)]` in library = no warnings in consumers (`b.unicode-test`)
- [x] Add `FieldBuilder::new(name, shape_fn, offset)` builder (`b.field-builder`)
- [x] Add `StructTypeBuilder::new(kind, fields)` builder (`b.structtype-builder`)
- [x] Add `VariantBuilder::new(name, data)` builder (`b.variant-builder`)
- [x] Add `EnumTypeBuilder::new(enum_repr, variants)` builder (`b.enumtype-builder`)
- [x] Create `::facet::𝟋` module with type aliases and builder exports (`b.prelude-mod`)
- [x] Update derive macro to use builder pattern for Field, StructType, Variant, EnumType (`b.derive-builders`)

Based on analysis in `facet-bloatbench/MACRO-OPTIMIZATION-ANALYSIS.md`:

- [x] Remove `let mut vtable = const... (continued)

1199 of 3227 new or added lines in 72 files covered. (37.16%)

145 existing lines in 29 files now uncovered.

24253 of 41280 relevant lines covered (58.75%)

504.69 hits per line

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

90.6
/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(
462✔
13
    name: impl quote::ToTokens,
462✔
14
    discriminant: impl quote::ToTokens,
462✔
15
    attributes: impl quote::ToTokens,
462✔
16
    struct_kind: impl quote::ToTokens,
462✔
17
    fields: impl quote::ToTokens,
462✔
18
    doc: impl quote::ToTokens,
462✔
19
) -> TokenStream {
462✔
20
    quote! {
462✔
21
        𝟋VarB::new(
22
            #name,
23
            𝟋STyB::new(#struct_kind, #fields).build()
24
        )
25
        .discriminant(Some(#discriminant as _))
26
        .attributes(#attributes)
27
        .doc(#doc)
28
        .build()
29
    }
30
}
462✔
31

32
/// Generate a unit variant using the pre-built StructType::UNIT constant.
33
/// NOTE: This function generates code that uses short aliases from the 𝟋 prelude.
34
/// It MUST be called within a context where `use #facet_crate::𝟋::*` has been emitted.
35
fn gen_unit_variant(
206✔
36
    name: impl quote::ToTokens,
206✔
37
    discriminant: impl quote::ToTokens,
206✔
38
    attributes: impl quote::ToTokens,
206✔
39
    doc: impl quote::ToTokens,
206✔
40
) -> TokenStream {
206✔
41
    quote! {
206✔
42
        𝟋VarB::new(#name, 𝟋STy::UNIT)
43
            .discriminant(Some(#discriminant as _))
44
            .attributes(#attributes)
45
            .doc(#doc)
46
            .build()
47
    }
48
}
206✔
49

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

55
    // Emit any collected errors as compile_error! with proper spans
56
    if !pe.container.attrs.errors.is_empty() {
276✔
57
        let errors = pe.container.attrs.errors.iter().map(|e| {
×
58
            let msg = &e.message;
×
59
            let span = e.span;
×
60
            quote_spanned! { span => compile_error!(#msg); }
×
61
        });
×
62
        return quote! { #(#errors)* };
×
63
    }
276✔
64

65
    let enum_name = &pe.container.name;
276✔
66
    let enum_name_str = enum_name.to_string();
276✔
67

68
    let opaque = pe
276✔
69
        .container
276✔
70
        .attrs
276✔
71
        .facet
276✔
72
        .iter()
276✔
73
        .any(|a| a.is_builtin() && a.key_str() == "opaque");
276✔
74

75
    // Get the facet crate path (custom or default ::facet)
76
    let facet_crate = pe.container.attrs.facet_crate();
276✔
77

78
    let type_name_fn =
276✔
79
        generate_type_name_fn(enum_name, parsed.generics.as_ref(), opaque, &facet_crate);
276✔
80

81
    // Determine trait sources and generate vtable accordingly
82
    let trait_sources = TraitSources::from_attrs(&pe.container.attrs);
276✔
83
    let vtable_code = gen_vtable(&facet_crate, &type_name_fn, &trait_sources);
276✔
84
    let vtable_init = quote! { const { #vtable_code } };
276✔
85

86
    let bgp = pe.container.bgp.clone();
276✔
87
    // Use the AST directly for where clauses and generics, as PContainer/PEnum doesn't store them
88
    let where_clauses_tokens = build_where_clauses(
276✔
89
        parsed.clauses.as_ref(),
276✔
90
        parsed.generics.as_ref(),
276✔
91
        opaque,
276✔
92
        &facet_crate,
276✔
93
    );
94
    let type_params_call = build_type_params_call(parsed.generics.as_ref(), opaque, &facet_crate);
276✔
95

96
    // Container-level docs - returns builder call only if there are doc comments and doc feature is enabled
97
    #[cfg(feature = "doc")]
98
    let doc_call = match &pe.container.attrs.doc[..] {
276✔
99
        [] => quote! {},
276✔
100
        doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
9✔
101
    };
102
    #[cfg(not(feature = "doc"))]
NEW
103
    let doc_call = quote! {};
×
104

105
    // Container attributes - returns builder call only if there are attributes
106
    let attributes_call = {
276✔
107
        let mut attribute_tokens: Vec<TokenStream> = Vec::new();
276✔
108
        for attr in &pe.container.attrs.facet {
276✔
109
            // These attributes are handled specially and not emitted to runtime:
110
            // - crate: sets the facet crate path
111
            // - traits: compile-time directive for vtable generation
112
            // - auto_traits: compile-time directive for vtable generation
113
            if attr.is_builtin() {
42✔
114
                let key = attr.key_str();
41✔
115
                if matches!(key.as_str(), "crate" | "traits" | "auto_traits") {
41✔
116
                    continue;
4✔
117
                }
37✔
118
            }
1✔
119
            // All attributes go through grammar dispatch
120
            let ext_attr = emit_attr(attr, &facet_crate);
38✔
121
            attribute_tokens.push(quote! { #ext_attr });
38✔
122
        }
123

124
        if attribute_tokens.is_empty() {
276✔
125
            quote! {}
246✔
126
        } else {
127
            quote! { .attributes(&const {[#(#attribute_tokens),*]}) }
30✔
128
        }
129
    };
130

131
    // Type tag - returns builder call only if present
132
    let type_tag_call = {
276✔
133
        if let Some(type_tag) = pe.container.attrs.get_builtin_args("type_tag") {
276✔
134
            quote! { .type_tag(#type_tag) }
×
135
        } else {
136
            quote! {}
276✔
137
        }
138
    };
139

140
    // Determine enum repr (already resolved by PEnum::parse())
141
    let valid_repr = &pe.repr;
276✔
142

143
    // Are these relevant for enums? Or is it always `repr(C)` if a `PrimitiveRepr` is present?
144
    let repr = match &valid_repr {
276✔
145
        PRepr::Transparent => unreachable!("this should be caught by PRepr::parse"),
×
146
        PRepr::Rust(_) => quote! { 𝟋Repr::RUST },
235✔
147
        PRepr::C(_) => quote! { 𝟋Repr::C },
41✔
148
        PRepr::RustcWillCatch => {
149
            // rustc will emit the error - return empty TokenStream
150
            return quote! {};
×
151
        }
152
    };
153

154
    // Helper for EnumRepr TS (token stream) generation for primitives
155
    let enum_repr_ts_from_primitive = |primitive_repr: PrimitiveRepr| -> TokenStream {
276✔
156
        let type_name_str = primitive_repr.type_name().to_string();
237✔
157
        let enum_repr_variant_ident = format_ident!("{}", type_name_str.to_uppercase());
237✔
158
        quote! { #facet_crate::EnumRepr::#enum_repr_variant_ident }
237✔
159
    };
237✔
160

161
    // --- Processing code for shadow struct/fields/variant_expressions ---
162
    // A. C-style enums have shadow-discriminant, shadow-union, shadow-struct
163
    // B. Primitive enums have simpler layout.
164
    let (shadow_struct_defs, variant_expressions, enum_repr_type_tokenstream) = match valid_repr {
276✔
165
        PRepr::C(prim_opt) => {
41✔
166
            // Shadow discriminant
167
            let shadow_discriminant_name = quote::format_ident!("_D");
41✔
168
            let all_variant_names: Vec<Ident> = pe
41✔
169
                .variants
41✔
170
                .iter()
41✔
171
                .map(|pv| match &pv.name.raw {
102✔
172
                    IdentOrLiteral::Ident(id) => id.clone(),
102✔
173
                    IdentOrLiteral::Literal(n) => format_ident!("_{}", n), // Should not happen for enums
×
174
                })
102✔
175
                .collect();
41✔
176

177
            let repr_attr_content = match prim_opt {
41✔
178
                Some(p) => p.type_name(),
2✔
179
                None => quote! { C },
39✔
180
            };
181
            let mut shadow_defs = vec![quote! {
41✔
182
                #[repr(#repr_attr_content)]
183
                #[allow(dead_code)]
184
                enum #shadow_discriminant_name { #(#all_variant_names),* }
185
            }];
186

187
            // Shadow union
188
            let shadow_union_name = quote::format_ident!("_U");
41✔
189
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("ʄ")));
41✔
190
            let bgp_with_bounds = facet_bgp.display_with_bounds();
41✔
191
            let bgp_without_bounds = facet_bgp.display_without_bounds();
41✔
192
            let phantom_data = facet_bgp.display_as_phantom_data();
41✔
193
            let all_union_fields: Vec<TokenStream> = pe.variants.iter().map(|pv| {
102✔
194
                // Each field is named after the variant, struct for its fields.
195
                let variant_ident = match &pv.name.raw {
102✔
196
                    IdentOrLiteral::Ident(id) => id.clone(),
102✔
197
                     IdentOrLiteral::Literal(idx) => format_ident!("_{}", idx), // Should not happen
×
198
                };
199
                let shadow_field_name_ident = quote::format_ident!("_F{}", variant_ident);
102✔
200
                quote! {
102✔
201
                    #variant_ident: ::core::mem::ManuallyDrop<#shadow_field_name_ident #bgp_without_bounds>
202
                }
203
            }).collect();
102✔
204

205
            shadow_defs.push(quote! {
41✔
206
                #[repr(C)]
207
                #[allow(non_snake_case, dead_code)]
208
                union #shadow_union_name #bgp_with_bounds #where_clauses_tokens { #(#all_union_fields),* }
209
            });
210

211
            // Shadow repr struct for enum as a whole
212
            let shadow_repr_name = quote::format_ident!("_R");
41✔
213
            shadow_defs.push(quote! {
41✔
214
                #[repr(C)]
215
                #[allow(non_snake_case)]
216
                #[allow(dead_code)]
217
                struct #shadow_repr_name #bgp_with_bounds #where_clauses_tokens {
218
                    _discriminant: #shadow_discriminant_name,
219
                    _phantom: #phantom_data,
220
                    _fields: #shadow_union_name #bgp_without_bounds,
221
                }
222
            });
223

224
            // Generate variant_expressions
225
            let mut discriminant: Option<&TokenStream> = None;
41✔
226
            let mut discriminant_offset: i64 = 0;
41✔
227
            let mut exprs = Vec::new();
41✔
228

229
            for pv in pe.variants.iter() {
102✔
230
                if let Some(dis) = &pv.discriminant {
102✔
231
                    discriminant = Some(dis);
×
232
                    discriminant_offset = 0;
×
233
                }
102✔
234

235
                let discriminant_ts = if let Some(discriminant) = discriminant {
102✔
236
                    if discriminant_offset > 0 {
×
237
                        quote! { #discriminant + #discriminant_offset }
×
238
                    } else {
239
                        quote! { #discriminant }
×
240
                    }
241
                } else {
242
                    quote! { #discriminant_offset }
102✔
243
                };
244

245
                let display_name = pv.name.effective.clone();
102✔
246
                let name_token = TokenTree::Literal(Literal::string(&display_name));
102✔
247
                let variant_attributes = {
102✔
248
                    if pv.attrs.facet.is_empty() {
102✔
249
                        quote! { &[] }
93✔
250
                    } else {
251
                        let attrs_list: Vec<TokenStream> = pv
9✔
252
                            .attrs
9✔
253
                            .facet
9✔
254
                            .iter()
9✔
255
                            .map(|attr| {
9✔
256
                                let ext_attr = emit_attr(attr, &facet_crate);
9✔
257
                                quote! { #ext_attr }
9✔
258
                            })
9✔
259
                            .collect();
9✔
260
                        quote! { &const {[#(#attrs_list),*]} }
9✔
261
                    }
262
                };
263

264
                #[cfg(feature = "doc")]
265
                let variant_doc = match &pv.attrs.doc[..] {
102✔
266
                    [] => quote! { &[] },
102✔
NEW
267
                    doc_lines => quote! { &[#(#doc_lines),*] },
×
268
                };
269
                #[cfg(not(feature = "doc"))]
NEW
270
                let variant_doc = quote! { &[] };
×
271

272
                let shadow_struct_name = match &pv.name.raw {
102✔
273
                    IdentOrLiteral::Ident(id) => quote::format_ident!("_F{}", id),
102✔
NEW
274
                    IdentOrLiteral::Literal(idx) => quote::format_ident!("_F{}", idx),
×
275
                };
276

277
                let variant_offset = quote! {
102✔
278
                    ::core::mem::offset_of!(#shadow_repr_name #bgp_without_bounds, _fields)
279
                };
280

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

365
                        // Handle empty fields case explicitly
366
                        let struct_fields = if fields_with_types.is_empty() {
40✔
367
                            // Only add phantom data for empty struct variants
368
                            quote! { _phantom: #phantom_data }
×
369
                        } else {
370
                            // Add fields plus phantom data for non-empty struct variants
371
                            quote! { #(#fields_with_types),*, _phantom: #phantom_data }
40✔
372
                        };
373
                        shadow_defs.push(quote! {
40✔
374
                            #[repr(C)]
375
                            #[allow(non_snake_case, dead_code)]
376
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
377
                                #struct_fields
378
                            }
379
                        });
380

381
                        let field_defs: Vec<TokenStream> = fields
40✔
382
                            .iter()
40✔
383
                            .map(|pf| {
68✔
384
                                gen_field_from_pfield(
68✔
385
                                    pf,
68✔
386
                                    &shadow_struct_name,
68✔
387
                                    &facet_bgp,
68✔
388
                                    Some(variant_offset.clone()),
68✔
389
                                    &facet_crate,
68✔
390
                                )
391
                            })
68✔
392
                            .collect();
40✔
393

394
                        let kind = quote! { 𝟋Sk::Struct };
40✔
395
                        let variant = gen_variant(
40✔
396
                            &name_token,
40✔
397
                            &discriminant_ts,
40✔
398
                            &variant_attributes,
40✔
399
                            &kind,
40✔
400
                            &quote! { fields },
40✔
401
                            &variant_doc,
40✔
402
                        );
403
                        exprs.push(quote! {{
40✔
404
                            let fields: &'static [𝟋Fld] = &const {[
405
                                #(#field_defs),*
406
                            ]};
407
                            #variant
408
                        }});
409
                    }
410
                };
411

412
                // C-style enums increment discriminant unless explicitly set
413
                discriminant_offset += 1;
102✔
414
            }
415

416
            // Generate the EnumRepr token stream
417
            let repr_type_ts = match prim_opt {
41✔
418
                None => {
419
                    quote! { #facet_crate::EnumRepr::from_discriminant_size::<#shadow_discriminant_name>() }
39✔
420
                }
421
                Some(p) => enum_repr_ts_from_primitive(*p),
2✔
422
            };
423

424
            (shadow_defs, exprs, repr_type_ts)
41✔
425
        }
426
        PRepr::Rust(Some(prim)) => {
235✔
427
            // Treat as primitive repr
428
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("ʄ")));
235✔
429
            let bgp_with_bounds = facet_bgp.display_with_bounds();
235✔
430
            let phantom_data = facet_bgp.display_as_phantom_data();
235✔
431
            let discriminant_rust_type = prim.type_name();
235✔
432
            let mut shadow_defs = Vec::new();
235✔
433

434
            // Generate variant_expressions
435
            let mut discriminant: Option<&TokenStream> = None;
235✔
436
            let mut discriminant_offset: i64 = 0;
235✔
437

438
            let mut exprs = Vec::new();
235✔
439

440
            for pv in pe.variants.iter() {
566✔
441
                if let Some(dis) = &pv.discriminant {
566✔
442
                    discriminant = Some(dis);
26✔
443
                    discriminant_offset = 0;
26✔
444
                }
540✔
445

446
                let discriminant_ts = if let Some(discriminant) = discriminant {
566✔
447
                    if discriminant_offset > 0 {
26✔
448
                        quote! { #discriminant + #discriminant_offset }
×
449
                    } else {
450
                        quote! { #discriminant }
26✔
451
                    }
452
                } else {
453
                    quote! { #discriminant_offset }
540✔
454
                };
455

456
                let display_name = pv.name.effective.clone();
566✔
457
                let name_token = TokenTree::Literal(Literal::string(&display_name));
566✔
458
                let variant_attributes = {
566✔
459
                    if pv.attrs.facet.is_empty() {
566✔
460
                        quote! { &[] }
551✔
461
                    } else {
462
                        let attrs_list: Vec<TokenStream> = pv
15✔
463
                            .attrs
15✔
464
                            .facet
15✔
465
                            .iter()
15✔
466
                            .map(|attr| {
15✔
467
                                let ext_attr = emit_attr(attr, &facet_crate);
15✔
468
                                quote! { #ext_attr }
15✔
469
                            })
15✔
470
                            .collect();
15✔
471
                        quote! { &const {[#(#attrs_list),*]} }
15✔
472
                    }
473
                };
474

475
                #[cfg(feature = "doc")]
476
                let variant_doc = match &pv.attrs.doc[..] {
566✔
477
                    [] => quote! { &[] },
566✔
478
                    doc_lines => quote! { &[#(#doc_lines),*] },
27✔
479
                };
480
                #[cfg(not(feature = "doc"))]
NEW
481
                let variant_doc = quote! { &[] };
×
482

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

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

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

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

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

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

732
    // Generate the impl
733
    quote! {
276✔
734
        #static_decl
735

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

747
        #trait_assertion_fn
748

749
        #[automatically_derived]
750
        #[allow(non_camel_case_types)]
751
        unsafe impl #bgp_def #facet_crate::Facet<'ʄ> for #enum_name #bgp_without_bounds #where_clauses_tokens {
752
            const SHAPE: &'static #facet_crate::Shape = &const {
753
                use #facet_crate::𝟋::*;
754
                #(#shadow_struct_defs)*
755
                #fields
756
                𝟋ShpB::for_sized::<Self>(#type_name_fn, #enum_name_str)
757
                    .vtable(#vtable_init)
758
                    .ty(#ty_field)
759
                    .def(𝟋Def::Undefined)
760
                    #type_params_call
761
                    #doc_call
762
                    #attributes_call
763
                    #type_tag_call
764
                    .build()
765
            };
766
        }
767
    }
768
}
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