1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
// SPDX-License-Identifier: Apache-2.0 OR MIT
#[cfg(not(kernel))]
use proc_macro2 as proc_macro;
use crate::helpers::{parse_generics, Generics};
use proc_macro::{Group, Punct, Spacing, TokenStream, TokenTree};
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
// `pin_init::__pin_data!`.
let (
Generics {
impl_generics,
decl_generics,
ty_generics,
},
rest,
) = parse_generics(input);
// 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<_>>();
// This should be the body of the struct `{...}`.
let last = rest.pop();
let mut quoted = quote!(::pin_init::__pin_data! {
parse_input:
@args(#args),
@sig(#(#rest)*),
@impl_generics(#(#impl_generics)*),
@ty_generics(#(#ty_generics)*),
@decl_generics(#(#decl_generics)*),
@body(#last),
});
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(),
))],
}
}