|
1 | 1 | // SPDX-License-Identifier: Apache-2.0 OR MIT
|
2 | 2 |
|
3 |
| -use proc_macro2::{TokenStream, TokenTree}; |
4 |
| -use quote::quote; |
| 3 | +use proc_macro2::TokenStream; |
| 4 | +use quote::{quote, ToTokens}; |
| 5 | +use syn::{parse_quote, Error, ImplItem, ImplItemFn, ItemImpl, Token}; |
5 | 6 |
|
6 |
| -pub(crate) fn pinned_drop(_args: TokenStream, input: TokenStream) -> TokenStream { |
7 |
| - let mut toks = input.into_iter().collect::<Vec<_>>(); |
8 |
| - assert!(!toks.is_empty()); |
9 |
| - // Ensure that we have an `impl` item. |
10 |
| - assert!(matches!(&toks[0], TokenTree::Ident(i) if i.to_string() == "impl")); |
11 |
| - // Ensure that we are implementing `PinnedDrop`. |
12 |
| - let mut nesting: usize = 0; |
13 |
| - let mut pinned_drop_idx = None; |
14 |
| - for (i, tt) in toks.iter().enumerate() { |
15 |
| - match tt { |
16 |
| - TokenTree::Punct(p) if p.as_char() == '<' => { |
17 |
| - nesting += 1; |
| 7 | +struct PinnedDropImpl(ItemImpl, ImplItemFn); |
| 8 | + |
| 9 | +impl TryFrom<ItemImpl> for PinnedDropImpl { |
| 10 | + type Error = (Error, TokenStream); |
| 11 | + fn try_from(mut impl_: ItemImpl) -> Result<Self, Self::Error> { |
| 12 | + let aux_impl = match &impl_ { |
| 13 | + ItemImpl { |
| 14 | + attrs, |
| 15 | + impl_token, |
| 16 | + generics, |
| 17 | + trait_: Some((polarity, path, for_)), |
| 18 | + self_ty, |
| 19 | + .. |
| 20 | + } if path.is_ident("PinnedDrop") => { |
| 21 | + let (impl_generics, _, whr) = generics.split_for_impl(); |
| 22 | + quote! { |
| 23 | + #(#attrs)* |
| 24 | + unsafe #impl_token #impl_generics #polarity ::kernel::init::PinnedDrop #for_ #self_ty |
| 25 | + #whr |
| 26 | + { |
| 27 | + fn drop( |
| 28 | + self: ::core::pin::Pin<&mut Self>, |
| 29 | + _: ::kernel::init::__internal::OnlyCallFromDrop, |
| 30 | + ) {} |
| 31 | + } |
| 32 | + } |
| 33 | + } |
| 34 | + _ => quote!(#impl_), |
| 35 | + }; |
| 36 | + if impl_.unsafety.is_some() { |
| 37 | + return Err(( |
| 38 | + Error::new_spanned( |
| 39 | + impl_.unsafety, |
| 40 | + "The `PinnedDrop` impl must not be `unsafe`.", |
| 41 | + ), |
| 42 | + aux_impl, |
| 43 | + )); |
| 44 | + } |
| 45 | + let trait_tokens = match &impl_.trait_ { |
| 46 | + None => quote!(), |
| 47 | + Some((None, path, _)) => quote!(#path), |
| 48 | + Some((Some(not), path, _)) => quote!(#not #path), |
| 49 | + }; |
| 50 | + match &impl_.trait_ { |
| 51 | + Some((None, path, _)) if path.is_ident("PinnedDrop") => {} |
| 52 | + None | Some((None, ..)) => { |
| 53 | + return Err(( |
| 54 | + Error::new_spanned( |
| 55 | + trait_tokens, |
| 56 | + "`#[pinned_drop]` can only be used on `PinnedDrop` impls.", |
| 57 | + ), |
| 58 | + aux_impl, |
| 59 | + )); |
18 | 60 | }
|
19 |
| - TokenTree::Punct(p) if p.as_char() == '>' => { |
20 |
| - nesting = nesting.checked_sub(1).unwrap(); |
21 |
| - continue; |
| 61 | + Some((Some(_), ..)) => { |
| 62 | + return Err(( |
| 63 | + Error::new_spanned( |
| 64 | + trait_tokens, |
| 65 | + "`#[pinned_drop]` can only be used on positive `PinnedDrop` impls.", |
| 66 | + ), |
| 67 | + aux_impl, |
| 68 | + )); |
22 | 69 | }
|
23 |
| - _ => {} |
24 | 70 | }
|
25 |
| - if i >= 1 && nesting == 0 { |
26 |
| - // Found the end of the generics, this should be `PinnedDrop`. |
27 |
| - assert!( |
28 |
| - matches!(tt, TokenTree::Ident(i) if i.to_string() == "PinnedDrop"), |
29 |
| - "expected 'PinnedDrop', found: '{:?}'", |
30 |
| - tt |
31 |
| - ); |
32 |
| - pinned_drop_idx = Some(i); |
33 |
| - break; |
| 71 | + if impl_.items.len() != 1 { |
| 72 | + return Err(( |
| 73 | + Error::new( |
| 74 | + impl_.brace_token.span.join(), |
| 75 | + "Expected exactly one function in the `PinnedDrop` impl.", |
| 76 | + ), |
| 77 | + aux_impl, |
| 78 | + )); |
34 | 79 | }
|
| 80 | + let ImplItem::Fn(drop) = impl_.items.pop().unwrap() else { |
| 81 | + return Err(( |
| 82 | + Error::new(impl_.brace_token.span.join(), "Expected a function."), |
| 83 | + aux_impl, |
| 84 | + )); |
| 85 | + }; |
| 86 | + |
| 87 | + Ok(Self(impl_, drop)) |
35 | 88 | }
|
36 |
| - let idx = pinned_drop_idx |
37 |
| - .unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`.")); |
38 |
| - // Fully qualify the `PinnedDrop`, as to avoid any tampering. |
39 |
| - toks.splice(idx..idx, quote!(::kernel::init::)); |
40 |
| - // Take the `{}` body and call the declarative macro. |
41 |
| - if let Some(TokenTree::Group(last)) = toks.pop() { |
42 |
| - let last = last.stream(); |
43 |
| - quote!(::kernel::__pinned_drop! { |
44 |
| - @impl_sig(#(#toks)*), |
45 |
| - @impl_body(#last), |
46 |
| - }) |
47 |
| - } else { |
48 |
| - TokenStream::from_iter(toks) |
| 89 | +} |
| 90 | + |
| 91 | +impl ToTokens for PinnedDropImpl { |
| 92 | + fn to_tokens(&self, tokens: &mut TokenStream) { |
| 93 | + self.0.to_tokens(tokens) |
49 | 94 | }
|
50 | 95 | }
|
| 96 | + |
| 97 | +pub(crate) fn pinned_drop(drop_impl: ItemImpl) -> Result<TokenStream, (Error, TokenStream)> { |
| 98 | + let PinnedDropImpl(mut drop_impl, mut drop) = drop_impl.try_into()?; |
| 99 | + drop.sig |
| 100 | + .inputs |
| 101 | + .push(parse_quote!(_: ::kernel::init::__internal::OnlyCallFromDrop)); |
| 102 | + let span = drop_impl.impl_token.span; |
| 103 | + |
| 104 | + drop_impl.items.push(ImplItem::Fn(drop)); |
| 105 | + drop_impl.unsafety = Some(Token); |
| 106 | + drop_impl.trait_ = Some(( |
| 107 | + None, |
| 108 | + parse_quote!(::kernel::init::PinnedDrop), |
| 109 | + Token, |
| 110 | + )); |
| 111 | + Ok(quote! { #drop_impl }) |
| 112 | +} |
0 commit comments