// SPDX-License-Identifier: GPL-2.0 use crate::helpers::{parse_generics, Generics}; use proc_macro::{TokenStream, TokenTree}; pub(crate) fn derive(input: TokenStream) -> TokenStream { let ( Generics { impl_generics, ty_generics, }, mut rest, ) = parse_generics(input); // This should be the body of the struct `{...}`. let last = rest.pop(); // Now we insert `Zeroable` as a bound for every generic parameter in `impl_generics`. let mut new_impl_generics = Vec::with_capacity(impl_generics.len()); // Are we inside of a generic where we want to add `Zeroable`? let mut in_generic = !impl_generics.is_empty(); // Have we already inserted `Zeroable`? let mut inserted = false; // Level of `<>` nestings. let mut nested = 0; for tt in impl_generics { match &tt { // If we find a `,`, then we have finished a generic/constant/lifetime parameter. TokenTree::Punct(p) if nested == 0 && p.as_char() == ',' => { if in_generic && !inserted { new_impl_generics.extend(quote! { : ::kernel::init::Zeroable }); } in_generic = true; inserted = false; new_impl_generics.push(tt); } // If we find `'`, then we are entering a lifetime. TokenTree::Punct(p) if nested == 0 && p.as_char() == '\'' => { in_generic = false; new_impl_generics.push(tt); } TokenTree::Punct(p) if nested == 0 && p.as_char() == ':' => { new_impl_generics.push(tt); if in_generic { new_impl_generics.extend(quote! { ::kernel::init::Zeroable + }); inserted = true; } } TokenTree::Punct(p) if p.as_char() == '<' => { nested += 1; new_impl_generics.push(tt); } TokenTree::Punct(p) if p.as_char() == '>' => { assert!(nested > 0); nested -= 1; new_impl_generics.push(tt); } _ => new_impl_generics.push(tt), } } assert_eq!(nested, 0); if in_generic && !inserted { new_impl_generics.extend(quote! { : ::kernel::init::Zeroable }); } quote! { ::kernel::__derive_zeroable!( parse_input: @sig(#(#rest)*), @impl_generics(#(#new_impl_generics)*), @ty_generics(#(#ty_generics)*), @body(#last), ); } }