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

facet-rs / facet / 19903754435

03 Dec 2025 05:59PM UTC coverage: 59.12% (+0.1%) from 59.013%
19903754435

push

github

fasterthanlime
fix: use strict provenance APIs in facet-value

For inline values (null, booleans, short strings), we pack data directly
into pointer bits - there's no actual memory being pointed to. Previously
we used `bits as *mut u8` which is an integer-to-pointer cast that
violates strict provenance.

Now we use:
- `ptr::without_provenance_mut()` for creating fake pointers from integers
- `.addr()` instead of `as usize` for getting the address

This makes miri happy with `-Zmiri-strict-provenance`.

4 of 7 new or added lines in 1 file covered. (57.14%)

420 existing lines in 9 files now uncovered.

20944 of 35426 relevant lines covered (59.12%)

537.13 hits per line

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

86.53
/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✔
UNCOV
15
        let errors = pe.container.attrs.errors.iter().map(|e| {
×
UNCOV
16
            let msg = &e.message;
×
UNCOV
17
            let span = e.span;
×
UNCOV
18
            quote_spanned! { span => compile_error!(#msg); }
×
UNCOV
19
        });
×
UNCOV
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✔
UNCOV
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✔
UNCOV
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
UNCOV
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✔
UNCOV
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✔
UNCOV
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✔
UNCOV
176
                    discriminant = Some(dis);
×
UNCOV
177
                    discriminant_offset = 0;
×
178
                }
90✔
179

180
                let discriminant_ts = if let Some(discriminant) = discriminant {
90✔
UNCOV
181
                    if discriminant_offset > 0 {
×
UNCOV
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 {
UNCOV
197
                        let attrs_list: Vec<TokenStream> = pv
×
198
                            .attrs
×
UNCOV
199
                            .facet
×
UNCOV
200
                            .iter()
×
UNCOV
201
                            .map(|attr| {
×
UNCOV
202
                                let ext_attr = emit_attr(attr, &facet_crate);
×
UNCOV
203
                                quote! { #ext_attr }
×
UNCOV
204
                            })
×
UNCOV
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✔
UNCOV
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
                    }
UNCOV
220
                    IdentOrLiteral::Literal(idx) => {
×
221
                        // Use the same naming convention as in the union definition
UNCOV
222
                        quote::format_ident!(
×
223
                            "__Shadow_CRepr_Field{}_{}",
224
                            enum_name_str,
UNCOV
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(_) => {
UNCOV
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
UNCOV
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✔
UNCOV
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(_) => {
UNCOV
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
UNCOV
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(_) => {
UNCOV
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 => {
UNCOV
570
            return quote! {
×
571
                compile_error!("#[repr(transparent)] is not supported on enums by Facet");
572
            };
573
        }
574
        PRepr::Rust(None) => {
UNCOV
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✔
UNCOV
599
        (
×
UNCOV
600
            quote! {
×
UNCOV
601
                .ty(#facet_crate::Type::User(#facet_crate::UserType::Opaque))
×
UNCOV
602
            },
×
UNCOV
603
            quote! {},
×
UNCOV
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 the impl
626
    quote! {
265✔
627
        #static_decl
628

629
        #[automatically_derived]
630
        #[allow(non_camel_case_types)]
631
        unsafe impl #bgp_def #facet_crate::Facet<'__facet> for #enum_name #bgp_without_bounds #where_clauses_tokens {
632
            const SHAPE: &'static #facet_crate::Shape = &const {
633
                #(#shadow_struct_defs)*
634
                #fields
635
                #facet_crate::Shape::builder_for_sized::<Self>()
636
                    .vtable({
637
                        #facet_crate::value_vtable!(Self, #type_name_fn)
638
                    })
639
                    .type_identifier(#enum_name_str)
640
                    #type_params
641
                    #ty
642
                    #maybe_container_doc
643
                    #container_attributes_tokens
644
                    #type_tag_maybe
645
                    .build()
646
            };
647
        }
648
    }
649
}
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