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

facet-rs / facet / 19922182031

04 Dec 2025 08:16AM UTC coverage: 59.212% (+0.01%) from 59.201%
19922182031

push

github

fasterthanlime
fix: use explicit type annotations for generic enum variant constructors

When constructing enum variants to suppress dead_code warnings, use
explicit type annotations (e.g., `let _: E<T, C> = E::Unit;`) instead
of relying on type inference. This fixes compilation errors with
generic enums that have const generics or unused type parameters,
where the compiler couldn't infer all type parameters from the
variant constructor alone.

3 of 3 new or added lines in 1 file covered. (100.0%)

1 existing line in 1 file now uncovered.

20993 of 35454 relevant lines covered (59.21%)

538.31 hits per line

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

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

8
/// Processes an enum to implement Facet
9
pub(crate) fn process_enum(parsed: Enum) -> TokenStream {
265✔
10
    // Use already-parsed PEnum, including container/variant/field attributes and rename rules
11
    let pe = PEnum::parse(&parsed);
265✔
12

13
    // Emit any collected errors as compile_error! with proper spans
14
    if !pe.container.attrs.errors.is_empty() {
265✔
15
        let errors = pe.container.attrs.errors.iter().map(|e| {
×
16
            let msg = &e.message;
×
17
            let span = e.span;
×
18
            quote_spanned! { span => compile_error!(#msg); }
×
19
        });
×
20
        return quote! { #(#errors)* };
×
21
    }
265✔
22

23
    let enum_name = &pe.container.name;
265✔
24
    let enum_name_str = enum_name.to_string();
265✔
25

26
    let opaque = pe
265✔
27
        .container
265✔
28
        .attrs
265✔
29
        .facet
265✔
30
        .iter()
265✔
31
        .any(|a| a.is_builtin() && a.key_str() == "opaque");
265✔
32

33
    // Get the facet crate path (custom or default ::facet)
34
    let facet_crate = pe.container.attrs.facet_crate();
265✔
35

36
    let type_name_fn =
265✔
37
        generate_type_name_fn(enum_name, parsed.generics.as_ref(), opaque, &facet_crate);
265✔
38

39
    let bgp = pe.container.bgp.clone();
265✔
40
    // Use the AST directly for where clauses and generics, as PContainer/PEnum doesn't store them
41
    let where_clauses_tokens = build_where_clauses(
265✔
42
        parsed.clauses.as_ref(),
265✔
43
        parsed.generics.as_ref(),
265✔
44
        opaque,
265✔
45
        &facet_crate,
265✔
46
    );
47
    let type_params = build_type_params(parsed.generics.as_ref(), opaque, &facet_crate);
265✔
48

49
    // Container-level docs from PAttrs
50
    let maybe_container_doc = match &pe.container.attrs.doc[..] {
265✔
51
        [] => quote! {},
265✔
52
        doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
9✔
53
    };
54

55
    let container_attributes_tokens = {
265✔
56
        let mut attribute_tokens: Vec<TokenStream> = Vec::new();
265✔
57
        for attr in &pe.container.attrs.facet {
265✔
58
            // Skip crate attribute - it's handled specially
59
            if attr.is_builtin() && attr.key_str() == "crate" {
37✔
60
                continue;
1✔
61
            }
36✔
62
            // All attributes go through grammar dispatch
63
            let ext_attr = emit_attr(attr, &facet_crate);
36✔
64
            attribute_tokens.push(quote! { #ext_attr });
36✔
65
        }
66

67
        if attribute_tokens.is_empty() {
265✔
68
            quote! {}
237✔
69
        } else {
70
            quote! { .attributes(&const { [#(#attribute_tokens),*] }) }
28✔
71
        }
72
    };
73

74
    let type_tag_maybe = {
265✔
75
        if let Some(type_tag) = pe.container.attrs.get_builtin_args("type_tag") {
265✔
76
            quote! { .type_tag(#type_tag) }
×
77
        } else {
78
            quote! {}
265✔
79
        }
80
    };
81

82
    // Determine enum repr (already resolved by PEnum::parse())
83
    let valid_repr = &pe.repr;
265✔
84

85
    // Are these relevant for enums? Or is it always `repr(C)` if a `PrimitiveRepr` is present?
86
    let repr = match &valid_repr {
265✔
87
        PRepr::Transparent => unreachable!("this should be caught by PRepr::parse"),
×
88
        PRepr::Rust(_) => quote! { #facet_crate::Repr::default() },
228✔
89
        PRepr::C(_) => quote! { #facet_crate::Repr::c() },
37✔
90
        PRepr::RustcWillCatch => {
91
            // rustc will emit the error - return empty TokenStream
92
            return quote! {};
×
93
        }
94
    };
95

96
    // Helper for EnumRepr TS (token stream) generation for primitives
97
    let enum_repr_ts_from_primitive = |primitive_repr: PrimitiveRepr| -> TokenStream {
265✔
98
        let type_name_str = primitive_repr.type_name().to_string();
230✔
99
        let enum_repr_variant_ident = format_ident!("{}", type_name_str.to_uppercase());
230✔
100
        quote! { #facet_crate::EnumRepr::#enum_repr_variant_ident }
230✔
101
    };
230✔
102

103
    // --- Processing code for shadow struct/fields/variant_expressions ---
104
    // A. C-style enums have shadow-discriminant, shadow-union, shadow-struct
105
    // B. Primitive enums have simpler layout.
106
    let (shadow_struct_defs, variant_expressions, enum_repr_type_tokenstream) = match valid_repr {
265✔
107
        PRepr::C(prim_opt) => {
37✔
108
            // Shadow discriminant
109
            let shadow_discriminant_name =
37✔
110
                quote::format_ident!("__Shadow_CRepr_Discriminant_for_{}", enum_name_str);
37✔
111
            let all_variant_names: Vec<Ident> = pe
37✔
112
                .variants
37✔
113
                .iter()
37✔
114
                .map(|pv| match &pv.name.raw {
90✔
115
                    IdentOrLiteral::Ident(id) => id.clone(),
90✔
116
                    IdentOrLiteral::Literal(n) => format_ident!("_{}", n), // Should not happen for enums
×
117
                })
90✔
118
                .collect();
37✔
119

120
            let repr_attr_content = match prim_opt {
37✔
121
                Some(p) => p.type_name(),
2✔
122
                None => quote! { C },
35✔
123
            };
124
            let mut shadow_defs = vec![quote! {
37✔
125
                #[repr(#repr_attr_content)]
126
                #[allow(dead_code)]
127
                enum #shadow_discriminant_name { #(#all_variant_names),* }
128
            }];
129

130
            // Shadow union
131
            let shadow_union_name =
37✔
132
                quote::format_ident!("__Shadow_CRepr_Fields_Union_for_{}", enum_name_str);
37✔
133
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
37✔
134
            let bgp_with_bounds = facet_bgp.display_with_bounds();
37✔
135
            let bgp_without_bounds = facet_bgp.display_without_bounds();
37✔
136
            let phantom_data = facet_bgp.display_as_phantom_data();
37✔
137
            let all_union_fields: Vec<TokenStream> = pe.variants.iter().map(|pv| {
90✔
138
                // Each field is named after the variant, struct for its fields.
139
                let variant_ident = match &pv.name.raw {
90✔
140
                    IdentOrLiteral::Ident(id) => id.clone(),
90✔
141
                     IdentOrLiteral::Literal(idx) => format_ident!("_{}", idx), // Should not happen
×
142
                };
143
                let shadow_field_name_ident = quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, variant_ident);
90✔
144
                quote! {
90✔
145
                    #variant_ident: ::core::mem::ManuallyDrop<#shadow_field_name_ident #bgp_without_bounds>
146
                }
147
            }).collect();
90✔
148

149
            shadow_defs.push(quote! {
37✔
150
                #[repr(C)]
151
                #[allow(non_snake_case, dead_code)]
152
                union #shadow_union_name #bgp_with_bounds #where_clauses_tokens { #(#all_union_fields),* }
153
            });
154

155
            // Shadow repr struct for enum as a whole
156
            let shadow_repr_name =
37✔
157
                quote::format_ident!("__Shadow_CRepr_Struct_for_{}", enum_name_str);
37✔
158
            shadow_defs.push(quote! {
37✔
159
                #[repr(C)]
160
                #[allow(non_snake_case)]
161
                #[allow(dead_code)]
162
                struct #shadow_repr_name #bgp_with_bounds #where_clauses_tokens {
163
                    _discriminant: #shadow_discriminant_name,
164
                    _phantom: #phantom_data,
165
                    _fields: #shadow_union_name #bgp_without_bounds,
166
                }
167
            });
168

169
            // Generate variant_expressions
170
            let mut discriminant: Option<&TokenStream> = None;
37✔
171
            let mut discriminant_offset: i64 = 0;
37✔
172
            let mut exprs = Vec::new();
37✔
173

174
            for pv in pe.variants.iter() {
90✔
175
                if let Some(dis) = &pv.discriminant {
90✔
176
                    discriminant = Some(dis);
×
177
                    discriminant_offset = 0;
×
178
                }
90✔
179

180
                let discriminant_ts = if let Some(discriminant) = discriminant {
90✔
181
                    if discriminant_offset > 0 {
×
182
                        quote! { #discriminant + #discriminant_offset }
×
183
                    } else {
184
                        quote! { #discriminant }
×
185
                    }
186
                } else {
187
                    quote! { #discriminant_offset }
90✔
188
                };
189

190
                let display_name = pv.name.effective.clone();
90✔
191
                let variant_attrs_tokens = {
90✔
192
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
90✔
193
                    // All attributes go through grammar dispatch
194
                    if pv.attrs.facet.is_empty() {
90✔
195
                        quote! { .name(#name_token) }
90✔
196
                    } else {
197
                        let attrs_list: Vec<TokenStream> = pv
×
198
                            .attrs
×
199
                            .facet
×
200
                            .iter()
×
201
                            .map(|attr| {
×
202
                                let ext_attr = emit_attr(attr, &facet_crate);
×
203
                                quote! { #ext_attr }
×
204
                            })
×
205
                            .collect();
×
206
                        quote! { .name(#name_token).attributes(&const { [#(#attrs_list),*] }) }
×
207
                    }
208
                };
209

210
                let maybe_doc = match &pv.attrs.doc[..] {
90✔
211
                    [] => quote! {},
90✔
212
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
×
213
                };
214

215
                let shadow_struct_name = match &pv.name.raw {
90✔
216
                    IdentOrLiteral::Ident(id) => {
90✔
217
                        // Use the same naming convention as in the union definition
218
                        quote::format_ident!("__Shadow_CRepr_Field{}_{}", enum_name_str, id)
90✔
219
                    }
220
                    IdentOrLiteral::Literal(idx) => {
×
221
                        // Use the same naming convention as in the union definition
222
                        quote::format_ident!(
×
223
                            "__Shadow_CRepr_Field{}_{}",
224
                            enum_name_str,
225
                            format_ident!("_{}", idx) // Should not happen
×
226
                        )
227
                    }
228
                };
229

230
                let variant_offset = quote! {
90✔
231
                    ::core::mem::offset_of!(#shadow_repr_name #bgp_without_bounds, _fields)
232
                };
233

234
                // Determine field structure for the variant
235
                match &pv.kind {
90✔
236
                    PVariantKind::Unit => {
14✔
237
                        // Generate unit shadow struct for the variant
14✔
238
                        shadow_defs.push(quote! {
14✔
239
                            #[repr(C)]
14✔
240
                            #[allow(non_snake_case, dead_code)]
14✔
241
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens { _phantom: #phantom_data }
14✔
242
                        });
14✔
243
                        exprs.push(quote! {
14✔
244
                            #facet_crate::Variant::builder()
14✔
245
                                #variant_attrs_tokens
14✔
246
                                .discriminant(#discriminant_ts as i64)
14✔
247
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).unit().build())
14✔
248
                                #maybe_doc
14✔
249
                                .build()
14✔
250
                        });
14✔
251
                    }
14✔
252
                    PVariantKind::Tuple { fields } => {
38✔
253
                        // Tuple shadow struct
254
                        let fields_with_types: Vec<TokenStream> = fields
38✔
255
                            .iter()
38✔
256
                            .enumerate()
38✔
257
                            .map(|(idx, pf)| {
50✔
258
                                let field_ident = format_ident!("_{}", idx);
50✔
259
                                let typ = &pf.ty;
50✔
260
                                quote! { #field_ident: #typ }
50✔
261
                            })
50✔
262
                            .collect();
38✔
263
                        shadow_defs.push(quote! {
38✔
264
                            #[repr(C)]
265
                            #[allow(non_snake_case, dead_code)]
266
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
267
                                #(#fields_with_types),* ,
268
                                _phantom: #phantom_data
269
                            }
270
                        });
271
                        let field_defs: Vec<TokenStream> = fields
38✔
272
                            .iter()
38✔
273
                            .enumerate()
38✔
274
                            .map(|(idx, pf)| {
50✔
275
                                let mut pfield = pf.clone();
50✔
276
                                let field_ident = format_ident!("_{}", idx);
50✔
277
                                pfield.name.raw = IdentOrLiteral::Ident(field_ident);
50✔
278
                                gen_field_from_pfield(
50✔
279
                                    &pfield,
50✔
280
                                    &shadow_struct_name,
50✔
281
                                    &facet_bgp,
50✔
282
                                    Some(variant_offset.clone()),
50✔
283
                                    &facet_crate,
50✔
284
                                )
285
                            })
50✔
286
                            .collect();
38✔
287
                        exprs.push(quote! {{
38✔
288
                            let fields: &'static [#facet_crate::Field] = &const {[
289
                                #(#field_defs),*
290
                            ]};
291
                            #facet_crate::Variant::builder()
292
                                #variant_attrs_tokens
293
                                .discriminant(#discriminant_ts as i64)
294
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).tuple().fields(fields).build())
295
                                #maybe_doc
296
                                .build()
297
                        }});
298
                    }
299
                    PVariantKind::Struct { fields } => {
38✔
300
                        let fields_with_types: Vec<TokenStream> = fields
38✔
301
                            .iter()
38✔
302
                            .map(|pf| {
65✔
303
                                // Use raw name for struct field definition
304
                                let field_name = match &pf.name.raw {
65✔
305
                                    IdentOrLiteral::Ident(id) => quote! { #id },
65✔
306
                                    IdentOrLiteral::Literal(_) => {
307
                                        panic!("Struct variant cannot have literal field names")
×
308
                                    }
309
                                };
310
                                let typ = &pf.ty;
65✔
311
                                quote! { #field_name: #typ }
65✔
312
                            })
65✔
313
                            .collect();
38✔
314

315
                        // Handle empty fields case explicitly
316
                        let struct_fields = if fields_with_types.is_empty() {
38✔
317
                            // Only add phantom data for empty struct variants
318
                            quote! { _phantom: #phantom_data }
×
319
                        } else {
320
                            // Add fields plus phantom data for non-empty struct variants
321
                            quote! { #(#fields_with_types),*, _phantom: #phantom_data }
38✔
322
                        };
323
                        shadow_defs.push(quote! {
38✔
324
                            #[repr(C)]
325
                            #[allow(non_snake_case, dead_code)]
326
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
327
                                #struct_fields
328
                            }
329
                        });
330

331
                        let field_defs: Vec<TokenStream> = fields
38✔
332
                            .iter()
38✔
333
                            .map(|pf| {
65✔
334
                                gen_field_from_pfield(
65✔
335
                                    pf,
65✔
336
                                    &shadow_struct_name,
65✔
337
                                    &facet_bgp,
65✔
338
                                    Some(variant_offset.clone()),
65✔
339
                                    &facet_crate,
65✔
340
                                )
341
                            })
65✔
342
                            .collect();
38✔
343

344
                        exprs.push(quote! {{
38✔
345
                            let fields: &'static [#facet_crate::Field] = &const {[
346
                                #(#field_defs),*
347
                            ]};
348
                            #facet_crate::Variant::builder()
349
                                #variant_attrs_tokens
350
                                .discriminant(#discriminant_ts as i64)
351
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).struct_().fields(fields).build())
352
                                #maybe_doc
353
                                .build()
354
                        }});
355
                    }
356
                };
357

358
                // C-style enums increment discriminant unless explicitly set
359
                discriminant_offset += 1;
90✔
360
            }
361

362
            // Generate the EnumRepr token stream
363
            let repr_type_ts = match prim_opt {
37✔
364
                None => {
365
                    quote! { #facet_crate::EnumRepr::from_discriminant_size::<#shadow_discriminant_name>() }
35✔
366
                }
367
                Some(p) => enum_repr_ts_from_primitive(*p),
2✔
368
            };
369

370
            (shadow_defs, exprs, repr_type_ts)
37✔
371
        }
372
        PRepr::Rust(Some(prim)) => {
228✔
373
            // Treat as primitive repr
374
            let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
228✔
375
            let bgp_with_bounds = facet_bgp.display_with_bounds();
228✔
376
            let phantom_data = facet_bgp.display_as_phantom_data();
228✔
377
            let discriminant_rust_type = prim.type_name();
228✔
378
            let mut shadow_defs = Vec::new();
228✔
379

380
            // Generate variant_expressions
381
            let mut discriminant: Option<&TokenStream> = None;
228✔
382
            let mut discriminant_offset: i64 = 0;
228✔
383

384
            let mut exprs = Vec::new();
228✔
385

386
            for pv in pe.variants.iter() {
535✔
387
                if let Some(dis) = &pv.discriminant {
535✔
388
                    discriminant = Some(dis);
26✔
389
                    discriminant_offset = 0;
26✔
390
                }
509✔
391

392
                let discriminant_ts = if let Some(discriminant) = discriminant {
535✔
393
                    if discriminant_offset > 0 {
26✔
394
                        quote! { #discriminant + #discriminant_offset }
×
395
                    } else {
396
                        quote! { #discriminant }
26✔
397
                    }
398
                } else {
399
                    quote! { #discriminant_offset }
509✔
400
                };
401

402
                let display_name = pv.name.effective.clone();
535✔
403
                let variant_attrs_tokens = {
535✔
404
                    let name_token = TokenTree::Literal(Literal::string(&display_name));
535✔
405
                    // All attributes go through grammar dispatch
406
                    if pv.attrs.facet.is_empty() {
535✔
407
                        quote! { .name(#name_token) }
523✔
408
                    } else {
409
                        let attrs_list: Vec<TokenStream> = pv
12✔
410
                            .attrs
12✔
411
                            .facet
12✔
412
                            .iter()
12✔
413
                            .map(|attr| {
12✔
414
                                let ext_attr = emit_attr(attr, &facet_crate);
12✔
415
                                quote! { #ext_attr }
12✔
416
                            })
12✔
417
                            .collect();
12✔
418
                        quote! { .name(#name_token).attributes(&const { [#(#attrs_list),*] }) }
12✔
419
                    }
420
                };
421

422
                let maybe_doc = match &pv.attrs.doc[..] {
535✔
423
                    [] => quote! {},
535✔
424
                    doc_lines => quote! { .doc(&[#(#doc_lines),*]) },
27✔
425
                };
426

427
                match &pv.kind {
535✔
428
                    PVariantKind::Unit => {
174✔
429
                        exprs.push(quote! {
174✔
430
                            #facet_crate::Variant::builder()
174✔
431
                                #variant_attrs_tokens
174✔
432
                                .discriminant(#discriminant_ts as i64)
174✔
433
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).unit().build())
174✔
434
                                #maybe_doc
174✔
435
                                .build()
174✔
436
                        });
174✔
437
                    }
174✔
438
                    PVariantKind::Tuple { fields } => {
222✔
439
                        let shadow_struct_name = match &pv.name.raw {
222✔
440
                            IdentOrLiteral::Ident(id) => {
222✔
441
                                quote::format_ident!(
222✔
442
                                    "__Shadow_RustRepr_Tuple_for_{}_{}",
443
                                    enum_name_str,
444
                                    id
445
                                )
446
                            }
447
                            IdentOrLiteral::Literal(_) => {
448
                                panic!(
×
449
                                    "Enum variant names cannot be literals for tuple variants in #[repr(Rust)]"
450
                                )
451
                            }
452
                        };
453
                        let fields_with_types: Vec<TokenStream> = fields
222✔
454
                            .iter()
222✔
455
                            .enumerate()
222✔
456
                            .map(|(idx, pf)| {
246✔
457
                                let field_ident = format_ident!("_{}", idx);
246✔
458
                                let typ = &pf.ty;
246✔
459
                                quote! { #field_ident: #typ }
246✔
460
                            })
246✔
461
                            .collect();
222✔
462
                        shadow_defs.push(quote! {
222✔
463
                            #[repr(C)] // Layout variants like C structs
464
                            #[allow(non_snake_case, dead_code)]
465
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
466
                                _discriminant: #discriminant_rust_type,
467
                                _phantom: #phantom_data,
468
                                #(#fields_with_types),*
469
                            }
470
                        });
471
                        let field_defs: Vec<TokenStream> = fields
222✔
472
                            .iter()
222✔
473
                            .enumerate()
222✔
474
                            .map(|(idx, pf)| {
246✔
475
                                let mut pf = pf.clone();
246✔
476
                                let field_ident = format_ident!("_{}", idx);
246✔
477
                                pf.name.raw = IdentOrLiteral::Ident(field_ident);
246✔
478
                                gen_field_from_pfield(
246✔
479
                                    &pf,
246✔
480
                                    &shadow_struct_name,
246✔
481
                                    &facet_bgp,
246✔
482
                                    None,
246✔
483
                                    &facet_crate,
246✔
484
                                )
485
                            })
246✔
486
                            .collect();
222✔
487
                        exprs.push(quote! {{
222✔
488
                            let fields: &'static [#facet_crate::Field] = &const {[
489
                                #(#field_defs),*
490
                            ]};
491
                            #facet_crate::Variant::builder()
492
                                #variant_attrs_tokens
493
                                .discriminant(#discriminant_ts as i64)
494
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).tuple().fields(fields).build())
495
                                #maybe_doc
496
                                .build()
497
                        }});
498
                    }
499
                    PVariantKind::Struct { fields } => {
139✔
500
                        let shadow_struct_name = match &pv.name.raw {
139✔
501
                            IdentOrLiteral::Ident(id) => {
139✔
502
                                // Use a more descriptive name, similar to the Tuple variant case
503
                                quote::format_ident!(
139✔
504
                                    "__Shadow_RustRepr_Struct_for_{}_{}",
505
                                    enum_name_str,
506
                                    id
507
                                )
508
                            }
509
                            IdentOrLiteral::Literal(_) => {
510
                                // This case should ideally not happen for named struct variants
511
                                panic!(
×
512
                                    "Enum variant names cannot be literals for struct variants in #[repr(Rust)]"
513
                                )
514
                            }
515
                        };
516
                        let fields_with_types: Vec<TokenStream> = fields
139✔
517
                            .iter()
139✔
518
                            .map(|pf| {
205✔
519
                                let field_name = match &pf.name.raw {
205✔
520
                                    IdentOrLiteral::Ident(id) => quote! { #id },
205✔
521
                                    IdentOrLiteral::Literal(_) => {
522
                                        panic!("Struct variant cannot have literal field names")
×
523
                                    }
524
                                };
525
                                let typ = &pf.ty;
205✔
526
                                quote! { #field_name: #typ }
205✔
527
                            })
205✔
528
                            .collect();
139✔
529
                        shadow_defs.push(quote! {
139✔
530
                            #[repr(C)] // Layout variants like C structs
531
                            #[allow(non_snake_case, dead_code)]
532
                            struct #shadow_struct_name #bgp_with_bounds #where_clauses_tokens {
533
                                _discriminant: #discriminant_rust_type,
534
                                _phantom: #phantom_data,
535
                                #(#fields_with_types),*
536
                            }
537
                        });
538
                        let field_defs: Vec<TokenStream> = fields
139✔
539
                            .iter()
139✔
540
                            .map(|pf| {
205✔
541
                                gen_field_from_pfield(
205✔
542
                                    pf,
205✔
543
                                    &shadow_struct_name,
205✔
544
                                    &facet_bgp,
205✔
545
                                    None,
205✔
546
                                    &facet_crate,
205✔
547
                                )
548
                            })
205✔
549
                            .collect();
139✔
550
                        exprs.push(quote! {{
139✔
551
                            let fields: &'static [#facet_crate::Field] = &const {[
552
                                #(#field_defs),*
553
                            ]};
554
                            #facet_crate::Variant::builder()
555
                                #variant_attrs_tokens
556
                                .discriminant(#discriminant_ts as i64)
557
                                .data(#facet_crate::StructType::builder().repr(#facet_crate::Repr::c()).struct_().fields(fields).build())
558
                                #maybe_doc
559
                                .build()
560
                        }});
561
                    }
562
                }
563
                // Rust-style enums increment discriminant unless explicitly set
564
                discriminant_offset += 1;
535✔
565
            }
566
            let repr_type_ts = enum_repr_ts_from_primitive(*prim);
228✔
567
            (shadow_defs, exprs, repr_type_ts)
228✔
568
        }
569
        PRepr::Transparent => {
570
            return quote! {
×
571
                compile_error!("#[repr(transparent)] is not supported on enums by Facet");
572
            };
573
        }
574
        PRepr::Rust(None) => {
575
            return quote! {
×
576
                compile_error!("Facet requires enums to have an explicit representation (e.g., #[repr(C)], #[repr(u8)])");
577
            };
578
        }
579
        PRepr::RustcWillCatch => {
580
            // rustc will emit an error for the invalid repr (e.g., conflicting hints).
581
            // Return empty TokenStream so we don't add misleading errors.
582
            return quote! {};
×
583
        }
584
    };
585

586
    // Only make static_decl for non-generic enums
587
    let static_decl = if parsed.generics.is_none() {
265✔
588
        generate_static_decl(enum_name, &facet_crate)
255✔
589
    } else {
590
        quote! {}
10✔
591
    };
592

593
    // Set up generics for impl blocks
594
    let facet_bgp = bgp.with_lifetime(LifetimeName(format_ident!("__facet")));
265✔
595
    let bgp_def = facet_bgp.display_with_bounds();
265✔
596
    let bgp_without_bounds = bgp.display_without_bounds();
265✔
597

598
    let (ty, fields) = if opaque {
265✔
599
        (
×
600
            quote! {
×
601
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Opaque))
×
602
            },
×
603
            quote! {},
×
604
        )
×
605
    } else {
606
        (
607
            quote! {
265✔
608
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Enum(#facet_crate::EnumType::builder()
609
                        // Use variant expressions that just reference the shadow structs
610
                        // which are now defined above
611
                        .variants(__facet_variants)
612
                        .repr(#repr)
613
                        .enum_repr(#enum_repr_type_tokenstream)
614
                        .build())
615
                ))
616
            },
617
            quote! {
265✔
618
                let __facet_variants: &'static [#facet_crate::Variant] = &const {[
619
                    #(#variant_expressions),*
620
                ]};
621
            },
622
        )
623
    };
624

625
    // Generate constructor expressions to suppress dead_code warnings on enum variants.
626
    // When variants are constructed via reflection (e.g., facet_args::from_std_args()),
627
    // the compiler doesn't see them being used and warns about dead code.
628
    // This ensures all variants are "constructed" from the compiler's perspective.
629
    // We use explicit type annotations to help inference with const generics and
630
    // unused type parameters.
631
    let variant_constructors: Vec<TokenStream> = pe
265✔
632
        .variants
265✔
633
        .iter()
265✔
634
        .map(|pv| {
625✔
635
            let variant_ident = match &pv.name.raw {
625✔
636
                IdentOrLiteral::Ident(id) => id.clone(),
625✔
637
                IdentOrLiteral::Literal(n) => format_ident!("_{}", n),
×
638
            };
639
            match &pv.kind {
625✔
640
                PVariantKind::Unit => quote! { let _: #enum_name #bgp_without_bounds = #enum_name::#variant_ident },
188✔
641
                PVariantKind::Tuple { fields } => {
260✔
642
                    let todos = fields.iter().map(|_| quote! { todo!() });
296✔
643
                    quote! { let _: #enum_name #bgp_without_bounds = #enum_name::#variant_ident(#(#todos),*) }
260✔
644
                }
645
                PVariantKind::Struct { fields } => {
177✔
646
                    let field_inits: Vec<TokenStream> = fields
177✔
647
                        .iter()
177✔
648
                        .map(|pf| {
270✔
649
                            let field_name = match &pf.name.raw {
270✔
650
                                IdentOrLiteral::Ident(id) => id.clone(),
270✔
UNCOV
651
                                IdentOrLiteral::Literal(n) => format_ident!("_{}", n),
×
652
                            };
653
                            quote! { #field_name: todo!() }
270✔
654
                        })
270✔
655
                        .collect();
177✔
656
                    quote! { let _: #enum_name #bgp_without_bounds = #enum_name::#variant_ident { #(#field_inits),* } }
177✔
657
                }
658
            }
659
        })
625✔
660
        .collect();
265✔
661

662
    // Generate the impl
663
    quote! {
265✔
664
        #static_decl
665

666
        // Suppress dead_code warnings for enum variants constructed via reflection.
667
        // See: https://github.com/facet-rs/facet/issues/996
668
        const _: () = {
669
            #[allow(dead_code, unreachable_code, clippy::multiple_bound_locations, clippy::diverging_sub_expression)]
670
            fn __facet_construct_all_variants #bgp_def () -> #enum_name #bgp_without_bounds #where_clauses_tokens {
671
                loop {
672
                    #(#variant_constructors;)*
673
                }
674
            }
675
        };
676

677
        #[automatically_derived]
678
        #[allow(non_camel_case_types)]
679
        unsafe impl #bgp_def #facet_crate::Facet<'__facet> for #enum_name #bgp_without_bounds #where_clauses_tokens {
680
            const SHAPE: &'static #facet_crate::Shape = &const {
681
                #(#shadow_struct_defs)*
682
                #fields
683
                #facet_crate::Shape::builder_for_sized::<Self>()
684
                    .vtable({
685
                        #facet_crate::value_vtable!(Self, #type_name_fn)
686
                    })
687
                    .type_identifier(#enum_name_str)
688
                    #type_params
689
                    #ty
690
                    #maybe_container_doc
691
                    #container_attributes_tokens
692
                    #type_tag_maybe
693
                    .build()
694
            };
695
        }
696
    }
697
}
265✔
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