diff options
author | Haibo Huang <hhb@google.com> | 2020-07-14 08:50:28 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2020-07-14 08:50:28 +0000 |
commit | 943844cca149d51a0569aa0b29c64260453a78b0 (patch) | |
tree | 8bceab18c5a2b8dcf0cfa390e53d1cbbd9114775 | |
parent | 27dda6474b2fed371024beec388352867d8dfb1b (diff) | |
parent | da3cbc7de1c67c57198a4a91605983c8f26832c8 (diff) | |
download | pin-project-internal-943844cca149d51a0569aa0b29c64260453a78b0.tar.gz |
Upgrade rust/crates/pin-project-internal to 0.4.22 am: a51d04878a am: 75b4792f09 am: da3cbc7de1
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/pin-project-internal/+/1360739
Change-Id: Iadc3aa774a0dcc56ba1ddf0d2e435e4a5646e91e
-rw-r--r-- | Android.bp | 8 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | Cargo.toml.orig | 2 | ||||
-rw-r--r-- | METADATA | 6 | ||||
-rw-r--r-- | build.rs | 34 | ||||
-rw-r--r-- | src/lib.rs | 417 | ||||
-rw-r--r-- | src/pin_project/attribute.rs | 26 | ||||
-rw-r--r-- | src/pin_project/derive.rs | 810 | ||||
-rw-r--r-- | src/pinned_drop.rs | 60 | ||||
-rw-r--r-- | src/project.rs | 117 | ||||
-rw-r--r-- | src/utils.rs | 206 |
11 files changed, 974 insertions, 714 deletions
@@ -5,6 +5,10 @@ rust_proc_macro { crate_name: "pin_project_internal", srcs: ["src/lib.rs"], edition: "2018", + flags: [ + "--cfg deprecated_proc_macro", + "--cfg underscore_consts", + ], rustlibs: [ "libproc_macro2", "libquote", @@ -19,6 +23,10 @@ rust_test_host { test_suites: ["general-tests"], auto_gen_config: true, edition: "2018", + flags: [ + "--cfg deprecated_proc_macro", + "--cfg underscore_consts", + ], rustlibs: [ "libproc_macro2", "libquote", @@ -13,7 +13,7 @@ [package] edition = "2018" name = "pin-project-internal" -version = "0.4.17" +version = "0.4.22" authors = ["Taiki Endo <te316e89@gmail.com>"] description = "An internal crate to support pin_project - do not use directly\n" homepage = "https://github.com/taiki-e/pin-project" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index e5dce33..a973af7 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,6 +1,6 @@ [package] name = "pin-project-internal" -version = "0.4.17" +version = "0.4.22" authors = ["Taiki Endo <te316e89@gmail.com>"] edition = "2018" license = "Apache-2.0 OR MIT" @@ -9,11 +9,11 @@ third_party { type: GIT value: "https://github.com/taiki-e/pin-project" } - version: "0.4.17" + version: "0.4.22" license_type: NOTICE last_upgrade_date { year: 2020 - month: 5 - day: 18 + month: 7 + day: 10 } } diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..b4d314c --- /dev/null +++ b/build.rs @@ -0,0 +1,34 @@ +use std::{env, process::Command, str}; + +// The rustc-cfg strings below are *not* public API. Please let us know by +// opening a GitHub issue if your build environment requires some way to enable +// these cfgs other than by executing our build script. +fn main() { + let minor = match rustc_minor_version() { + Some(minor) => minor, + None => return, + }; + + // Underscore const names requires Rust 1.37: + // https://github.com/rust-lang/rust/pull/61347 + if minor >= 37 { + println!("cargo:rustc-cfg=underscore_consts"); + } + + // #[deprecated] on proc-macro requires Rust 1.40: + // https://github.com/rust-lang/rust/pull/65666 + if minor >= 40 { + println!("cargo:rustc-cfg=deprecated_proc_macro"); + } +} + +fn rustc_minor_version() -> Option<u32> { + let rustc = env::var_os("RUSTC")?; + let output = Command::new(rustc).arg("--version").output().ok()?; + let version = str::from_utf8(&output.stdout).ok()?; + let mut pieces = version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + pieces.next()?.parse().ok() +} @@ -1,7 +1,6 @@ //! An internal crate to support pin_project - **do not use directly** -#![recursion_limit = "256"] -#![doc(html_root_url = "https://docs.rs/pin-project-internal/0.4.17")] +#![doc(html_root_url = "https://docs.rs/pin-project-internal/0.4.22")] #![doc(test( no_crate_inject, attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code)) @@ -12,6 +11,8 @@ // mem::take and #[non_exhaustive] requires Rust 1.40 #![allow(clippy::mem_replace_with_default, clippy::manual_non_exhaustive)] #![allow(clippy::needless_doctest_main)] +// https://github.com/rust-lang/rust-clippy/issues/5704 +#![allow(clippy::unnested_or_patterns)] // older compilers require explicit `extern crate`. #[allow(unused_extern_crates)] @@ -26,20 +27,20 @@ mod project; use proc_macro::TokenStream; -use crate::utils::{Immutable, Mutable, Owned}; +use crate::utils::ProjKind; -/// An attribute that creates a projection type covering all the fields of struct or enum. +/// An attribute that creates projection types covering all the fields of +/// struct or enum. /// -/// This attribute creates a projection type according to the following rules: +/// This attribute creates projection types according to the following rules: /// -/// * For the field that uses `#[pin]` attribute, makes the pinned reference to the field. -/// * For the other fields, makes the unpinned reference to the field. +/// * For the fields that use `#[pin]` attribute, create the pinned reference to +/// the field. +/// * For the other fields, create a normal reference to the field. /// -/// And the following methods are implemented on the original `#[pin_project]` type: +/// And the following methods are implemented on the original type: /// -/// ``` -/// # #[rustversion::since(1.36)] -/// # fn dox() { +/// ```rust /// # use std::pin::Pin; /// # type Projection<'a> = &'a (); /// # type ProjectionRef<'a> = &'a (); @@ -47,7 +48,6 @@ use crate::utils::{Immutable, Mutable, Owned}; /// fn project(self: Pin<&mut Self>) -> Projection<'_>; /// fn project_ref(self: Pin<&Self>) -> ProjectionRef<'_>; /// # } -/// # } /// ``` /// /// By passing an argument with the same name as the method to the attribute, @@ -57,40 +57,54 @@ use crate::utils::{Immutable, Mutable, Owned}; /// use pin_project::pin_project; /// use std::pin::Pin; /// -/// #[pin_project(project = EnumProj)] -/// enum Enum<T> { -/// Variant(#[pin] T), +/// #[pin_project(project = StructProj)] +/// struct Struct<T> { +/// #[pin] +/// field: T, /// } /// -/// fn func<T>(x: Pin<&mut Enum<T>>) { -/// match x.project() { -/// EnumProj::Variant(y) => { -/// let _: Pin<&mut T> = y; -/// } +/// impl<T> Struct<T> { +/// fn method(self: Pin<&mut Self>) { +/// let this: StructProj<'_, T> = self.project(); +/// let StructProj { field } = this; +/// let _: Pin<&mut T> = field; /// } /// } /// ``` /// -/// The visibility of the projected type and projection method is based on the original type. -/// However, if the visibility of the original type is `pub`, the visibility of the projected type -/// and the projection method is downgraded to `pub(crate)`. +/// Note that the projection types returned by `project` and `project_ref` have +/// an additional lifetime at the beginning of generics. +/// +/// The visibility of the projected type and projection method is based on the +/// original type. However, if the visibility of the original type is `pub`, the +/// visibility of the projected type and the projection method is downgraded to +/// `pub(crate)`. /// /// # Safety /// -/// This attribute is completely safe. In the absence of other `unsafe` code *that you write*, -/// it is impossible to cause [undefined behavior][undefined-behavior] with this attribute. +/// This attribute is completely safe. In the absence of other `unsafe` code +/// *that you write*, it is impossible to cause [undefined +/// behavior][undefined-behavior] with this attribute. /// /// This is accomplished by enforcing the four requirements for pin projection /// stated in [the Rust documentation][pin-projection]: /// -/// 1. The struct must only be [`Unpin`] if all the structural fields are [`Unpin`]. +/// 1. The struct must only be [`Unpin`] if all the structural fields are +/// [`Unpin`]. +/// +/// To enforce this, this attribute will automatically generate an [`Unpin`] +/// implementation for you, which will require that all structurally pinned +/// fields be [`Unpin`]. /// -/// To enforce this, this attribute will automatically generate an [`Unpin`] implementation -/// for you, which will require that all structurally pinned fields be [`Unpin`] -/// If you wish to provide an manual [`Unpin`] impl, you can do so via the +/// If you attempt to provide an [`Unpin`] impl, the blanket impl will then +/// apply to your type, causing a compile-time error due to the conflict with +/// the second impl. +/// +/// If you wish to provide a manual [`Unpin`] impl, you can do so via the /// [`UnsafeUnpin`][unsafe-unpin] argument. /// -/// 2. The destructor of the struct must not move structural fields out of its argument. +/// 2. The destructor of the struct must not move structural fields out of its +/// argument. /// /// To enforce this, this attribute will generate code like this: /// @@ -101,70 +115,48 @@ use crate::utils::{Immutable, Mutable, Owned}; /// impl MyStructMustNotImplDrop for MyStruct {} /// ``` /// -/// If you attempt to provide an [`Drop`] impl, the blanket impl will -/// then apply to your type, causing a compile-time error due to -/// the conflict with the second impl. +/// If you attempt to provide an [`Drop`] impl, the blanket impl will then +/// apply to your type, causing a compile-time error due to the conflict with +/// the second impl. /// /// If you wish to provide a custom [`Drop`] impl, you can annotate an impl -/// with [`#[pinned_drop]`][pinned-drop]. This impl takes a pinned version of your struct - -/// that is, [`Pin`]`<&mut MyStruct>` where `MyStruct` is the type of your struct. +/// with [`#[pinned_drop]`][pinned-drop]. This impl takes a pinned version of +/// your struct - that is, [`Pin`]`<&mut MyStruct>` where `MyStruct` is the +/// type of your struct. /// /// You can call `project()` on this type as usual, along with any other /// methods you have defined. Because your code is never provided with /// a `&mut MyStruct`, it is impossible to move out of pin-projectable /// fields in safe code in your destructor. /// -/// 3. You must make sure that you uphold the [`Drop` guarantee][drop-guarantee]: once your struct is pinned, -/// the memory that contains the content is not overwritten or deallocated without calling the content's destructors. +/// 3. You must make sure that you uphold the [`Drop` +/// guarantee][drop-guarantee]: once your struct is pinned, the memory that +/// contains the content is not overwritten or deallocated without calling +/// the content's destructors. /// -/// Safe code doesn't need to worry about this - the only wait to violate this requirement -/// is to manually deallocate memory (which is `unsafe`), or to overwrite a field with something else. -/// Because your custom destructor takes [`Pin`]`<&mut MyStruct>`, it's impossible to obtain -/// a mutable reference to a pin-projected field in safe code. +/// Safe code doesn't need to worry about this - the only wait to violate +/// this requirement is to manually deallocate memory (which is `unsafe`), +/// or to overwrite a field with something else. +/// Because your custom destructor takes [`Pin`]`<&mut MyStruct>`, it's +/// impossible to obtain a mutable reference to a pin-projected field in safe +/// code. /// -/// 4. You must not offer any other operations that could lead to data being moved out of the structural fields when your type is pinned. +/// 4. You must not offer any other operations that could lead to data being +/// moved out of the structural fields when your type is pinned. /// -/// As with requirement 3, it is impossible for safe code to violate this. This crate ensures that safe code can never -/// obtain a mutable reference to `#[pin]` fields, which prevents you from ever moving out of them in safe code. +/// As with requirement 3, it is impossible for safe code to violate this. +/// This crate ensures that safe code can never obtain a mutable reference to +/// `#[pin]` fields, which prevents you from ever moving out of them in safe +/// code. /// -/// Pin projections are also incompatible with [`#[repr(packed)]`][repr-packed] structs. Attempting to use this attribute -/// on a [`#[repr(packed)]`][repr-packed] struct results in a compile-time error. +/// Pin projections are also incompatible with [`#[repr(packed)]`][repr-packed] +/// structs. Attempting to use this attribute on a +/// [`#[repr(packed)]`][repr-packed] struct results in a compile-time error. /// /// # Examples /// -/// Using `#[pin_project]` will automatically create the appropriate -/// conditional [`Unpin`] implementation: -/// -/// ```rust -/// use pin_project::pin_project; -/// use std::pin::Pin; -/// -/// #[pin_project] -/// struct Struct<T, U> { -/// #[pin] -/// pinned: T, -/// unpinned: U, -/// } -/// -/// impl<T, U> Struct<T, U> { -/// fn method(self: Pin<&mut Self>) { -/// let this = self.project(); -/// let _: Pin<&mut T> = this.pinned; // Pinned reference to the field -/// let _: &mut U = this.unpinned; // Normal reference to the field -/// } -/// } -/// ``` -/// -/// If you want to call the `project()` method multiple times or later use the -/// original [`Pin`] type, it needs to use [`.as_mut()`][`Pin::as_mut`] to avoid -/// consuming the [`Pin`]. -/// -/// ## Supported Items -/// /// `#[pin_project]` can be used on structs and enums. /// -/// [Structs](https://doc.rust-lang.org/reference/items/structs.html): -/// /// ```rust /// use pin_project::pin_project; /// use std::pin::Pin; @@ -185,8 +177,6 @@ use crate::utils::{Immutable, Mutable, Owned}; /// } /// ``` /// -/// [Tuple structs](https://doc.rust-lang.org/reference/items/structs.html): -/// /// ```rust /// use pin_project::pin_project; /// use std::pin::Pin; @@ -203,10 +193,8 @@ use crate::utils::{Immutable, Mutable, Owned}; /// } /// ``` /// -/// [Enums](https://doc.rust-lang.org/reference/items/enumerations.html): -/// -/// `#[pin_project]` supports enums, but to use it, you need to name the -/// projection type returned from the method or to use with the [`project`] attribute. +/// To use `#[pin_project]` on enums, you need to name the projection type +/// returned from the method. /// /// ```rust /// use pin_project::pin_project; @@ -234,9 +222,30 @@ use crate::utils::{Immutable, Mutable, Owned}; /// } /// ``` /// -/// See also [`project`] and [`project_ref`] attributes. +/// If you want to call the `project()` method multiple times or later use the +/// original [`Pin`] type, it needs to use [`.as_mut()`][`Pin::as_mut`] to avoid +/// consuming the [`Pin`]. +/// +/// ```rust +/// use pin_project::pin_project; +/// use std::pin::Pin; +/// +/// #[pin_project] +/// struct Struct<T> { +/// #[pin] +/// field: T, +/// } +/// +/// impl<T> Struct<T> { +/// fn call_project_twice(mut self: Pin<&mut Self>) { +/// // `project` consumes `self`, so reborrow the `Pin<&mut Self>` via `as_mut`. +/// self.as_mut().project(); +/// self.as_mut().project(); +/// } +/// } +/// ``` /// -/// ## `!Unpin` +/// # `!Unpin` /// /// If you want to ensure that [`Unpin`] is not implemented, use the `!Unpin` /// argument to `#[pin_project]`. @@ -245,24 +254,21 @@ use crate::utils::{Immutable, Mutable, Owned}; /// use pin_project::pin_project; /// /// #[pin_project(!Unpin)] -/// struct Struct<T, U> { -/// #[pin] -/// pinned: T, -/// unpinned: U, +/// struct Struct<T> { +/// field: T, /// } /// ``` /// -/// You can also ensure `!Unpin` by using `#[pin]` attribute for [`PhantomPinned`] field. +/// This is equivalent to using `#[pin]` attribute for the [`PhantomPinned`] +/// field. /// /// ```rust /// use pin_project::pin_project; /// use std::marker::PhantomPinned; /// /// #[pin_project] -/// struct Struct<T, U> { -/// #[pin] -/// pinned: T, -/// unpinned: U, +/// struct Struct<T> { +/// field: T, /// #[pin] /// _pin: PhantomPinned, /// } @@ -270,7 +276,7 @@ use crate::utils::{Immutable, Mutable, Owned}; /// /// Note that using [`PhantomPinned`] without `#[pin]` attribute has no effect. /// -/// ## `UnsafeUnpin` +/// # `UnsafeUnpin` /// /// If you want to implement [`Unpin`] manually, you must use the `UnsafeUnpin` /// argument to `#[pin_project]`. @@ -289,23 +295,24 @@ use crate::utils::{Immutable, Mutable, Owned}; /// ``` /// /// 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`]. +/// [`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`]. /// /// See [`UnsafeUnpin`] trait for more details. /// -/// ## `#[pinned_drop]` +/// # `#[pinned_drop]` /// /// In order to correctly implement pin projections, a type's [`Drop`] impl must -/// not move out of any structurally pinned fields. Unfortunately, [`Drop::drop`] -/// takes `&mut Self`, not [`Pin`]`<&mut Self>`. +/// not move out of any structurally 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 an impl -/// block annotated with `#[pinned_drop]` if you use the `PinnedDrop` argument -/// to `#[pin_project]`. +/// To ensure that this requirement is upheld, the `#[pin_project]` attribute +/// will provide a [`Drop`] impl for you. This [`Drop`] impl will delegate to +/// an impl block annotated with `#[pinned_drop]` if you use the `PinnedDrop` +/// argument to `#[pin_project]`. /// /// This impl block acts just like a normal [`Drop`] impl, /// except for the following two: @@ -324,7 +331,8 @@ use crate::utils::{Immutable, Mutable, Owned}; /// implemented. To drop a type that implements `PinnedDrop`, use the [`drop`] /// function just like dropping a type that directly implements [`Drop`]. /// -/// In particular, it will never be called more than once, just like [`Drop::drop`]. +/// In particular, it will never be called more than once, just like +/// [`Drop::drop`]. /// /// For example: /// @@ -352,65 +360,80 @@ use crate::utils::{Immutable, Mutable, Owned}; /// } /// ``` /// -/// See also [`pinned_drop`] attribute. +/// See also [`#[pinned_drop]`][`pinned_drop`] attribute. /// -/// ## `project_replace()` +/// # `project_replace()` /// /// In addition to the `project()` and `project_ref()` methods which are always -/// provided when you use the `#[pin_project]` attribute, there is a third method, -/// `project_replace()` which can be useful in some situations. It is equivalent -/// to [`Pin::set`], except that the unpinned fields are moved and returned, -/// instead of being dropped in-place. +/// provided when you use the `#[pin_project]` attribute, there is a third +/// method, `project_replace()` which can be useful in some situations. It is +/// equivalent to [`Pin::set`], except that the unpinned fields are moved and +/// returned, instead of being dropped in-place. /// -/// ``` -/// # #[rustversion::since(1.36)] -/// # fn dox() { +/// ```rust /// # use std::pin::Pin; /// # type ProjectionOwned = (); /// # trait Dox { /// fn project_replace(self: Pin<&mut Self>, other: Self) -> ProjectionOwned; /// # } -/// # } /// ``` /// /// The `ProjectionOwned` type is identical to the `Self` type, except that /// all pinned fields have been replaced by equivalent [`PhantomData`] types. /// /// This method is opt-in, because it is only supported for [`Sized`] types, and -/// because it is incompatible with the [`#[pinned_drop]`][pinned-drop] attribute described -/// above. It can be enabled by using `#[pin_project(Replace)]`. +/// because it is incompatible with the [`#[pinned_drop]`][pinned-drop] +/// attribute described above. It can be enabled by using +/// `#[pin_project(project_replace)]`. /// /// For example: /// /// ```rust -/// use pin_project::{pin_project, project_replace}; +/// use pin_project::pin_project; +/// use std::{marker::PhantomData, pin::Pin}; /// -/// #[pin_project(Replace)] -/// enum Struct<T> { +/// #[pin_project(project_replace)] +/// struct Struct<T, U> { +/// #[pin] +/// pinned_field: T, +/// unpinned_field: U, +/// } +/// +/// impl<T, U> Struct<T, U> { +/// fn method(self: Pin<&mut Self>, other: Self) { +/// let this = self.project_replace(other); +/// let _: U = this.unpinned_field; +/// let _: PhantomData<T> = this.pinned_field; +/// } +/// } +/// ``` +/// +/// By passing the value to the `project_replace` argument, you can name the +/// returned type of `project_replace()`. This is necessary whenever +/// destructuring the return type of `project_replace()`, and work in exactly +/// the same way as the `project` and `project_ref` arguments. +/// +/// ```rust +/// use pin_project::pin_project; +/// +/// #[pin_project(project_replace = EnumProjOwn)] +/// enum Enum<T, U> { /// A { /// #[pin] -/// pinned_field: i32, -/// unpinned_field: T, +/// pinned_field: T, +/// unpinned_field: U, /// }, /// B, /// } /// -/// #[project_replace] -/// fn main() { -/// let mut x = Box::pin(Struct::A { pinned_field: 42, unpinned_field: "hello" }); +/// let mut x = Box::pin(Enum::A { pinned_field: 42, unpinned_field: "hello" }); /// -/// #[project_replace] -/// match x.as_mut().project_replace(Struct::B) { -/// Struct::A { unpinned_field, .. } => assert_eq!(unpinned_field, "hello"), -/// Struct::B => unreachable!(), -/// } +/// match x.as_mut().project_replace(Enum::B) { +/// EnumProjOwn::A { unpinned_field, .. } => assert_eq!(unpinned_field, "hello"), +/// EnumProjOwn::B => unreachable!(), /// } /// ``` /// -/// The [`project_replace`] attributes are necessary whenever destructuring the return -/// type of `project_replace()`, and work in exactly the same way as the -/// [`project`] and [`project_ref`] attributes. -/// /// [`PhantomData`]: core::marker::PhantomData /// [`PhantomPinned`]: core::marker::PhantomPinned /// [`Pin::as_mut`]: core::pin::Pin::as_mut @@ -418,13 +441,10 @@ use crate::utils::{Immutable, Mutable, Owned}; /// [`Pin`]: core::pin::Pin /// [`UnsafeUnpin`]: https://docs.rs/pin-project/0.4/pin_project/trait.UnsafeUnpin.html /// [`pinned_drop`]: ./attr.pinned_drop.html -/// [`project_ref`]: ./attr.project_ref.html -/// [`project_replace`]: ./attr.project_replace.html -/// [`project`]: ./attr.project.html /// [drop-guarantee]: https://doc.rust-lang.org/nightly/std/pin/index.html#drop-guarantee +/// [pin-projection]: https://doc.rust-lang.org/nightly/std/pin/index.html#projections-and-structural-pinning /// [pinned-drop]: ./attr.pin_project.html#pinned_drop /// [repr-packed]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked -/// [pin-projection]: https://doc.rust-lang.org/nightly/std/pin/index.html#projections-and-structural-pinning /// [undefined-behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html /// [unsafe-unpin]: ./attr.pin_project.html#unsafeunpin #[proc_macro_attribute] @@ -454,9 +474,10 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// implemented. To drop a type that implements `PinnedDrop`, use the [`drop`] /// function just like dropping a type that directly implements [`Drop`]. /// -/// In particular, it will never be called more than once, just like [`Drop::drop`]. +/// In particular, it will never be called more than once, just like +/// [`Drop::drop`]. /// -/// ## Example +/// # Example /// /// ```rust /// use pin_project::{pin_project, pinned_drop}; @@ -482,7 +503,7 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// /// See also ["pinned-drop" section of `#[pin_project]` attribute][pinned-drop]. /// -/// ## Why `#[pinned_drop]` attribute is needed? +/// # Why `#[pinned_drop]` attribute is needed? /// /// Implementing `PinnedDrop::drop` is safe, but calling it is not safe. // This is because destructors can be called multiple times in safe code and @@ -490,8 +511,8 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// /// Ideally, it would be desirable to be able to forbid manual calls in /// the same way as [`Drop::drop`], but the library cannot do it. So, by using -/// macros and replacing them with private traits like the following, we prevent users from -/// calling `PinnedDrop::drop` in safe code. +/// macros and replacing them with private traits like the following, we prevent +/// users from calling `PinnedDrop::drop` in safe code. /// /// ```rust /// # use std::pin::Pin; @@ -501,8 +522,8 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// ``` /// /// This allows implementing [`Drop`] safely using `#[pinned_drop]`. -/// Also by using the [`drop`] function just like dropping a type that directly implements [`Drop`], -/// can drop safely a type that implements `PinnedDrop`. +/// Also by using the [`drop`] function just like dropping a type that directly +/// implements [`Drop`], can drop safely a type that implements `PinnedDrop`. /// /// [`Pin`]: core::pin::Pin /// [pinned-drop]: ./attr.pin_project.html#pinned_drop @@ -512,19 +533,24 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { pinned_drop::attribute(&args.into(), input).into() } -/// An attribute to provide way to refer to the projected type returned by +/// (deprecated) An attribute to provide way to refer to the projected type returned by /// `project` method. /// +/// **This attribute is deprecated. Consider naming projected type by passing +/// `project` argument to `#[pin_project]` attribute instead, see [release note] +/// for details** +/// /// The following syntaxes are supported. /// -/// ## `let` bindings +/// # `let` bindings /// /// *The attribute at the expression position is not stable, so you need to use /// a dummy `#[project]` attribute for the function.* /// -/// ### Examples +/// ## Examples /// /// ```rust +/// # #![allow(deprecated)] /// use pin_project::{pin_project, project}; /// use std::pin::Pin; /// @@ -547,43 +573,44 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// ``` /// -/// ## `match` expressions +/// # `match` expressions /// /// *The attribute at the expression position is not stable, so you need to use /// a dummy `#[project]` attribute for the function.* /// -/// ### Examples +/// ## Examples /// /// ```rust +/// # #![allow(deprecated)] /// use pin_project::{pin_project, project}; /// use std::pin::Pin; /// /// #[pin_project] -/// enum Foo<A, B, C> { +/// enum Enum<A, B, C> { /// Tuple(#[pin] A, B), /// Struct { field: C }, /// Unit, /// } /// -/// impl<A, B, C> Foo<A, B, C> { +/// impl<A, B, C> Enum<A, B, C> { /// #[project] // Nightly does not need a dummy attribute to the function. /// fn baz(self: Pin<&mut Self>) { /// #[project] /// match self.project() { -/// Foo::Tuple(x, y) => { +/// Enum::Tuple(x, y) => { /// let _: Pin<&mut A> = x; /// let _: &mut B = y; /// } -/// Foo::Struct { field } => { +/// Enum::Struct { field } => { /// let _: &mut C = field; /// } -/// Foo::Unit => {} +/// Enum::Unit => {} /// } /// } /// } /// ``` /// -/// ## `impl` blocks +/// # `impl` blocks /// /// All methods and associated functions in `#[project] impl` block become /// methods of the projected type. If you want to implement methods on the @@ -592,9 +619,10 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// To call a method implemented in `#[project] impl` block, you need to first /// get the projected-type with `let this = self.project();`. /// -/// ### Examples +/// ## Examples /// /// ```rust +/// # #![allow(deprecated)] /// use pin_project::{pin_project, project}; /// use std::pin::Pin; /// @@ -624,11 +652,12 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// ``` /// -/// ## `use` statements +/// # `use` statements /// -/// ### Examples +/// ## Examples /// /// ```rust +/// # #![allow(deprecated)] /// # mod dox { /// use pin_project::pin_project; /// @@ -655,43 +684,85 @@ pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream { /// } /// # } /// ``` +/// +/// [release note]: https://github.com/taiki-e/pin-project/releases/tag/v0.4.21 +#[cfg_attr( + deprecated_proc_macro, + deprecated( + since = "0.4.21", + note = "consider naming projected type by passing `project` \ + argument to #[pin_project] attribute instead, see release note \ + <https://github.com/taiki-e/pin-project/releases/tag/v0.4.21> \ + for details" + ) +)] #[proc_macro_attribute] pub fn project(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input); - project::attribute(&args.into(), input, Mutable).into() + project::attribute(&args.into(), input, ProjKind::Mutable).into() } -/// An attribute to provide way to refer to the projected type returned by +/// (deprecated) An attribute to provide way to refer to the projected type returned by /// `project_ref` method. /// -/// This is the same as [`project`] attribute except it refers to the projected -/// type returned by the `project_ref` method. +/// **This attribute is deprecated. Consider naming projected type by passing +/// `project_ref` argument to `#[pin_project]` attribute instead, see [release note] +/// for details** /// -/// See [`project`] attribute for more details. +/// This is the same as [`#[project]`][`project`] attribute except it refers to +/// the projected type returned by the `project_ref` method. /// +/// See [`#[project]`][`project`] attribute for more details. +/// +/// [release note]: https://github.com/taiki-e/pin-project/releases/tag/v0.4.21 /// [`project`]: ./attr.project.html +#[cfg_attr( + deprecated_proc_macro, + deprecated( + since = "0.4.21", + note = "consider naming projected type by passing `project_ref` \ + argument to #[pin_project] attribute instead, see release note \ + <https://github.com/taiki-e/pin-project/releases/tag/v0.4.21> \ + for details" + ) +)] #[proc_macro_attribute] pub fn project_ref(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input); - project::attribute(&args.into(), input, Immutable).into() + project::attribute(&args.into(), input, ProjKind::Immutable).into() } -/// An attribute to provide way to refer to the projected type returned by +/// (deprecated) An attribute to provide way to refer to the projected type returned by /// `project_replace` method. /// -/// This is the same as [`project`] attribute except it refers to the projected -/// type returned by the `project_replace` method. +/// **This attribute is deprecated. Consider naming projected type by passing +/// `project_replace` argument to `#[pin_project]` attribute instead, see [release note] +/// for details** +/// +/// This is the same as [`#[project]`][`project`] attribute except it refers to +/// the projected type returned by the `project_replace` method. /// -/// See [`project`] attribute for more details. +/// See [`#[project]`][`project`] attribute for more details. /// +/// [release note]: https://github.com/taiki-e/pin-project/releases/tag/v0.4.21 /// [`project`]: ./attr.project.html +#[cfg_attr( + deprecated_proc_macro, + deprecated( + since = "0.4.21", + note = "consider naming projected type by passing `project_replace` \ + argument to #[pin_project] attribute instead, see release note \ + <https://github.com/taiki-e/pin-project/releases/tag/v0.4.21> \ + for details" + ) +)] #[proc_macro_attribute] pub fn project_replace(args: TokenStream, input: TokenStream) -> TokenStream { let input = syn::parse_macro_input!(input); - project::attribute(&args.into(), input, Owned).into() + project::attribute(&args.into(), input, ProjKind::Owned).into() } -/// An internal helper macro. +// An internal helper macro. Not public API. #[doc(hidden)] #[proc_macro_derive(__PinProjectInternalDerive, attributes(pin))] pub fn __pin_project_internal_derive(input: TokenStream) -> TokenStream { diff --git a/src/pin_project/attribute.rs b/src/pin_project/attribute.rs index 74251be..34e32f5 100644 --- a/src/pin_project/attribute.rs +++ b/src/pin_project/attribute.rs @@ -10,23 +10,25 @@ use crate::utils::SliceExt; // To generate the correct `Unpin` implementation and the projection methods, // we need to collect the types of the pinned fields. -// However, since proc-macro-attribute is applied before `#[cfg]` and `#[cfg_attr]` on fields, -// we cannot be collecting field types properly at this timing. -// So instead of generating the `Unpin` implementation and the projection methods here, -// delegate their processing to proc-macro-derive. +// However, since proc-macro-attribute is applied before `cfg` and `cfg_attr` +// on fields, we cannot be collecting field types properly at this timing. +// So instead of generating the `Unpin` implementation and the projection +// methods here, delegate their processing to proc-macro-derive. // // At this stage, only attributes are parsed and the following attributes are // added to the attributes of the item. -// * `#[derive(InternalDerive)]` - An internal helper macro that does the above processing. -// * `#[pin(__private(#args))]` - Pass the argument of `#[pin_project]` to proc-macro-derive (`InternalDerive`). +// * `#[derive(InternalDerive)]` - An internal helper macro that does the above +// processing. +// * `#[pin(__private(#args))]` - Pass the argument of `#[pin_project]` to +// proc-macro-derive (`InternalDerive`). pub(super) fn parse_attribute(args: &TokenStream, input: TokenStream) -> Result<TokenStream> { let Input { attrs: mut tokens, body } = syn::parse2(input)?; tokens.extend(quote!(#[derive(::pin_project::__private::__PinProjectInternalDerive)])); - // Use `__private` to prevent users from trying to control `InternalDerive` manually. - // `__private` does not guarantee compatibility between patch versions, - // so it should be sufficient for this purpose in most cases. + // Use `__private` to prevent users from trying to control `InternalDerive` + // manually. `__private` does not guarantee compatibility between patch + // versions, so it should be sufficient for this purpose in most cases. tokens.extend(quote!(#[pin(__private(#args))])); tokens.extend(body); @@ -45,9 +47,9 @@ impl Parse for Input { let ahead = input.fork(); let _: Visibility = ahead.parse()?; - if !ahead.peek(token::Struct) && !ahead.peek(token::Enum) { - // If we check this only on proc-macro-derive, it may generate unhelpful error messages. - // So it is preferable to be able to detect it here. + if !ahead.peek(Token![struct]) && !ahead.peek(Token![enum]) { + // If we check this only on proc-macro-derive, it may generate unhelpful error + // messages. So it is preferable to be able to detect it here. Err(error!( input.parse::<TokenStream>()?, "#[pin_project] attribute may only be used on structs or enums" diff --git a/src/pin_project/derive.rs b/src/pin_project/derive.rs index 25e9623..8c31252 100644 --- a/src/pin_project/derive.rs +++ b/src/pin_project/derive.rs @@ -1,24 +1,32 @@ -use proc_macro2::{Span, TokenStream}; -use quote::{format_ident, quote, quote_spanned}; +use proc_macro2::{Delimiter, Group, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{ - parse::{Parse, ParseBuffer, ParseStream}, + parse::{Parse, ParseStream}, + spanned::Spanned, visit_mut::VisitMut, *, }; use super::PIN; use crate::utils::{ - determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, Immutable, Mutable, - Owned, ParseBufferExt, ReplaceReceiver, SliceExt, Variants, + determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ParseBufferExt, + ProjKind, ReplaceReceiver, SliceExt, Variants, }; pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { - let mut input = syn::parse2(input)?; - let DeriveInput { attrs, vis, ident, generics, data } = &mut input; - let mut cx = Context::new(attrs, vis, ident, generics)?; + let mut input: DeriveInput = syn::parse2(input)?; + + let ident = &input.ident; + let ty_generics = input.generics.split_for_impl().1; + let self_ty = parse_quote!(#ident #ty_generics); + let mut visitor = ReplaceReceiver(&self_ty); + visitor.visit_generics_mut(&mut input.generics); + visitor.visit_data_mut(&mut input.data); + + let mut cx = Context::new(&input.attrs, &input.vis, &input.ident, &mut input.generics)?; let packed_check; - let (mut items, scoped_items) = match data { + let (mut items, scoped_items) = match &input.data { Data::Struct(data) => { // Do this first for a better error message. packed_check = Some(cx.ensure_not_packed(&data.fields)?); @@ -40,7 +48,11 @@ pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { let unpin_impl = cx.make_unpin_impl(); let drop_impl = cx.make_drop_impl(); - let dummy_const = format_ident!("__SCOPE_{}", ident); + let dummy_const = if cfg!(underscore_consts) { + format_ident!("_") + } else { + format_ident!("__SCOPE_{}", ident) + }; items.extend(quote! { // All items except projected types are generated inside a `const` scope. // This makes it impossible for user code to refer to these types. @@ -59,6 +71,7 @@ pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { #[doc(hidden)] #[allow(non_upper_case_globals)] #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(clippy::used_underscore_binding)] const #dummy_const: () = { #scoped_items #unpin_impl @@ -106,51 +119,92 @@ fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { struct Args { /// `PinnedDrop` argument. pinned_drop: Option<Span>, - /// `Replace` argument. - replace: Option<Span>, /// `UnsafeUnpin` or `!Unpin` argument. unpin_impl: UnpinImpl, - /// `project = <ident>`. + /// `project = <ident>` argument. project: Option<Ident>, - /// `project_ref = <ident>`. + /// `project_ref = <ident>` argument. project_ref: Option<Ident>, + /// `project_replace [= <ident>]` or `Replace` argument. + project_replace: ProjReplace, +} + +enum ProjReplace { + None, + /// `project_replace` or `Replace`. + Unnamed { + span: Span, + }, /// `project_replace = <ident>`. - project_replace: Option<Ident>, + Named { + span: Span, + ident: Ident, + }, +} + +impl ProjReplace { + fn span(&self) -> Option<Span> { + match self { + ProjReplace::None => None, + ProjReplace::Named { span, .. } | ProjReplace::Unnamed { span, .. } => Some(*span), + } + } + + fn ident(&self) -> Option<&Ident> { + if let ProjReplace::Named { ident, .. } = self { Some(ident) } else { None } + } } const DUPLICATE_PIN: &str = "duplicate #[pin] attribute"; impl Args { fn get(attrs: &[Attribute]) -> Result<Self> { - let mut prev: Option<(&Attribute, Result<Self>)> = None; - - for attr in attrs.iter().filter(|attr| attr.path.is_ident(PIN)) { - if let Some((prev_attr, prev_res)) = &prev { - // As the `#[pin]` attribute generated by `#[pin_project]` - // has the same span as `#[pin_project]`, it is possible - // that a useless error message will be generated. - let res = syn::parse2::<Self>(attr.tokens.clone()); - let span = match (&prev_res, res) { - (Ok(_), Ok(_)) => unreachable!(), - (_, Ok(_)) => prev_attr, - (Ok(_), _) => attr, - (Err(prev_err), Err(_)) => { - if prev_err.to_string() == DUPLICATE_PIN { - attr - } else { - prev_attr - } + // `(__private(<args>))` -> `<args>` + struct Input(Option<TokenStream>); + + impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result<Self> { + Ok(Self((|| { + let content = input.parenthesized().ok()?; + let private = content.parse::<Ident>().ok()?; + if private == "__private" { + content.parenthesized().ok()?.parse::<TokenStream>().ok() + } else { + None } - }; - // This error message is not ideal, but as this should basically be - // rejected by attribute side, users will rarely actually see this error. - return Err(error!(span, DUPLICATE_PIN)); + })())) } - prev = Some((attr, syn::parse2::<Self>(attr.tokens.clone()))); } - // This `unwrap` only fails if another macro removes `#[pin]`. - prev.unwrap().1 + if let Some(attr) = attrs.find("pin_project") { + return Err(error!(attr, "duplicate #[pin_project] attribute")); + } + + let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); + + let prev = if let Some(attr) = attrs.next() { + (attr, syn::parse2::<Input>(attr.tokens.clone()).unwrap().0) + } else { + // This only fails if another macro removes `#[pin]`. + return Err(error!(TokenStream::new(), "#[pin_project] attribute has been removed")); + }; + + if let Some(attr) = attrs.next() { + let (prev_attr, prev_res) = &prev; + // As the `#[pin]` attribute generated by `#[pin_project]` + // has the same span as `#[pin_project]`, it is possible + // that a useless error message will be generated. + let res = syn::parse2::<Input>(attr.tokens.clone()).unwrap().0; + let span = match (&prev_res, res) { + (Some(_), _) => attr, + (_, Some(_)) => prev_attr, + (None, None) => prev_attr, + }; + Err(error!(span, DUPLICATE_PIN)) + } else { + // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`. + syn::parse2(prev.1.unwrap()) + } } } @@ -160,110 +214,152 @@ impl Parse for Args { syn::custom_keyword!(Unpin); } - // `(__private(<args>))` -> `<args>` - fn parse_input(input: ParseStream<'_>) -> Result<ParseBuffer<'_>> { - if let Ok(content) = input.parenthesized() { - if let Ok(private) = content.parse::<Ident>() { - if private == "__private" { - if let Ok(args) = content.parenthesized() { - return Ok(args); - } - } - } + // Parses `= <value>` in `<name> = <value>` and returns value and span of name-value pair. + fn parse_value( + input: ParseStream<'_>, + name: &Ident, + has_prev: bool, + ) -> Result<(Ident, TokenStream)> { + if input.is_empty() { + return Err(error!(name, "expected `{0} = <identifier>`, found `{0}`", name)); } - - // If this fails, it means that there is a `#[pin]` attribute - // inserted by something other than `#[pin_project]` attribute. - // This error message is not ideal, but as this should basically be - // rejected by attribute side, users will rarely actually see this error. - Err(error!(TokenStream::new(), DUPLICATE_PIN)) - } - - // Replace `prev` with `new`. Returns `Err` if `prev` is `Some`. - fn update<T>(prev: &mut Option<T>, new: T, token: &Ident) -> Result<()> { - if prev.replace(new).is_some() { - Err(error!(token, "duplicate `{}` argument", token)) + let eq_token: Token![=] = input.parse()?; + if input.is_empty() { + let span = quote!(#name #eq_token); + return Err(error!(span, "expected `{0} = <identifier>`, found `{0} =`", name)); + } + let value: Ident = input.parse()?; + let span = quote!(#name #value); + if has_prev { + Err(error!(span, "duplicate `{}` argument", name)) } else { - Ok(()) + Ok((value, span)) } } - let input = parse_input(input)?; let mut pinned_drop = None; - let mut replace = None; let mut unsafe_unpin = None; let mut not_unpin = None; let mut project = None; let mut project_ref = None; - let mut project_replace: Option<(Span, Ident)> = None; + + let mut replace = None; + let mut project_replace_value = None; + let mut project_replace_span = None; + while !input.is_empty() { - if input.peek(token::Bang) { - let t: token::Bang = input.parse()?; - let k: kw::Unpin = input.parse()?; - if not_unpin.replace(k).is_some() { - let span = quote!(#t #k); - return Err(error!(span, "duplicate `!Unpin` argument",)); + if input.peek(Token![!]) { + let bang: Token![!] = input.parse()?; + if input.is_empty() { + return Err(error!(bang, "expected `!Unpin`, found `!`")); + } + let unpin: kw::Unpin = input.parse()?; + let span = quote!(#bang #unpin); + if not_unpin.replace(span.span()).is_some() { + return Err(error!(span, "duplicate `!Unpin` argument")); } } else { let token = input.parse::<Ident>()?; match &*token.to_string() { - "PinnedDrop" => update(&mut pinned_drop, token.span(), &token)?, - "Replace" => update(&mut replace, token.span(), &token)?, - "UnsafeUnpin" => update(&mut unsafe_unpin, token.span(), &token)?, + "PinnedDrop" => { + if pinned_drop.replace(token.span()).is_some() { + return Err(error!(token, "duplicate `PinnedDrop` argument")); + } + } + "UnsafeUnpin" => { + if unsafe_unpin.replace(token.span()).is_some() { + return Err(error!(token, "duplicate `UnsafeUnpin` argument")); + } + } "project" => { - let _: token::Eq = input.parse()?; - update(&mut project, input.parse()?, &token)?; + project = Some(parse_value(input, &token, project.is_some())?.0); } "project_ref" => { - let _: token::Eq = input.parse()?; - update(&mut project_ref, input.parse()?, &token)?; + project_ref = Some(parse_value(input, &token, project_ref.is_some())?.0); } "project_replace" => { - let _: token::Eq = input.parse()?; - update(&mut project_replace, (token.span(), input.parse()?), &token)?; + if input.peek(Token![=]) { + let (value, span) = + parse_value(input, &token, project_replace_span.is_some())?; + project_replace_value = Some(value); + project_replace_span = Some(span.span()); + } else if project_replace_span.is_some() { + return Err(error!(token, "duplicate `project_replace` argument")); + } else { + project_replace_span = Some(token.span()); + } + } + "Replace" => { + if replace.replace(token.span()).is_some() { + return Err(error!(token, "duplicate `Replace` argument")); + } } _ => return Err(error!(token, "unexpected argument: {}", token)), } } - if !input.is_empty() { - let _: token::Comma = input.parse()?; + if input.is_empty() { + break; } + let _: Token![,] = input.parse()?; } - if let (Some(span), Some(_)) = (pinned_drop, replace) { - return Err(Error::new( - span, - "arguments `PinnedDrop` and `Replace` are mutually exclusive", - )); + if project.is_some() || project_ref.is_some() { + if project == project_ref { + return Err(error!( + project_ref, + "name `{}` is already specified by `project` argument", + project_ref.as_ref().unwrap() + )); + } + if let Some(ident) = &project_replace_value { + if project == project_replace_value { + return Err(error!( + ident, + "name `{}` is already specified by `project` argument", ident + )); + } else if project_ref == project_replace_value { + return Err(error!( + ident, + "name `{}` is already specified by `project_ref` argument", ident + )); + } + } + } + + if let Some(span) = pinned_drop { + if project_replace_span.is_some() { + return Err(Error::new( + span, + "arguments `PinnedDrop` and `project_replace` are mutually exclusive", + )); + } else if replace.is_some() { + return Err(Error::new( + span, + "arguments `PinnedDrop` and `Replace` are mutually exclusive", + )); + } } + let project_replace = match (project_replace_span, project_replace_value, replace) { + (None, _, None) => ProjReplace::None, + // If both `project_replace` and `Replace` are specified, + // We always prefer `project_replace`'s span, + (Some(span), Some(ident), _) => ProjReplace::Named { ident, span }, + (Some(span), ..) | (None, _, Some(span)) => ProjReplace::Unnamed { span }, + }; let unpin_impl = match (unsafe_unpin, not_unpin) { + (None, None) => UnpinImpl::Default, + (Some(span), None) => UnpinImpl::Unsafe(span), + (None, Some(span)) => UnpinImpl::Negative(span), (Some(span), Some(_)) => { return Err(Error::new( span, "arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive", )); } - (None, None) => UnpinImpl::Default, - (Some(span), None) => UnpinImpl::Unsafe(span), - (None, Some(span)) => UnpinImpl::Negative(span.span), }; - if let (Some((span, _)), None) = (&project_replace, replace) { - Err(Error::new( - *span, - "`project_replace` argument can only be used together with `Replace` argument", - )) - } else { - Ok(Self { - pinned_drop, - replace, - unpin_impl, - project, - project_ref, - project_replace: project_replace.map(|(_, i)| i), - }) - } + Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace }) } } @@ -309,11 +405,10 @@ struct ProjectedVariants { struct ProjectedFields { proj_pat: TokenStream, proj_body: TokenStream, + proj_own_body: TokenStream, proj_fields: TokenStream, proj_ref_fields: TokenStream, proj_own_fields: TokenStream, - proj_move: TokenStream, - proj_drop: Vec<Ident>, } struct Context<'a> { @@ -323,18 +418,17 @@ struct Context<'a> { proj: ProjectedType, /// Types of the pinned fields. pinned_fields: Vec<Type>, + /// `PinnedDrop` argument. pinned_drop: Option<Span>, - /// `Replace` argument (requires Sized bound) - replace: Option<Span>, /// `UnsafeUnpin` or `!Unpin` argument. unpin_impl: UnpinImpl, /// `project` argument. project: bool, /// `project_ref` argument. project_ref: bool, - /// `project_replace` argument. - project_replace: bool, + /// `project_replace [= <ident>]` or `Replace` argument. + project_replace: ProjReplace, } #[derive(Clone, Copy)] @@ -353,42 +447,39 @@ impl<'a> Context<'a> { ident: &'a Ident, generics: &'a mut Generics, ) -> Result<Self> { - let Args { pinned_drop, unpin_impl, replace, project, project_ref, project_replace } = + let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } = Args::get(attrs)?; - let ty_generics = generics.split_for_impl().1; - let self_ty = syn::parse_quote!(#ident #ty_generics); - let mut visitor = ReplaceReceiver(&self_ty); - visitor.visit_where_clause_mut(generics.make_where_clause()); - let mut lifetime_name = String::from("'pin"); determine_lifetime_name(&mut lifetime_name, generics); let lifetime = Lifetime::new(&lifetime_name, Span::call_site()); - let mut proj_generics = generics.clone(); let ty_generics = generics.split_for_impl().1; - let ty_generics_as_generics = syn::parse_quote!(#ty_generics); + let ty_generics_as_generics = parse_quote!(#ty_generics); + let mut proj_generics = generics.clone(); let pred = insert_lifetime_and_bound( &mut proj_generics, lifetime.clone(), &ty_generics_as_generics, ident, ); - let mut where_clause = generics.clone().make_where_clause().clone(); + let mut where_clause = generics.make_where_clause().clone(); where_clause.predicates.push(pred); + let own_ident = + project_replace.ident().cloned().unwrap_or_else(|| ProjKind::Owned.proj_ident(ident)); + Ok(Self { pinned_drop, - replace, unpin_impl, project: project.is_some(), project_ref: project_ref.is_some(), - project_replace: project_replace.is_some(), + project_replace, proj: ProjectedType { vis: determine_visibility(vis), - mut_ident: project.unwrap_or_else(|| Mutable.proj_ident(ident)), - ref_ident: project_ref.unwrap_or_else(|| Immutable.proj_ident(ident)), - own_ident: project_replace.unwrap_or_else(|| Owned.proj_ident(ident)), + mut_ident: project.unwrap_or_else(|| ProjKind::Mutable.proj_ident(ident)), + ref_ident: project_ref.unwrap_or_else(|| ProjKind::Immutable.proj_ident(ident)), + own_ident, lifetime, generics: proj_generics, where_clause, @@ -398,6 +489,37 @@ impl<'a> Context<'a> { }) } + /// Returns attributes used on projected types. + fn proj_attrs(&self) -> (TokenStream, TokenStream, TokenStream) { + // If the user gave it a name, it should appear in the document. + let doc_attr = quote!(#[doc(hidden)]); + let doc_proj = if self.project { None } else { Some(&doc_attr) }; + let doc_proj_ref = if self.project_ref { None } else { Some(&doc_attr) }; + let doc_proj_own = + if self.project_replace.ident().is_some() { None } else { Some(&doc_attr) }; + + let proj_mut = quote! { + #doc_proj + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. + #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326} + }; + let proj_ref = quote! { + #doc_proj_ref + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 + }; + let proj_own = quote! { + #doc_proj_own + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(unreachable_pub)] // This lint warns `pub` field in private struct. + }; + (proj_mut, proj_ref, proj_own) + } + fn parse_struct( &mut self, DataStruct { fields, .. }: &DataStruct, @@ -410,11 +532,10 @@ impl<'a> Context<'a> { proj_fields, proj_ref_fields, proj_own_fields, - proj_move, - proj_drop, + proj_own_body, } = match fields { - Fields::Named(fields) => self.visit_named(fields)?, - Fields::Unnamed(fields) => self.visit_unnamed(fields)?, + Fields::Named(_) => self.visit_fields(None, fields, Delimiter::Brace)?, + Fields::Unnamed(_) => self.visit_fields(None, fields, Delimiter::Parenthesis)?, Fields::Unit => unreachable!(), }; @@ -425,46 +546,34 @@ impl<'a> Context<'a> { let mut orig_generics = self.orig.generics.clone(); let orig_where_clause = orig_generics.where_clause.take(); let proj_generics = &self.proj.generics; - let where_clause = &self.proj.where_clause; + let proj_where_clause = &self.proj.where_clause; // For tuple structs, we need to generate `(T1, T2) where Foo: Bar` // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }` let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields { Fields::Named(_) => ( - quote!(#where_clause #proj_fields), - quote!(#where_clause #proj_ref_fields), + quote!(#proj_where_clause #proj_fields), + quote!(#proj_where_clause #proj_ref_fields), quote!(#orig_where_clause #proj_own_fields), ), Fields::Unnamed(_) => ( - quote!(#proj_fields #where_clause;), - quote!(#proj_ref_fields #where_clause;), + quote!(#proj_fields #proj_where_clause;), + quote!(#proj_ref_fields #proj_where_clause;), quote!(#proj_own_fields #orig_where_clause;), ), Fields::Unit => unreachable!(), }; - // If the user gave it a name, it should appear in the document. - let doc_attr = quote!(#[doc(hidden)]); - let doc_proj = if self.project { None } else { Some(&doc_attr) }; - let doc_proj_ref = if self.project_ref { None } else { Some(&doc_attr) }; - let doc_proj_own = if self.project_replace { None } else { Some(&doc_attr) }; + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs(); let mut proj_items = quote! { - #doc_proj - #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. - #[allow(dead_code)] // This lint warns unused fields/variants. - #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #proj_attrs #vis struct #proj_ident #proj_generics #where_clause_fields - #doc_proj_ref - #[allow(dead_code)] // This lint warns unused fields/variants. - #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #proj_ref_attrs #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields }; - if self.replace.is_some() { - // Currently, using quote_spanned here does not seem to have any effect on the diagnostics. + if self.project_replace.span().is_some() { proj_items.extend(quote! { - #doc_proj_own - #[allow(dead_code)] // This lint warns unused fields/variants. - #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #proj_own_attrs #vis struct #proj_own_ident #orig_generics #where_clause_own_fields }); } @@ -480,28 +589,7 @@ impl<'a> Context<'a> { let proj_own_body = quote! { let __self_ptr: *mut Self = self.get_unchecked_mut(); let Self #proj_pat = &mut *__self_ptr; - - // First, extract all the unpinned fields - let __result = #proj_own_ident #proj_move; - - // Destructors will run in reverse order, so next create a guard to overwrite - // `self` with the replacement value without calling destructors. - let __guard = ::pin_project::__private::UnsafeOverwriteGuard { - target: __self_ptr, - value: ::pin_project::__reexport::mem::ManuallyDrop::new(__replacement), - }; - - // Now create guards to drop all the pinned fields - // - // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) - // this must be in its own scope, or else `__result` will not be dropped - // if any of the destructors panic. - { - #( let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(#proj_drop); )* - } - - // Finally, return the result - __result + #proj_own_body }; let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body); @@ -530,34 +618,22 @@ impl<'a> Context<'a> { let mut orig_generics = self.orig.generics.clone(); let orig_where_clause = orig_generics.where_clause.take(); let proj_generics = &self.proj.generics; - let where_clause = &self.proj.where_clause; + let proj_where_clause = &self.proj.where_clause; - // If the user gave it a name, it should appear in the document. - let doc_attr = quote!(#[doc(hidden)]); - let doc_proj = if self.project { None } else { Some(&doc_attr) }; - let doc_proj_ref = if self.project_ref { None } else { Some(&doc_attr) }; - let doc_proj_own = if self.project_replace { None } else { Some(&doc_attr) }; + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs(); let mut proj_items = quote! { - #doc_proj - #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. - #[allow(dead_code)] // This lint warns unused fields/variants. - #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 - #vis enum #proj_ident #proj_generics #where_clause { + #proj_attrs + #vis enum #proj_ident #proj_generics #proj_where_clause { #proj_variants } - #doc_proj_ref - #[allow(dead_code)] // This lint warns unused fields/variants. - #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 - #vis enum #proj_ref_ident #proj_generics #where_clause { + #proj_ref_attrs + #vis enum #proj_ref_ident #proj_generics #proj_where_clause { #proj_ref_variants } }; - if self.replace.is_some() { - // Currently, using quote_spanned here does not seem to have any effect on the diagnostics. + if self.project_replace.span().is_some() { proj_items.extend(quote! { - #doc_proj_own - #[allow(dead_code)] // This lint warns unused fields/variants. - #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #proj_own_attrs #vis enum #proj_own_ident #orig_generics #orig_where_clause { #proj_own_variants } @@ -600,18 +676,21 @@ impl<'a> Context<'a> { proj_fields, proj_ref_fields, proj_own_fields, - proj_move, - proj_drop, + proj_own_body, } = match fields { - Fields::Named(fields) => self.visit_named(fields)?, - Fields::Unnamed(fields) => self.visit_unnamed(fields)?, - Fields::Unit => ProjectedFields::default(), + Fields::Named(_) => self.visit_fields(Some(ident), fields, Delimiter::Brace)?, + Fields::Unnamed(_) => { + self.visit_fields(Some(ident), fields, Delimiter::Parenthesis)? + } + Fields::Unit => ProjectedFields { + proj_own_body: self.proj_own_body(Some(ident), None, &[]), + ..ProjectedFields::default() + }, }; let orig_ident = self.orig.ident; let proj_ident = &self.proj.mut_ident; let proj_ref_ident = &self.proj.ref_ident; - let proj_own_ident = &self.proj.own_ident; proj_variants.extend(quote! { #ident #proj_fields, }); @@ -633,27 +712,7 @@ impl<'a> Context<'a> { }); proj_own_arms.extend(quote! { #orig_ident::#ident #proj_pat => { - // First, extract all the unpinned fields - let __result = #proj_own_ident::#ident #proj_move; - - // Destructors will run in reverse order, so next create a guard to overwrite - // `self` with the replacement value without calling destructors. - let __guard = ::pin_project::__private::UnsafeOverwriteGuard { - target: __self_ptr, - value: ::pin_project::__reexport::mem::ManuallyDrop::new(__replacement), - }; - - // Now create guards to drop all the pinned fields - // - // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) - // this must be in its own scope, or else `__result` will not be dropped - // if any of the destructors panic. - { - #( let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(#proj_drop); )* - } - - // Finally, return the result - __result + #proj_own_body } }); } @@ -668,119 +727,124 @@ impl<'a> Context<'a> { }) } - fn visit_named( + fn visit_fields( &mut self, - FieldsNamed { named: fields, .. }: &FieldsNamed, + variant_ident: Option<&Ident>, + fields: &Fields, + delim: Delimiter, ) -> Result<ProjectedFields> { - let mut proj_pat = Vec::with_capacity(fields.len()); - let mut proj_body = Vec::with_capacity(fields.len()); - let mut proj_fields = Vec::with_capacity(fields.len()); - let mut proj_ref_fields = Vec::with_capacity(fields.len()); - let mut proj_own_fields = Vec::with_capacity(fields.len()); - let mut proj_move = Vec::with_capacity(fields.len()); - let mut proj_drop = Vec::with_capacity(fields.len()); - - for Field { attrs, vis, ident, ty, .. } in fields { + let mut proj_pat = TokenStream::new(); + let mut proj_body = TokenStream::new(); + let mut proj_fields = TokenStream::new(); + let mut proj_ref_fields = TokenStream::new(); + let mut proj_own_fields = TokenStream::new(); + let mut proj_move = TokenStream::new(); + let mut pinned_bindings = Vec::with_capacity(fields.len()); + + for (i, Field { attrs, vis, ident, colon_token, ty, .. }) in fields.iter().enumerate() { + let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i)); + proj_pat.extend(quote!(#binding,)); if attrs.position_exact(PIN)?.is_some() { - self.pinned_fields.push(ty.clone()); - proj_drop.push(ident.as_ref().cloned().unwrap()); - let lifetime = &self.proj.lifetime; - proj_fields.push( - quote!(#vis #ident: ::pin_project::__reexport::pin::Pin<&#lifetime mut (#ty)>), - ); - proj_ref_fields.push( - quote!(#vis #ident: ::pin_project::__reexport::pin::Pin<&#lifetime (#ty)>), - ); - proj_own_fields - .push(quote!(#vis #ident: ::pin_project::__reexport::marker::PhantomData<#ty>)); - proj_body.push( - quote!(#ident: ::pin_project::__reexport::pin::Pin::new_unchecked(#ident)), - ); - proj_move.push(quote!(#ident: ::pin_project::__reexport::marker::PhantomData)); + proj_fields.extend(quote! { + #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>, + }); + proj_ref_fields.extend(quote! { + #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>, + }); + proj_own_fields.extend(quote! { + #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>, + }); + proj_body.extend(quote! { + #ident #colon_token ::pin_project::__private::Pin::new_unchecked(#binding), + }); + proj_move.extend(quote! { + #ident #colon_token ::pin_project::__private::PhantomData, + }); + + self.pinned_fields.push(ty.clone()); + pinned_bindings.push(binding); } else { let lifetime = &self.proj.lifetime; - proj_fields.push(quote!(#vis #ident: &#lifetime mut (#ty))); - proj_ref_fields.push(quote!(#vis #ident: &#lifetime (#ty))); - proj_own_fields.push(quote!(#vis #ident: #ty)); - proj_body.push(quote!(#ident)); - proj_move.push(quote!(#ident: ::pin_project::__reexport::ptr::read(#ident))); + proj_fields.extend(quote! { + #vis #ident #colon_token &#lifetime mut (#ty), + }); + proj_ref_fields.extend(quote! { + #vis #ident #colon_token &#lifetime (#ty), + }); + proj_own_fields.extend(quote! { + #vis #ident #colon_token #ty, + }); + proj_body.extend(quote! { + #binding, + }); + proj_move.extend(quote! { + #ident #colon_token ::pin_project::__private::ptr::read(#binding), + }); } - proj_pat.push(ident); } - let proj_pat = quote!({ #(#proj_pat),* }); - let proj_body = quote!({ #(#proj_body),* }); - let proj_fields = quote!({ #(#proj_fields),* }); - let proj_ref_fields = quote!({ #(#proj_ref_fields),* }); - let proj_own_fields = quote!({ #(#proj_own_fields),* }); - let proj_move = quote!({ #(#proj_move),* }); + fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { + Group::new(delim, tokens).into_token_stream() + } + + let proj_pat = surround(delim, proj_pat); + let proj_body = surround(delim, proj_body); + let proj_fields = surround(delim, proj_fields); + let proj_ref_fields = surround(delim, proj_ref_fields); + let proj_own_fields = surround(delim, proj_own_fields); + + let proj_move = Group::new(delim, proj_move); + let proj_own_body = self.proj_own_body(variant_ident, Some(proj_move), &pinned_bindings); Ok(ProjectedFields { proj_pat, proj_body, + proj_own_body, proj_fields, proj_ref_fields, proj_own_fields, - proj_move, - proj_drop, }) } - fn visit_unnamed( - &mut self, - FieldsUnnamed { unnamed: fields, .. }: &FieldsUnnamed, - ) -> Result<ProjectedFields> { - let mut proj_pat = Vec::with_capacity(fields.len()); - let mut proj_body = Vec::with_capacity(fields.len()); - let mut proj_fields = Vec::with_capacity(fields.len()); - let mut proj_ref_fields = Vec::with_capacity(fields.len()); - let mut proj_own_fields = Vec::with_capacity(fields.len()); - let mut proj_move = Vec::with_capacity(fields.len()); - let mut proj_drop = Vec::with_capacity(fields.len()); - - for (i, Field { attrs, vis, ty, .. }) in fields.iter().enumerate() { - let id = format_ident!("_{}", i); - if attrs.position_exact(PIN)?.is_some() { - self.pinned_fields.push(ty.clone()); - proj_drop.push(id.clone()); + /// Generates the processing that `project_replace` does for the struct or each variant. + fn proj_own_body( + &self, + variant_ident: Option<&'a Ident>, + proj_move: Option<Group>, + pinned_fields: &[Ident], + ) -> TokenStream { + let ident = &self.proj.own_ident; + let proj_own = match variant_ident { + Some(variant_ident) => quote!(#ident::#variant_ident), + None => quote!(#ident), + }; - let lifetime = &self.proj.lifetime; - proj_fields - .push(quote!(#vis ::pin_project::__reexport::pin::Pin<&#lifetime mut (#ty)>)); - proj_ref_fields - .push(quote!(#vis ::pin_project::__reexport::pin::Pin<&#lifetime (#ty)>)); - proj_own_fields - .push(quote!(#vis ::pin_project::__reexport::marker::PhantomData<#ty>)); - proj_body.push(quote!(::pin_project::__reexport::pin::Pin::new_unchecked(#id))); - proj_move.push(quote!(::pin_project::__reexport::marker::PhantomData)); - } else { - let lifetime = &self.proj.lifetime; - proj_fields.push(quote!(#vis &#lifetime mut (#ty))); - proj_ref_fields.push(quote!(#vis &#lifetime (#ty))); - proj_own_fields.push(quote!(#vis #ty)); - proj_body.push(quote!(#id)); - proj_move.push(quote!(::pin_project::__reexport::ptr::read(#id))); - } - proj_pat.push(id); - } + quote! { + // First, extract all the unpinned fields + let __result = #proj_own #proj_move; - let proj_pat = quote!((#(#proj_pat),*)); - let proj_body = quote!((#(#proj_body),*)); - let proj_fields = quote!((#(#proj_fields),*)); - let proj_ref_fields = quote!((#(#proj_ref_fields),*)); - let proj_own_fields = quote!((#(#proj_own_fields),*)); - let proj_move = quote!((#(#proj_move),*)); + // Destructors will run in reverse order, so next create a guard to overwrite + // `self` with the replacement value without calling destructors. + let __guard = ::pin_project::__private::UnsafeOverwriteGuard { + target: __self_ptr, + value: ::pin_project::__private::ManuallyDrop::new(__replacement), + }; - Ok(ProjectedFields { - proj_pat, - proj_body, - proj_fields, - proj_ref_fields, - proj_own_fields, - proj_move, - proj_drop, - }) + // Now create guards to drop all the pinned fields + // + // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) + // this must be in its own scope, or else `__result` will not be dropped + // if any of the destructors panic. + { + #( + let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(#pinned_fields); + )* + } + + // Finally, return the result + __result + } } /// Creates `Unpin` implementation for original type. @@ -791,18 +855,19 @@ impl<'a> Context<'a> { let orig_ident = self.orig.ident; let lifetime = &self.proj.lifetime; - proj_generics.make_where_clause().predicates.push( - // Make the error message highlight `UnsafeUnpin` argument. - parse_quote_spanned! { span => - ::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin - }, - ); + // Make the error message highlight `UnsafeUnpin` argument. + proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span => + ::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin + }); let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); let ty_generics = self.orig.generics.split_for_impl().1; - quote! { - impl #impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #where_clause {} + quote_spanned! { span => + impl #impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics + #where_clause + { + } } } UnpinImpl::Negative(span) => { @@ -812,26 +877,35 @@ impl<'a> Context<'a> { proj_generics.make_where_clause().predicates.push(parse_quote! { ::pin_project::__private::Wrapper< - #lifetime, ::pin_project::__reexport::marker::PhantomPinned - >: ::pin_project::__reexport::marker::Unpin + #lifetime, ::pin_project::__private::PhantomPinned + >: ::pin_project::__private::Unpin }); let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl(); let (impl_generics, ty_generics, orig_where_clause) = self.orig.generics.split_for_impl(); - // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be call-site span. - let unsafety = token::Unsafe::default(); + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); quote_spanned! { span => - impl #proj_impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #proj_where_clause {} + impl #proj_impl_generics ::pin_project::__private::Unpin + for #orig_ident #ty_generics + #proj_where_clause + { + } // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. // // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` - // impls, we emit one ourselves. If the user ends up writing a `UnsafeUnpin` impl, - // they'll get a "conflicting implementations of trait" error when coherence - // checks are run. - #unsafety impl #impl_generics ::pin_project::UnsafeUnpin for #orig_ident #ty_generics #orig_where_clause {} + // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` + // impl, they'll get a "conflicting implementations of trait" error when + // coherence checks are run. + #unsafety impl #impl_generics ::pin_project::UnsafeUnpin + for #orig_ident #ty_generics + #orig_where_clause + { + } } } UnpinImpl::Default => { @@ -883,8 +957,8 @@ impl<'a> Context<'a> { let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); - full_where_clause.predicates.push(syn::parse_quote! { - #struct_ident #proj_ty_generics: ::pin_project::__reexport::marker::Unpin + full_where_clause.predicates.push(parse_quote! { + #struct_ident #proj_ty_generics: ::pin_project::__private::Unpin }); quote! { @@ -899,21 +973,31 @@ impl<'a> Context<'a> { // However, we ensure that the user can never actually reference // this 'public' type by creating this type in the inside of `const`. #vis struct #struct_ident #proj_generics #where_clause { - __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<#lifetime, (#(#type_params),*)>, + __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin< + #lifetime, (#(#type_params),*) + >, #(#fields,)* #(#lifetime_fields,)* } - impl #proj_impl_generics ::pin_project::__reexport::marker::Unpin for #orig_ident #ty_generics #full_where_clause {} + impl #proj_impl_generics ::pin_project::__private::Unpin + for #orig_ident #ty_generics + #full_where_clause + { + } // A dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. // // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` - // impls, we emit one ourselves. If the user ends up writing a `UnsafeUnpin` impl, - // they'll get a "conflicting implementations of trait" error when coherence - // checks are run. - unsafe impl #impl_generics ::pin_project::UnsafeUnpin for #orig_ident #ty_generics #where_clause {} + // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` + // impl, they'll get a "conflicting implementations of trait" error when + // coherence checks are run. + unsafe impl #impl_generics ::pin_project::UnsafeUnpin + for #orig_ident #ty_generics + #where_clause + { + } } } } @@ -924,15 +1008,20 @@ impl<'a> Context<'a> { let ident = self.orig.ident; let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); - if let Some(pinned_drop) = self.pinned_drop { - // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be call-site span. - let unsafety = token::Unsafe::default(); - quote_spanned! { pinned_drop => - impl #impl_generics ::pin_project::__reexport::ops::Drop for #ident #ty_generics #where_clause { + if let Some(span) = self.pinned_drop { + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); + quote_spanned! { span => + impl #impl_generics ::pin_project::__private::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 = #unsafety { ::pin_project::__reexport::pin::Pin::new_unchecked(self) }; + let pinned_self = #unsafety { + ::pin_project::__private::Pin::new_unchecked(self) + }; // We call `pinned_drop` only once. Since `PinnedDrop::drop` // is an unsafe method and a private API, it is never called again in safe // code *unless the user uses a maliciously crafted macro*. @@ -969,7 +1058,7 @@ impl<'a> Context<'a> { // This will result in a compilation error, which is exactly what we want. trait #trait_ident {} #[allow(clippy::drop_bounds)] - impl<T: ::pin_project::__reexport::ops::Drop> #trait_ident for T {} + impl<T: ::pin_project::__private::Drop> #trait_ident for T {} impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {} // A dummy impl of `PinnedDrop`, to ensure that the user cannot implement it. @@ -982,8 +1071,10 @@ impl<'a> Context<'a> { // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl, // they'll get a "conflicting implementations of trait" error when coherence // checks are run. - impl #impl_generics ::pin_project::__private::PinnedDrop for #ident #ty_generics #where_clause { - unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} + impl #impl_generics ::pin_project::__private::PinnedDrop for #ident #ty_generics + #where_clause + { + unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {} } } } @@ -1007,14 +1098,16 @@ impl<'a> Context<'a> { let proj_ty_generics = self.proj.generics.split_for_impl().1; let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl(); - let replace_impl = self.replace.map(|_| { - // Currently, using quote_spanned here does not seem to have any effect on the diagnostics. - quote! { + let replace_impl = self.project_replace.span().map(|span| { + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); + quote_spanned! { span => #vis fn project_replace( - self: ::pin_project::__reexport::pin::Pin<&mut Self>, + self: ::pin_project::__private::Pin<&mut Self>, __replacement: Self, ) -> #proj_own_ident #orig_ty_generics { - unsafe { + #unsafety { #proj_own_body } } @@ -1024,14 +1117,14 @@ impl<'a> Context<'a> { quote! { impl #impl_generics #orig_ident #ty_generics #where_clause { #vis fn project<#lifetime>( - self: ::pin_project::__reexport::pin::Pin<&#lifetime mut Self>, + self: ::pin_project::__private::Pin<&#lifetime mut Self>, ) -> #proj_ident #proj_ty_generics { unsafe { #proj_body } } #vis fn project_ref<#lifetime>( - self: ::pin_project::__reexport::pin::Pin<&#lifetime Self>, + self: ::pin_project::__private::Pin<&#lifetime Self>, ) -> #proj_ref_ident #proj_ty_generics { unsafe { #proj_ref_body @@ -1044,9 +1137,9 @@ impl<'a> Context<'a> { fn ensure_not_packed(&self, fields: &Fields) -> Result<TokenStream> { for meta in self.orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { - if let Meta::List(l) = meta { - if l.path.is_ident("repr") { - for repr in l.nested.iter() { + if let Meta::List(list) = meta { + if list.path.is_ident("repr") { + for repr in list.nested.iter() { match repr { NestedMeta::Meta(Meta::Path(path)) | NestedMeta::Meta(Meta::List(MetaList { path, .. })) @@ -1095,8 +1188,9 @@ impl<'a> Context<'a> { // // There is one exception: If the type of a struct field has an alignment 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 alignment 1, - // our generated method will not trigger an error if the struct is `#[repr(packed)]`. + // is `#[repr(packed)]`. If the struct is composed entirely of types of + // alignment 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 diff --git a/src/pinned_drop.rs b/src/pinned_drop.rs index 4d1e8b2..42619e3 100644 --- a/src/pinned_drop.rs +++ b/src/pinned_drop.rs @@ -6,23 +6,29 @@ use crate::utils::{parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream { if let Err(e) = parse_as_empty(args).and_then(|()| parse(&mut input)) { - let self_ty = &input.self_ty; - let (impl_generics, _, where_clause) = input.generics.split_for_impl(); - let mut tokens = e.to_compile_error(); - // Generate a dummy `PinnedDrop` implementation. - // In many cases, `#[pinned_drop] impl` is declared after `#[pin_project]`. - // Therefore, if `pinned_drop` compile fails, you will also get an error - // about `PinnedDrop` not being implemented. - // This can be prevented to some extent by generating a dummy - // `PinnedDrop` implementation. - // We already know that we will get a compile error, so this won't - // accidentally compile successfully. - tokens.extend(quote! { - impl #impl_generics ::pin_project::__private::PinnedDrop for #self_ty #where_clause { - unsafe fn drop(self: ::pin_project::__reexport::pin::Pin<&mut Self>) {} - } - }); + if let Type::Path(self_ty) = &*input.self_ty { + let (impl_generics, _, where_clause) = input.generics.split_for_impl(); + + // A dummy impl of `PinnedDrop`. + // In many cases, `#[pinned_drop] impl` is declared after `#[pin_project]`. + // Therefore, if `pinned_drop` compile fails, you will also get an error + // about `PinnedDrop` not being implemented. + // This can be prevented to some extent by generating a dummy + // `PinnedDrop` implementation. + // We already know that we will get a compile error, so this won't + // accidentally compile successfully. + // + // However, if `input.self_ty` is not Type::Path, there is a high possibility that + // the type does not exist, so do not generate a dummy impl. + tokens.extend(quote! { + impl #impl_generics ::pin_project::__private::PinnedDrop for #self_ty + #where_clause + { + unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {} + } + }); + } tokens } else { input.into_token_stream() @@ -116,6 +122,16 @@ fn parse(item: &mut ItemImpl) -> Result<()> { return Err(error!(item, "not all trait items implemented, missing: `drop`")); } + match &*item.self_ty { + Type::Path(_) => {} + ty => { + return Err(error!( + ty, + "implementing the trait `PinnedDrop` on this type is unsupported" + )); + } + } + item.items .iter() .enumerate() @@ -171,21 +187,25 @@ fn expand_item(item: &mut ItemImpl) { prepend_underscore_to_self(&mut ident.ident); } } - let mut visitor = ReplaceReceiver(&item.self_ty); + let self_ty = if let Type::Path(ty) = &*item.self_ty { ty } else { unreachable!() }; + let mut visitor = ReplaceReceiver(self_ty); visitor.visit_signature_mut(&mut drop_inner.sig); visitor.visit_block_mut(&mut drop_inner.block); // `fn drop(mut self: Pin<&mut Self>)` -> `unsafe fn drop(self: Pin<&mut Self>)` - method.sig.unsafety = Some(token::Unsafe::default()); + method.sig.unsafety = Some(<Token![unsafe]>::default()); + let mut self_token = None; if let FnArg::Typed(arg) = &mut method.sig.inputs[0] { if let Pat::Ident(ident) = &mut *arg.pat { ident.mutability = None; + self_token = Some(&ident.ident); } } + assert!(self_token.is_some()); - method.block.stmts = syn::parse_quote! { + method.block.stmts = parse_quote! { #[allow(clippy::needless_pass_by_value)] // This lint does not warn the receiver. #drop_inner - __drop_inner(self); + __drop_inner(#self_token); }; } diff --git a/src/project.rs b/src/project.rs index d8bd288..a00e4c2 100644 --- a/src/project.rs +++ b/src/project.rs @@ -6,25 +6,22 @@ use syn::{ }; use crate::utils::{ - determine_lifetime_name, insert_lifetime, parse_as_empty, Immutable, Mutability, Mutable, - Owned, SliceExt, VecExt, + determine_lifetime_name, insert_lifetime, parse_as_empty, ProjKind, SliceExt, VecExt, }; -pub(crate) fn attribute(args: &TokenStream, input: Stmt, mutability: Mutability) -> TokenStream { - parse_as_empty(args) - .and_then(|()| parse(input, mutability)) - .unwrap_or_else(|e| e.to_compile_error()) +pub(crate) fn attribute(args: &TokenStream, input: Stmt, kind: ProjKind) -> TokenStream { + parse_as_empty(args).and_then(|()| parse(input, kind)).unwrap_or_else(|e| e.to_compile_error()) } -fn replace_expr(expr: &mut Expr, mutability: Mutability) { +fn replace_expr(expr: &mut Expr, kind: ProjKind) { match expr { Expr::Match(expr) => { - Context::new(mutability).replace_expr_match(expr); + Context::new(kind).replace_expr_match(expr); } Expr::If(expr_if) => { let mut expr_if = expr_if; while let Expr::Let(ref mut expr) = &mut *expr_if.cond { - Context::new(mutability).replace_expr_let(expr); + Context::new(kind).replace_expr_let(expr); if let Some((_, ref mut expr)) = expr_if.else_branch { if let Expr::If(new_expr_if) = &mut **expr { expr_if = new_expr_if; @@ -38,13 +35,13 @@ fn replace_expr(expr: &mut Expr, mutability: Mutability) { } } -fn parse(mut stmt: Stmt, mutability: Mutability) -> Result<TokenStream> { +fn parse(mut stmt: Stmt, kind: ProjKind) -> Result<TokenStream> { match &mut stmt { - Stmt::Expr(expr) | Stmt::Semi(expr, _) => replace_expr(expr, mutability), - Stmt::Local(local) => Context::new(mutability).replace_local(local)?, - Stmt::Item(Item::Fn(item)) => replace_item_fn(item, mutability)?, - Stmt::Item(Item::Impl(item)) => replace_item_impl(item, mutability)?, - Stmt::Item(Item::Use(item)) => replace_item_use(item, mutability)?, + Stmt::Expr(expr) | Stmt::Semi(expr, _) => replace_expr(expr, kind), + Stmt::Local(local) => Context::new(kind).replace_local(local)?, + Stmt::Item(Item::Fn(item)) => replace_item_fn(item, kind)?, + Stmt::Item(Item::Impl(item)) => replace_item_impl(item, kind)?, + Stmt::Item(Item::Use(item)) => replace_item_use(item, kind)?, _ => {} } @@ -54,18 +51,16 @@ fn parse(mut stmt: Stmt, mutability: Mutability) -> Result<TokenStream> { struct Context { register: Option<(Ident, usize)>, replaced: bool, - mutability: Mutability, + kind: ProjKind, } impl Context { - fn new(mutability: Mutability) -> Self { - Self { register: None, replaced: false, mutability } + fn new(kind: ProjKind) -> Self { + Self { register: None, replaced: false, kind } } fn update(&mut self, ident: &Ident, len: usize) { - if self.register.is_none() { - self.register = Some((ident.clone(), len)); - } + self.register.get_or_insert_with(|| (ident.clone(), len)); } fn compare_paths(&self, ident: &Ident, len: usize) -> bool { @@ -76,8 +71,8 @@ impl Context { } fn replace_local(&mut self, local: &mut Local) -> Result<()> { - if let Some(attr) = local.attrs.find(self.mutability.method_name()) { - return Err(error!(attr, "duplicate #[{}] attribute", self.mutability.method_name())); + if let Some(attr) = local.attrs.find(self.kind.method_name()) { + return Err(error!(attr, "duplicate #[{}] attribute", self.kind.method_name())); } if let Some(Expr::Match(expr)) = local.init.as_mut().map(|(_, expr)| &mut **expr) { @@ -141,7 +136,7 @@ impl Context { if self.register.is_none() || self.compare_paths(&path.segments[0].ident, len) { self.update(&path.segments[0].ident, len); self.replaced = true; - replace_ident(&mut path.segments[0].ident, self.mutability); + replace_ident(&mut path.segments[0].ident, self.kind); } } } @@ -161,13 +156,13 @@ fn is_replaceable(pat: &Pat, allow_pat_path: bool) -> bool { } } -fn replace_ident(ident: &mut Ident, mutability: Mutability) { - *ident = mutability.proj_ident(ident); +fn replace_ident(ident: &mut Ident, kind: ProjKind) { + *ident = kind.proj_ident(ident); } -fn replace_item_impl(item: &mut ItemImpl, mutability: Mutability) -> Result<()> { - if let Some(attr) = item.attrs.find(mutability.method_name()) { - return Err(error!(attr, "duplicate #[{}] attribute", mutability.method_name())); +fn replace_item_impl(item: &mut ItemImpl, kind: ProjKind) -> Result<()> { + if let Some(attr) = item.attrs.find(kind.method_name()) { + return Err(error!(attr, "duplicate #[{}] attribute", kind.method_name())); } let PathSegment { ident, arguments } = match &mut *item.self_ty { @@ -175,7 +170,7 @@ fn replace_item_impl(item: &mut ItemImpl, mutability: Mutability) -> Result<()> _ => return Ok(()), }; - replace_ident(ident, mutability); + replace_ident(ident, kind); let mut lifetime_name = String::from("'pin"); determine_lifetime_name(&mut lifetime_name, &mut item.generics); @@ -189,17 +184,17 @@ fn replace_item_impl(item: &mut ItemImpl, mutability: Mutability) -> Result<()> match arguments { PathArguments::None => { - *arguments = PathArguments::AngleBracketed(syn::parse_quote!(<#lifetime>)); + *arguments = PathArguments::AngleBracketed(parse_quote!(<#lifetime>)); } PathArguments::AngleBracketed(args) => { - args.args.insert(0, syn::parse_quote!(#lifetime)); + args.args.insert(0, parse_quote!(#lifetime)); } PathArguments::Parenthesized(_) => unreachable!(), } Ok(()) } -fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> { +fn replace_item_fn(item: &mut ItemFn, kind: ProjKind) -> Result<()> { struct FnVisitor(Result<()>); impl FnVisitor { @@ -210,17 +205,17 @@ fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> { visit_mut::visit_local_mut(self, local); let mut prev = None; - for &mutability in &[Immutable, Mutable, Owned] { - if let Some(attr) = local.attrs.find_remove(mutability.method_name())? { - if let Some(prev) = prev.replace(mutability) { + for &kind in &ProjKind::ALL { + if let Some(attr) = local.attrs.find_remove(kind.method_name())? { + if let Some(prev) = prev.replace(kind) { return Err(error!( attr, "attributes `{}` and `{}` are mutually exclusive", prev.method_name(), - mutability.method_name(), + kind.method_name(), )); } - Context::new(mutability).replace_local(local)?; + Context::new(kind).replace_local(local)?; } } @@ -236,41 +231,39 @@ fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> { match node { Expr::Match(expr) => { let mut prev = None; - for &mutability in &[Immutable, Mutable, Owned] { - if let Some(attr) = expr.attrs.find_remove(mutability.method_name())? { - if let Some(prev) = prev.replace(mutability) { + for &kind in &ProjKind::ALL { + if let Some(attr) = expr.attrs.find_remove(kind.method_name())? { + if let Some(prev) = prev.replace(kind) { return Err(error!( attr, "attributes `{}` and `{}` are mutually exclusive", prev.method_name(), - mutability.method_name(), + kind.method_name(), )); } } } - if let Some(mutability) = prev { - replace_expr(node, mutability); + if let Some(kind) = prev { + replace_expr(node, kind); } } Expr::If(expr_if) => { if let Expr::Let(_) = &*expr_if.cond { let mut prev = None; - for &mutability in &[Immutable, Mutable, Owned] { - if let Some(attr) = - expr_if.attrs.find_remove(mutability.method_name())? - { - if let Some(prev) = prev.replace(mutability) { + for &kind in &ProjKind::ALL { + if let Some(attr) = expr_if.attrs.find_remove(kind.method_name())? { + if let Some(prev) = prev.replace(kind) { return Err(error!( attr, "attributes `{}` and `{}` are mutually exclusive", prev.method_name(), - mutability.method_name(), + kind.method_name(), )); } } } - if let Some(mutability) = prev { - replace_expr(node, mutability); + if let Some(kind) = prev { + replace_expr(node, kind); } } } @@ -304,8 +297,8 @@ fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> { } } - if let Some(attr) = item.attrs.find(mutability.method_name()) { - return Err(error!(attr, "duplicate #[{}] attribute", mutability.method_name())); + if let Some(attr) = item.attrs.find(kind.method_name()) { + return Err(error!(attr, "duplicate #[{}] attribute", kind.method_name())); } let mut visitor = FnVisitor(Ok(())); @@ -313,10 +306,10 @@ fn replace_item_fn(item: &mut ItemFn, mutability: Mutability) -> Result<()> { visitor.0 } -fn replace_item_use(item: &mut ItemUse, mutability: Mutability) -> Result<()> { +fn replace_item_use(item: &mut ItemUse, kind: ProjKind) -> Result<()> { struct UseTreeVisitor { res: Result<()>, - mutability: Mutability, + kind: ProjKind, } impl VisitMut for UseTreeVisitor { @@ -327,19 +320,19 @@ fn replace_item_use(item: &mut ItemUse, mutability: Mutability) -> Result<()> { match node { // Desugar `use tree::<name>` into `tree::__<name>Projection`. - UseTree::Name(name) => replace_ident(&mut name.ident, self.mutability), + UseTree::Name(name) => replace_ident(&mut name.ident, self.kind), UseTree::Glob(glob) => { self.res = Err(error!( glob, "#[{}] attribute may not be used on glob imports", - self.mutability.method_name() + self.kind.method_name() )); } UseTree::Rename(rename) => { self.res = Err(error!( rename, "#[{}] attribute may not be used on renamed imports", - self.mutability.method_name() + self.kind.method_name() )); } UseTree::Path(_) | UseTree::Group(_) => visit_mut::visit_use_tree_mut(self, node), @@ -347,11 +340,11 @@ fn replace_item_use(item: &mut ItemUse, mutability: Mutability) -> Result<()> { } } - if let Some(attr) = item.attrs.find(mutability.method_name()) { - return Err(error!(attr, "duplicate #[{}] attribute", mutability.method_name())); + if let Some(attr) = item.attrs.find(kind.method_name()) { + return Err(error!(attr, "duplicate #[{}] attribute", kind.method_name())); } - let mut visitor = UseTreeVisitor { res: Ok(()), mutability }; + let mut visitor = UseTreeVisitor { res: Ok(()), kind }; visitor.visit_item_use_mut(item); visitor.res } diff --git a/src/utils.rs b/src/utils.rs index 2a344d1..716d764 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,16 +1,14 @@ -use proc_macro2::{Group, TokenStream, TokenTree}; -use quote::format_ident; +use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use std::{iter::FromIterator, mem}; use syn::{ - parse::{ParseBuffer, ParseStream}, + parse::{Parse, ParseBuffer, ParseStream}, punctuated::Punctuated, visit_mut::{self, VisitMut}, *, }; -pub(crate) type Variants = Punctuated<Variant, token::Comma>; - -pub(crate) use Mutability::{Immutable, Mutable, Owned}; +pub(crate) type Variants = Punctuated<Variant, Token![,]>; macro_rules! error { ($span:expr, $msg:expr) => { @@ -28,39 +26,43 @@ macro_rules! parse_quote_spanned { } #[derive(Clone, Copy, Eq, PartialEq)] -pub(crate) enum Mutability { +pub(crate) enum ProjKind { Mutable, Immutable, Owned, } -impl Mutability { - /// Returns the name of method and attribute. +impl ProjKind { + pub(crate) const ALL: [Self; 3] = [ProjKind::Mutable, ProjKind::Immutable, ProjKind::Owned]; + + /// Returns the name of the projection method. pub(crate) fn method_name(self) -> &'static str { match self { - Mutable => "project", - Immutable => "project_ref", - Owned => "project_replace", + ProjKind::Mutable => "project", + ProjKind::Immutable => "project_ref", + ProjKind::Owned => "project_replace", } } - /// Creates the ident of the projected type from the ident of the original type. + /// Creates the ident of the projected type from the ident of the original + /// type. pub(crate) fn proj_ident(self, ident: &Ident) -> Ident { match self { - Mutable => format_ident!("__{}Projection", ident), - Immutable => format_ident!("__{}ProjectionRef", ident), - Owned => format_ident!("__{}ProjectionOwned", ident), + ProjKind::Mutable => format_ident!("__{}Projection", ident), + ProjKind::Immutable => format_ident!("__{}ProjectionRef", ident), + ProjKind::Owned => format_ident!("__{}ProjectionOwned", ident), } } } -/// Determines the lifetime names. Ensure it doesn't overlap with any existing lifetime names. +/// Determines the lifetime names. Ensure it doesn't overlap with any existing +/// lifetime names. pub(crate) fn determine_lifetime_name(lifetime_name: &mut String, generics: &mut Generics) { struct CollectLifetimes(Vec<String>); impl VisitMut for CollectLifetimes { - fn visit_lifetime_def_mut(&mut self, node: &mut LifetimeDef) { - self.0.push(node.lifetime.to_string()) + fn visit_lifetime_def_mut(&mut self, def: &mut LifetimeDef) { + self.0.push(def.lifetime.to_string()); } } @@ -85,24 +87,23 @@ pub(crate) fn insert_lifetime_and_bound( ) -> WherePredicate { insert_lifetime(generics, lifetime.clone()); - let orig_type: Type = syn::parse_quote!(#orig_ident #orig_generics); + let orig_type: Type = parse_quote!(#orig_ident #orig_generics); let mut punct = Punctuated::new(); punct.push(TypeParamBound::Lifetime(lifetime)); WherePredicate::Type(PredicateType { lifetimes: None, bounded_ty: orig_type, - colon_token: token::Colon::default(), + colon_token: <Token![:]>::default(), bounds: punct, }) } /// Inserts a `lifetime` at position `0` of `generics.params`. pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { - generics.lt_token.get_or_insert_with(token::Lt::default); - generics.gt_token.get_or_insert_with(token::Gt::default); - - generics.params.insert(0, GenericParam::Lifetime(LifetimeDef::new(lifetime))); + generics.lt_token.get_or_insert_with(<Token![<]>::default); + generics.gt_token.get_or_insert_with(<Token![>]>::default); + generics.params.insert(0, LifetimeDef::new(lifetime).into()); } /// Determines the visibility of the projected type and projection method. @@ -115,12 +116,31 @@ pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility { } /// Check if `tokens` is an empty `TokenStream`. -/// This is almost equivalent to `syn::parse2::<Nothing>()`, -/// but produces a better error message and does not require ownership of `tokens`. +/// This is almost equivalent to `syn::parse2::<Nothing>()`, but produces +/// a better error message and does not require ownership of `tokens`. pub(crate) fn parse_as_empty(tokens: &TokenStream) -> Result<()> { if tokens.is_empty() { Ok(()) } else { Err(error!(tokens, "unexpected token: {}", tokens)) } } +pub(crate) fn respan<T>(node: &T, span: Span) -> T +where + T: ToTokens + Parse, +{ + let tokens = node.to_token_stream(); + let respanned = respan_tokens(tokens, span); + syn::parse2(respanned).unwrap() +} + +fn respan_tokens(tokens: TokenStream, span: Span) -> TokenStream { + tokens + .into_iter() + .map(|mut token| { + token.set_span(span); + token + }) + .collect() +} + // ================================================================================================= // extension traits @@ -183,11 +203,15 @@ impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> { // visitors // Replace `self`/`Self` with `__self`/`self_ty`. -// Based on https://github.com/dtolnay/async-trait/blob/0.1.30/src/receiver.rs +// Based on https://github.com/dtolnay/async-trait/blob/0.1.35/src/receiver.rs -pub(crate) struct ReplaceReceiver<'a>(pub(crate) &'a Type); +pub(crate) struct ReplaceReceiver<'a>(pub(crate) &'a TypePath); impl ReplaceReceiver<'_> { + fn self_ty(&self, span: Span) -> TypePath { + respan(self.0, span) + } + fn self_to_qself(&self, qself: &mut Option<QSelf>, path: &mut Path) { if path.leading_colon.is_some() { return; @@ -203,18 +227,16 @@ impl ReplaceReceiver<'_> { return; } + let span = first.ident.span(); *qself = Some(QSelf { - lt_token: token::Lt::default(), - ty: Box::new(self.0.clone()), + lt_token: Token![<](span), + ty: Box::new(self.self_ty(span).into()), position: 0, as_token: None, - gt_token: token::Gt::default(), + gt_token: Token![>](span), }); - match path.segments.pairs().next().unwrap().punct() { - Some(&&colon) => path.leading_colon = Some(colon), - None => return, - } + path.leading_colon = Some(**path.segments.pairs().next().unwrap().punct().unwrap()); let segments = mem::replace(&mut path.segments, Punctuated::new()); path.segments = segments.into_pairs().skip(1).collect(); @@ -230,25 +252,66 @@ impl ReplaceReceiver<'_> { return; } - if let Type::Path(self_ty) = &self.0 { - let variant = mem::replace(path, self_ty.path.clone()); - for segment in &mut path.segments { - if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments { - if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() { - bracketed.colon2_token = Some(token::Colon2::default()); - } + let self_ty = self.self_ty(first.ident.span()); + let variant = mem::replace(path, self_ty.path); + for segment in &mut path.segments { + if let PathArguments::AngleBracketed(bracketed) = &mut segment.arguments { + if bracketed.colon2_token.is_none() && !bracketed.args.is_empty() { + bracketed.colon2_token = Some(<Token![::]>::default()); } } - if variant.segments.len() > 1 { - path.segments.push_punct(token::Colon2::default()); - path.segments.extend(variant.segments.into_pairs().skip(1)); + } + if variant.segments.len() > 1 { + path.segments.push_punct(<Token![::]>::default()); + path.segments.extend(variant.segments.into_pairs().skip(1)); + } + } + + fn visit_token_stream(&self, tokens: &mut TokenStream) -> bool { + let mut out = Vec::new(); + let mut modified = false; + let mut iter = tokens.clone().into_iter().peekable(); + while let Some(tt) = iter.next() { + match tt { + TokenTree::Ident(mut ident) => { + modified |= prepend_underscore_to_self(&mut ident); + if ident == "Self" { + modified = true; + let self_ty = self.self_ty(ident.span()); + match iter.peek() { + Some(TokenTree::Punct(p)) + if p.as_char() == ':' && p.spacing() == Spacing::Joint => + { + let next = iter.next().unwrap(); + match iter.peek() { + Some(TokenTree::Punct(p)) if p.as_char() == ':' => { + let span = ident.span(); + out.extend(quote_spanned!(span=> <#self_ty>)) + } + _ => out.extend(quote!(#self_ty)), + } + out.push(next); + } + _ => out.extend(quote!(#self_ty)), + } + } else { + out.push(TokenTree::Ident(ident)); + } + } + TokenTree::Group(group) => { + let mut content = group.stream(); + modified |= self.visit_token_stream(&mut content); + let mut new = Group::new(group.delimiter(), content); + new.set_span(group.span()); + out.push(TokenTree::Group(new)); + } + other => out.push(other), } - } else { - let span = path.segments[0].ident.span(); - let msg = "Self type of this impl is unsupported in expression position"; - let error = Error::new(span, msg).to_compile_error(); - *path = parse_quote!(::pin_project::__reexport::marker::PhantomData::<#error>); } + if modified { + *tokens = TokenStream::from_iter(out); + } + modified } } @@ -257,7 +320,7 @@ impl VisitMut for ReplaceReceiver<'_> { fn visit_type_mut(&mut self, ty: &mut Type) { if let Type::Path(node) = ty { if node.qself.is_none() && node.path.is_ident("Self") { - *ty = self.0.clone(); + *ty = self.self_ty(node.path.segments[0].ident.span()).into(); } else { self.visit_type_path_mut(node); } @@ -305,25 +368,25 @@ impl VisitMut for ReplaceReceiver<'_> { visit_mut::visit_pat_tuple_struct_mut(self, pat); } - fn visit_item_mut(&mut self, node: &mut Item) { - match node { + fn visit_item_mut(&mut self, item: &mut Item) { + match item { // Visit `macro_rules!` because locally defined macros can refer to `self`. - Item::Macro(node) if node.mac.path.is_ident("macro_rules") => { - self.visit_macro_mut(&mut node.mac) + Item::Macro(item) if item.mac.path.is_ident("macro_rules") => { + self.visit_macro_mut(&mut item.mac) } // Otherwise, do not recurse into nested items. _ => {} } } - fn visit_macro_mut(&mut self, node: &mut Macro) { + fn visit_macro_mut(&mut self, mac: &mut Macro) { // We can't tell in general whether `self` inside a macro invocation // refers to the self in the argument list or a different self // introduced within the macro. Heuristic: if the macro input contains // `fn`, then `self` is more likely to refer to something other than the // outer function's self argument. - if !contains_fn(node.tokens.clone()) { - visit_token_stream(&mut node.tokens); + if !contains_fn(mac.tokens.clone()) { + self.visit_token_stream(&mut mac.tokens); } } } @@ -336,31 +399,6 @@ fn contains_fn(tokens: TokenStream) -> bool { }) } -fn visit_token_stream(tokens: &mut TokenStream) -> bool { - let mut out = Vec::new(); - let mut modified = false; - for tt in tokens.clone() { - match tt { - TokenTree::Ident(mut ident) => { - modified |= prepend_underscore_to_self(&mut ident); - out.push(TokenTree::Ident(ident)); - } - TokenTree::Group(group) => { - let mut content = group.stream(); - modified |= visit_token_stream(&mut content); - let mut new = Group::new(group.delimiter(), content); - new.set_span(group.span()); - out.push(TokenTree::Group(new)); - } - other => out.push(other), - } - } - if modified { - *tokens = TokenStream::from_iter(out); - } - modified -} - pub(crate) fn prepend_underscore_to_self(ident: &mut Ident) -> bool { let modified = ident == "self"; if modified { |