diff --git a/README.md b/README.md index 644cb61c..982a0ccc 100644 --- a/README.md +++ b/README.md @@ -34,13 +34,13 @@ The current version of pin-project requires Rust 1.33 or later. ## Examples -[`pin_projectable`] attribute creates a projection struct covering all the fields. +[`pin_project`] attribute creates a projection struct covering all the fields. ```rust -use pin_project::pin_projectable; +use pin_project::pin_project; use std::pin::Pin; -#[pin_projectable] +#[pin_project] struct Foo { #[pin] future: T, @@ -58,7 +58,7 @@ impl Foo { [Code like this will be generated](doc/struct-example-1.md) -[`pin_projectable`]: https://docs.rs/pin-project/0.3/pin_project/attr.pin_projectable.html +[`pin_project`]: https://docs.rs/pin-project/0.3/pin_project/attr.pin_project.html ## License diff --git a/pin-project-internal/src/lib.rs b/pin-project-internal/src/lib.rs index ffbfcc33..99b6c839 100644 --- a/pin-project-internal/src/lib.rs +++ b/pin-project-internal/src/lib.rs @@ -12,13 +12,14 @@ extern crate proc_macro; #[macro_use] mod utils; -mod pin_projectable; +mod pin_project; +mod pinned_drop; #[cfg(feature = "project_attr")] mod project; use proc_macro::TokenStream; -use crate::utils::Nothing; +use utils::Nothing; #[cfg(feature = "project_attr")] #[proc_macro_attribute] @@ -27,17 +28,15 @@ pub fn project(args: TokenStream, input: TokenStream) -> TokenStream { project::attribute(input.into()).into() } -/// This is a doc comment from the defining crate! -#[proc_macro] -pub fn pin_project(input: TokenStream) -> TokenStream { - pin_projectable::pin_project(input.into()).unwrap_or_else(|e| e.to_compile_error()).into() +#[proc_macro_attribute] +pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { + pin_project::attribute(args.into(), input.into()).into() } #[proc_macro_attribute] -pub fn pin_projectable(args: TokenStream, input: TokenStream) -> TokenStream { - pin_projectable::attribute(args.into(), input.into()) - .unwrap_or_else(|e| e.to_compile_error()) - .into() +pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { + let _: Nothing = syn::parse_macro_input!(args); + pinned_drop::attribute(input.into()).into() } #[cfg(feature = "renamed")] diff --git a/pin-project-internal/src/pin_projectable/enums.rs b/pin-project-internal/src/pin_project/enums.rs similarity index 95% rename from pin-project-internal/src/pin_projectable/enums.rs rename to pin-project-internal/src/pin_project/enums.rs index ef1967f0..b98ad50b 100644 --- a/pin-project-internal/src/pin_projectable/enums.rs +++ b/pin-project-internal/src/pin_project/enums.rs @@ -6,13 +6,8 @@ use crate::utils::{proj_ident, VecExt}; use super::*; -pub(super) fn parse( - args: TokenStream, - mut item: ItemEnum, - pinned_drop: Option, -) -> Result { - let impl_drop = ImplDrop::new(item.generics.clone(), pinned_drop)?; - let mut impl_unpin = ImplUnpin::new(args, &item.generics)?; +pub(super) fn parse(args: Args, mut item: ItemEnum) -> Result { + let mut impl_unpin = args.impl_unpin(&item.generics); if item.variants.is_empty() { return Err(error!(item, "cannot be implemented for enums without variants")); @@ -34,6 +29,7 @@ pub(super) fn parse( let (proj_item_body, proj_arms) = variants(&mut item, &proj_ident, &mut impl_unpin)?; let ident = &item.ident; + let impl_drop = args.impl_drop(&item.generics); let proj_generics = proj_generics(&item.generics); let proj_ty_generics = proj_generics.split_for_impl().1; let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); diff --git a/pin-project-internal/src/pin_project/mod.rs b/pin-project-internal/src/pin_project/mod.rs new file mode 100644 index 00000000..3fc00650 --- /dev/null +++ b/pin-project-internal/src/pin_project/mod.rs @@ -0,0 +1,254 @@ +use proc_macro2::{Ident, Span, TokenStream}; +use quote::{quote, quote_spanned}; +use syn::{ + parse::{Parse, ParseStream}, + Fields, FieldsNamed, FieldsUnnamed, Generics, Index, Item, ItemStruct, Meta, NestedMeta, + Result, Type, +}; + +use crate::utils::{crate_path, Nothing}; + +mod enums; +mod structs; + +/// The annotation for pinned type. +const PIN: &str = "pin"; + +pub(super) fn attribute(args: TokenStream, input: TokenStream) -> TokenStream { + parse(args, input).unwrap_or_else(|e| e.to_compile_error()) +} + +#[derive(Clone, Copy)] +struct Args { + pinned_drop: Option, + unsafe_unpin: Option, +} + +impl Args { + fn impl_drop(self, generics: &Generics) -> ImplDrop<'_> { + ImplDrop::new(generics, self.pinned_drop) + } + + fn impl_unpin(self, generics: &Generics) -> ImplUnpin { + ImplUnpin::new(generics, self.unsafe_unpin) + } +} + +impl Parse for Args { + fn parse(input: ParseStream<'_>) -> Result { + let mut pinned_drop = None; + let mut unsafe_unpin = None; + while !input.is_empty() { + let i = input.parse::()?; + match &*i.to_string() { + "PinnedDrop" => pinned_drop = Some(i.span()), + "unsafe_Unpin" => unsafe_unpin = Some(i.span()), + _ => return Err(error!(i, "an invalid argument was passed")), + } + } + Ok(Self { pinned_drop, unsafe_unpin }) + } +} + +fn parse(args: TokenStream, input: TokenStream) -> Result { + let args = syn::parse2(args)?; + match syn::parse2(input)? { + Item::Struct(item) => { + let packed_check = ensure_not_packed(&item)?; + let mut res = structs::parse(args, item)?; + res.extend(packed_check); + Ok(res) + } + Item::Enum(item) => { + // We don't need to check for '#[repr(packed)]', + // since it does not apply to enums + enums::parse(args, item) + } + item => Err(error!(item, "may only be used on structs or enums")), + } +} + +fn ensure_not_packed(item: &ItemStruct) -> Result { + for meta in item.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + if let Meta::List(l) = meta { + if l.ident == "repr" { + for repr in l.nested.iter() { + if let NestedMeta::Meta(Meta::Word(w)) = repr { + if w == "packed" { + return Err(error!( + w, + "pin_project may not be used on #[repr(packed)] types" + )); + } + } + } + } + } + } + + // Workaround for https://github.com/taiki-e/pin-project/issues/32 + // Through the tricky use of proc macros, it's possible to bypass + // the above check for the 'repr' attribute. + // To ensure that it's impossible to use pin projections on a #[repr(packed)][ + // struct, we generate code like this: + // + // #[deny(safe_packed_borrows)] + // fn enforce_not_packed_for_MyStruct(val: MyStruct) { + // let _field1 = &val.field1; + // let _field2 = &val.field2; + // ... + // let _fieldn = &val.fieldn; + // } + // + // Taking a reference to a packed field is unsafe, amd appplying + // #[deny(safe_packed_borrows)] makes sure that doing this without + // an 'unsafe' block (which we deliberately do not generate) + // is a hard error. + // + // If the struct ends up having #[repr(packed)] applied somehow, + // this will generate an (unfriendly) error message. Under all reasonable + // circumstances, we'll detect the #[repr(packed)] attribute, and generate + // a much nicer error above. + // + // There is one exception: If the type of a struct field has a alignemtn of 1 + // (e.g. u8), it is always safe to take a reference to it, even if the struct + // is #[repr(packed)]. If the struct is composed entirely of types of alignent 1, + // our generated method will not trigger an error if the struct is #[repr(packed)] + // + // Fortunately, this should have no observable consequence - #[repr(packed)] + // is essentially a no-op on such a type. Nevertheless, we include a test + // to ensure that the compiler doesn't ever try to copy the fields on + // such a struct when trying to drop it - which is reason we prevent + // #[repr(packed)] in the first place + let mut field_refs = vec![]; + match &item.fields { + Fields::Named(FieldsNamed { named, .. }) => { + for field in named.iter() { + let ident = field.ident.as_ref().unwrap(); + field_refs.push(quote!(&val.#ident;)); + } + } + Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { + for (i, _) in unnamed.iter().enumerate() { + let index = Index::from(i); + field_refs.push(quote!(&val.#index;)); + } + } + Fields::Unit => {} + } + + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let struct_name = &item.ident; + let method_name = Ident::new( + &("__pin_project_assert_not_repr_packed_".to_string() + &item.ident.to_string()), + Span::call_site(), + ); + let test_fn = quote! { + #[deny(safe_packed_borrows)] + fn #method_name #impl_generics (val: #struct_name #ty_generics) #where_clause { + #(#field_refs)* + } + }; + Ok(test_fn) +} + +/// Makes the generics of projected type from the reference of the original generics. +fn proj_generics(generics: &Generics) -> Generics { + let mut generics = generics.clone(); + generics.params.insert(0, syn::parse_quote!('__a)); + generics +} + +// ================================================================================================= +// Drop implementation + +struct ImplDrop<'a> { + generics: &'a Generics, + pinned_drop: Option, +} + +impl<'a> ImplDrop<'a> { + fn new(generics: &'a Generics, pinned_drop: Option) -> Self { + Self { generics, pinned_drop } + } + + fn build(self, ident: &Ident) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + + if let Some(pinned_drop) = self.pinned_drop { + let crate_path = crate_path(); + let call = quote_spanned! { pinned_drop => + ::#crate_path::__private::UnsafePinnedDrop::pinned_drop(pinned_self) + }; + + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { + fn drop(&mut self) { + // Safety - we're in 'drop', so we know that 'self' will + // never move again + let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) }; + // We call `pinned_drop` only once. Since `UnsafePinnedDrop::pinned_drop` + // is an unsafe function and a private API, it is never called again in safe + // code *unless the user uses a maliciously crafted macro*. + unsafe { + #call; + } + } + } + } + } else { + quote! { + impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { + fn drop(&mut self) { + // Do nothing. The precense of this Drop + // impl ensures that the user can't provide one of their own + } + } + } + } + } +} + +// ================================================================================================= +// conditional Unpin implementation + +struct ImplUnpin { + generics: Generics, + unsafe_unpin: bool, +} + +impl ImplUnpin { + fn new(generics: &Generics, unsafe_unpin: Option) -> Self { + let mut generics = generics.clone(); + if let Some(unsafe_unpin) = unsafe_unpin { + let crate_path = crate_path(); + generics.make_where_clause().predicates.push( + syn::parse2(quote_spanned! { unsafe_unpin => + ::#crate_path::__private::Wrapper: ::#crate_path::UnsafeUnpin + }) + .unwrap(), + ); + } + + Self { generics, unsafe_unpin: unsafe_unpin.is_some() } + } + + fn push(&mut self, ty: &Type) { + // We only add bounds for automatically generated impls + if !self.unsafe_unpin { + self.generics + .make_where_clause() + .predicates + .push(syn::parse_quote!(#ty: ::core::marker::Unpin)); + } + } + + /// Creates `Unpin` implementation. + fn build(self, ident: &Ident) -> TokenStream { + let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); + quote! { + impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} + } + } +} diff --git a/pin-project-internal/src/pin_projectable/structs.rs b/pin-project-internal/src/pin_project/structs.rs similarity index 93% rename from pin-project-internal/src/pin_projectable/structs.rs rename to pin-project-internal/src/pin_project/structs.rs index 41220f5c..8a695beb 100644 --- a/pin-project-internal/src/pin_projectable/structs.rs +++ b/pin-project-internal/src/pin_project/structs.rs @@ -6,13 +6,8 @@ use crate::utils::{proj_ident, Nothing, VecExt}; use super::*; -pub(super) fn parse( - args: TokenStream, - mut item: ItemStruct, - pinned_drop: Option, -) -> Result { - let impl_drop = ImplDrop::new(item.generics.clone(), pinned_drop)?; - let mut impl_unpin = ImplUnpin::new(args, &item.generics)?; +pub(super) fn parse(args: Args, mut item: ItemStruct) -> Result { + let mut impl_unpin = args.impl_unpin(&item.generics); let (proj_item_body, proj_init_body) = match &mut item.fields { Fields::Named(FieldsNamed { named: fields, .. }) @@ -28,6 +23,7 @@ pub(super) fn parse( }; let ident = &item.ident; + let impl_drop = args.impl_drop(&item.generics); let proj_ident = proj_ident(&item.ident); let proj_generics = proj_generics(&item.generics); let proj_ty_generics = proj_generics.split_for_impl().1; diff --git a/pin-project-internal/src/pin_projectable/mod.rs b/pin-project-internal/src/pin_projectable/mod.rs deleted file mode 100644 index e70cb287..00000000 --- a/pin-project-internal/src/pin_projectable/mod.rs +++ /dev/null @@ -1,327 +0,0 @@ -use proc_macro2::{Ident, Span, TokenStream}; -use quote::{quote, ToTokens}; -use syn::parse::{Parse, ParseStream}; -use syn::{ - Fields, FieldsNamed, FieldsUnnamed, Generics, Index, Item, ItemEnum, ItemFn, ItemStruct, Meta, - NestedMeta, Result, ReturnType, Type, TypeTuple, -}; - -use crate::utils::{Nothing, VecExt}; - -mod enums; -mod structs; - -/// The annotation for pinned type. -const PIN: &str = "pin"; - -const PINNED_DROP: &str = "pinned_drop"; - -struct PinProject { - items: Vec, -} - -impl Parse for PinProject { - fn parse(input: ParseStream<'_>) -> Result { - let mut items = vec![]; - while !input.is_empty() { - items.push(input.parse()?); - } - Ok(Self { items }) - } -} - -fn handle_type(args: TokenStream, item: Item, pinned_drop: Option) -> Result { - match item { - Item::Struct(item) => { - let packed_check = ensure_not_packed(&item)?; - let mut res = structs::parse(args, item, pinned_drop)?; - res.extend(packed_check); - Ok(res) - } - Item::Enum(item) => { - // We don't need to check for '#[repr(packed)]', - // since it does not apply to enums - enums::parse(args, item, pinned_drop) - } - _ => unreachable!(), - } -} - -pub(super) fn pin_project(input: TokenStream) -> Result { - let span = span!(input); - let items: Vec = syn::parse2::(input)?.items; - - let mut found_type = None; - let mut found_pinned_drop = None; - - for mut item in items { - match &mut item { - Item::Struct(ItemStruct { attrs, .. }) | Item::Enum(ItemEnum { attrs, .. }) => { - if found_type.is_none() { - if let Some(attr) = attrs.find_remove("pin_projectable") { - let args = match attr.parse_meta()? { - Meta::List(l) => l.nested.into_token_stream(), - Meta::Word(_) => TokenStream::new(), - _ => return Err(error!(span!(attr), "invalid arguments")) - }; - - found_type = Some((item.clone(), args)); - } else { - return Err(error!(item, "type declared in pin_project! must have pin_projectable attribute")) - } - } else { - return Err(error!(item, "cannot declare multiple types within pinned module")) - } - }, - Item::Fn(fn_) => { - if let Some(attr)= fn_.attrs.find_remove(PINNED_DROP) { - let _: Nothing = syn::parse2(attr.tts)?; - if found_pinned_drop.is_none() { - if let ReturnType::Type(_, ty) = &fn_.decl.output { - match &**ty { - Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => {} - _ => return Err(error!(ty, "#[pinned_drop] function must return the unit type")), - } - } - found_pinned_drop = Some(fn_.clone()); - } else { - return Err(error!(fn_, "cannot declare multiple functions within pinned module")); - } - } else { - return Err(error!(fn_, "only #[pinned_drop] functions can be declared within a pin_project! block")); - } - }, - _ => return Err(error!(item, "pin_project! block may only contain a struct/enum with an optional #[pinned_drop] function")) - } - } - - let (type_, args) = match found_type { - Some(t) => t, - None => return Err(error!(span, "No #[pin_projectable] type found!")), - }; - - handle_type(args, type_, found_pinned_drop) -} - -pub(super) fn attribute(args: TokenStream, input: TokenStream) -> Result { - let item = syn::parse2(input)?; - match &item { - Item::Struct(_) | Item::Enum(_) => handle_type(args, item, None), - _ => Err(error!(item, "may only be used on structs or enums")), - } -} - -fn ensure_not_packed(item: &ItemStruct) -> Result { - for meta in item.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - if let Meta::List(l) = meta { - if l.ident == "repr" { - for repr in l.nested.iter() { - if let NestedMeta::Meta(Meta::Word(w)) = repr { - if w == "packed" { - return Err(error!( - w, - "pin_projectable may not be used on #[repr(packed)] types" - )); - } - } - } - } - } - } - - // Workaround for https://github.com/taiki-e/pin-project/issues/32 - // Through the tricky use of proc macros, it's possible to bypass - // the above check for the 'repr' attribute. - // To ensure that it's impossible to use pin projections on a #[repr(packed)][ - // struct, we generate code like this: - // - // #[deny(safe_packed_borrows)] - // fn enforce_not_packed_for_MyStruct(val: MyStruct) { - // let _field1 = &val.field1; - // let _field2 = &val.field2; - // ... - // let _fieldn = &val.fieldn; - // } - // - // Taking a reference to a packed field is unsafe, amd appplying - // #[deny(safe_packed_borrows)] makes sure that doing this without - // an 'unsafe' block (which we deliberately do not generate) - // is a hard error. - // - // If the struct ends up having #[repr(packed)] applied somehow, - // this will generate an (unfriendly) error message. Under all reasonable - // circumstances, we'll detect the #[repr(packed)] attribute, and generate - // a much nicer error above. - // - // There is one exception: If the type of a struct field has a alignemtn of 1 - // (e.g. u8), it is always safe to take a reference to it, even if the struct - // is #[repr(packed)]. If the struct is composed entirely of types of alignent 1, - // our generated method will not trigger an error if the struct is #[repr(packed)] - // - // Fortunately, this should have no observable consequence - #[repr(packed)] - // is essentially a no-op on such a type. Nevertheless, we include a test - // to ensure that the compiler doesn't ever try to copy the fields on - // such a struct when trying to drop it - which is reason we prevent - // #[repr(packed)] in the first place - let mut field_refs = vec![]; - match &item.fields { - Fields::Named(FieldsNamed { named, .. }) => { - for field in named.iter() { - let ident = field.ident.as_ref().unwrap(); - field_refs.push(quote!(&val.#ident;)); - } - } - Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { - for (i, _) in unnamed.iter().enumerate() { - let index = Index::from(i); - field_refs.push(quote!(&val.#index;)); - } - } - Fields::Unit => {} - } - - let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); - - let struct_name = &item.ident; - let method_name = Ident::new( - &("__pin_project_assert_not_repr_packed_".to_string() + &item.ident.to_string()), - Span::call_site(), - ); - let test_fn = quote! { - #[deny(safe_packed_borrows)] - fn #method_name #impl_generics (val: #struct_name #ty_generics) #where_clause { - #(#field_refs)* - } - }; - Ok(test_fn) -} - -/// Makes the generics of projected type from the reference of the original generics. -fn proj_generics(generics: &Generics) -> Generics { - let mut generics = generics.clone(); - generics.params.insert(0, syn::parse_quote!('__a)); - generics -} - -struct ImplDrop { - generics: Generics, - pinned_drop: Option, -} - -impl ImplDrop { - /// Parses attribute arguments. - fn new(generics: Generics, pinned_drop: Option) -> Result { - Ok(Self { generics, pinned_drop }) - } - - fn build(self, ident: &Ident) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); - - if let Some(fn_) = self.pinned_drop { - let fn_name = fn_.ident.clone(); - quote! { - impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { - fn drop(&mut self) { - // Declare the #[pinned_drop] function *inside* our drop function - // This guarantees that it's impossible for any other user code - // to call it - #fn_ - // Safety - we're in 'drop', so we know that 'self' will - // never move again - let pinned_self = unsafe { ::core::pin::Pin::new_unchecked(self) }; - // 'pinned_drop' is a free function - if it were part of a trait impl, - // it would be possible for user code to call it by directly invoking - // the trait. - #fn_name(pinned_self); - } - } - } - } else { - quote! { - impl #impl_generics ::core::ops::Drop for #ident #ty_generics #where_clause { - fn drop(&mut self) { - // Do nothing. The precense of this Drop - // impl ensures that the user can't provide one of their own - } - } - } - } - } -} - -// ================================================================================================= -// conditional Unpin implementation - -struct ImplUnpin { - generics: Generics, - auto: bool, -} - -impl ImplUnpin { - /// Parses attribute arguments. - fn new(args: TokenStream, generics: &Generics) -> Result { - let mut generics = generics.clone(); - generics.make_where_clause(); - - match &*args.to_string() { - "" => Ok(Self { generics, auto: true }), - "unsafe_Unpin" => Ok(Self { generics, auto: false }), - _ => Err(error!(args, "an invalid argument was passed")), - } - } - - fn push(&mut self, ty: &Type) { - // We only add bounds for automatically generated impls - if self.auto { - self.generics - .make_where_clause() - .predicates - .push(syn::parse_quote!(#ty: ::core::marker::Unpin)); - } - } - - /// Creates `Unpin` implementation. - fn build(self, ident: &Ident) -> TokenStream { - let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl(); - let mut where_clause = where_clause.unwrap().clone(); // Created in 'new' - - if self.auto { - quote! { - impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} - } - } else { - let pin_project_crate = pin_project_crate_path(); - where_clause.predicates.push(syn::parse_quote!(::#pin_project_crate::Wrapper<#ident #ty_generics>: ::#pin_project_crate::UnsafeUnpin)); - - quote! { - impl #impl_generics ::core::marker::Unpin for #ident #ty_generics #where_clause {} - } - } - } -} - -/// If the 'renamed' feature is enabled, we detect -/// the actual name of the 'pin-project' crate in the consumer's Cargo.toml -#[cfg(feature = "renamed")] -fn pin_project_crate_path() -> Ident { - use crate::PIN_PROJECT_CRATE; - // This is fairly subtle. - // Normally, you would use `env!("CARGO_PKG_NAME")` to get the name of the package, - // since it's set at compile time. - // However, we're in a proc macro, which runs while *another* crate is being compiled. - // By retreiving the runtime value of `CARGO_PKG_NAME`, we can figure out the name - // of the crate that's calling us. - let cur_crate = std::env::var("CARGO_PKG_NAME") - .expect("Could not find CARGO_PKG_NAME environemnt variable"); - Ident::new( - if cur_crate == "pin-project" { "pin_project" } else { PIN_PROJECT_CRATE.as_str() }, - Span::call_site(), - ) -} - -/// If the 'renamed' feature is not enabled, we just -/// assume that the 'pin-project' dependency has not been renamed -#[cfg(not(feature = "renamed"))] -fn pin_project_crate_path() -> Ident { - Ident::new("pin_project", Span::call_site()) -} diff --git a/pin-project-internal/src/pinned_drop.rs b/pin-project-internal/src/pinned_drop.rs new file mode 100644 index 00000000..9764e022 --- /dev/null +++ b/pin-project-internal/src/pinned_drop.rs @@ -0,0 +1,71 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + ArgCaptured, FnArg, GenericArgument, ItemFn, PathArguments, Result, ReturnType, Type, TypePath, + TypeReference, TypeTuple, +}; + +use crate::utils::crate_path; + +pub(super) fn attribute(input: TokenStream) -> TokenStream { + parse(input).unwrap_or_else(|e| e.to_compile_error()) +} + +fn parse_arg(arg: &FnArg) -> Result<&Type> { + if let FnArg::Captured(ArgCaptured { ty: Type::Path(TypePath { qself: None, path }), .. }) = + &arg + { + let ty = &path.segments[path.segments.len() - 1]; + if let PathArguments::AngleBracketed(args) = &ty.arguments { + if args.args.len() == 1 && ty.ident == "Pin" { + // &mut + if let GenericArgument::Type(Type::Reference(TypeReference { + mutability: Some(_), + elem, + .. + })) = &args.args[0] + { + return Ok(&**elem); + } + } + } + } + + Err(error!(&arg, "#[pinned_drop] function must take a argument `Pin<&mut Type>`")) +} + +fn parse(input: TokenStream) -> Result { + let item: ItemFn = syn::parse2(input)?; + if let ReturnType::Type(_, ty) = &item.decl.output { + match &**ty { + Type::Tuple(TypeTuple { elems, .. }) if elems.is_empty() => {} + _ => return Err(error!(ty, "#[pinned_drop] function must return the unit type")), + } + } + if item.decl.inputs.len() != 1 { + return Err(error!( + item.decl.inputs, + "#[pinned_drop] function must take exactly one argument" + )); + } + + let crate_path = crate_path(); + let type_ = parse_arg(&item.decl.inputs[0])?; + let fn_name = &item.ident; + let (impl_generics, _, where_clause) = item.decl.generics.split_for_impl(); + + Ok(quote! { + unsafe impl #impl_generics ::#crate_path::__private::UnsafePinnedDrop for #type_ #where_clause { + unsafe fn pinned_drop(self: ::core::pin::Pin<&mut Self>) { + // Declare the #[pinned_drop] function *inside* our pinned_drop function + // This guarantees that it's impossible for any other user code + // to call it + #item + // #[pinned_drop] function is a free function - if it were part of a trait impl, + // it would be possible for user code to call it by directly invoking + // the trait. + #fn_name(self); + } + } + }) +} diff --git a/pin-project-internal/src/utils.rs b/pin-project-internal/src/utils.rs index fca284e1..fa35ab21 100644 --- a/pin-project-internal/src/utils.rs +++ b/pin-project-internal/src/utils.rs @@ -29,10 +29,29 @@ impl Parse for Nothing { } } -macro_rules! span { - ($expr:expr) => { - $expr.clone() - }; +/// If the 'renamed' feature is enabled, we detect +/// the actual name of the 'pin-project' crate in the consumer's Cargo.toml +#[cfg(feature = "renamed")] +pub(crate) fn crate_path() -> Ident { + // This is fairly subtle. + // Normally, you would use `env!("CARGO_PKG_NAME")` to get the name of the package, + // since it's set at compile time. + // However, we're in a proc macro, which runs while *another* crate is being compiled. + // By retreiving the runtime value of `CARGO_PKG_NAME`, we can figure out the name + // of the crate that's calling us. + let cur_crate = std::env::var("CARGO_PKG_NAME") + .expect("Could not find CARGO_PKG_NAME environemnt variable"); + Ident::new( + if cur_crate == "pin-project" { "pin_project" } else { crate::PIN_PROJECT_CRATE.as_str() }, + Span::call_site(), + ) +} + +/// If the 'renamed' feature is not enabled, we just +/// assume that the 'pin-project' dependency has not been renamed +#[cfg(not(feature = "renamed"))] +pub(crate) fn crate_path() -> Ident { + Ident::new("pin_project", Span::call_site()) } macro_rules! error { diff --git a/src/lib.rs b/src/lib.rs index f00454c2..ba725033 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,13 +2,13 @@ //! //! ## Examples //! -//! [`pin_projectable`] attribute creates a projection struct covering all the fields. +//! [`pin_project`] attribute creates a projection struct covering all the fields. //! //! ```rust -//! use pin_project::pin_projectable; +//! use pin_project::pin_project; //! use std::pin::Pin; //! -//! #[pin_projectable] +//! #[pin_project] //! struct Foo { //! #[pin] //! future: T, @@ -64,17 +64,17 @@ //! //! //! -//! [`pin_projectable`] also supports enums, but to use it ergonomically, you need +//! [`pin_project`] also supports enums, but to use it ergonomically, you need //! to use the [`project`] attribute. //! //! ```rust //! # #[cfg(feature = "project_attr")] -//! use pin_project::{project, pin_projectable}; +//! use pin_project::{project, pin_project}; //! # #[cfg(feature = "project_attr")] //! use std::pin::Pin; //! //! # #[cfg(feature = "project_attr")] -//! #[pin_projectable] +//! #[pin_project] //! enum Foo { //! Future(#[pin] T), //! Done(U), @@ -136,14 +136,15 @@ //! //! //! -//! See [`pin_projectable`] and [`project`] for more details. +//! See [`pin_project`] and [`project`] for more details. //! -//! [`pin_projectable`]: ./attr.pin_projectable.html +//! [`pin_project`]: ./attr.pin_project.html //! [`project`]: ./attr.project.html -//! + #![recursion_limit = "256"] #![doc(html_root_url = "https://docs.rs/pin-project/0.3.4")] #![doc(test(attr(deny(warnings), allow(dead_code, unused_assignments, unused_variables))))] +#![no_std] #![warn(unsafe_code)] #![warn(rust_2018_idioms, unreachable_pub)] #![warn(single_use_lifetimes)] @@ -162,10 +163,10 @@ /// ### `let` bindings /// /// ```rust -/// use pin_project::{pin_projectable, project}; -/// # use std::pin::Pin; +/// use pin_project::{pin_project, project}; +/// use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// struct Foo { /// #[pin] /// future: T, @@ -187,10 +188,10 @@ /// ### `match` expressions /// /// ```rust -/// use pin_project::{project, pin_projectable}; -/// # use std::pin::Pin; +/// use pin_project::{project, pin_project}; +/// use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// enum Foo { /// Tuple(#[pin] A, B), /// Struct { field: C }, @@ -272,14 +273,14 @@ pub use pin_project_internal::project; /// /// ## Examples /// -/// Using `#[pin_projectable]` will automatically create the appropriate +/// Using `#[pin_project]` will automatically create the appropriate /// conditional [`Unpin`] implementation: /// /// ```rust -/// use pin_project::pin_projectable; +/// use pin_project::pin_project; /// use std::pin::Pin; /// -/// #[pin_projectable] +/// #[pin_project] /// struct Foo { /// #[pin] /// future: T, @@ -297,13 +298,13 @@ pub use pin_project_internal::project; /// /// If you want to implement [`Unpin`] manually, /// you must use the `unsafe_Unpin` argument to -/// `#[pin_projectable]`. +/// `#[pin_project]`. /// /// ```rust -/// use pin_project::{pin_projectable, UnsafeUnpin}; +/// use pin_project::{pin_project, UnsafeUnpin}; /// use std::pin::Pin; /// -/// #[pin_projectable(unsafe_Unpin)] +/// #[pin_project(unsafe_Unpin)] /// struct Foo { /// #[pin] /// future: T, @@ -321,14 +322,55 @@ pub use pin_project_internal::project; /// unsafe impl UnsafeUnpin for Foo {} // Conditional Unpin impl /// ``` /// -/// Note the usage of the unsafe `UnsafeUnpin` trait, instead of the usual -/// `Unpin` trait. `UnsafeUnpin` behaves exactly like `Unpin`, except that is +/// Note the usage of the unsafe [`UnsafeUnpin`] trait, instead of the usual +/// [`Unpin`] trait. [`UnsafeUnpin`] behaves exactly like [`Unpin`], except that is /// unsafe to implement. This unsafety comes from the fact that pin projections -/// are being used. If you implement `UnsafeUnpin`, you must ensure that it is -/// only implemented when all pin-projected fields implement `Unpin`. +/// are being used. If you implement [`UnsafeUnpin`], you must ensure that it is +/// only implemented when all pin-projected fields implement [`Unpin`]. /// /// Note that borrowing the field where `#[pin]` attribute is used multiple -/// times requires using `.as_mut()` to avoid consuming the `Pin`. +/// times requires using [`.as_mut()`](core::pin::Pin::as_mut) to avoid +/// consuming the `Pin`. +/// +/// ### `#[pinned_drop]` +/// +/// In order to correctly implement pin projections, a type's `Drop` impl must +/// not move out of any stucturally pinned fields. Unfortunately, [`Drop::drop`] +/// takes `&mut Self`, not `Pin<&mut Self>`. +/// +/// To ensure that this requirement is upheld, the `pin_project` attribute will +/// provide a `Drop` impl for you. This `Drop` impl will delegate to a function +/// annotated with `#[pinned_drop]` if you use the `PinnedDrop` argument to +/// `#[pin_project]`. This function acts just like a normal [`drop`] impl, except +/// for the fact that it takes `Pin<&mut Self>`. In particular, it will never be +/// called more than once, just like [`Drop::drop`]. +/// +/// For example: +/// +/// ```rust +/// use pin_project::{pin_project, pinned_drop}; +/// use std::fmt::Debug; +/// use std::pin::Pin; +/// +/// #[pin_project(PinnedDrop)] +/// pub struct Foo { +/// #[pin] pinned_field: T, +/// unpin_field: U +/// } +/// +/// #[pinned_drop] +/// fn my_drop_fn(foo: Pin<&mut Foo>) { +/// let foo = foo.project(); +/// println!("Dropping pinned field: {:?}", foo.pinned_field); +/// println!("Dropping unpin field: {:?}", foo.unpin_field); +/// } +/// +/// fn main() { +/// Foo { pinned_field: true, unpin_field: 40 }; +/// } +/// ``` +/// +/// See also [`pinned_drop`] attribute. /// /// ## Supported Items /// @@ -337,9 +379,10 @@ pub use pin_project_internal::project; /// ### Structs (structs with named fields): /// /// ```rust -/// # use pin_project::pin_projectable; -/// # use std::pin::Pin; -/// #[pin_projectable] +/// use pin_project::pin_project; +/// use std::pin::Pin; +/// +/// #[pin_project] /// struct Foo { /// #[pin] /// future: T, @@ -358,9 +401,10 @@ pub use pin_project_internal::project; /// ### Tuple structs (structs with unnamed fields): /// /// ```rust -/// # use pin_project::pin_projectable; -/// # use std::pin::Pin; -/// #[pin_projectable] +/// use pin_project::pin_project; +/// use std::pin::Pin; +/// +/// #[pin_project] /// struct Foo(#[pin] T, U); /// /// impl Foo { @@ -377,17 +421,17 @@ pub use pin_project_internal::project; /// /// ### Enums /// -/// `pin_projectable` also supports enums, but to use it ergonomically, you need +/// `pin_project` also supports enums, but to use it ergonomically, you need /// to use the [`project`] attribute. /// /// ```rust /// # #[cfg(feature = "project_attr")] -/// use pin_project::{project, pin_projectable}; +/// use pin_project::{project, pin_project}; /// # #[cfg(feature = "project_attr")] -/// # use std::pin::Pin; +/// use std::pin::Pin; /// /// # #[cfg(feature = "project_attr")] -/// #[pin_projectable] +/// #[pin_project] /// enum Foo { /// Tuple(#[pin] A, B), /// Struct { field: C }, @@ -415,93 +459,52 @@ pub use pin_project_internal::project; /// /// Enums without variants (zero-variant enums) are not supported. /// -/// ### `#[pinned_drop]` -/// -/// In order to correctly implement pin projections, a type's Drop impl must -/// not move out of any stucturally pinned fields. Unfortunately, `Drop::drop` takes -/// `&mut Self`, not `Pin<&mut Self>`. +/// See also [`project`] attribute. /// -/// To ensure that this requirement is upheld, the `pin_projectable` attribute will -/// provide a `Drop` impl for you. This `Drop` impl will delegate to a function -/// annotated with `#[pinned_drop]`, if present in the same `pin_project!` block. -/// This function acts just like a normal `drop` impl, except for the fact that it -/// takes `Pin<&mut Self>`. In particular, it will never be called more than once, -/// just like `Drop::drop`. -/// -/// For example: -/// -/// ```rust -/// use std::fmt::Debug; -/// use pin_project::pin_project; -/// use std::pin::Pin; -/// -/// pin_project! { -/// #[pin_projectable] -/// pub struct Foo { -/// #[pin] pinned_field: T, -/// unpin_field: U -/// } -/// -/// #[pinned_drop] -/// fn my_drop_fn(foo: Pin<&mut Foo>) { -/// let foo = foo.project(); -/// println!("Dropping pinned field: {:?}", foo.pinned_field); -/// println!("Dropping unpin field: {:?}", foo.unpin_field); -/// } -/// } -/// -/// fn main() { -/// Foo { pinned_field: true, unpin_field: 40 }; -/// } -/// ``` -/// -/// -/// -/// Also see [`project`] attribute. -/// -/// [`Unpin`]: core::marker::Unpin /// [`drop`]: Drop::drop /// [`project`]: ./attr.project.html +/// [`pinned_drop`]: ./attr.pinned_drop.html #[doc(inline)] -pub use pin_project_internal::pin_projectable; +pub use pin_project_internal::pin_project; -/// A helper macro for working with `pin_projectable`. +/// An attribute for annotating a function that implements [`Drop`]. /// -/// This macro is only needed when you wish to provide a `Drop` -/// impl for your type. You may include a single `#[pin_projectable]` -/// type, and (optionally) one `#[pinned_drop]` function. Writing -/// anything else within the `pin_projectable` block is an error. +/// This attribute is only needed when you wish to provide a [`Drop`] +/// impl for your type. The function annotated with `#[pinned_drop]` acts just +/// like a normal [`drop`](Drop::drop) impl, except for the fact that it takes +/// `Pin<&mut Self>`. In particular, it will never be called more than once, +/// just like [`Drop::drop`]. /// /// Example: /// /// ```rust -/// +/// use pin_project::{pin_project, pinned_drop}; /// use std::pin::Pin; -/// use pin_project::pin_project; -/// -/// pin_project! { /// -/// #[pin_projectable] -/// struct Foo { -/// #[pin] field: u8 -/// } +/// #[pin_project(PinnedDrop)] +/// struct Foo { +/// #[pin] field: u8 +/// } /// -/// #[pinned_drop] -/// fn my_drop(foo: Pin<&mut Foo>) { -/// println!("Dropping: {}", foo.field); -/// } +/// #[pinned_drop] +/// fn my_drop(foo: Pin<&mut Foo>) { +/// println!("Dropping: {}", foo.field); /// } /// /// fn main() { /// Foo { field: 50 }; /// } -///``` +/// ``` +/// +/// See [`pin_project`] attribute for more. +/// +/// [`pin_project`]: ./attr.pin_project.html #[doc(inline)] -pub use pin_project_internal::pin_project; +pub use pin_project_internal::pinned_drop; /// A trait used for custom implementations of [`Unpin`]. /// This trait is used in conjunction with the `unsafe_Unpin` -/// argument to [`pin_projectable`] +/// argument to [`pin_project`] /// /// The Rust [`Unpin`] trait is safe to implement - by itself, /// implementing it cannot lead to undefined behavior. Undefined @@ -513,7 +516,7 @@ pub use pin_project_internal::pin_project; /// you to violate any of the guarnatees required by pin projection. /// /// However, things change if you want to provide a custom `Unpin` impl -/// for your `#[pin_projectable]` type. As stated in [the Rust +/// for your `#[pin_project]` type. As stated in [the Rust /// documentation](https://doc.rust-lang.org/beta/std/pin/index.html#projections-and-structural-pinning), /// you must be sure to only implement `Unpin` when all of your `#[pin]` fields (i.e. struturally /// pinend fields) are also `Unpin`. @@ -526,7 +529,7 @@ pub use pin_project_internal::pin_project; /// you must be sure that your `UnsafeUnpinned` impls follows all of /// the requirements for an `Unpin` impl of a structurally-pinned type. /// -/// Note that if you specify `#[pin_projectable(unsafe_Unpin)]`, but do *not* +/// Note that if you specify `#[pin_project(unsafe_Unpin)]`, but do *not* /// provide an impl of `UnsafeUnpin`, your type will never implement `Unpin`. /// This is effectly the same thing as adding a `PhantomUnpin` to your type /// @@ -539,9 +542,9 @@ pub use pin_project_internal::pin_project; /// fields be `Unpin`, imposes an additional requirement: /// /// ```rust -/// use pin_project::{pin_projectable, UnsafeUnpin}; +/// use pin_project::{pin_project, UnsafeUnpin}; /// -/// #[pin_projectable(unsafe_Unpin)] +/// #[pin_project(unsafe_Unpin)] /// struct Foo { /// #[pin] /// field_1: K, @@ -551,57 +554,71 @@ pub use pin_project_internal::pin_project; /// unsafe impl UnsafeUnpin for Foo where K: Unpin + Clone {} /// ``` /// -/// [`pin_projectable`]: ./attr.pin_projectable.html +/// [`pin_project`]: ./attr.pin_project.html #[allow(unsafe_code)] pub unsafe trait UnsafeUnpin {} -// This is an internal helper struct used by `pin-project-internal`. -// This allows us to force an error if the user tries to provide -// a regular `Unpin` impl when they specify the `unsafe_Unpin` argument -// This is why we need Wrapper: -// -// Supposed we have the following code: -// -// #[pin_projectable(unsafe_Unpin)] -// struct MyStruct { -// #[pin] field: T -// } -// -// impl Unpin for MyStruct where MyStruct: UnsafeUnpinned {} // generated by pin-project-internal -// impl Unpin for MyStruct where T: Copy // written by the user -// -// We want this code to be rejected - the user is completely bypassing unsafe_Unpin, -// and providing an unsound Unpin impl in safe code! -// -// Unfortunately, the Rust compiler will accept the above code. -// Because MyStruct is declared in the same crate as the user-provided impl, -// the compiler will notice that 'MyStruct: UnsafeUnpinned' never holds. -// -// The solution is to introduce the 'Wrapper' struct, which is defined -// in the 'pin-project' crate. -// -// We now have code that looks like this: -// -// impl Unpin for MyStruct where Wrapper>: UnsafeUnpinned {} // generated by pin-project-internal -// impl Unpin for MyStruct where T: Copy // written by the user -// -// We also have 'unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {}' in the -// 'pin-project' crate. -// -// Now, our generated impl has a bound involving a type defined in another crate - Wrapper. -// This will cause rust to conservatively assume that 'Wrapper>: UnsafeUnpinned' -// holds, in the interest of preserving forwards compatibility (in case such an impl is added -// for Wrapper in a new version of the crate). -// -// This will cause rust to reject any other Unpin impls for MyStruct, since it will -// assume that our generated impl could potentially apply in any situation. -// -// This acheives the desired effect - when the user writes `#[pin_projectable(unsafe_Unpin)]`, -// the user must either provide no impl of `UnsafeUnpinned` (which is equivalent -// to making the type never implement Unpin), or provide an impl of `UnsafeUnpin`. -// It is impossible for them to provide an impl of `Unpin` #[doc(hidden)] -pub struct Wrapper(T); +pub mod __private { + use super::UnsafeUnpin; + use core::pin::Pin; -#[allow(unsafe_code)] -unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {} + // do not call or define it directly. + #[allow(unsafe_code)] + #[doc(hidden)] + pub unsafe trait UnsafePinnedDrop { + #[doc(hidden)] + unsafe fn pinned_drop(self: Pin<&mut Self>); + } + + // This is an internal helper struct used by `pin-project-internal`. + // This allows us to force an error if the user tries to provide + // a regular `Unpin` impl when they specify the `unsafe_Unpin` argument + // This is why we need Wrapper: + // + // Supposed we have the following code: + // + // #[pin_project(unsafe_Unpin)] + // struct MyStruct { + // #[pin] field: T + // } + // + // impl Unpin for MyStruct where MyStruct: UnsafeUnpinned {} // generated by pin-project-internal + // impl Unpin for MyStruct where T: Copy // written by the user + // + // We want this code to be rejected - the user is completely bypassing unsafe_Unpin, + // and providing an unsound Unpin impl in safe code! + // + // Unfortunately, the Rust compiler will accept the above code. + // Because MyStruct is declared in the same crate as the user-provided impl, + // the compiler will notice that 'MyStruct: UnsafeUnpinned' never holds. + // + // The solution is to introduce the 'Wrapper' struct, which is defined + // in the 'pin-project' crate. + // + // We now have code that looks like this: + // + // impl Unpin for MyStruct where Wrapper>: UnsafeUnpinned {} // generated by pin-project-internal + // impl Unpin for MyStruct where T: Copy // written by the user + // + // We also have 'unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {}' in the + // 'pin-project' crate. + // + // Now, our generated impl has a bound involving a type defined in another crate - Wrapper. + // This will cause rust to conservatively assume that 'Wrapper>: UnsafeUnpinned' + // holds, in the interest of preserving forwards compatibility (in case such an impl is added + // for Wrapper in a new version of the crate). + // + // This will cause rust to reject any other Unpin impls for MyStruct, since it will + // assume that our generated impl could potentially apply in any situation. + // + // This acheives the desired effect - when the user writes `#[pin_project(unsafe_Unpin)]`, + // the user must either provide no impl of `UnsafeUnpinned` (which is equivalent + // to making the type never implement Unpin), or provide an impl of `UnsafeUnpin`. + // It is impossible for them to provide an impl of `Unpin` + #[doc(hidden)] + pub struct Wrapper(T); + + #[allow(unsafe_code)] + unsafe impl UnsafeUnpin for Wrapper where T: UnsafeUnpin {} +} diff --git a/tests/compile-test.rs b/tests/compile-test.rs index dce6cfce..8a68893d 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -1,5 +1,6 @@ #![cfg(compile_test)] #![cfg(feature = "project_attr")] +#![warn(rust_2018_idioms)] use std::{env, path::PathBuf}; diff --git a/tests/pin_projectable.rs b/tests/pin_project.rs similarity index 80% rename from tests/pin_projectable.rs rename to tests/pin_project.rs index 5e097ab0..4ca66646 100644 --- a/tests/pin_projectable.rs +++ b/tests/pin_project.rs @@ -5,13 +5,13 @@ #![allow(dead_code)] use core::pin::Pin; -use pin_project_internal::{pin_project, pin_projectable}; +use pin_project::pin_project; #[test] -fn test_pin_projectable() { +fn test_pin_project() { // struct - #[pin_projectable] + #[pin_project] struct Foo { #[pin] field1: T, @@ -30,7 +30,7 @@ fn test_pin_projectable() { // tuple struct - #[pin_projectable] + #[pin_project] struct Bar(#[pin] T, U); let mut bar = Bar(1, 2); @@ -45,7 +45,7 @@ fn test_pin_projectable() { // enum - #[pin_projectable] + #[pin_project] enum Baz { Variant1(#[pin] A, B), Variant2 { @@ -107,7 +107,7 @@ fn test_pin_projectable() { fn where_clause_and_associated_type_fields() { // struct - #[pin_projectable] + #[pin_project] struct Foo where I: Iterator, @@ -119,7 +119,7 @@ fn where_clause_and_associated_type_fields() { // enum - #[pin_projectable] + #[pin_project] enum Baz where I: Iterator, @@ -133,39 +133,19 @@ fn where_clause_and_associated_type_fields() { fn trait_bounds_on_type_generics() { // struct - #[pin_projectable] + #[pin_project] pub struct Foo<'a, T: ?Sized> { field: &'a mut T, } // tuple struct - #[pin_projectable] + #[pin_project] pub struct Bar<'a, T: ?Sized>(&'a mut T); // enum - #[pin_projectable] + #[pin_project] enum Baz<'a, T: ?Sized> { Variant(&'a mut T), } } - -pin_project! { - #[pin_projectable] - pub struct Foo<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } - - #[pinned_drop] - fn do_drop(foo: Pin<&mut Foo<'_>>) { - **foo.project().was_dropped = true; - } -} - -#[test] -fn safe_project() { - let mut was_dropped = false; - drop(Foo { was_dropped: &mut was_dropped, field_2: 42 }); - assert!(was_dropped); -} diff --git a/tests/pinned_drop.rs b/tests/pinned_drop.rs new file mode 100644 index 00000000..6c870abd --- /dev/null +++ b/tests/pinned_drop.rs @@ -0,0 +1,27 @@ +#![recursion_limit = "128"] +#![no_std] +#![warn(unsafe_code)] +#![warn(rust_2018_idioms)] +#![allow(dead_code)] + +use core::pin::Pin; +use pin_project::{pin_project, pinned_drop}; + +#[pin_project(PinnedDrop)] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} + +#[pinned_drop] +fn do_drop(foo: Pin<&mut Foo<'_>>) { + **foo.project().was_dropped = true; +} + +#[test] +fn safe_project() { + let mut was_dropped = false; + drop(Foo { was_dropped: &mut was_dropped, field_2: 42 }); + assert!(was_dropped); +} diff --git a/tests/project.rs b/tests/project.rs index c83741ea..a9c1f151 100644 --- a/tests/project.rs +++ b/tests/project.rs @@ -6,14 +6,14 @@ #![cfg(feature = "project_attr")] use core::pin::Pin; -use pin_project_internal::{pin_projectable, project}; +use pin_project::{pin_project, project}; #[project] // Nightly does not need a dummy attribute to the function. #[test] fn test_project_attr() { // struct - #[pin_projectable] + #[pin_project] struct Foo { #[pin] field1: T, @@ -33,7 +33,7 @@ fn test_project_attr() { // tuple struct - #[pin_projectable] + #[pin_project] struct Bar(#[pin] T, U); let mut bar = Bar(1, 2); @@ -49,7 +49,7 @@ fn test_project_attr() { // enum - #[pin_projectable] + #[pin_project] enum Baz { Variant1(#[pin] A, B), Variant2 { diff --git a/tests/repr_packed.rs b/tests/repr_packed.rs index 0d9d64bc..9b92f227 100644 --- a/tests/repr_packed.rs +++ b/tests/repr_packed.rs @@ -7,7 +7,7 @@ use std::cell::Cell; // Ensure that the compiler doesn't copy the fields // of #[repr(packed)] types during drop, if the field has alignment 1 // (that is, any reference to the field is guaranteed to have proper alignment) -// We are currently unable to statically prevent the usage of #[pin_projectable] +// We are currently unable to statically prevent the usage of #[pin_project] // on #[repr(packed)] types composed entirely of fields of alignment 1. // This shouldn't lead to undefined behavior, as long as the compiler doesn't // try to move the field anyway during drop. diff --git a/tests/ui/pin_projectable/auxiliary/sneaky_macro.rs b/tests/ui/pin_project/auxiliary/sneaky_macro.rs similarity index 100% rename from tests/ui/pin_projectable/auxiliary/sneaky_macro.rs rename to tests/ui/pin_project/auxiliary/sneaky_macro.rs diff --git a/tests/ui/pin_project/drop_conflict.rs b/tests/ui/pin_project/drop_conflict.rs new file mode 100644 index 00000000..5b4fda51 --- /dev/null +++ b/tests/ui/pin_project/drop_conflict.rs @@ -0,0 +1,19 @@ +// compile-fail + +#![deny(warnings, unsafe_code)] + +use pin_project::pin_project; +use std::pin::Pin; + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +impl Drop for Foo { + fn drop(&mut self) {} +} + +fn main() {} diff --git a/tests/ui/pin_project/drop_conflict.stderr b/tests/ui/pin_project/drop_conflict.stderr new file mode 100644 index 00000000..3f5fbc09 --- /dev/null +++ b/tests/ui/pin_project/drop_conflict.stderr @@ -0,0 +1,12 @@ +error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `Foo<_, _>`: + --> $DIR/drop_conflict.rs:8:1 + | +8 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +... +15 | impl Drop for Foo { + | ----------------------------- first implementation here + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/pin_project/forget-unsafe-unpin.rs b/tests/ui/pin_project/forget-unsafe-unpin.rs new file mode 100644 index 00000000..7045838e --- /dev/null +++ b/tests/ui/pin_project/forget-unsafe-unpin.rs @@ -0,0 +1,20 @@ +// run-pass + +#![deny(warnings, unsafe_code)] + +use pin_project::{pin_project, UnsafeUnpin}; + +#[test] +fn unsafe_unpin() { + #[pin_project(unsafe_Unpin)] + pub struct Blah { + field_1: u8, + #[pin] + field_2: Option, + } + + #[allow(unsafe_code)] + unsafe impl UnsafeUnpin for Blah {} +} + +fn main() {} diff --git a/tests/ui/pin_project/forget-unsafe-unpin.stderr b/tests/ui/pin_project/forget-unsafe-unpin.stderr new file mode 100644 index 00000000..e69de29b diff --git a/tests/ui/pin_projectable/invalid.rs b/tests/ui/pin_project/invalid.rs similarity index 71% rename from tests/ui/pin_projectable/invalid.rs rename to tests/ui/pin_project/invalid.rs index cb5ff845..ac7c0a56 100644 --- a/tests/ui/pin_projectable/invalid.rs +++ b/tests/ui/pin_project/invalid.rs @@ -1,24 +1,24 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_projectable; +use pin_project::pin_project; -#[pin_projectable] +#[pin_project] struct A { #[pin()] //~ ERROR unexpected token future: T, } -#[pin_projectable] +#[pin_project] struct B(#[pin(foo)] T); //~ ERROR unexpected token -#[pin_projectable] +#[pin_project] enum C { A(#[pin(foo)] T), //~ ERROR unexpected token } -#[pin_projectable] +#[pin_project] enum D { A { #[pin(foo)] //~ ERROR unexpected token diff --git a/tests/ui/pin_projectable/invalid.stderr b/tests/ui/pin_project/invalid.stderr similarity index 100% rename from tests/ui/pin_projectable/invalid.stderr rename to tests/ui/pin_project/invalid.stderr diff --git a/tests/ui/pin_projectable/packed.rs b/tests/ui/pin_project/packed.rs similarity index 62% rename from tests/ui/pin_projectable/packed.rs rename to tests/ui/pin_project/packed.rs index d78bfb1e..fcddd0f8 100644 --- a/tests/ui/pin_projectable/packed.rs +++ b/tests/ui/pin_project/packed.rs @@ -1,20 +1,19 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] +use pin_project::pin_project; -use pin_project::pin_projectable; - -#[pin_projectable] +#[pin_project] #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type struct Foo { #[pin] field: u8, } -// Test putting 'repr' before the 'pin_projectable' attribute +// Test putting 'repr' before the 'pin_project' attribute #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type -#[pin_projectable] +#[pin_project] struct Foo2 { #[pin] field: u8, diff --git a/tests/ui/pin_project/packed.stderr b/tests/ui/pin_project/packed.stderr new file mode 100644 index 00000000..c6dc2bd9 --- /dev/null +++ b/tests/ui/pin_project/packed.stderr @@ -0,0 +1,14 @@ +error: pin_project may not be used on #[repr(packed)] types + --> $DIR/packed.rs:8:8 + | +8 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type + | ^^^^^^ + +error: pin_project may not be used on #[repr(packed)] types + --> $DIR/packed.rs:15:8 + | +15 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type + | ^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/pin_projectable/packed_sneaky.rs b/tests/ui/pin_project/packed_sneaky.rs similarity index 50% rename from tests/ui/pin_projectable/packed_sneaky.rs rename to tests/ui/pin_project/packed_sneaky.rs index c2212170..37c24bf7 100644 --- a/tests/ui/pin_projectable/packed_sneaky.rs +++ b/tests/ui/pin_project/packed_sneaky.rs @@ -4,13 +4,13 @@ #[macro_use] extern crate sneaky_macro; -use pin_project::pin_projectable; +use pin_project::pin_project; -#[pin_projectable] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block +#[pin_project] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block #[hidden_repr(packed)] struct Bar { #[pin] - field: u32 + field: u32, } fn main() {} diff --git a/tests/ui/pin_projectable/packed_sneaky.stderr b/tests/ui/pin_project/packed_sneaky.stderr similarity index 71% rename from tests/ui/pin_projectable/packed_sneaky.stderr rename to tests/ui/pin_project/packed_sneaky.stderr index 0c02158c..02460b22 100644 --- a/tests/ui/pin_projectable/packed_sneaky.stderr +++ b/tests/ui/pin_project/packed_sneaky.stderr @@ -1,14 +1,14 @@ error: borrow of packed field is unsafe and requires unsafe function or block (error E0133) --> $DIR/packed_sneaky.rs:9:1 | -9 | #[pin_projectable] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block - | ^^^^^^^^^^^^^^^^^^ +9 | #[pin_project] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block + | ^^^^^^^^^^^^^^ | note: lint level defined here --> $DIR/packed_sneaky.rs:9:1 | -9 | #[pin_projectable] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block - | ^^^^^^^^^^^^^^^^^^ +9 | #[pin_project] //~ ERROR borrow of packed field is unsafe and requires unsafe function or block + | ^^^^^^^^^^^^^^ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #46043 = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior diff --git a/tests/ui/pin_project/unpin_conflict.rs b/tests/ui/pin_project/unpin_conflict.rs new file mode 100644 index 00000000..4f21f35a --- /dev/null +++ b/tests/ui/pin_project/unpin_conflict.rs @@ -0,0 +1,42 @@ +// compile-fail + +#![deny(warnings, unsafe_code)] + +use pin_project::pin_project; +use std::pin::Pin; + +// The same implementation. + +#[pin_project] //~ ERROR E0119 +struct Foo { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl + +// The implementation that under different conditions. + +#[pin_project] //~ ERROR E0119 +struct Bar { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Bar {} // Non-conditional Unpin impl + +#[pin_project] //~ ERROR E0119 +struct Baz { + #[pin] + future: T, + field: U, +} + +// conflicting implementations +impl Unpin for Baz {} // Conditional Unpin impl + +fn main() {} diff --git a/tests/ui/pin_projectable/unpin_conflict.stderr b/tests/ui/pin_project/unpin_conflict.stderr similarity index 54% rename from tests/ui/pin_projectable/unpin_conflict.stderr rename to tests/ui/pin_project/unpin_conflict.stderr index 7baa4bef..8a6df905 100644 --- a/tests/ui/pin_projectable/unpin_conflict.stderr +++ b/tests/ui/pin_project/unpin_conflict.stderr @@ -1,28 +1,28 @@ error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`: --> $DIR/unpin_conflict.rs:10:1 | -10 | #[pin_projectable] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` +10 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>` ... -26 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl +18 | impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl | --------------------------------------------- first implementation here error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`: - --> $DIR/unpin_conflict.rs:30:1 + --> $DIR/unpin_conflict.rs:22:1 | -30 | #[pin_projectable] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` +22 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>` ... -46 | impl Unpin for Bar {} // Non-conditional Unpin impl +30 | impl Unpin for Bar {} // Non-conditional Unpin impl | ------------------------------ first implementation here error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`: - --> $DIR/unpin_conflict.rs:48:1 + --> $DIR/unpin_conflict.rs:32:1 | -48 | #[pin_projectable] //~ ERROR E0119 - | ^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` +32 | #[pin_project] //~ ERROR E0119 + | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>` ... -64 | impl Unpin for Baz {} // Conditional Unpin impl +40 | impl Unpin for Baz {} // Conditional Unpin impl | -------------------------------------------- first implementation here error: aborting due to 3 previous errors diff --git a/tests/ui/pin_projectable/unsupported.rs b/tests/ui/pin_project/unsupported.rs similarity index 75% rename from tests/ui/pin_projectable/unsupported.rs rename to tests/ui/pin_project/unsupported.rs index 777f4fc0..b5b4a17e 100644 --- a/tests/ui/pin_projectable/unsupported.rs +++ b/tests/ui/pin_project/unsupported.rs @@ -1,27 +1,27 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_projectable; +use pin_project::pin_project; -#[pin_projectable] +#[pin_project] struct Struct1 {} //~ ERROR cannot be implemented for structs with zero fields -#[pin_projectable] +#[pin_project] struct Struct2(); //~ ERROR cannot be implemented for structs with zero fields -#[pin_projectable] +#[pin_project] struct Struct3; //~ ERROR cannot be implemented for structs with units -#[pin_projectable] +#[pin_project] enum Enum1 {} //~ ERROR cannot be implemented for enums without variants -#[pin_projectable] +#[pin_project] enum Enum2 { A = 2, //~ ERROR cannot be implemented for enums with discriminants } -#[pin_projectable] +#[pin_project] enum Enum1 { A, //~ ERROR cannot be implemented for enums that have no field B, diff --git a/tests/ui/pin_projectable/unsupported.stderr b/tests/ui/pin_project/unsupported.stderr similarity index 100% rename from tests/ui/pin_projectable/unsupported.stderr rename to tests/ui/pin_project/unsupported.stderr diff --git a/tests/ui/pin_projectable/packed.stderr b/tests/ui/pin_projectable/packed.stderr deleted file mode 100644 index e627e2a6..00000000 --- a/tests/ui/pin_projectable/packed.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: pin_projectable may not be used on #[repr(packed)] types - --> $DIR/packed.rs:9:8 - | -9 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type - | ^^^^^^ - -error: pin_projectable may not be used on #[repr(packed)] types - --> $DIR/packed.rs:16:8 - | -16 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] type - | ^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/pin_projectable/unpin_conflict.rs b/tests/ui/pin_projectable/unpin_conflict.rs deleted file mode 100644 index d7923483..00000000 --- a/tests/ui/pin_projectable/unpin_conflict.rs +++ /dev/null @@ -1,66 +0,0 @@ -// compile-fail - -#![deny(warnings)] - -use pin_project::pin_projectable; -use std::pin::Pin; - -// The same implementation. - -#[pin_projectable] //~ ERROR E0119 -struct Foo { - #[pin] - future: T, - field: U, -} - -impl Foo { - fn baz(mut self: Pin<&mut Self>) { - let this = self.project(); - let _: Pin<&mut T> = this.future; // Pinned reference to the field - let _: &mut U = this.field; // Normal reference to the field - } -} - -// conflicting implementations -impl Unpin for Foo where T: Unpin {} // Conditional Unpin impl - -// The implementation that under different conditions. - -#[pin_projectable] //~ ERROR E0119 -struct Bar { - #[pin] - future: T, - field: U, -} - -impl Bar { - fn baz(mut self: Pin<&mut Self>) { - let this = self.project(); - let _: Pin<&mut T> = this.future; // Pinned reference to the field - let _: &mut U = this.field; // Normal reference to the field - } -} - -// conflicting implementations -impl Unpin for Bar {} // Non-conditional Unpin impl - -#[pin_projectable] //~ ERROR E0119 -struct Baz { - #[pin] - future: T, - field: U, -} - -impl Baz { - fn baz(mut self: Pin<&mut Self>) { - let this = self.project(); - let _: Pin<&mut T> = this.future; // Pinned reference to the field - let _: &mut U = this.field; // Normal reference to the field - } -} - -// conflicting implementations -impl Unpin for Baz {} // Conditional Unpin impl - -fn main() {} diff --git a/tests/ui/pinned_drop/forget-pinned-drop.rs b/tests/ui/pinned_drop/forget-pinned-drop.rs new file mode 100644 index 00000000..d6a86cb7 --- /dev/null +++ b/tests/ui/pinned_drop/forget-pinned-drop.rs @@ -0,0 +1,15 @@ +// compile-fail + +#![deny(warnings, unsafe_code)] + +use core::pin::Pin; +use pin_project::{pin_project, pinned_drop}; + +#[pin_project(PinnedDrop)] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} + +fn main() {} diff --git a/tests/ui/pinned_drop/forget-pinned-drop.stderr b/tests/ui/pinned_drop/forget-pinned-drop.stderr new file mode 100644 index 00000000..8c894a46 --- /dev/null +++ b/tests/ui/pinned_drop/forget-pinned-drop.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `Foo<'a>: pin_project::__private::UnsafePinnedDrop` is not satisfied + --> $DIR/forget-pinned-drop.rs:8:15 + | +8 | #[pin_project(PinnedDrop)] + | ^^^^^^^^^^ the trait `pin_project::__private::UnsafePinnedDrop` is not implemented for `Foo<'a>` + | + = note: required by `pin_project::__private::UnsafePinnedDrop::pinned_drop` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/pinned_drop/invalid.rs b/tests/ui/pinned_drop/invalid.rs index 41c6aa1f..d7ec7a92 100644 --- a/tests/ui/pinned_drop/invalid.rs +++ b/tests/ui/pinned_drop/invalid.rs @@ -1,19 +1,28 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_project; +use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; -pin_project! { - #[pin_projectable] - pub struct Foo<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } +#[pin_project] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} + +#[pinned_drop(foo)] //~ ERROR unexpected token +fn do_drop(_foo: Pin<&mut Foo<'_>>) {} - #[pinned_drop(foo)] //~ ERROR unexpected token - fn do_drop(_foo: Pin<&mut Foo<'_>>) {} +#[pin_project] +pub struct Bar<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, } +#[pinned_drop] +fn do_drop(_bar: &mut Bar<'_>) {} //~ ERROR #[pinned_drop] function must take a argument `Pin<&mut Type>` + fn main() {} diff --git a/tests/ui/pinned_drop/invalid.stderr b/tests/ui/pinned_drop/invalid.stderr index 2fef4391..212c044d 100644 --- a/tests/ui/pinned_drop/invalid.stderr +++ b/tests/ui/pinned_drop/invalid.stderr @@ -1,8 +1,14 @@ error: unexpected token - --> $DIR/invalid.rs:15:18 + --> $DIR/invalid.rs:15:15 | -15 | #[pinned_drop(foo)] //~ ERROR unexpected token - | ^^^^^ +15 | #[pinned_drop(foo)] //~ ERROR unexpected token + | ^^^ -error: aborting due to previous error +error: #[pinned_drop] function must take a argument `Pin<&mut Type>` + --> $DIR/invalid.rs:26:12 + | +26 | fn do_drop(_bar: &mut Bar<'_>) {} //~ ERROR #[pinned_drop] function must take a argument `Pin<&mut Type>` + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/tests/ui/pinned_drop/return-type.rs b/tests/ui/pinned_drop/return-type.rs index 5d259d66..0bb41acc 100644 --- a/tests/ui/pinned_drop/return-type.rs +++ b/tests/ui/pinned_drop/return-type.rs @@ -1,35 +1,35 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::pin_project; +use pin_project::{pin_project, pinned_drop}; use std::pin::Pin; -pin_project! { - #[pin_projectable] - pub struct Foo<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } +#[pin_project] +pub struct Foo<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} - #[pinned_drop] - fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { //~ ERROR #[pinned_drop] function must return the unit type - **foo.project().was_dropped = true; - true - } +#[pinned_drop] +fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { + //~ ERROR #[pinned_drop] function must return the unit type + **foo.project().was_dropped = true; + true } -pin_project! { - #[pin_projectable] - pub struct Bar<'a> { - was_dropped: &'a mut bool, - #[pin] field_2: u8 - } +#[pin_project] +pub struct Bar<'a> { + was_dropped: &'a mut bool, + #[pin] + field_2: u8, +} - #[pinned_drop] - fn do_drop(foo: Pin<&mut Bar<'_>>) -> () { // OK - **foo.project().was_dropped = true; - } +// OK +#[pinned_drop] +fn do_drop(foo: Pin<&mut Bar<'_>>) -> () { + **foo.project().was_dropped = true; } fn main() {} diff --git a/tests/ui/pinned_drop/return-type.stderr b/tests/ui/pinned_drop/return-type.stderr index b7d3c45a..8a44eb5b 100644 --- a/tests/ui/pinned_drop/return-type.stderr +++ b/tests/ui/pinned_drop/return-type.stderr @@ -1,8 +1,8 @@ error: #[pinned_drop] function must return the unit type - --> $DIR/return-type.rs:16:43 + --> $DIR/return-type.rs:16:39 | -16 | fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { //~ ERROR #[pinned_drop] function must return the unit type - | ^^^^ +16 | fn do_drop(foo: Pin<&mut Foo<'_>>) -> bool { + | ^^^^ error: aborting due to previous error diff --git a/tests/ui/project/invalid.rs b/tests/ui/project/invalid.rs index def05443..ea20a55d 100644 --- a/tests/ui/project/invalid.rs +++ b/tests/ui/project/invalid.rs @@ -1,10 +1,10 @@ // compile-fail -#![deny(warnings)] +#![deny(warnings, unsafe_code)] -use pin_project::{pin_projectable, project}; +use pin_project::{pin_project, project}; -#[pin_projectable] +#[pin_project] struct A { #[pin] future: T, diff --git a/tests/unsafe_unpin.rs b/tests/unsafe_unpin.rs index 5b1c3192..73ba6a0d 100644 --- a/tests/unsafe_unpin.rs +++ b/tests/unsafe_unpin.rs @@ -4,11 +4,11 @@ #![warn(rust_2018_idioms)] #![allow(dead_code)] -use pin_project::pin_projectable; +use pin_project::pin_project; #[test] fn unsafe_unpin() { - #[pin_projectable(unsafe_Unpin)] + #[pin_project(unsafe_Unpin)] pub struct Blah { field_1: u8, #[pin]