Skip to content

Commit 4eecf5a

Browse files
committed
Allow other attributes in #[pin_data] structs
When using a macro with custom attributes in a `#[pin_data]` struct it can mess up the generated `__Unpin` struct. This commit moves the decision-making of which attributes to keep into a new, separate function, so that it is unified in all places. Only `#[cfg]` and `#[doc]` attributes are kept since all other can cause issues. With this commit a struct can use both #[pin] and other custom attributes, allowing combining multiple attribute macros without clashing. To keep this functionality this commit also adds a test with a custom attribute that fails without this fix. Signed-off-by: Martin Kletzander <mkletzan@redhat.com>
1 parent 9af36ce commit 4eecf5a

File tree

6 files changed

+54
-6
lines changed

6 files changed

+54
-6
lines changed

Cargo.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ trybuild = { version = "1.0", features = ["diff"] }
3535
macrotest = "1.0"
3636
# needed for macrotest, have to enable verbatim feature to be able to format `&raw` expressions.
3737
prettyplease = { version = "0.2.37", features = ["verbatim"] }
38+
dummy = { path = "tests/dummy" }
3839

3940
[lints.rust]
4041
stable_features = "allow"

internal/src/pin_data.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use syn::{
77
parse_quote, parse_quote_spanned,
88
spanned::Spanned,
99
visit_mut::VisitMut,
10-
Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
10+
Attribute, Field, Generics, Ident, Item, PathSegment, Type, TypePath, Visibility, WhereClause,
1111
};
1212

1313
use crate::diagnostics::{DiagCtxt, ErrorGuaranteed};
@@ -35,6 +35,11 @@ impl Parse for Args {
3535
}
3636
}
3737

38+
fn keep_attr(a: &Attribute) -> bool {
39+
let p = a.path();
40+
["cfg", "doc"].iter().any(|a| p.is_ident(a))
41+
}
42+
3843
pub(crate) fn pin_data(
3944
args: Args,
4045
input: Item,
@@ -77,9 +82,9 @@ pub(crate) fn pin_data(
7782
.fields
7883
.iter_mut()
7984
.map(|field| {
80-
let len = field.attrs.len();
81-
field.attrs.retain(|a| !a.path().is_ident("pin"));
82-
(len != field.attrs.len(), &*field)
85+
let pinned = field.attrs.iter().any(|a| a.path().is_ident("pin"));
86+
field.attrs.retain(keep_attr);
87+
(pinned, &*field)
8388
})
8489
.collect();
8590

@@ -259,7 +264,7 @@ fn generate_projections(
259264
},
260265
)| {
261266
let mut attrs = attrs.clone();
262-
attrs.retain(|a| !a.path().is_ident("pin"));
267+
attrs.retain(keep_attr);
263268
let mut no_doc_attrs = attrs.clone();
264269
no_doc_attrs.retain(|a| !a.path().is_ident("doc"));
265270
let ident = ident
@@ -359,7 +364,7 @@ fn generate_the_pin_data(
359364
pinned: bool,
360365
) -> TokenStream {
361366
let mut attrs = attrs.clone();
362-
attrs.retain(|a| !a.path().is_ident("pin"));
367+
attrs.retain(keep_attr);
363368
let ident = ident
364369
.as_ref()
365370
.expect("only structs with named fields are supported");

tests/dummy/Cargo.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "dummy"
3+
version = "0.0.0"
4+
edition = "2024"
5+
6+
license = "MIT OR Apache-2.0"
7+
description = "Proc macro only for test reproduction."
8+
9+
publish = false
10+
11+
[lib]
12+
proc-macro = true

tests/dummy/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[proc_macro_derive(Dummy, attributes(dummy_attr))]
2+
pub fn derive_device(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
3+
proc_macro::TokenStream::new()
4+
}

tests/extra_attrs.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use dummy::Dummy;
2+
use pin_init::*;
3+
4+
#[pin_data]
5+
#[derive(Dummy)]
6+
struct Pointless {
7+
#[pin]
8+
#[dummy_attr]
9+
#[cfg(test)]
10+
member: i8,
11+
#[pin]
12+
#[dummy_attr]
13+
#[cfg(not(test))]
14+
member: u8,
15+
}
16+
17+
#[test]
18+
fn multiple_attributes() {
19+
stack_pin_init!(let p = init!(Pointless { member: 0 }));
20+
println!("{}", p.member);
21+
}

0 commit comments

Comments
 (0)