• 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.17
/facet-macros-impl/src/process_struct.rs
1
//! Struct processing and vtable generation for the Facet derive macro.
2
//!
3
//! # Vtable Trait Detection
4
//!
5
//! The vtable contains function pointers for various trait implementations (Debug, Clone,
6
//! PartialEq, etc.). There are three ways these can be populated:
7
//!
8
//! ## 1. Derive Detection (Fastest - No Specialization)
9
//!
10
//! When `#[derive(Debug, Clone, Facet)]` is used, the macro can see the other derives
11
//! and knows those traits are implemented. It generates direct function pointers without
12
//! any specialization overhead.
13
//!
14
//! ```ignore
15
//! #[derive(Debug, Clone, Facet)]  // Debug and Clone detected from derives
16
//! struct Foo { ... }
17
//! ```
18
//!
19
//! ## 2. Explicit Declaration (No Specialization)
20
//!
21
//! For traits that are implemented manually (not via derive), use `#[facet(traits(...))]`:
22
//!
23
//! ```ignore
24
//! #[derive(Facet)]
25
//! #[facet(traits(Debug, PartialEq))]  // Explicit declaration
26
//! struct Foo { ... }
27
//!
28
//! impl Debug for Foo { ... }  // Manual implementation
29
//! impl PartialEq for Foo { ... }
30
//! ```
31
//!
32
//! This generates compile-time assertions to verify the traits are actually implemented.
33
//!
34
//! ## 3. Auto-Detection (Uses Specialization)
35
//!
36
//! For backward compatibility or when you don't want to list traits manually, use
37
//! `#[facet(auto_traits)]`. This uses the `impls!` macro to detect traits at compile
38
//! time via specialization tricks:
39
//!
40
//! ```ignore
41
//! #[derive(Debug, Facet)]
42
//! #[facet(auto_traits)]  // Auto-detect all other traits
43
//! struct Foo { ... }
44
//! ```
45
//!
46
//! **Note:** Auto-detection is slower to compile because it generates specialization
47
//! code for each trait. Use derive detection or explicit declaration when possible.
48
//!
49
//! ## Layered Resolution
50
//!
51
//! For each vtable entry, the macro checks sources in order:
52
//! 1. Is the trait in `#[derive(...)]`? → Use direct impl
53
//! 2. Is the trait in `#[facet(traits(...))]`? → Use direct impl
54
//! 3. Is `#[facet(auto_traits)]` present? → Use `impls!` detection
55
//! 4. Otherwise → Set to `None`
56
//!
57
//! Note: `#[facet(traits(...))]` and `#[facet(auto_traits)]` are mutually exclusive.
58
//! You can combine derives with either one:
59
//! ```ignore
60
//! #[derive(Debug, Clone, Facet)]  // Debug, Clone detected from derives
61
//! #[facet(traits(Display))]       // Display declared explicitly (manual impl)
62
//! struct Foo { ... }
63
//! ```
64

65
use quote::{format_ident, quote, quote_spanned};
66

67
use super::*;
68
use crate::parsed::{DeclaredTraits, KnownDerives, PAttrs};
69

70
/// Sources of trait information for vtable generation.
71
///
72
/// The vtable generation uses a layered approach:
73
/// 1. **Derives** - traits detected from `#[derive(...)]` next to `#[derive(Facet)]`
74
/// 2. **Declared** - traits explicitly listed in `#[facet(traits(...))]`
75
/// 3. **Implied** - traits implied by other attributes (e.g., `#[facet(default)]` implies Default)
76
/// 4. **Auto** - if `#[facet(auto_traits)]` is present, use `impls!` for remaining traits
77
/// 5. **None** - if none of the above apply, emit `None` for that trait
78
pub(crate) struct TraitSources<'a> {
79
    /// Traits detected from #[derive(...)] attributes
80
    pub known_derives: &'a KnownDerives,
81
    /// Traits explicitly declared via #[facet(traits(...))]
82
    pub declared_traits: Option<&'a DeclaredTraits>,
83
    /// Whether to auto-detect remaining traits via specialization
84
    pub auto_traits: bool,
85
    /// Whether `#[facet(default)]` is present (implies Default trait)
86
    pub facet_default: bool,
87
}
88

89
impl<'a> TraitSources<'a> {
90
    /// Create trait sources from parsed attributes
91
    pub fn from_attrs(attrs: &'a PAttrs) -> Self {
1,970✔
92
        Self {
1,970✔
93
            known_derives: &attrs.known_derives,
1,970✔
94
            declared_traits: attrs.declared_traits.as_ref(),
1,970✔
95
            auto_traits: attrs.auto_traits,
1,970✔
96
            facet_default: attrs.has_builtin("default"),
1,970✔
97
        }
1,970✔
98
    }
1,970✔
99

100
    /// Check if a trait is known from derives
101
    fn has_derive(&self, check: impl FnOnce(&KnownDerives) -> bool) -> bool {
17,788✔
102
        check(self.known_derives)
17,788✔
103
    }
17,788✔
104

105
    /// Check if a trait is explicitly declared
106
    fn has_declared(&self, check: impl FnOnce(&DeclaredTraits) -> bool) -> bool {
25,755✔
107
        self.declared_traits.is_some_and(check)
25,755✔
108
    }
25,755✔
109

110
    /// Check if we should use auto-detection for this trait
111
    fn should_auto(&self) -> bool {
17,725✔
112
        self.auto_traits
17,725✔
113
    }
17,725✔
114
}
115

116
/// Generates the vtable for a type based on trait sources.
117
///
118
/// Uses a layered approach for each trait:
119
/// 1. If known from derives → direct impl (no specialization)
120
/// 2. If explicitly declared → direct impl (no specialization)
121
/// 3. If auto_traits enabled → use `impls!` macro for detection
122
/// 4. Otherwise → None
123
pub(crate) fn gen_vtable(
1,970✔
124
    facet_crate: &TokenStream,
1,970✔
125
    type_name_fn: &TokenStream,
1,970✔
126
    sources: &TraitSources<'_>,
1,970✔
127
) -> TokenStream {
1,970✔
128
    // Helper to generate a direct implementation (no specialization)
129
    let direct_display = quote! {
1,970✔
130
        Some(|data, f| {
131
            let data = unsafe { data.get::<Self>() };
132
            core::fmt::Display::fmt(data, f)
133
        })
134
    };
135
    let direct_debug = quote! {
1,970✔
136
        Some(|data, f| {
137
            let data = unsafe { data.get::<Self>() };
138
            core::fmt::Debug::fmt(data, f)
139
        })
140
    };
141
    let direct_default = quote! {
1,970✔
142
        Some(|target| unsafe {
143
            target.put(<Self as core::default::Default>::default())
144
        })
145
    };
146
    let direct_clone = quote! {
1,970✔
147
        Some(|src, dst| unsafe {
148
            let src = src.get::<Self>();
149
            dst.put(<Self as core::clone::Clone>::clone(src))
150
        })
151
    };
152
    let direct_partial_eq = quote! {
1,970✔
153
        Some(|left, right| {
154
            let left = unsafe { left.get::<Self>() };
155
            let right = unsafe { right.get::<Self>() };
156
            <Self as core::cmp::PartialEq>::eq(left, right)
157
        })
158
    };
159
    let direct_partial_ord = quote! {
1,970✔
160
        Some(|left, right| {
161
            let left = unsafe { left.get::<Self>() };
162
            let right = unsafe { right.get::<Self>() };
163
            <Self as core::cmp::PartialOrd>::partial_cmp(left, right)
164
        })
165
    };
166
    let direct_ord = quote! {
1,970✔
167
        Some(|left, right| {
168
            let left = unsafe { left.get::<Self>() };
169
            let right = unsafe { right.get::<Self>() };
170
            <Self as core::cmp::Ord>::cmp(left, right)
171
        })
172
    };
173
    let direct_hash = quote! {
1,970✔
174
        Some(|value, hasher| {
175
            let value = unsafe { value.get::<Self>() };
176
            <Self as core::hash::Hash>::hash(value, hasher)
177
        })
178
    };
179

180
    // Auto-detection versions using spez
181
    let auto_display = quote! {
1,970✔
182
        if #facet_crate::spez::impls!(Self: core::fmt::Display) {
183
            Some(|data, f| {
184
                let data = unsafe { data.get::<Self>() };
185
                use #facet_crate::spez::*;
186
                (&&Spez(data)).spez_display(f)
187
            })
188
        } else {
189
            None
190
        }
191
    };
192
    let auto_debug = quote! {
1,970✔
193
        if #facet_crate::spez::impls!(Self: core::fmt::Debug) {
194
            Some(|data, f| {
195
                let data = unsafe { data.get::<Self>() };
196
                use #facet_crate::spez::*;
197
                (&&Spez(data)).spez_debug(f)
198
            })
199
        } else {
200
            None
201
        }
202
    };
203
    let auto_default = quote! {
1,970✔
204
        if #facet_crate::spez::impls!(Self: core::default::Default) {
205
            Some(|target| unsafe {
206
                use #facet_crate::spez::*;
207
                (&&SpezEmpty::<Self>::SPEZ).spez_default_in_place(target)
208
            })
209
        } else {
210
            None
211
        }
212
    };
213
    let auto_clone = quote! {
1,970✔
214
        if #facet_crate::spez::impls!(Self: core::clone::Clone) {
215
            Some(|src, dst| unsafe {
216
                use #facet_crate::spez::*;
217
                let src = src.get::<Self>();
218
                (&&Spez(src)).spez_clone_into(dst)
219
            })
220
        } else {
221
            None
222
        }
223
    };
224
    let auto_partial_eq = quote! {
1,970✔
225
        if #facet_crate::spez::impls!(Self: core::cmp::PartialEq) {
226
            Some(|left, right| {
227
                let left = unsafe { left.get::<Self>() };
228
                let right = unsafe { right.get::<Self>() };
229
                use #facet_crate::spez::*;
230
                (&&Spez(left)).spez_partial_eq(&&Spez(right))
231
            })
232
        } else {
233
            None
234
        }
235
    };
236
    let auto_partial_ord = quote! {
1,970✔
237
        if #facet_crate::spez::impls!(Self: core::cmp::PartialOrd) {
238
            Some(|left, right| {
239
                let left = unsafe { left.get::<Self>() };
240
                let right = unsafe { right.get::<Self>() };
241
                use #facet_crate::spez::*;
242
                (&&Spez(left)).spez_partial_cmp(&&Spez(right))
243
            })
244
        } else {
245
            None
246
        }
247
    };
248
    let auto_ord = quote! {
1,970✔
249
        if #facet_crate::spez::impls!(Self: core::cmp::Ord) {
250
            Some(|left, right| {
251
                let left = unsafe { left.get::<Self>() };
252
                let right = unsafe { right.get::<Self>() };
253
                use #facet_crate::spez::*;
254
                (&&Spez(left)).spez_cmp(&&Spez(right))
255
            })
256
        } else {
257
            None
258
        }
259
    };
260
    let auto_hash = quote! {
1,970✔
261
        if #facet_crate::spez::impls!(Self: core::hash::Hash) {
262
            Some(|value, hasher| {
263
                let value = unsafe { value.get::<Self>() };
264
                use #facet_crate::spez::*;
265
                (&&Spez(value)).spez_hash(&mut { hasher })
266
            })
267
        } else {
268
            None
269
        }
270
    };
271
    let auto_parse = quote! {
1,970✔
272
        if #facet_crate::spez::impls!(Self: core::str::FromStr) {
273
            Some(|s, target| {
274
                use #facet_crate::spez::*;
275
                unsafe { (&&SpezEmpty::<Self>::SPEZ).spez_parse(s, target) }
276
            })
277
        } else {
278
            None
279
        }
280
    };
281

282
    // For each trait: derive > declared > auto > none
283
    // Only emit the builder call if we have a value (not None)
284

285
    // Display: no derive exists, so check declared then auto
286
    let display_call = if sources.has_declared(|d| d.display) {
1,970✔
NEW
287
        quote! { .display_opt(#direct_display) }
×
288
    } else if sources.should_auto() {
1,970✔
289
        quote! { .display_opt(#auto_display) }
29✔
290
    } else {
291
        quote! {}
1,941✔
292
    };
293

294
    // Debug: check derive, then declared, then auto
295
    let debug_call = if sources.has_derive(|d| d.debug) || sources.has_declared(|d| d.debug) {
1,970✔
296
        quote! { .debug_opt(#direct_debug) }
2✔
297
    } else if sources.should_auto() {
1,968✔
298
        quote! { .debug_opt(#auto_debug) }
29✔
299
    } else {
300
        quote! {}
1,939✔
301
    };
302

303
    // Default: check derive, then declared, then facet(default), then auto
304
    // Note: #[facet(default)] implies the type implements Default
305
    let default_call = if sources.has_derive(|d| d.default)
1,970✔
306
        || sources.has_declared(|d| d.default)
1,970✔
307
        || sources.facet_default
1,970✔
308
    {
309
        quote! { .default_in_place_opt(#direct_default) }
3✔
310
    } else if sources.should_auto() {
1,967✔
311
        quote! { .default_in_place_opt(#auto_default) }
29✔
312
    } else {
313
        quote! {}
1,938✔
314
    };
315

316
    // Clone: check derive (including Copy which implies Clone), then declared, then auto
317
    let clone_call = if sources.has_derive(|d| d.clone || d.copy)
1,970✔
318
        || sources.has_declared(|d| d.clone || d.copy)
1,970✔
319
    {
NEW
320
        quote! { .clone_into_opt(#direct_clone) }
×
321
    } else if sources.should_auto() {
1,970✔
322
        quote! { .clone_into_opt(#auto_clone) }
29✔
323
    } else {
324
        quote! {}
1,941✔
325
    };
326

327
    // PartialEq: check derive, then declared, then auto
328
    let partial_eq_call =
1,970✔
329
        if sources.has_derive(|d| d.partial_eq) || sources.has_declared(|d| d.partial_eq) {
1,970✔
NEW
330
            quote! { .partial_eq_opt(#direct_partial_eq) }
×
331
        } else if sources.should_auto() {
1,970✔
332
            quote! { .partial_eq_opt(#auto_partial_eq) }
29✔
333
        } else {
334
            quote! {}
1,941✔
335
        };
336

337
    // PartialOrd: check derive, then declared, then auto
338
    let partial_ord_call =
1,970✔
339
        if sources.has_derive(|d| d.partial_ord) || sources.has_declared(|d| d.partial_ord) {
1,970✔
NEW
340
            quote! { .partial_ord_opt(#direct_partial_ord) }
×
341
        } else if sources.should_auto() {
1,970✔
342
            quote! { .partial_ord_opt(#auto_partial_ord) }
29✔
343
        } else {
344
            quote! {}
1,941✔
345
        };
346

347
    // Ord: check derive, then declared, then auto
348
    let ord_call = if sources.has_derive(|d| d.ord) || sources.has_declared(|d| d.ord) {
1,970✔
NEW
349
        quote! { .ord_opt(#direct_ord) }
×
350
    } else if sources.should_auto() {
1,970✔
351
        quote! { .ord_opt(#auto_ord) }
29✔
352
    } else {
353
        quote! {}
1,941✔
354
    };
355

356
    // Hash: check derive, then declared, then auto
357
    let hash_call = if sources.has_derive(|d| d.hash) || sources.has_declared(|d| d.hash) {
1,970✔
NEW
358
        quote! { .hash_opt(#direct_hash) }
×
359
    } else if sources.should_auto() {
1,970✔
360
        quote! { .hash_opt(#auto_hash) }
29✔
361
    } else {
362
        quote! {}
1,941✔
363
    };
364

365
    // Parse (FromStr): no derive exists, only auto-detect if enabled
366
    let parse_call = if sources.should_auto() {
1,970✔
367
        quote! { .parse_opt(#auto_parse) }
29✔
368
    } else {
369
        quote! {}
1,941✔
370
    };
371

372
    // Marker traits - these set bitflags in MarkerTraits
373
    // Copy: derive (Copy implies Clone), declared, or auto
374
    let has_copy =
1,970✔
375
        sources.has_derive(|d| d.copy) || sources.has_declared(|d| d.copy) || sources.auto_traits;
1,970✔
376
    // Send: declared or auto (no standard derive for Send)
377
    let has_send = sources.has_declared(|d| d.send) || sources.auto_traits;
1,970✔
378
    // Sync: declared or auto (no standard derive for Sync)
379
    let has_sync = sources.has_declared(|d| d.sync) || sources.auto_traits;
1,970✔
380
    // Eq: derive (PartialEq + Eq), declared, or auto
381
    let has_eq =
1,970✔
382
        sources.has_derive(|d| d.eq) || sources.has_declared(|d| d.eq) || sources.auto_traits;
1,970✔
383
    // Unpin: declared or auto (no standard derive for Unpin)
384
    let has_unpin = sources.has_declared(|d| d.unpin) || sources.auto_traits;
1,970✔
385

386
    // Build markers expression
387
    let markers_entry = if has_copy || has_send || has_sync || has_eq || has_unpin {
1,970✔
388
        // At least one marker trait might be set
389
        let copy_check = if has_copy
29✔
390
            && sources.auto_traits
29✔
391
            && !sources.has_derive(|d| d.copy)
29✔
392
            && !sources.has_declared(|d| d.copy)
29✔
393
        {
394
            // Auto-detect Copy
395
            quote! {
29✔
396
                if #facet_crate::spez::impls!(Self: core::marker::Copy) {
397
                    markers = markers.with_copy();
398
                }
399
            }
NEW
400
        } else if sources.has_derive(|d| d.copy) || sources.has_declared(|d| d.copy) {
×
401
            // Directly known to be Copy
NEW
402
            quote! { markers = markers.with_copy(); }
×
403
        } else {
NEW
404
            quote! {}
×
405
        };
406

407
        let send_check = if has_send && sources.auto_traits && !sources.has_declared(|d| d.send) {
29✔
408
            quote! {
29✔
409
                if #facet_crate::spez::impls!(Self: core::marker::Send) {
410
                    markers = markers.with_send();
411
                }
412
            }
NEW
413
        } else if sources.has_declared(|d| d.send) {
×
NEW
414
            quote! { markers = markers.with_send(); }
×
415
        } else {
NEW
416
            quote! {}
×
417
        };
418

419
        let sync_check = if has_sync && sources.auto_traits && !sources.has_declared(|d| d.sync) {
29✔
420
            quote! {
29✔
421
                if #facet_crate::spez::impls!(Self: core::marker::Sync) {
422
                    markers = markers.with_sync();
423
                }
424
            }
NEW
425
        } else if sources.has_declared(|d| d.sync) {
×
NEW
426
            quote! { markers = markers.with_sync(); }
×
427
        } else {
NEW
428
            quote! {}
×
429
        };
430

431
        let eq_check = if has_eq
29✔
432
            && sources.auto_traits
29✔
433
            && !sources.has_derive(|d| d.eq)
29✔
434
            && !sources.has_declared(|d| d.eq)
29✔
435
        {
436
            quote! {
29✔
437
                if #facet_crate::spez::impls!(Self: core::cmp::Eq) {
438
                    markers = markers.with_eq();
439
                }
440
            }
NEW
441
        } else if sources.has_derive(|d| d.eq) || sources.has_declared(|d| d.eq) {
×
NEW
442
            quote! { markers = markers.with_eq(); }
×
443
        } else {
NEW
444
            quote! {}
×
445
        };
446

447
        let unpin_check = if has_unpin && sources.auto_traits && !sources.has_declared(|d| d.unpin)
29✔
448
        {
449
            quote! {
29✔
450
                if #facet_crate::spez::impls!(Self: core::marker::Unpin) {
451
                    markers = markers.with_unpin();
452
                }
453
            }
NEW
454
        } else if sources.has_declared(|d| d.unpin) {
×
NEW
455
            quote! { markers = markers.with_unpin(); }
×
456
        } else {
NEW
457
            quote! {}
×
458
        };
459

460
        quote! {{
29✔
461
            let mut markers = #facet_crate::MarkerTraits::EMPTY;
462
            #copy_check
463
            #send_check
464
            #sync_check
465
            #eq_check
466
            #unpin_check
467
            markers
468
        }}
469
    } else {
470
        quote! { #facet_crate::MarkerTraits::EMPTY }
1,941✔
471
    };
472

473
    quote! {
1,970✔
474
        #facet_crate::ValueVTable::builder(#type_name_fn)
475
            .drop_in_place(#facet_crate::ValueVTable::drop_in_place_for::<Self>())
476
            #display_call
477
            #debug_call
478
            #default_call
479
            #clone_call
480
            #partial_eq_call
481
            #partial_ord_call
482
            #ord_call
483
            #hash_call
484
            #parse_call
485
            .markers(#markers_entry)
486
            .build()
487
    }
488
}
1,970✔
489

490
/// Generate trait bounds for static assertions.
491
/// Returns a TokenStream of bounds like `core::fmt::Debug + core::clone::Clone`
492
/// that can be used in a where clause.
493
///
494
/// `facet_default` is true when `#[facet(default)]` is present, which implies Default.
495
pub(crate) fn gen_trait_bounds(
1,970✔
496
    declared: Option<&DeclaredTraits>,
1,970✔
497
    facet_default: bool,
1,970✔
498
) -> Option<TokenStream> {
1,970✔
499
    let mut bounds = Vec::new();
1,970✔
500

501
    if let Some(declared) = declared {
1,970✔
502
        if declared.display {
2✔
NEW
503
            bounds.push(quote! { core::fmt::Display });
×
504
        }
2✔
505
        if declared.debug {
2✔
506
            bounds.push(quote! { core::fmt::Debug });
2✔
507
        }
2✔
508
        if declared.clone {
2✔
NEW
509
            bounds.push(quote! { core::clone::Clone });
×
510
        }
2✔
511
        if declared.copy {
2✔
NEW
512
            bounds.push(quote! { core::marker::Copy });
×
513
        }
2✔
514
        if declared.partial_eq {
2✔
NEW
515
            bounds.push(quote! { core::cmp::PartialEq });
×
516
        }
2✔
517
        if declared.eq {
2✔
NEW
518
            bounds.push(quote! { core::cmp::Eq });
×
519
        }
2✔
520
        if declared.partial_ord {
2✔
NEW
521
            bounds.push(quote! { core::cmp::PartialOrd });
×
522
        }
2✔
523
        if declared.ord {
2✔
NEW
524
            bounds.push(quote! { core::cmp::Ord });
×
525
        }
2✔
526
        if declared.hash {
2✔
NEW
527
            bounds.push(quote! { core::hash::Hash });
×
528
        }
2✔
529
        if declared.default {
2✔
NEW
530
            bounds.push(quote! { core::default::Default });
×
531
        }
2✔
532
        if declared.send {
2✔
NEW
533
            bounds.push(quote! { core::marker::Send });
×
534
        }
2✔
535
        if declared.sync {
2✔
NEW
536
            bounds.push(quote! { core::marker::Sync });
×
537
        }
2✔
538
        if declared.unpin {
2✔
NEW
539
            bounds.push(quote! { core::marker::Unpin });
×
540
        }
2✔
541
    }
1,968✔
542

543
    // #[facet(default)] implies Default trait
544
    if facet_default && !declared.is_some_and(|d| d.default) {
1,970✔
545
        bounds.push(quote! { core::default::Default });
3✔
546
    }
1,967✔
547

548
    if bounds.is_empty() {
1,970✔
549
        None
1,965✔
550
    } else {
551
        Some(quote! { #(#bounds)+* })
5✔
552
    }
553
}
1,970✔
554

555
/// Generates the `::facet::Field` definition `TokenStream` from a `PStructField`.
556
pub(crate) fn gen_field_from_pfield(
3,377✔
557
    field: &PStructField,
3,377✔
558
    struct_name: &Ident,
3,377✔
559
    bgp: &BoundedGenericParams,
3,377✔
560
    base_offset: Option<TokenStream>,
3,377✔
561
    facet_crate: &TokenStream,
3,377✔
562
) -> TokenStream {
3,377✔
563
    let field_name_effective = &field.name.effective;
3,377✔
564
    let field_name_raw = &field.name.raw;
3,377✔
565
    let field_type = &field.ty;
3,377✔
566

567
    let bgp_without_bounds = bgp.display_without_bounds();
3,377✔
568

569
    #[cfg(feature = "doc")]
570
    let doc_lines: Vec<String> = field
3,377✔
571
        .attrs
3,377✔
572
        .doc
3,377✔
573
        .iter()
3,377✔
574
        .map(|doc| doc.as_str().replace("\\\"", "\""))
3,377✔
575
        .collect();
3,377✔
576
    #[cfg(not(feature = "doc"))]
NEW
577
    let doc_lines: Vec<String> = Vec::new();
×
578

579
    // Check if this field is marked as a recursive type
580
    let is_recursive = field.attrs.has_builtin("recursive_type");
3,377✔
581

582
    // Generate the shape expression directly using the field type
583
    // For opaque fields, wrap in Opaque<T>
584
    // NOTE: Uses short alias from `use #facet_crate::𝟋::*` in the enclosing const block
585
    let shape_expr = if field.attrs.has_builtin("opaque") {
3,377✔
586
        quote! { <#facet_crate::Opaque<#field_type> as 𝟋Fct>::SHAPE }
33✔
587
    } else {
588
        quote! { <#field_type as 𝟋Fct>::SHAPE }
3,344✔
589
    };
590

591
    // All attributes go through grammar dispatch
592
    // Note: deserialize_with and serialize_with have been REMOVED from the grammar.
593
    // Use #[facet(proxy = Type)] for custom serialization instead.
594
    let mut attribute_list: Vec<TokenStream> = field
3,377✔
595
        .attrs
3,377✔
596
        .facet
3,377✔
597
        .iter()
3,377✔
598
        .map(|attr| {
3,377✔
599
            let ext_attr = emit_attr_for_field(attr, field_name_raw, field_type, facet_crate);
1,471✔
600
            quote! { #ext_attr }
1,471✔
601
        })
1,471✔
602
        .collect();
3,377✔
603

604
    // Generate proxy conversion function pointers when proxy attribute is present
605
    if let Some(attr) = field
3,377✔
606
        .attrs
3,377✔
607
        .facet
3,377✔
608
        .iter()
3,377✔
609
        .find(|a| a.is_builtin() && a.key_str() == "proxy")
3,377✔
610
    {
39✔
611
        let proxy_type = &attr.args;
39✔
612

39✔
613
        // Generate __proxy_in: converts proxy -> field type via TryFrom
39✔
614
        attribute_list.push(quote! {
39✔
615
            #facet_crate::ExtensionAttr {
39✔
616
                ns: ::core::option::Option::None,
39✔
617
                key: "__proxy_in",
39✔
618
                data: &const {
39✔
619
                    extern crate alloc as __alloc;
39✔
620
                    unsafe fn __proxy_convert_in<'mem>(
39✔
621
                        proxy_ptr: #facet_crate::PtrConst<'mem>,
39✔
622
                        field_ptr: #facet_crate::PtrUninit<'mem>,
39✔
623
                    ) -> ::core::result::Result<#facet_crate::PtrMut<'mem>, __alloc::string::String> {
39✔
624
                        let proxy: #proxy_type = proxy_ptr.read();
39✔
625
                        match <#field_type as ::core::convert::TryFrom<#proxy_type>>::try_from(proxy) {
39✔
626
                            ::core::result::Result::Ok(value) => ::core::result::Result::Ok(field_ptr.put(value)),
39✔
627
                            ::core::result::Result::Err(e) => ::core::result::Result::Err(__alloc::string::ToString::to_string(&e)),
39✔
628
                        }
39✔
629
                    }
39✔
630
                    __proxy_convert_in as #facet_crate::ProxyConvertInFn
39✔
631
                } as *const #facet_crate::ProxyConvertInFn as *const (),
39✔
632
                shape: <() as #facet_crate::Facet>::SHAPE,
39✔
633
            }
39✔
634
        });
39✔
635

39✔
636
        // Generate __proxy_out: converts &field type -> proxy via TryFrom
39✔
637
        attribute_list.push(quote! {
39✔
638
            #facet_crate::ExtensionAttr {
39✔
639
                ns: ::core::option::Option::None,
39✔
640
                key: "__proxy_out",
39✔
641
                data: &const {
39✔
642
                    extern crate alloc as __alloc;
39✔
643
                    unsafe fn __proxy_convert_out<'mem>(
39✔
644
                        field_ptr: #facet_crate::PtrConst<'mem>,
39✔
645
                        proxy_ptr: #facet_crate::PtrUninit<'mem>,
39✔
646
                    ) -> ::core::result::Result<#facet_crate::PtrMut<'mem>, __alloc::string::String> {
39✔
647
                        let field_ref: &#field_type = field_ptr.get();
39✔
648
                        match <#proxy_type as ::core::convert::TryFrom<&#field_type>>::try_from(field_ref) {
39✔
649
                            ::core::result::Result::Ok(proxy) => ::core::result::Result::Ok(proxy_ptr.put(proxy)),
39✔
650
                            ::core::result::Result::Err(e) => ::core::result::Result::Err(__alloc::string::ToString::to_string(&e)),
39✔
651
                        }
39✔
652
                    }
39✔
653
                    __proxy_convert_out as #facet_crate::ProxyConvertOutFn
39✔
654
                } as *const #facet_crate::ProxyConvertOutFn as *const (),
39✔
655
                shape: <() as #facet_crate::Facet>::SHAPE,
39✔
656
            }
39✔
657
        });
39✔
658
    }
3,338✔
659

660
    let maybe_attributes = if attribute_list.is_empty() {
3,377✔
661
        quote! { &[] }
2,238✔
662
    } else {
663
        quote! { &const {[#(#attribute_list),*]} }
1,139✔
664
    };
665

666
    let maybe_field_doc = if doc_lines.is_empty() {
3,377✔
667
        quote! { &[] }
3,313✔
668
    } else {
669
        quote! { &[#(#doc_lines),*] }
64✔
670
    };
671

672
    // Calculate the final offset, incorporating the base_offset if present
673
    let final_offset = match base_offset {
3,377✔
674
        Some(base) => {
127✔
675
            quote! { #base + ::core::mem::offset_of!(#struct_name #bgp_without_bounds, #field_name_raw) }
127✔
676
        }
677
        None => {
678
            quote! { ::core::mem::offset_of!(#struct_name #bgp_without_bounds, #field_name_raw) }
3,250✔
679
        }
680
    };
681

682
    // Use FieldBuilder for more compact generated code
683
    // NOTE: Uses short alias from `use #facet_crate::𝟋::*` in the enclosing const block
684
    //
685
    // For most fields, use `new` with a direct shape reference (more efficient).
686
    // For recursive type fields (marked with #[facet(recursive_type)]), use `new_lazy`
687
    // with a closure to break cycles.
688
    let builder = if is_recursive {
3,377✔
689
        quote! {
7✔
690
            𝟋FldB::new_lazy(
691
                #field_name_effective,
692
                || #shape_expr,
693
                #final_offset,
694
            )
695
        }
696
    } else {
697
        quote! {
3,370✔
698
            𝟋FldB::new(
699
                #field_name_effective,
700
                #shape_expr,
701
                #final_offset,
702
            )
703
        }
704
    };
705

706
    // Only chain .attributes() and .doc() if they have values
707
    let has_attrs = !attribute_list.is_empty();
3,377✔
708
    let has_doc = !doc_lines.is_empty();
3,377✔
709

710
    if has_attrs && has_doc {
3,377✔
711
        quote! { #builder.attributes(#maybe_attributes).doc(#maybe_field_doc).build() }
49✔
712
    } else if has_attrs {
3,328✔
713
        quote! { #builder.attributes(#maybe_attributes).build() }
1,090✔
714
    } else if has_doc {
2,238✔
715
        quote! { #builder.doc(#maybe_field_doc).build() }
15✔
716
    } else {
717
        quote! { #builder.build() }
2,223✔
718
    }
719
}
3,377✔
720

721
/// Processes a regular struct to implement Facet
722
///
723
/// Example input:
724
/// ```rust
725
/// struct Blah {
726
///     foo: u32,
727
///     bar: String,
728
/// }
729
/// ```
730
pub(crate) fn process_struct(parsed: Struct) -> TokenStream {
1,694✔
731
    let ps = PStruct::parse(&parsed); // Use the parsed representation
1,694✔
732

733
    // Emit any collected errors as compile_error! with proper spans
734
    if !ps.container.attrs.errors.is_empty() {
1,694✔
735
        let errors = ps.container.attrs.errors.iter().map(|e| {
×
736
            let msg = &e.message;
×
737
            let span = e.span;
×
738
            quote_spanned! { span => compile_error!(#msg); }
×
739
        });
×
740
        return quote! { #(#errors)* };
×
741
    }
1,694✔
742

743
    let struct_name_ident = format_ident!("{}", ps.container.name);
1,694✔
744
    let struct_name = &ps.container.name;
1,694✔
745
    let struct_name_str = struct_name.to_string();
1,694✔
746

747
    let opaque = ps.container.attrs.has_builtin("opaque");
1,694✔
748

749
    // Get the facet crate path (custom or default ::facet)
750
    let facet_crate = ps.container.attrs.facet_crate();
1,694✔
751

752
    let type_name_fn =
1,694✔
753
        generate_type_name_fn(struct_name, parsed.generics.as_ref(), opaque, &facet_crate);
1,694✔
754

755
    // Determine trait sources and generate vtable accordingly
756
    let trait_sources = TraitSources::from_attrs(&ps.container.attrs);
1,694✔
757
    let vtable_code = gen_vtable(&facet_crate, &type_name_fn, &trait_sources);
1,694✔
758
    let vtable_init = quote! { const { #vtable_code } };
1,694✔
759

760
    // TODO: I assume the `PrimitiveRepr` is only relevant for enums, and does not need to be preserved?
761
    // NOTE: Uses short aliases from `use #facet_crate::𝟋::*` in the const block
762
    let repr = match &ps.container.attrs.repr {
1,694✔
763
        PRepr::Transparent => quote! { 𝟋Repr::TRANSPARENT },
4✔
764
        PRepr::Rust(_) => quote! { 𝟋Repr::RUST },
1,689✔
765
        PRepr::C(_) => quote! { 𝟋Repr::C },
1✔
766
        PRepr::RustcWillCatch => {
767
            // rustc will emit an error for the invalid repr.
768
            // Return empty TokenStream so we don't add misleading errors.
769
            return quote! {};
×
770
        }
771
    };
772

773
    // Use PStruct for kind and fields
774
    let (kind, fields_vec) = match &ps.kind {
1,694✔
775
        PStructKind::Struct { fields } => {
1,591✔
776
            let kind = quote!(𝟋Sk::Struct);
1,591✔
777
            let fields_vec = fields
1,591✔
778
                .iter()
1,591✔
779
                .map(|field| {
2,665✔
780
                    gen_field_from_pfield(field, struct_name, &ps.container.bgp, None, &facet_crate)
2,665✔
781
                })
2,665✔
782
                .collect::<Vec<_>>();
1,591✔
783
            (kind, fields_vec)
1,591✔
784
        }
785
        PStructKind::TupleStruct { fields } => {
95✔
786
            let kind = quote!(𝟋Sk::TupleStruct);
95✔
787
            let fields_vec = fields
95✔
788
                .iter()
95✔
789
                .map(|field| {
118✔
790
                    gen_field_from_pfield(field, struct_name, &ps.container.bgp, None, &facet_crate)
118✔
791
                })
118✔
792
                .collect::<Vec<_>>();
95✔
793
            (kind, fields_vec)
95✔
794
        }
795
        PStructKind::UnitStruct => {
796
            let kind = quote!(𝟋Sk::Unit);
8✔
797
            (kind, vec![])
8✔
798
        }
799
    };
800

801
    // Still need original AST for where clauses and type params for build_ helpers
802
    let where_clauses_ast = match &parsed.kind {
1,694✔
803
        StructKind::Struct { clauses, .. } => clauses.as_ref(),
1,591✔
804
        StructKind::TupleStruct { clauses, .. } => clauses.as_ref(),
95✔
805
        StructKind::UnitStruct { clauses, .. } => clauses.as_ref(),
8✔
806
    };
807
    let where_clauses = build_where_clauses(
1,694✔
808
        where_clauses_ast,
1,694✔
809
        parsed.generics.as_ref(),
1,694✔
810
        opaque,
1,694✔
811
        &facet_crate,
1,694✔
812
    );
813
    let type_params_call = build_type_params_call(parsed.generics.as_ref(), opaque, &facet_crate);
1,694✔
814

815
    // Static decl using PStruct BGP
816
    let static_decl = if ps.container.bgp.params.is_empty() {
1,694✔
817
        generate_static_decl(struct_name, &facet_crate)
1,628✔
818
    } else {
819
        TokenStream::new()
66✔
820
    };
821

822
    // Doc comments from PStruct - returns value for struct literal
823
    // doc call - only emit if there are doc comments and doc feature is enabled
824
    #[cfg(feature = "doc")]
825
    let doc_call = if ps.container.attrs.doc.is_empty() {
1,694✔
826
        quote! {}
1,662✔
827
    } else {
828
        let doc_lines = ps.container.attrs.doc.iter().map(|s| quote!(#s));
118✔
829
        quote! { .doc(&[#(#doc_lines),*]) }
32✔
830
    };
831
    #[cfg(not(feature = "doc"))]
NEW
832
    let doc_call = quote! {};
×
833

834
    // Container attributes - most go through grammar dispatch
835
    // Filter out `invariants` and `crate` since they're handled specially
836
    // Returns builder call only if there are attributes
837
    let attributes_call = {
1,694✔
838
        let items: Vec<TokenStream> = ps
1,694✔
839
            .container
1,694✔
840
            .attrs
1,694✔
841
            .facet
1,694✔
842
            .iter()
1,694✔
843
            .filter(|attr| {
1,694✔
844
                // These attributes are handled specially and not emitted to runtime:
845
                // - invariants: populates vtable.invariants
846
                // - crate: sets the facet crate path
847
                // - traits: compile-time directive for vtable generation
848
                // - auto_traits: compile-time directive for vtable generation
849
                if attr.is_builtin() {
163✔
850
                    let key = attr.key_str();
148✔
851
                    !matches!(
34✔
852
                        key.as_str(),
148✔
853
                        "invariants" | "crate" | "traits" | "auto_traits"
148✔
854
                    )
855
                } else {
856
                    true
15✔
857
                }
858
            })
163✔
859
            .map(|attr| {
1,694✔
860
                let ext_attr = emit_attr(attr, &facet_crate);
129✔
861
                quote! { #ext_attr }
129✔
862
            })
129✔
863
            .collect();
1,694✔
864

865
        if items.is_empty() {
1,694✔
866
            quote! {}
1,585✔
867
        } else {
868
            quote! { .attributes(&const {[#(#items),*]}) }
109✔
869
        }
870
    };
871

872
    // Type tag from PStruct - returns builder call only if present
873
    let type_tag_call = {
1,694✔
874
        if let Some(type_tag) = ps.container.attrs.get_builtin_args("type_tag") {
1,694✔
875
            quote! { .type_tag(#type_tag) }
8✔
876
        } else {
877
            quote! {}
1,686✔
878
        }
879
    };
880

881
    // Invariants from PStruct - extract invariant function expressions
882
    let invariant_maybe = {
1,694✔
883
        let invariant_exprs: Vec<&TokenStream> = ps
1,694✔
884
            .container
1,694✔
885
            .attrs
1,694✔
886
            .facet
1,694✔
887
            .iter()
1,694✔
888
            .filter(|attr| attr.is_builtin() && attr.key_str() == "invariants")
1,694✔
889
            .map(|attr| &attr.args)
1,694✔
890
            .collect();
1,694✔
891

892
        if !invariant_exprs.is_empty() {
1,694✔
893
            let tests = invariant_exprs.iter().map(|expr| {
3✔
894
                quote! {
3✔
895
                    if !#expr(value) {
896
                        return false;
897
                    }
898
                }
899
            });
3✔
900

901
            let bgp_display = ps.container.bgp.display_without_bounds();
3✔
902
            quote! {
3✔
903
                unsafe fn invariants<'mem>(value: #facet_crate::PtrConst<'mem>) -> bool {
904
                    let value = value.get::<#struct_name_ident #bgp_display>();
905
                    #(#tests)*
906
                    true
907
                }
908

909
                {
910
                    vtable.invariants = Some(invariants);
911
                }
912
            }
913
        } else {
914
            quote! {}
1,691✔
915
        }
916
    };
917

918
    // Transparent logic using PStruct
919
    let inner_field = if ps.container.attrs.has_builtin("transparent") {
1,694✔
920
        match &ps.kind {
36✔
921
            PStructKind::TupleStruct { fields } => {
36✔
922
                if fields.len() > 1 {
36✔
923
                    return quote! {
×
924
                        compile_error!("Transparent structs must be tuple structs with zero or one field");
925
                    };
926
                }
36✔
927
                fields.first().cloned() // Use first field if it exists, None otherwise (ZST case)
36✔
928
            }
929
            _ => {
930
                return quote! {
×
931
                    compile_error!("Transparent structs must be tuple structs");
932
                };
933
            }
934
        }
935
    } else {
936
        None
1,658✔
937
    };
938

939
    // Add try_from_inner implementation for transparent types
940
    let try_from_inner_code = if ps.container.attrs.has_builtin("transparent") {
1,694✔
941
        if let Some(inner_field) = &inner_field {
36✔
942
            if !inner_field.attrs.has_builtin("opaque") {
36✔
943
                // Transparent struct with one field
944
                let inner_field_type = &inner_field.ty;
34✔
945
                let bgp_without_bounds = ps.container.bgp.display_without_bounds();
34✔
946

947
                quote! {
34✔
948
                    // Define the try_from function for the value vtable
949
                    unsafe fn try_from<'src, 'dst>(
950
                        src_ptr: #facet_crate::PtrConst<'src>,
951
                        src_shape: &'static #facet_crate::Shape,
952
                        dst: #facet_crate::PtrUninit<'dst>
953
                    ) -> Result<#facet_crate::PtrMut<'dst>, #facet_crate::TryFromError> {
954
                        // Try the inner type's try_from function if it exists
955
                        let inner_result = match <#inner_field_type as #facet_crate::Facet>::SHAPE.vtable.try_from {
956
                            Some(inner_try) => unsafe { (inner_try)(src_ptr, src_shape, dst) },
957
                            None => Err(#facet_crate::TryFromError::UnsupportedSourceShape {
958
                                src_shape,
959
                                expected: const { &[ &<#inner_field_type as #facet_crate::Facet>::SHAPE ] },
960
                            })
961
                        };
962

963
                        match inner_result {
964
                            Ok(result) => Ok(result),
965
                            Err(_) => {
966
                                // If inner_try failed, check if source shape is exactly the inner shape
967
                                if src_shape != <#inner_field_type as #facet_crate::Facet>::SHAPE {
968
                                    return Err(#facet_crate::TryFromError::UnsupportedSourceShape {
969
                                        src_shape,
970
                                        expected: const { &[ &<#inner_field_type as #facet_crate::Facet>::SHAPE ] },
971
                                    });
972
                                }
973
                                // Read the inner value and construct the wrapper.
974
                                let inner: #inner_field_type = unsafe { src_ptr.read() };
975
                                Ok(unsafe { dst.put(inner) }) // Construct wrapper
976
                            }
977
                        }
978
                    }
979

980
                    // Define the try_into_inner function for the value vtable
981
                    unsafe fn try_into_inner<'src, 'dst>(
982
                        src_ptr: #facet_crate::PtrMut<'src>,
983
                        dst: #facet_crate::PtrUninit<'dst>
984
                    ) -> Result<#facet_crate::PtrMut<'dst>, #facet_crate::TryIntoInnerError> {
985
                        let wrapper = unsafe { src_ptr.get::<#struct_name_ident #bgp_without_bounds>() };
986
                        Ok(unsafe { dst.put(wrapper.0.clone()) }) // Assume tuple struct field 0
987
                    }
988

989
                    // Define the try_borrow_inner function for the value vtable
990
                    unsafe fn try_borrow_inner<'src>(
991
                        src_ptr: #facet_crate::PtrConst<'src>
992
                    ) -> Result<#facet_crate::PtrConst<'src>, #facet_crate::TryBorrowInnerError> {
993
                        let wrapper = unsafe { src_ptr.get::<#struct_name_ident #bgp_without_bounds>() };
994
                        // Return a pointer to the inner field (field 0 for tuple struct)
995
                        Ok(#facet_crate::PtrConst::new(::core::ptr::NonNull::from(&wrapper.0)))
996
                    }
997

998
                    {
999
                        vtable.try_from = Some(try_from);
1000
                        vtable.try_into_inner = Some(try_into_inner);
1001
                        vtable.try_borrow_inner = Some(try_borrow_inner);
1002
                    }
1003
                }
1004
            } else {
1005
                quote! {} // No try_from can be done for opaque
2✔
1006
            }
1007
        } else {
1008
            // Transparent ZST struct (like struct Unit;)
1009
            quote! {
×
1010
                // Define the try_from function for the value vtable (ZST case)
1011
                unsafe fn try_from<'src, 'dst>(
1012
                    src_ptr: #facet_crate::PtrConst<'src>,
1013
                    src_shape: &'static #facet_crate::Shape,
1014
                    dst: #facet_crate::PtrUninit<'dst>
1015
                ) -> Result<#facet_crate::PtrMut<'dst>, #facet_crate::TryFromError> {
1016
                    if src_shape.layout.size() == 0 {
1017
                         Ok(unsafe { dst.put(#struct_name_ident) }) // Construct ZST
1018
                    } else {
1019
                        Err(#facet_crate::TryFromError::UnsupportedSourceShape {
1020
                            src_shape,
1021
                            expected: const { &[ <() as #facet_crate::Facet>::SHAPE ] }, // Expect unit-like shape
1022
                        })
1023
                    }
1024
                }
1025

1026
                {
1027
                    vtable.try_from = Some(try_from);
1028
                }
1029

1030
                // ZSTs cannot be meaningfully borrowed or converted *into* an inner value
1031
                // try_into_inner and try_borrow_inner remain None
1032
            }
1033
        }
1034
    } else {
1035
        quote! {} // Not transparent
1,658✔
1036
    };
1037

1038
    // Generate the inner shape field value for transparent types
1039
    // inner call - only emit for transparent types
1040
    let inner_call = if ps.container.attrs.has_builtin("transparent") {
1,694✔
1041
        let inner_shape_val = if let Some(inner_field) = &inner_field {
36✔
1042
            let ty = &inner_field.ty;
36✔
1043
            if inner_field.attrs.has_builtin("opaque") {
36✔
1044
                quote! { <#facet_crate::Opaque<#ty> as #facet_crate::Facet>::SHAPE }
2✔
1045
            } else {
1046
                quote! { <#ty as #facet_crate::Facet>::SHAPE }
34✔
1047
            }
1048
        } else {
1049
            // Transparent ZST case
1050
            quote! { <() as #facet_crate::Facet>::SHAPE }
×
1051
        };
1052
        quote! { .inner(#inner_shape_val) }
36✔
1053
    } else {
1054
        quote! {}
1,658✔
1055
    };
1056

1057
    // Generics from PStruct
1058
    let facet_bgp = ps
1,694✔
1059
        .container
1,694✔
1060
        .bgp
1,694✔
1061
        .with_lifetime(LifetimeName(format_ident!("ʄ")));
1,694✔
1062
    let bgp_def = facet_bgp.display_with_bounds();
1,694✔
1063
    let bgp_without_bounds = ps.container.bgp.display_without_bounds();
1,694✔
1064

1065
    let (ty_field, fields) = if opaque {
1,694✔
1066
        (
2✔
1067
            quote! {
2✔
1068
                #facet_crate::Type::User(#facet_crate::UserType::Opaque)
2✔
1069
            },
2✔
1070
            quote! {},
2✔
1071
        )
2✔
1072
    } else {
1073
        // Optimize: use &[] for empty fields to avoid const block overhead
1074
        if fields_vec.is_empty() {
1,692✔
1075
            (
19✔
1076
                quote! {
19✔
1077
                    𝟋Ty::User(𝟋UTy::Struct(
19✔
1078
                        𝟋STyB::new(#kind, &[]).repr(#repr).build()
19✔
1079
                    ))
19✔
1080
                },
19✔
1081
                quote! {},
19✔
1082
            )
19✔
1083
        } else {
1084
            // Inline the const block directly into the builder call
1085
            (
1086
                quote! {
1,673✔
1087
                    𝟋Ty::User(𝟋UTy::Struct(
1088
                        𝟋STyB::new(#kind, &const {[#(#fields_vec),*]}).repr(#repr).build()
1089
                    ))
1090
                },
1091
                quote! {},
1,673✔
1092
            )
1093
        }
1094
    };
1095

1096
    // Generate code to suppress dead_code warnings on structs constructed via reflection.
1097
    // When structs are constructed via reflection (e.g., facet_args::from_std_args()),
1098
    // the compiler doesn't see them being used and warns about dead code.
1099
    // This function ensures the struct type is "used" from the compiler's perspective.
1100
    // See: https://github.com/facet-rs/facet/issues/996
1101
    let dead_code_suppression = quote! {
1,694✔
1102
        const _: () = {
1103
            #[allow(dead_code, clippy::multiple_bound_locations)]
1104
            fn __facet_use_struct #bgp_def (__v: &#struct_name_ident #bgp_without_bounds) #where_clauses {
1105
                let _ = __v;
1106
            }
1107
        };
1108
    };
1109

1110
    // Generate static assertions for declared traits (catches lies at compile time)
1111
    // We put this in a generic function outside the const block so it can reference generic parameters
1112
    let facet_default = ps.container.attrs.has_builtin("default");
1,694✔
1113
    let trait_assertion_fn = if let Some(bounds) =
1,694✔
1114
        gen_trait_bounds(ps.container.attrs.declared_traits.as_ref(), facet_default)
1,694✔
1115
    {
1116
        // Note: where_clauses already includes "where" keyword if non-empty
1117
        // We need to add the trait bounds as an additional constraint
1118
        quote! {
5✔
1119
            const _: () = {
1120
                #[allow(dead_code, clippy::multiple_bound_locations)]
1121
                fn __facet_assert_traits #bgp_def (_: &#struct_name_ident #bgp_without_bounds)
1122
                where
1123
                    #struct_name_ident #bgp_without_bounds: #bounds
1124
                {}
1125
            };
1126
        }
1127
    } else {
1128
        quote! {}
1,689✔
1129
    };
1130

1131
    // Check if we need vtable mutations (invariants or transparent type functions)
1132
    let has_invariants = ps
1,694✔
1133
        .container
1,694✔
1134
        .attrs
1,694✔
1135
        .facet
1,694✔
1136
        .iter()
1,694✔
1137
        .any(|attr| attr.is_builtin() && attr.key_str() == "invariants");
1,694✔
1138
    let is_transparent = ps.container.attrs.has_builtin("transparent");
1,694✔
1139
    let needs_vtable_mutations = has_invariants || is_transparent;
1,694✔
1140

1141
    // Generate vtable field - use simpler form when no mutations needed
1142
    let vtable_field = if needs_vtable_mutations {
1,694✔
1143
        quote! {
39✔
1144
            {
1145
                let mut vtable = #vtable_init;
1146
                #invariant_maybe
1147
                #try_from_inner_code
1148
                vtable
1149
            }
1150
        }
1151
    } else {
1152
        quote! { #vtable_init }
1,655✔
1153
    };
1154

1155
    // Final quote block using refactored parts
1156
    let result = quote! {
1,694✔
1157
        #static_decl
1158

1159
        #dead_code_suppression
1160

1161
        #trait_assertion_fn
1162

1163
        #[automatically_derived]
1164
        unsafe impl #bgp_def #facet_crate::Facet<'ʄ> for #struct_name_ident #bgp_without_bounds #where_clauses {
1165
            const SHAPE: &'static #facet_crate::Shape = &const {
1166
                use #facet_crate::𝟋::*;
1167
                #fields
1168

1169
                𝟋ShpB::for_sized::<Self>(#type_name_fn, #struct_name_str)
1170
                    .vtable(#vtable_field)
1171
                    .ty(#ty_field)
1172
                    .def(𝟋Def::Undefined)
1173
                    #type_params_call
1174
                    #doc_call
1175
                    #attributes_call
1176
                    #type_tag_call
1177
                    #inner_call
1178
                    .build()
1179
            };
1180
        }
1181
    };
1182

1183
    result
1,694✔
1184
}
1,694✔
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