Skip to main content

zerocopy_derive/
util.rs

1// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT
2//
3// Copyright 2019 The Fuchsia Authors
4//
5// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
6// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
7// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
8// This file may not be copied, modified, or distributed except according to
9// those terms.
10
11use std::num::NonZeroU32;
12
13use proc_macro2::{Span, TokenStream};
14use quote::{quote, quote_spanned, ToTokens};
15use syn::{
16    parse::ParseBuffer, parse_quote, spanned::Spanned as _, token::PathSep, Data, DataEnum,
17    DataStruct, DataUnion, DeriveInput, Error, Expr, ExprLit, Field, GenericParam, Ident, Index,
18    Lit, LitStr, Meta, Path, Type, Variant, Visibility, WherePredicate,
19};
20
21use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned};
22
23pub(crate) struct Ctx {
24    pub(crate) ast: DeriveInput,
25    pub(crate) zerocopy_crate: Path,
26
27    // The value of the last `#[zerocopy(on_error = ...)]` attribute, or `false`
28    // if none is provided.
29    pub(crate) skip_on_error: bool,
30
31    // The span of the last `#[zerocopy(on_error = ...)]` attribute, if any.
32    pub(crate) on_error_span: Option<proc_macro2::Span>,
33
34    /// The first field invariant on the source type, if any.
35    pub(crate) invariant_span: Option<Span>,
36}
37
38#[derive(Eq, PartialEq)]
39enum CratePath {
40    External,
41    CrateRelative,
42    ModuleRelative,
43}
44
45fn validate_crate_path(path: &Path) -> Result<CratePath, ()> {
46    if path.segments.is_empty() {
47        return Err(());
48    }
49
50    enum ModuleRelative {
51        Yes,
52        No,
53    }
54    let first = path.segments[0].ident.to_string();
55    let (mut prev_segment, path_type) = match first.as_str() {
56        "Self" => return Err(()),
57        "crate" => {
58            if path.leading_colon.is_some() {
59                return Err(());
60            }
61            (ModuleRelative::No, CratePath::CrateRelative)
62        }
63        "self" | "super" => {
64            if path.leading_colon.is_some() {
65                return Err(());
66            }
67            (ModuleRelative::Yes, CratePath::ModuleRelative)
68        }
69        _ => (ModuleRelative::No, CratePath::External),
70    };
71
72    for seg in path.segments.iter().skip(1) {
73        let ident = seg.ident.to_string();
74        match ident.as_str() {
75            "Self" | "crate" | "self" => return Err(()),
76            "super" => match prev_segment {
77                ModuleRelative::Yes => {}
78                ModuleRelative::No => return Err(()),
79            },
80            _ => {
81                prev_segment = ModuleRelative::No;
82            }
83        }
84    }
85    Ok(path_type)
86}
87
88impl Ctx {
89    /// Attempt to extract a crate path from the provided attributes. Defaults to
90    /// `::zerocopy` if not found.
91    pub(crate) fn try_from_derive_input(ast: DeriveInput) -> Result<Self, Error> {
92        let mut path = parse_quote!(::zerocopy);
93        let mut skip_on_error = false;
94        let mut on_error_span = None;
95
96        for attr in &ast.attrs {
97            if let Meta::List(ref meta_list) = attr.meta {
98                if path_is_ident(&meta_list.path, "zerocopy") {
99                    attr.parse_nested_meta(|meta| {
100                        if path_is_ident(&meta.path, "crate") {
101                            let expr = meta.value().and_then(ParseBuffer::parse);
102                            if let Ok(Expr::Lit(ExprLit { lit: Lit::Str(lit), .. })) = expr {
103                                if let Ok(mut path_lit) = lit.parse_with(Path::parse_mod_style) {
104                                    if let Ok(crate_path) = validate_crate_path(&path_lit) {
105                                        // If not expressly relative, absolutize.
106                                        if path_lit.leading_colon.is_none() && crate_path == CratePath::External {
107                                            path_lit.leading_colon = Some(PathSep::default());
108                                        }
109                                        path = path_lit;
110                                        return Ok(());
111                                    }
112
113                                    return Err(Error::new(
114                                        lit.span(),
115                                        "`crate` attribute requires a valid module path",
116                                    ));
117                                }
118
119                                return Err(Error::new(
120                                    lit.span(),
121                                    "`crate` attribute requires a path as the value",
122                                ));
123                            }
124
125                            return Err(Error::new(
126                                meta.path.span(),
127                                "`crate` attribute requires a path as the value",
128                            ));
129                        }
130
131                        if path_is_ident(&meta.path, "on_error") {
132                            on_error_span = Some(meta.path.span());
133                            let value = meta.value()?;
134                            let s: LitStr = value.parse()?;
135                            match s.value().as_str() {
136                                "skip" => skip_on_error = true,
137                                "fail" => skip_on_error = false,
138                                _ => return Err(Error::new(
139                                    s.span(),
140                                    "unrecognized value for `on_error` attribute from `zerocopy`; expected `skip` or `fail`",
141                                )),
142                            }
143                            return Ok(());
144                        }
145
146                        Err(Error::new(
147                            Span::call_site(),
148                            format!(
149                                "unknown attribute encountered: {}",
150                                meta.path.into_token_stream()
151                            ),
152                        ))
153                    })?;
154                }
155            }
156        }
157
158        let invariant_span = crate::invariant::validate(&ast)?;
159        Ok(Self { ast, zerocopy_crate: path, skip_on_error, on_error_span, invariant_span })
160    }
161
162    pub(crate) fn with_input(&self, input: &DeriveInput) -> Self {
163        Self {
164            ast: input.clone(),
165            zerocopy_crate: self.zerocopy_crate.clone(),
166            skip_on_error: self.skip_on_error,
167            on_error_span: self.on_error_span,
168            invariant_span: self.invariant_span,
169        }
170    }
171
172    pub(crate) fn skip_on_error(mut self) -> Self {
173        self.skip_on_error = true;
174        self
175    }
176
177    pub(crate) fn core_path(&self) -> TokenStream {
178        let zerocopy_crate = &self.zerocopy_crate;
179        quote!(#zerocopy_crate::util::macro_util::core_reexport)
180    }
181
182    /// Choose an implementation name absent from the caller's tokens. This is
183    /// best-effort; names introduced by nested macros are not considered.
184    /// Unlike local variables, type parameters are not hidden by mixed-site
185    /// hygiene.
186    pub(crate) fn fresh_ident(&self, name: &str) -> Ident {
187        let idents = crate::invariant::idents(self.ast.to_token_stream());
188        let mut name = name.to_owned();
189        while idents.iter().any(|ident| ident.to_string().trim_start_matches("r#") == name) {
190            name.push('_');
191        }
192        Ident::new(&name, Span::mixed_site())
193    }
194
195    /// Caller-authored invariant expressions must inherit the caller's lint
196    /// policy, rather than the blanket allowances for generated glue.
197    pub(crate) fn const_block(
198        &self,
199        items: impl IntoIterator<Item = Option<TokenStream>>,
200    ) -> TokenStream {
201        if self.invariant_span.is_none() {
202            const_block(items)
203        } else {
204            let items = items.into_iter().flatten();
205            quote! { const _: () = { #(#items)* }; }
206        }
207    }
208
209    pub(crate) fn cfg_compile_error(&self) -> TokenStream {
210        // By checking both during the compilation of the proc macro *and* in
211        // the generated code, we ensure that each cfg need only be passed when
212        // compiling this crate *or* when compiling the user's crate. The former
213        // is preferable, but in some situations (such as when cross-compiling
214        // using `cargo build --target`), it doesn't get propagated to this
215        // crate's build by default.
216        let on_error = if cfg!(zerocopy_unstable_linux) {
217            quote!()
218        } else if let Some(span) = self.on_error_span {
219            let core = self.core_path();
220            let error_message =
221                "`on_error` is experimental; pass '--cfg zerocopy_unstable_linux' to enable";
222            quote::quote_spanned! {span=>
223                #[allow(unused_attributes, unexpected_cfgs)]
224                const _: () = {
225                    #[cfg(not(zerocopy_unstable_linux))]
226                    #core::compile_error!(#error_message);
227                };
228            }
229        } else {
230            quote!()
231        };
232        let invariant = if cfg!(zerocopy_unstable_ptr) {
233            quote!()
234        } else if let Some(span) = self.invariant_span {
235            let core = self.core_path();
236            let error_message =
237                "`invariant` is experimental; pass '--cfg zerocopy_unstable_ptr' to enable";
238            quote::quote_spanned! {span=>
239                #[allow(unused_attributes, unexpected_cfgs)]
240                const _: () = {
241                    #[cfg(not(zerocopy_unstable_ptr))]
242                    #core::compile_error!(#error_message);
243                };
244            }
245        } else {
246            quote!()
247        };
248        quote!(#on_error #invariant)
249    }
250
251    pub(crate) fn error_or_skip<E>(&self, error: E) -> Result<TokenStream, E> {
252        if self.skip_on_error {
253            Ok(self.cfg_compile_error())
254        } else {
255            Err(error)
256        }
257    }
258}
259
260pub(crate) trait DataExt {
261    /// Extracts the names and types of all fields. For enums, extracts the
262    /// names and types of fields from each variant. For tuple structs, the
263    /// names are the indices used to index into the struct (ie, `0`, `1`, etc).
264    ///
265    /// FIXME: Extracting field names for enums doesn't really make sense. Types
266    /// makes sense because we don't care about where they live - we just care
267    /// about transitive ownership. But for field names, we'd only use them when
268    /// generating is_safe, which cares about where they live.
269    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)>;
270
271    fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)>;
272
273    fn tag(&self) -> Option<Ident>;
274}
275
276impl DataExt for Data {
277    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
278        match self {
279            Data::Struct(strc) => strc.fields(),
280            Data::Enum(enm) => enm.fields(),
281            Data::Union(un) => un.fields(),
282        }
283    }
284
285    fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
286        match self {
287            Data::Struct(strc) => strc.variants(),
288            Data::Enum(enm) => enm.variants(),
289            Data::Union(un) => un.variants(),
290        }
291    }
292
293    fn tag(&self) -> Option<Ident> {
294        match self {
295            Data::Struct(strc) => strc.tag(),
296            Data::Enum(enm) => enm.tag(),
297            Data::Union(un) => un.tag(),
298        }
299    }
300}
301
302impl DataExt for DataStruct {
303    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
304        map_fields(&self.fields)
305    }
306
307    fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
308        vec![(None, self.fields())]
309    }
310
311    fn tag(&self) -> Option<Ident> {
312        None
313    }
314}
315
316impl DataExt for DataEnum {
317    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
318        map_fields(self.variants.iter().flat_map(|var| &var.fields))
319    }
320
321    fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
322        self.variants.iter().map(|var| (Some(var), map_fields(&var.fields))).collect()
323    }
324
325    fn tag(&self) -> Option<Ident> {
326        Some(Ident::new("___ZerocopyTag", Span::call_site()))
327    }
328}
329
330impl DataExt for DataUnion {
331    fn fields(&self) -> Vec<(&Visibility, TokenStream, &Type)> {
332        map_fields(&self.fields.named)
333    }
334
335    fn variants(&self) -> Vec<(Option<&Variant>, Vec<(&Visibility, TokenStream, &Type)>)> {
336        vec![(None, self.fields())]
337    }
338
339    fn tag(&self) -> Option<Ident> {
340        None
341    }
342}
343
344fn map_fields<'a>(
345    fields: impl 'a + IntoIterator<Item = &'a Field>,
346) -> Vec<(&'a Visibility, TokenStream, &'a Type)> {
347    fields
348        .into_iter()
349        .enumerate()
350        .map(|(idx, f)| {
351            (
352                &f.vis,
353                f.ident
354                    .as_ref()
355                    .map(ToTokens::to_token_stream)
356                    .unwrap_or_else(|| Index::from(idx).to_token_stream()),
357                &f.ty,
358            )
359        })
360        .collect()
361}
362
363pub(crate) fn to_ident_str(t: &impl ToString) -> String {
364    let s = t.to_string();
365    if let Some(stripped) = s.strip_prefix("r#") {
366        stripped.to_string()
367    } else {
368        s
369    }
370}
371
372/// Does `path` consist solely of the identifier `expected`?
373///
374/// Unlike [`Path::is_ident`], this treats a raw identifier and its ordinary
375/// spelling as the same identifier. Rust applies that same normalization when
376/// interpreting attribute names and arguments.
377pub(crate) fn path_is_ident(path: &Path, expected: &str) -> bool {
378    match path.get_ident() {
379        Some(ident) => to_ident_str(ident) == expected,
380        None => false,
381    }
382}
383
384/// This enum describes what kind of padding check needs to be generated for the
385/// associated impl.
386pub(crate) enum PaddingCheck {
387    /// Check that the sum of the fields' sizes exactly equals the struct's
388    /// size.
389    Struct,
390    /// Check that a `repr(C)` struct has no padding.
391    ReprCStruct,
392    /// Check that the size of each field exactly equals the union's size.
393    Union,
394    /// Check that every variant of the enum contains no padding.
395    ///
396    /// Because doing so requires a tag enum, this padding check requires an
397    /// additional `TokenStream` which defines the tag enum as `___ZerocopyTag`.
398    Enum { tag_type_definition: TokenStream },
399}
400
401impl PaddingCheck {
402    /// Returns the idents of the trait to use and the macro to call in order to
403    /// validate that a type passes the relevant padding check.
404    pub(crate) fn validator_trait_and_macro_idents(&self) -> (Ident, Ident) {
405        let (trt, mcro) = match self {
406            PaddingCheck::Struct => ("PaddingFree", "struct_padding"),
407            PaddingCheck::ReprCStruct => ("DynamicPaddingFree", "repr_c_struct_has_padding"),
408            PaddingCheck::Union => ("PaddingFree", "union_padding"),
409            PaddingCheck::Enum { .. } => ("PaddingFree", "enum_padding"),
410        };
411
412        let trt = Ident::new(trt, Span::call_site());
413        let mcro = Ident::new(mcro, Span::call_site());
414        (trt, mcro)
415    }
416
417    /// Sometimes performing the padding check requires some additional
418    /// "context" code. For enums, this is the definition of the tag enum.
419    pub(crate) fn validator_macro_context(&self) -> Option<&TokenStream> {
420        match self {
421            PaddingCheck::Struct | PaddingCheck::ReprCStruct | PaddingCheck::Union => None,
422            PaddingCheck::Enum { tag_type_definition } => Some(tag_type_definition),
423        }
424    }
425}
426
427#[derive(Copy, Clone)]
428pub(crate) enum Client {
429    ProjectDerive,
430    TryFromBytesDerive,
431}
432
433impl ToTokens for Client {
434    fn to_tokens(&self, tokens: &mut TokenStream) {
435        let s = match self {
436            Client::ProjectDerive => "ProjectDerive",
437            Client::TryFromBytesDerive => "TryFromBytesDerive",
438        };
439        let ident = Ident::new(s, Span::call_site());
440        tokens.extend(quote!(project_clients::#ident));
441    }
442}
443
444impl Client {
445    pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path {
446        let zerocopy_crate = &ctx.zerocopy_crate;
447        parse_quote!(#zerocopy_crate::#self)
448    }
449}
450
451/// Bundles the trait parameters for `HasField` and `ProjectField`.
452#[derive(Clone)]
453pub(crate) struct FieldProjection {
454    pub(crate) variant_id: Box<Expr>,
455    pub(crate) field: Box<Type>,
456    pub(crate) field_id: Box<Expr>,
457}
458
459#[derive(Clone)]
460pub(crate) enum Trait {
461    KnownLayout,
462    HasTag { client: Client },
463    HasField { client: Client, projection: FieldProjection },
464    ProjectField { client: Client, projection: FieldProjection, invariants: Box<Type> },
465    Immutable,
466    Project,
467    TryFromBytes,
468    FromZeros,
469    FromBytes,
470    IntoBytes,
471    Unaligned,
472    Sized,
473    ByteHash,
474    ByteEq,
475    SplitAt,
476}
477
478impl ToTokens for Trait {
479    fn to_tokens(&self, tokens: &mut TokenStream) {
480        // According to [1], the format of the derived `Debug`` output is not
481        // stable and therefore not guaranteed to represent the variant names.
482        // Indeed with the (unstable) `fmt-debug` compiler flag [2], it can
483        // return only a minimalized output or empty string. To make sure this
484        // code will work in the future and independent of the compiler flag, we
485        // translate the variants to their names manually here.
486        //
487        // [1] https://doc.rust-lang.org/1.81.0/std/fmt/trait.Debug.html#stability
488        // [2] https://doc.rust-lang.org/beta/unstable-book/compiler-flags/fmt-debug.html
489        let s = match self {
490            Trait::HasField { .. } => "HasField",
491            Trait::ProjectField { .. } => "ProjectField",
492            Trait::KnownLayout => "KnownLayout",
493            Trait::HasTag { .. } => "HasTag",
494            Trait::Immutable => "Immutable",
495            Trait::Project => "Project",
496            Trait::TryFromBytes => "TryFromBytes",
497            Trait::FromZeros => "FromZeros",
498            Trait::FromBytes => "FromBytes",
499            Trait::IntoBytes => "IntoBytes",
500            Trait::Unaligned => "Unaligned",
501            Trait::Sized => "Sized",
502            Trait::ByteHash => "ByteHash",
503            Trait::ByteEq => "ByteEq",
504            Trait::SplitAt => "SplitAt",
505        };
506        let ident = Ident::new(s, Span::call_site());
507        let arguments: Option<syn::AngleBracketedGenericArguments> = match self {
508            Trait::HasTag { client } => Some(parse_quote!(<#client>)),
509            Trait::HasField { client, projection } => {
510                let FieldProjection { variant_id, field, field_id } = projection;
511                Some(parse_quote!(<#client, #field, #variant_id, #field_id>))
512            }
513            Trait::ProjectField { client, projection, invariants } => {
514                let FieldProjection { variant_id, field, field_id } = projection;
515                Some(parse_quote!(<#client, #field, #invariants, #variant_id, #field_id>))
516            }
517            Trait::KnownLayout
518            | Trait::Immutable
519            | Trait::Project
520            | Trait::TryFromBytes
521            | Trait::FromZeros
522            | Trait::FromBytes
523            | Trait::IntoBytes
524            | Trait::Unaligned
525            | Trait::Sized
526            | Trait::ByteHash
527            | Trait::ByteEq
528            | Trait::SplitAt => None,
529        };
530        tokens.extend(quote!(#ident #arguments));
531    }
532}
533
534impl Trait {
535    pub(crate) fn crate_path(&self, ctx: &Ctx) -> Path {
536        let zerocopy_crate = &ctx.zerocopy_crate;
537        let core = ctx.core_path();
538        match self {
539            Self::Sized => parse_quote!(#core::marker::#self),
540            Self::HasTag { client } => {
541                let client = client.crate_path(ctx);
542                parse_quote!(#zerocopy_crate::HasTag<#client>)
543            }
544            Self::HasField { client, projection } => {
545                let FieldProjection { variant_id, field, field_id } = projection;
546                let client = client.crate_path(ctx);
547                parse_quote!(#zerocopy_crate::HasField<#client, #field, #variant_id, #field_id>)
548            }
549            Self::ProjectField { client, projection, invariants } => {
550                let FieldProjection { variant_id, field, field_id } = projection;
551                let client = client.crate_path(ctx);
552                parse_quote!(
553                    #zerocopy_crate::ProjectField<
554                        #client,
555                        #field,
556                        #invariants,
557                        #variant_id,
558                        #field_id
559                    >
560                )
561            }
562            _ => parse_quote!(#zerocopy_crate::#self),
563        }
564    }
565}
566
567pub(crate) enum TraitBound {
568    Slf,
569    Other(Trait),
570}
571
572pub(crate) enum FieldBounds<'a> {
573    None,
574    All(&'a [TraitBound]),
575    Trailing(&'a [TraitBound]),
576    Explicit(Vec<WherePredicate>),
577}
578
579impl<'a> FieldBounds<'a> {
580    pub(crate) const ALL_SELF: FieldBounds<'a> = FieldBounds::All(&[TraitBound::Slf]);
581    pub(crate) const TRAILING_SELF: FieldBounds<'a> = FieldBounds::Trailing(&[TraitBound::Slf]);
582}
583
584pub(crate) enum SelfBounds<'a> {
585    None,
586    All(&'a [Trait]),
587}
588
589// FIXME(https://github.com/rust-lang/rust-clippy/issues/12908): This is a false
590// positive. Explicit lifetimes are actually necessary here.
591#[allow(clippy::needless_lifetimes)]
592impl<'a> SelfBounds<'a> {
593    pub(crate) const SIZED: Self = Self::All(&[Trait::Sized]);
594}
595
596/// Normalizes a slice of bounds by replacing [`TraitBound::Slf`] with `slf`.
597pub(crate) fn normalize_bounds<'a>(
598    slf: &'a Trait,
599    bounds: &'a [TraitBound],
600) -> impl 'a + Iterator<Item = Trait> {
601    bounds.iter().map(move |bound| match bound {
602        TraitBound::Slf => slf.clone(),
603        TraitBound::Other(trt) => trt.clone(),
604    })
605}
606
607pub(crate) struct ImplBlockBuilder<'a> {
608    ctx: &'a Ctx,
609    data: &'a dyn DataExt,
610    trt: Trait,
611    field_type_trait_bounds: FieldBounds<'a>,
612    self_type_trait_bounds: SelfBounds<'a>,
613    padding_check: Option<PaddingCheck>,
614    param_extras: Vec<GenericParam>,
615    inner_extras: Option<TokenStream>,
616    outer_extras: Option<TokenStream>,
617}
618
619impl<'a> ImplBlockBuilder<'a> {
620    pub(crate) fn new(
621        ctx: &'a Ctx,
622        data: &'a dyn DataExt,
623        trt: Trait,
624        field_type_trait_bounds: FieldBounds<'a>,
625    ) -> Self {
626        Self {
627            ctx,
628            data,
629            trt,
630            field_type_trait_bounds,
631            self_type_trait_bounds: SelfBounds::None,
632            padding_check: None,
633            param_extras: Vec::new(),
634            inner_extras: None,
635            outer_extras: None,
636        }
637    }
638
639    pub(crate) fn self_type_trait_bounds(mut self, self_type_trait_bounds: SelfBounds<'a>) -> Self {
640        self.self_type_trait_bounds = self_type_trait_bounds;
641        self
642    }
643
644    pub(crate) fn padding_check<P: Into<Option<PaddingCheck>>>(mut self, padding_check: P) -> Self {
645        self.padding_check = padding_check.into();
646        self
647    }
648
649    pub(crate) fn param_extras(mut self, param_extras: Vec<GenericParam>) -> Self {
650        self.param_extras.extend(param_extras);
651        self
652    }
653
654    pub(crate) fn inner_extras(mut self, inner_extras: TokenStream) -> Self {
655        self.inner_extras = Some(inner_extras);
656        self
657    }
658
659    pub(crate) fn outer_extras<T: Into<Option<TokenStream>>>(mut self, outer_extras: T) -> Self {
660        self.outer_extras = outer_extras.into();
661        self
662    }
663
664    pub(crate) fn build(self) -> TokenStream {
665        // In this documentation, we will refer to this hypothetical struct:
666        //
667        //   #[derive(FromBytes)]
668        //   struct Foo<T, I: Iterator>
669        //   where
670        //       T: Copy,
671        //       I: Clone,
672        //       I::Item: Clone,
673        //   {
674        //       a: u8,
675        //       b: T,
676        //       c: I::Item,
677        //   }
678        //
679        // We extract the field types, which in this case are `u8`, `T`, and
680        // `I::Item`. We re-use the existing parameters and where clauses. If
681        // `require_trait_bound == true` (as it is for `FromBytes), we add where
682        // bounds for each field's type:
683        //
684        //   impl<T, I: Iterator> FromBytes for Foo<T, I>
685        //   where
686        //       T: Copy,
687        //       I: Clone,
688        //       I::Item: Clone,
689        //       T: FromBytes,
690        //       I::Item: FromBytes,
691        //   {
692        //   }
693        //
694        // NOTE: It is standard practice to only emit bounds for the type
695        // parameters themselves, not for field types based on those parameters
696        // (e.g., `T` vs `T::Foo`). For a discussion of why this is standard
697        // practice, see https://github.com/rust-lang/rust/issues/26925.
698        //
699        // The reason we diverge from this standard is that doing it that way
700        // for us would be unsound. E.g., consider a type, `T` where `T:
701        // FromBytes` but `T::Foo: !FromBytes`. It would not be sound for us to
702        // accept a type with a `T::Foo` field as `FromBytes` simply because `T:
703        // FromBytes`.
704        //
705        // While there's no getting around this requirement for us, it does have
706        // the pretty serious downside that, when lifetimes are involved, the
707        // trait solver ties itself in knots:
708        //
709        //     #[derive(Unaligned)]
710        //     #[repr(C)]
711        //     struct Dup<'a, 'b> {
712        //         a: PhantomData<&'a u8>,
713        //         b: PhantomData<&'b u8>,
714        //     }
715        //
716        //     error[E0283]: type annotations required: cannot resolve `core::marker::PhantomData<&'a u8>: zerocopy::Unaligned`
717        //      --> src/main.rs:6:10
718        //       |
719        //     6 | #[derive(Unaligned)]
720        //       |          ^^^^^^^^^
721        //       |
722        //       = note: required by `zerocopy::Unaligned`
723
724        let type_ident = &self.ctx.ast.ident;
725        let trait_path = self.trt.crate_path(self.ctx);
726        let fields = self.data.fields();
727        let variants = self.data.variants();
728        let tag = self.data.tag();
729        let zerocopy_crate = &self.ctx.zerocopy_crate;
730
731        fn bound_tt(ty: &Type, traits: impl Iterator<Item = Trait>, ctx: &Ctx) -> WherePredicate {
732            let traits = traits.map(|t| t.crate_path(ctx));
733            parse_quote!(#ty: #(#traits)+*)
734        }
735        let field_type_bounds: Vec<_> = match (self.field_type_trait_bounds, &fields[..]) {
736            (FieldBounds::All(traits), _) => fields
737                .iter()
738                .map(|(_vis, _name, ty)| {
739                    bound_tt(ty, normalize_bounds(&self.trt, traits), self.ctx)
740                })
741                .collect(),
742            (FieldBounds::None, _) | (FieldBounds::Trailing(..), []) => vec![],
743            (FieldBounds::Trailing(traits), [.., last]) => {
744                vec![bound_tt(last.2, normalize_bounds(&self.trt, traits), self.ctx)]
745            }
746            (FieldBounds::Explicit(bounds), _) => bounds,
747        };
748
749        let padding_check_bound = self
750            .padding_check
751            .map(|check| {
752                // Parse the repr for `align` and `packed` modifiers. Note that
753                // `Repr::<PrimitiveRepr, NonZeroU32>` is more permissive than
754                // what Rust supports for structs, enums, or unions, and thus
755                // reliably extracts these modifiers for any kind of type.
756                let repr =
757                    Repr::<PrimitiveRepr, NonZeroU32>::from_attrs(&self.ctx.ast.attrs).unwrap();
758                let core = self.ctx.core_path();
759                let option = quote! { #core::option::Option };
760                let nonzero = quote! { #core::num::NonZeroUsize };
761                let none = quote! { #option::None::<#nonzero> };
762                let repr_align =
763                    repr.get_align().map(|spanned| {
764                        let n = spanned.t.get();
765                        quote_spanned! { spanned.span => (#nonzero::new(#n as usize)) }
766                    }).unwrap_or(quote! { (#none) });
767                let repr_packed =
768                    repr.get_packed().map(|packed| {
769                        let n = packed.get();
770                        quote! { (#nonzero::new(#n as usize)) }
771                    }).unwrap_or(quote! { (#none) });
772                let variant_types = variants.iter().map(|(_, fields)| {
773                    let types = fields.iter().map(|(_vis, _name, ty)| ty);
774                    quote!([#((#types)),*])
775                });
776                let validator_context = check.validator_macro_context();
777                let (trt, validator_macro) = check.validator_trait_and_macro_idents();
778                let t = tag.iter();
779                parse_quote! {
780                    (): #zerocopy_crate::util::macro_util::#trt<
781                        Self,
782                        {
783                            #validator_context
784                            #zerocopy_crate::#validator_macro!(Self, #repr_align, #repr_packed, #(#t,)* #(#variant_types),*)
785                        }
786                    >
787                }
788            });
789
790        let self_bounds: Option<WherePredicate> = match self.self_type_trait_bounds {
791            SelfBounds::None => None,
792            SelfBounds::All(traits) => {
793                Some(bound_tt(&parse_quote!(Self), traits.iter().cloned(), self.ctx))
794            }
795        };
796
797        let zerocopy_bounds =
798            field_type_bounds
799                .into_iter()
800                .chain(padding_check_bound)
801                .chain(self_bounds)
802                .map(|bound| {
803                    if self.ctx.skip_on_error {
804                        parse_quote!(for<'zc> #bound)
805                    } else {
806                        bound.clone()
807                    }
808                })
809                .collect::<Vec<_>>();
810
811        let bounds = self
812            .ctx
813            .ast
814            .generics
815            .where_clause
816            .as_ref()
817            .map(|where_clause| where_clause.predicates.iter())
818            .into_iter()
819            .flatten()
820            .chain(zerocopy_bounds.iter());
821
822        // The parameters with trait bounds, but without type defaults.
823        let mut params: Vec<_> = self
824            .ctx
825            .ast
826            .generics
827            .params
828            .clone()
829            .into_iter()
830            .map(|mut param| {
831                match &mut param {
832                    GenericParam::Type(ty) => ty.default = None,
833                    GenericParam::Const(cnst) => cnst.default = None,
834                    GenericParam::Lifetime(_) => {}
835                }
836                parse_quote!(#param)
837            })
838            .chain(self.param_extras)
839            .collect();
840
841        // For MSRV purposes, ensure that lifetimes precede types precede const
842        // generics.
843        params.sort_by_cached_key(|param| match param {
844            GenericParam::Lifetime(_) => 0,
845            GenericParam::Type(_) => 1,
846            GenericParam::Const(_) => 2,
847        });
848
849        // The identifiers of the parameters without trait bounds or type
850        // defaults.
851        let param_idents = self.ctx.ast.generics.params.iter().map(|param| match param {
852            GenericParam::Type(ty) => {
853                let ident = &ty.ident;
854                quote!(#ident)
855            }
856            GenericParam::Lifetime(l) => {
857                let ident = &l.lifetime;
858                quote!(#ident)
859            }
860            GenericParam::Const(cnst) => {
861                let ident = &cnst.ident;
862                quote!({#ident})
863            }
864        });
865
866        let inner_extras = self.inner_extras;
867        let allow_trivial_bounds =
868            if self.ctx.skip_on_error { quote!(#[allow(trivial_bounds)]) } else { quote!() };
869        let impl_tokens = quote! {
870            #allow_trivial_bounds
871            unsafe impl < #(#params),* > #trait_path for #type_ident < #(#param_idents),* >
872            where
873                #(#bounds,)*
874            {
875                fn only_derive_is_allowed_to_implement_this_trait() {}
876
877                #inner_extras
878            }
879        };
880
881        let outer_extras = self.outer_extras.filter(|e| !e.is_empty());
882        let cfg_compile_error = self.ctx.cfg_compile_error();
883        let items = [Some(cfg_compile_error), Some(impl_tokens), outer_extras];
884        if matches!(self.trt, Trait::TryFromBytes) {
885            self.ctx.const_block(items)
886        } else {
887            const_block(items)
888        }
889    }
890}
891
892// A polyfill for `Option::then_some`, which was added after our MSRV.
893//
894// The `#[allow(unused)]` is necessary because, on sufficiently recent toolchain
895// versions, `b.then_some(...)` resolves to the inherent method rather than to
896// this trait, and so this trait is considered unused.
897//
898// FIXME(#67): Remove this once our MSRV is >= 1.62.
899#[allow(unused)]
900trait BoolExt {
901    fn then_some<T>(self, t: T) -> Option<T>;
902}
903
904impl BoolExt for bool {
905    fn then_some<T>(self, t: T) -> Option<T> {
906        if self {
907            Some(t)
908        } else {
909            None
910        }
911    }
912}
913
914pub(crate) fn allow_generated_code() -> TokenStream {
915    quote! {
916        #[allow(
917            // FIXME(#553): Add a test that generates a warning when
918            // `#[allow(deprecated)]` isn't present.
919            deprecated,
920            // Required on some rustc versions due to a lint that is only
921            // triggered when `derive(KnownLayout)` is applied to `repr(C)`
922            // structs that are generated by macros. See #2177 for details.
923            private_bounds,
924            non_local_definitions,
925            non_camel_case_types,
926            non_upper_case_globals,
927            non_snake_case,
928            non_ascii_idents,
929            clippy::missing_inline_in_public_items,
930        )]
931    }
932}
933
934pub(crate) fn const_block(items: impl IntoIterator<Item = Option<TokenStream>>) -> TokenStream {
935    let items = items.into_iter().flatten();
936    let allow = allow_generated_code();
937    quote! {
938        #allow
939        #[deny(ambiguous_associated_items)]
940        // While there are not currently any warnings that this suppresses
941        // (that we're aware of), it's good future-proofing hygiene.
942        #[automatically_derived]
943        const _: () = {
944            #(#items)*
945        };
946    }
947}
948pub(crate) fn generate_tag_enum(ctx: &Ctx, repr: &EnumRepr, data: &DataEnum) -> TokenStream {
949    let zerocopy_crate = &ctx.zerocopy_crate;
950    let variants = data.variants.iter().map(|v| {
951        let ident = &v.ident;
952        if let Some((eq, discriminant)) = &v.discriminant {
953            quote! { #ident #eq #discriminant }
954        } else {
955            quote! { #ident }
956        }
957    });
958
959    // Don't include any `repr(align)` when generating the tag enum, as that
960    // could add padding after the tag but before any variants, which is not the
961    // correct behavior.
962    let repr = match repr {
963        EnumRepr::Transparent(span) => quote::quote_spanned! { *span => #[repr(transparent)] },
964        EnumRepr::Compound(c, _) => quote! { #c },
965    };
966
967    quote! {
968        #repr
969        #[allow(dead_code, clippy::derive_partial_eq_without_eq)]
970        #[derive(Copy, Clone, PartialEq)]
971        pub enum ___ZerocopyTag {
972            #(#variants,)*
973        }
974
975        // SAFETY: `___ZerocopyTag` has no fields, and so it does not permit
976        // interior mutation.
977        unsafe impl #zerocopy_crate::Immutable for ___ZerocopyTag {
978            fn only_derive_is_allowed_to_implement_this_trait() {}
979        }
980    }
981}
982pub(crate) fn enum_size_from_repr(repr: &EnumRepr) -> Result<usize, Error> {
983    use CompoundRepr::*;
984    use PrimitiveRepr::*;
985    use Repr::*;
986    match repr {
987        Transparent(span)
988        | Compound(
989            Spanned {
990                t: C | Rust | Primitive(U32 | I32 | U64 | I64 | U128 | I128 | Usize | Isize),
991                span,
992            },
993            _,
994        ) => Err(Error::new(
995            *span,
996            "`FromBytes` only supported on enums with `#[repr(...)]` attributes `u8`, `i8`, `u16`, or `i16`",
997        )),
998        Compound(Spanned { t: Primitive(U8 | I8), span: _ }, _align) => Ok(8),
999        Compound(Spanned { t: Primitive(U16 | I16), span: _ }, _align) => Ok(16),
1000    }
1001}
1002
1003#[cfg(test)]
1004pub(crate) mod testutil {
1005    use proc_macro2::TokenStream;
1006    use syn::visit::{self, Visit};
1007
1008    /// Checks for hygiene violations in the generated code.
1009    ///
1010    /// # Panics
1011    ///
1012    /// Panics if a hygiene violation is found.
1013    pub(crate) fn check_hygiene(ts: TokenStream) {
1014        struct AmbiguousItemVisitor;
1015
1016        fn panic_ambiguous_path(path: &impl quote::ToTokens) -> ! {
1017            panic!(
1018                "Found ambiguous path `{}` in generated output. \
1019                 All associated item access must be fully qualified (e.g., `<Self as Trait>::Item`) \
1020                 to prevent hygiene issues.",
1021                quote::quote!(#path)
1022            );
1023        }
1024
1025        impl<'ast> Visit<'ast> for AmbiguousItemVisitor {
1026            fn visit_expr_path(&mut self, i: &'ast syn::ExprPath) {
1027                if let Some(qself) = &i.qself {
1028                    if qself.position == 0 {
1029                        panic_ambiguous_path(i);
1030                    }
1031                }
1032                visit::visit_expr_path(self, i);
1033            }
1034
1035            fn visit_path(&mut self, i: &'ast syn::Path) {
1036                if i.segments.len() > 1 && i.segments.first().unwrap().ident == "Self" {
1037                    panic_ambiguous_path(i);
1038                }
1039                visit::visit_path(self, i);
1040            }
1041
1042            fn visit_type_path(&mut self, i: &'ast syn::TypePath) {
1043                if let Some(qself) = &i.qself {
1044                    if qself.position == 0 {
1045                        panic_ambiguous_path(i);
1046                    }
1047                }
1048                visit::visit_type_path(self, i);
1049            }
1050        }
1051
1052        let file = syn::parse2::<syn::File>(ts).expect("failed to parse generated output as File");
1053        AmbiguousItemVisitor.visit_file(&file);
1054    }
1055
1056    #[test]
1057    fn test_check_hygiene_success() {
1058        check_hygiene(quote::quote! {
1059            fn foo() {
1060                let _ = <Self as Trait>::Item;
1061            }
1062        });
1063    }
1064
1065    #[test]
1066    #[should_panic(expected = "Found ambiguous path `Self :: Ambiguous`")]
1067    fn test_check_hygiene_failure() {
1068        check_hygiene(quote::quote! {
1069            fn foo() {
1070                let _ = Self::Ambiguous;
1071            }
1072        });
1073    }
1074
1075    #[test]
1076    #[should_panic(expected = "Found ambiguous path `< T > :: Ambiguous`")]
1077    fn test_check_hygiene_type_relative_expr_failure() {
1078        check_hygiene(quote::quote! {
1079            fn foo<T>() {
1080                let _ = <T>::Ambiguous;
1081            }
1082        });
1083    }
1084
1085    #[test]
1086    #[should_panic(expected = "Found ambiguous path `< T > :: Ambiguous`")]
1087    fn test_check_hygiene_type_relative_type_failure() {
1088        check_hygiene(quote::quote! {
1089            fn foo<T>() {
1090                let _: <T>::Ambiguous;
1091            }
1092        });
1093    }
1094
1095    #[test]
1096    fn test_validate_crate_path() {
1097        use syn::parse_str;
1098
1099        let valid = [
1100            "zerocopy",
1101            "crate",
1102            "crate::foo::bar",
1103            "self",
1104            "self::foo",
1105            "self::super::foo",
1106            "super",
1107            "super::foo",
1108            "super::super::foo",
1109            "super::super::super",
1110            "foo::bar::baz",
1111            "::foo::bar",
1112        ];
1113
1114        for path_str in valid {
1115            let path = parse_str::<syn::Path>(path_str).unwrap();
1116            assert!(
1117                super::validate_crate_path(&path).is_ok(),
1118                "expected valid path for `{}`",
1119                path_str
1120            );
1121        }
1122
1123        let invalid = [
1124            "::crate::foo",
1125            "::self::foo",
1126            "::super::foo",
1127            "Self",
1128            "Self::foo",
1129            "foo::Self::bar",
1130            "foo::crate::bar",
1131            "foo::super::bar",
1132            "foo::self",
1133            "super::foo::super",
1134            "super::crate::foo",
1135            "crate::super::foo",
1136            "self::self::foo",
1137        ];
1138
1139        for path_str in invalid {
1140            let path = parse_str::<syn::Path>(path_str).unwrap();
1141            assert!(
1142                super::validate_crate_path(&path).is_err(),
1143                "expected invalid path for `{}`",
1144                path_str
1145            );
1146        }
1147    }
1148
1149    #[test]
1150    fn test_path_is_ident() {
1151        use syn::parse_str;
1152
1153        for (expected, ordinary, raw) in [
1154            ("doc", "doc", "r#doc"),
1155            ("repr", "repr", "r#repr"),
1156            ("zerocopy", "zerocopy", "r#zerocopy"),
1157            ("on_error", "on_error", "r#on_error"),
1158        ] {
1159            let ordinary = parse_str::<syn::Path>(ordinary).unwrap();
1160            let raw = parse_str::<syn::Path>(raw).unwrap();
1161            assert!(super::path_is_ident(&ordinary, expected));
1162            assert!(super::path_is_ident(&raw, expected));
1163        }
1164
1165        // `r#crate` is forbidden by Rust's raw identifier grammar.
1166        let crate_path = parse_str::<syn::Path>("crate").unwrap();
1167        assert!(super::path_is_ident(&crate_path, "crate"));
1168
1169        for path in ["::zerocopy", "module::zerocopy"] {
1170            let path = parse_str::<syn::Path>(path).unwrap();
1171            assert!(!super::path_is_ident(&path, "zerocopy"));
1172        }
1173    }
1174
1175    #[test]
1176    fn test_raw_zerocopy_attributes() {
1177        let ast: syn::DeriveInput = syn::parse_quote! {
1178            #[r#zerocopy(crate = "renamed", r#on_error = "skip")]
1179            struct Foo;
1180        };
1181
1182        let ctx = super::Ctx::try_from_derive_input(ast).unwrap();
1183        let path = &ctx.zerocopy_crate;
1184        assert_eq!(quote::quote!(#path).to_string(), ":: renamed");
1185        assert!(ctx.skip_on_error);
1186    }
1187}