summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHaibo Huang <hhb@google.com>2020-07-14 08:50:28 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-07-14 08:50:28 +0000
commit943844cca149d51a0569aa0b29c64260453a78b0 (patch)
tree8bceab18c5a2b8dcf0cfa390e53d1cbbd9114775
parent27dda6474b2fed371024beec388352867d8dfb1b (diff)
parentda3cbc7de1c67c57198a4a91605983c8f26832c8 (diff)
downloadpin-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.bp8
-rw-r--r--Cargo.toml2
-rw-r--r--Cargo.toml.orig2
-rw-r--r--METADATA6
-rw-r--r--build.rs34
-rw-r--r--src/lib.rs417
-rw-r--r--src/pin_project/attribute.rs26
-rw-r--r--src/pin_project/derive.rs810
-rw-r--r--src/pinned_drop.rs60
-rw-r--r--src/project.rs117
-rw-r--r--src/utils.rs206
11 files changed, 974 insertions, 714 deletions
diff --git a/Android.bp b/Android.bp
index c457649..52bd62a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -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",
diff --git a/Cargo.toml b/Cargo.toml
index 351fe27..760c663 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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"
diff --git a/METADATA b/METADATA
index eb7aaeb..148d2c8 100644
--- a/METADATA
+++ b/METADATA
@@ -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()
+}
diff --git a/src/lib.rs b/src/lib.rs
index b73888c..01be314 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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 {