| // 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), | |
| ); | |
| } | |
| } |