Skip to content

Commit bfabc61

Browse files
committed
rewrite the #[pinned_drop] attribute macro using syn
Rewrite the attribute macro for implementing `PinnedDrop` using `syn`. Otherwise no functional changes intended aside from improved error messages on syntactic and semantical errors. For example: When missing the `drop` function in the implementation, the old error was: error: no rules expected `)` --> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1 | 6 | #[pinned_drop] | ^^^^^^^^^^^^^^ no rules expected this token in macro call | note: while trying to match keyword `fn` --> src/macros.rs | | fn drop($($sig:tt)*) { | ^^ = note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info) And the new one is: error[E0046]: not all trait items implemented, missing: `drop` --> tests/ui/compile-fail/pinned_drop/no_fn.rs:7:1 | 7 | impl PinnedDrop for Foo {} | ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation | = help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }` Signed-off-by: Benno Lossin <[email protected]>
1 parent 47d64a7 commit bfabc61

File tree

9 files changed

+74
-107
lines changed

9 files changed

+74
-107
lines changed

internal/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ pub fn pin_data(inner: TokenStream, item: TokenStream) -> TokenStream {
2525

2626
#[proc_macro_attribute]
2727
pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
28-
pinned_drop::pinned_drop(args.into(), input.into()).into()
28+
pinned_drop::pinned_drop(
29+
parse_macro_input!(args as _),
30+
parse_macro_input!(input as _),
31+
)
32+
.into()
2933
}
3034

3135
#[proc_macro_derive(Zeroable)]

internal/src/pinned_drop.rs

Lines changed: 47 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,56 @@
11
// SPDX-License-Identifier: Apache-2.0 OR MIT
22

3-
use proc_macro2::{TokenStream, TokenTree};
4-
use quote::quote;
3+
use proc_macro2::TokenStream;
4+
use quote::{quote, quote_spanned};
5+
use syn::{parse::Nothing, parse_quote, spanned::Spanned, ImplItem, ItemImpl, Token};
56

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+
pub(crate) fn pinned_drop(_args: Nothing, mut input: ItemImpl) -> TokenStream {
8+
let mut errors = vec![];
9+
if let Some(unsafety) = input.unsafety {
10+
errors.push(quote_spanned! {unsafety.span=>
11+
::core::compile_error!("implementing `PinnedDrop` is safe");
12+
});
13+
}
14+
input.unsafety = Some(Token![unsafe](input.impl_token.span));
15+
match &mut input.trait_ {
16+
Some((not, path, _for)) => {
17+
if let Some(not) = not {
18+
errors.push(quote_spanned! {not.span=>
19+
::core::compile_error!("cannot implement `!PinnedDrop`");
20+
});
1821
}
19-
TokenTree::Punct(p) if p.as_char() == '>' => {
20-
nesting = nesting.checked_sub(1).unwrap();
21-
continue;
22+
for (seg, expected) in path
23+
.segments
24+
.iter()
25+
.rev()
26+
.zip(["PinnedDrop", "pin_init", ""])
27+
{
28+
if expected.is_empty() || seg.ident != expected {
29+
errors.push(quote_spanned! {seg.span()=>
30+
::core::compile_error!("bad import path for `PinnedDrop`");
31+
});
32+
}
33+
if !seg.arguments.is_none() {
34+
errors.push(quote_spanned! {seg.arguments.span()=>
35+
::core::compile_error!("unexpected arguments for `PinnedDrop` path");
36+
});
37+
}
2238
}
23-
_ => {}
24-
}
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: '{tt:?}'"
30-
);
31-
pinned_drop_idx = Some(i);
32-
break;
39+
*path = parse_quote!(::pin_init::PinnedDrop);
3340
}
41+
None => errors.push(quote_spanned! {input.impl_token.span=>
42+
::core::compile_error!("expected `impl ... PinnedDrop for ...`, got inherent impl");
43+
}),
3444
}
35-
let idx = pinned_drop_idx
36-
.unwrap_or_else(|| panic!("Expected an `impl` block implementing `PinnedDrop`."));
37-
// Fully qualify the `PinnedDrop`, as to avoid any tampering.
38-
toks.splice(idx..idx, quote!(::pin_init::));
39-
// Take the `{}` body and call the declarative macro.
40-
if let Some(TokenTree::Group(last)) = toks.pop() {
41-
let last = last.stream();
42-
quote!(::pin_init::__pinned_drop! {
43-
@impl_sig(#(#toks)*),
44-
@impl_body(#last),
45-
})
46-
} else {
47-
TokenStream::from_iter(toks)
45+
for item in &mut input.items {
46+
if let ImplItem::Fn(fn_item) = item {
47+
if fn_item.sig.ident == "drop" {
48+
fn_item
49+
.sig
50+
.inputs
51+
.push(parse_quote!(_: ::pin_init::__internal::OnlyCallFromDrop));
52+
}
53+
}
4854
}
55+
quote!(#(#errors)* #input)
4956
}

src/macros.rs

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -503,34 +503,6 @@ pub use ::macros::paste;
503503
#[cfg(not(kernel))]
504504
pub use ::paste::paste;
505505

506-
/// Creates a `unsafe impl<...> PinnedDrop for $type` block.
507-
///
508-
/// See [`PinnedDrop`] for more information.
509-
///
510-
/// [`PinnedDrop`]: crate::PinnedDrop
511-
#[doc(hidden)]
512-
#[macro_export]
513-
macro_rules! __pinned_drop {
514-
(
515-
@impl_sig($($impl_sig:tt)*),
516-
@impl_body(
517-
$(#[$($attr:tt)*])*
518-
fn drop($($sig:tt)*) {
519-
$($inner:tt)*
520-
}
521-
),
522-
) => {
523-
// SAFETY: TODO.
524-
unsafe $($impl_sig)* {
525-
// Inherit all attributes and the type/ident tokens for the signature.
526-
$(#[$($attr)*])*
527-
fn drop($($sig)*, _: $crate::__internal::OnlyCallFromDrop) {
528-
$($inner)*
529-
}
530-
}
531-
}
532-
}
533-
534506
/// This macro first parses the struct definition such that it separates pinned and not pinned
535507
/// fields. Afterwards it declares the struct and implement the `PinData` trait safely.
536508
#[doc(hidden)]

tests/ui/compile-fail/init/wrong_generics2.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ help: you might have forgotten to add the struct literal inside the block
1212
--> src/macros.rs
1313
|
1414
~ ::core::ptr::write($slot, $t { SomeStruct {
15-
|2 $($acc)*
15+
|4 $($acc)*
1616
~ } });
1717
|
1818

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,7 @@
1-
error: no rules expected `)`
2-
--> tests/ui/compile-fail/pinned_drop/no_fn.rs:6:1
1+
error[E0046]: not all trait items implemented, missing: `drop`
2+
--> tests/ui/compile-fail/pinned_drop/no_fn.rs:7:1
33
|
4-
6 | #[pinned_drop]
5-
| ^^^^^^^^^^^^^^ no rules expected this token in macro call
4+
7 | impl PinnedDrop for Foo {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation
66
|
7-
note: while trying to match keyword `fn`
8-
--> src/macros.rs
9-
|
10-
| fn drop($($sig:tt)*) {
11-
| ^^
12-
= note: this error originates in the attribute macro `pinned_drop` (in Nightly builds, run with -Z macro-backtrace for more info)
7+
= help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }`

tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use pin_init::*;
2+
use std::pin::Pin;
23

34
#[pin_data(PinnedDrop)]
45
struct Foo {}
Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
1-
error: no rules expected keyword `const`
2-
--> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:10:5
1+
error[E0438]: const `BAZ` is not a member of trait `pin_init::PinnedDrop`
2+
--> tests/ui/compile-fail/pinned_drop/unexpected_additional_item.rs:11:5
33
|
4-
10 | const BAZ: usize = 0;
5-
| ^^^^^ no rules expected this token in macro call
6-
|
7-
note: while trying to match `)`
8-
--> src/macros.rs
9-
|
10-
| ),
11-
| ^
4+
11 | const BAZ: usize = 0;
5+
| ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pin_init::PinnedDrop`
Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
1-
error[E0107]: trait takes 0 generic arguments but 1 generic argument was supplied
2-
--> tests/ui/compile-fail/pinned_drop/unexpected_generics.rs:10:9
1+
error: unexpected arguments for `PinnedDrop` path
2+
--> tests/ui/compile-fail/pinned_drop/unexpected_generics.rs:10:19
33
|
44
10 | impl<T> PinnedDrop<T> for Foo<T> {
5-
| ^^^^^^^^^^--- help: remove the unnecessary generics
6-
| |
7-
| expected 0 generic arguments
8-
|
9-
note: trait defined here, with 0 generic parameters
10-
--> src/lib.rs
11-
|
12-
| pub unsafe trait PinnedDrop: __internal::HasPinData {
13-
| ^^^^^^^^^^
5+
| ^^^
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
error: no rules expected keyword `const`
1+
error[E0438]: const `BAZ` is not a member of trait `pin_init::PinnedDrop`
22
--> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:8:5
33
|
44
8 | const BAZ: usize = 0;
5-
| ^^^^^ no rules expected this token in macro call
5+
| ^^^^^^^^^^^^^^^^^^^^^ not a member of trait `pin_init::PinnedDrop`
6+
7+
error[E0046]: not all trait items implemented, missing: `drop`
8+
--> tests/ui/compile-fail/pinned_drop/unexpected_item.rs:7:1
69
|
7-
note: while trying to match keyword `fn`
8-
--> src/macros.rs
10+
7 | impl PinnedDrop for Foo {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^ missing `drop` in implementation
912
|
10-
| fn drop($($sig:tt)*) {
11-
| ^^
13+
= help: implement the missing item: `fn drop(self: Pin<&mut Self>, _: OnlyCallFromDrop) { todo!() }`

0 commit comments

Comments
 (0)