xref: /linux/rust/zerocopy-derive/util.rs (revision 7c306f00835a237a70024a8f968dbc3bc8db8fcf)
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 
11 use std::num::NonZeroU32;
12 
13 use proc_macro2::{Span, TokenStream};
14 use quote::{quote, quote_spanned, ToTokens};
15 use 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 
21 use crate::repr::{CompoundRepr, EnumRepr, PrimitiveRepr, Repr, Spanned};
22 
23 pub(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)]
39 enum CratePath {
40     External,
41     CrateRelative,
42     ModuleRelative,
43 }
44 
45 fn 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 
88 impl 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 
260 pub(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 
276 impl 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 
302 impl 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 
316 impl 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 
330 impl 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 
344 fn 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 
363 pub(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.
377 pub(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.
386 pub(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 
401 impl 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)]
428 pub(crate) enum Client {
429     ProjectDerive,
430     TryFromBytesDerive,
431 }
432 
433 impl 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 
444 impl 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)]
453 pub(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)]
460 pub(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 
478 impl 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 
534 impl 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 
567 pub(crate) enum TraitBound {
568     Slf,
569     Other(Trait),
570 }
571 
572 pub(crate) enum FieldBounds<'a> {
573     None,
574     All(&'a [TraitBound]),
575     Trailing(&'a [TraitBound]),
576     Explicit(Vec<WherePredicate>),
577 }
578 
579 impl<'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 
584 pub(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)]
592 impl<'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`.
597 pub(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 
607 pub(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 
619 impl<'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)]
900 trait BoolExt {
901     fn then_some<T>(self, t: T) -> Option<T>;
902 }
903 
904 impl 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 
914 pub(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 
934 pub(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 }
948 pub(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 }
982 pub(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)]
1004 pub(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 }
1188