2023-04-08 15:25:51 +03:00
// SPDX-License-Identifier: Apache-2.0 OR MIT
2023-04-24 11:11:38 +03:00
use crate ::helpers ::{ parse_generics , Generics } ;
2023-04-24 11:11:43 +03:00
use proc_macro ::{ Group , Punct , Spacing , TokenStream , TokenTree } ;
2023-04-08 15:25:51 +03:00
pub ( crate ) fn pin_data ( args : TokenStream , input : TokenStream ) -> TokenStream {
// This proc-macro only does some pre-parsing and then delegates the actual parsing to
// `kernel::__pin_data!`.
2023-04-24 11:11:38 +03:00
let (
Generics {
impl_generics ,
ty_generics ,
} ,
2023-04-24 11:11:43 +03:00
rest ,
2023-04-24 11:11:38 +03:00
) = parse_generics ( input ) ;
2023-04-24 11:11:43 +03:00
// The struct definition might contain the `Self` type. Since `__pin_data!` will define a new
// type with the same generics and bounds, this poses a problem, since `Self` will refer to the
// new type as opposed to this struct definition. Therefore we have to replace `Self` with the
// concrete name.
// Errors that occur when replacing `Self` with `struct_name`.
let mut errs = TokenStream ::new ( ) ;
// The name of the struct with ty_generics.
let struct_name = rest
. iter ( )
. skip_while ( | tt | ! matches! ( tt , TokenTree ::Ident ( i ) if i . to_string ( ) = = " struct " ) )
. nth ( 1 )
. and_then ( | tt | match tt {
TokenTree ::Ident ( _ ) = > {
let tt = tt . clone ( ) ;
let mut res = vec! [ tt ] ;
if ! ty_generics . is_empty ( ) {
// We add this, so it is maximally compatible with e.g. `Self::CONST` which
// will be replaced by `StructName::<$generics>::CONST`.
res . push ( TokenTree ::Punct ( Punct ::new ( ':' , Spacing ::Joint ) ) ) ;
res . push ( TokenTree ::Punct ( Punct ::new ( ':' , Spacing ::Alone ) ) ) ;
res . push ( TokenTree ::Punct ( Punct ::new ( '<' , Spacing ::Alone ) ) ) ;
res . extend ( ty_generics . iter ( ) . cloned ( ) ) ;
res . push ( TokenTree ::Punct ( Punct ::new ( '>' , Spacing ::Alone ) ) ) ;
}
Some ( res )
}
_ = > None ,
} )
. unwrap_or_else ( | | {
// If we did not find the name of the struct then we will use `Self` as the replacement
// and add a compile error to ensure it does not compile.
errs . extend (
" ::core::compile_error!( \" Could not locate type name. \" ); "
. parse ::< TokenStream > ( )
. unwrap ( ) ,
) ;
" Self " . parse ::< TokenStream > ( ) . unwrap ( ) . into_iter ( ) . collect ( )
} ) ;
let impl_generics = impl_generics
. into_iter ( )
. flat_map ( | tt | replace_self_and_deny_type_defs ( & struct_name , tt , & mut errs ) )
. collect ::< Vec < _ > > ( ) ;
let mut rest = rest
. into_iter ( )
. flat_map ( | tt | {
// We ignore top level `struct` tokens, since they would emit a compile error.
if matches! ( & tt , TokenTree ::Ident ( i ) if i . to_string ( ) = = " struct " ) {
vec! [ tt ]
} else {
replace_self_and_deny_type_defs ( & struct_name , tt , & mut errs )
}
} )
. collect ::< Vec < _ > > ( ) ;
2023-04-08 15:25:51 +03:00
// This should be the body of the struct `{...}`.
let last = rest . pop ( ) ;
2023-04-24 11:11:43 +03:00
let mut quoted = quote! ( ::kernel ::__pin_data! {
2023-04-08 15:25:51 +03:00
parse_input :
@ args ( #args ) ,
@ sig ( #( #rest ) * ) ,
@ impl_generics ( #( #impl_generics ) * ) ,
@ ty_generics ( #( #ty_generics ) * ) ,
@ body ( #last ) ,
2023-04-24 11:11:43 +03:00
} ) ;
quoted . extend ( errs ) ;
quoted
}
/// Replaces `Self` with `struct_name` and errors on `enum`, `trait`, `struct` `union` and `impl`
/// keywords.
///
/// The error is appended to `errs` to allow normal parsing to continue.
fn replace_self_and_deny_type_defs (
struct_name : & Vec < TokenTree > ,
tt : TokenTree ,
errs : & mut TokenStream ,
) -> Vec < TokenTree > {
match tt {
TokenTree ::Ident ( ref i )
if i . to_string ( ) = = " enum "
| | i . to_string ( ) = = " trait "
| | i . to_string ( ) = = " struct "
| | i . to_string ( ) = = " union "
| | i . to_string ( ) = = " impl " = >
{
errs . extend (
format! (
" ::core::compile_error!( \" Cannot use `{i}` inside of struct definition with \
` #[ pin_data ] ` . \ " ); "
)
. parse ::< TokenStream > ( )
. unwrap ( )
. into_iter ( )
. map ( | mut tok | {
tok . set_span ( tt . span ( ) ) ;
tok
} ) ,
) ;
vec! [ tt ]
}
TokenTree ::Ident ( i ) if i . to_string ( ) = = " Self " = > struct_name . clone ( ) ,
TokenTree ::Literal ( _ ) | TokenTree ::Punct ( _ ) | TokenTree ::Ident ( _ ) = > vec! [ tt ] ,
TokenTree ::Group ( g ) = > vec! [ TokenTree ::Group ( Group ::new (
g . delimiter ( ) ,
g . stream ( )
. into_iter ( )
. flat_map ( | tt | replace_self_and_deny_type_defs ( struct_name , tt , errs ) )
. collect ( ) ,
) ) ] ,
}
2023-04-08 15:25:51 +03:00
}