summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChih-Hung Hsieh <chh@google.com>2020-10-29 20:40:59 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2020-10-29 20:40:59 +0000
commitc617f9a630d64222780a99b089a3539ef6b014bb (patch)
tree046d0eb65b1631264519144f629b13f5273268e0
parent89d4308f5f0a39a6b9d635f959d3174cf6f3135e (diff)
parent1241efe6e7a49281272b375cf9dd17fbaba1a840 (diff)
downloadpin-project-internal-c617f9a630d64222780a99b089a3539ef6b014bb.tar.gz
Upgrade rust/crates/pin-project-internal to 1.0.1 am: 9018f669e4 am: 6781a0015d am: ec331065ae am: 1241efe6e7
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/pin-project-internal/+/1475360 Change-Id: I04ca53648cb9c46ae663e3e043212e01bbdc129d
-rw-r--r--Android.bp12
-rw-r--r--Cargo.toml10
-rw-r--r--Cargo.toml.orig10
-rw-r--r--LICENSE-APACHE25
-rw-r--r--METADATA10
-rw-r--r--TEST_MAPPING1
-rw-r--r--build.rs34
-rw-r--r--src/lib.rs378
-rw-r--r--src/pin_project/args.rs257
-rw-r--r--src/pin_project/derive.rs1911
-rw-r--r--src/pin_project/mod.rs1
-rw-r--r--src/pinned_drop.rs217
-rw-r--r--src/project.rs350
-rw-r--r--src/utils.rs56
14 files changed, 1380 insertions, 1892 deletions
diff --git a/Android.bp b/Android.bp
index 52bd62a..1df8422 100644
--- a/Android.bp
+++ b/Android.bp
@@ -5,10 +5,6 @@ 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",
@@ -23,10 +19,6 @@ 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",
@@ -35,7 +27,7 @@ rust_test_host {
}
// dependent_library ["feature_list"]
-// proc-macro2-1.0.18 "default,proc-macro"
+// proc-macro2-1.0.24 "default,proc-macro"
// quote-1.0.7 "default,proc-macro"
-// syn-1.0.33 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
+// syn-1.0.48 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut"
// unicode-xid-0.2.1 "default"
diff --git a/Cargo.toml b/Cargo.toml
index b22fcb1..234def0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,9 +13,9 @@
[package]
edition = "2018"
name = "pin-project-internal"
-version = "0.4.23"
+version = "1.0.1"
authors = ["Taiki Endo <te316e89@gmail.com>"]
-description = "An internal crate to support pin_project - do not use directly\n"
+description = "Implementation detail of the `pin-project` crate.\n"
homepage = "https://github.com/taiki-e/pin-project"
documentation = "https://docs.rs/pin-project-internal"
keywords = ["pin", "macros", "attribute"]
@@ -28,11 +28,11 @@ targets = ["x86_64-unknown-linux-gnu"]
[lib]
proc-macro = true
[dependencies.proc-macro2]
-version = "1.0"
+version = "1"
[dependencies.quote]
-version = "1.0"
+version = "1"
[dependencies.syn]
-version = "1.0.13"
+version = "1.0.44"
features = ["full", "visit-mut"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index ac5e137..4a4d1d2 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "pin-project-internal"
-version = "0.4.23"
+version = "1.0.1"
authors = ["Taiki Endo <te316e89@gmail.com>"]
edition = "2018"
license = "Apache-2.0 OR MIT"
@@ -10,7 +10,7 @@ documentation = "https://docs.rs/pin-project-internal"
keywords = ["pin", "macros", "attribute"]
categories = ["no-std", "rust-patterns"]
description = """
-An internal crate to support pin_project - do not use directly
+Implementation detail of the `pin-project` crate.
"""
[package.metadata.docs.rs]
@@ -20,7 +20,7 @@ targets = ["x86_64-unknown-linux-gnu"]
proc-macro = true
[dependencies]
-proc-macro2 = "1.0"
-quote = "1.0"
-syn = { version = "1.0.13", features = ["full", "visit-mut"] }
+proc-macro2 = "1"
+quote = "1"
+syn = { version = "1.0.44", features = ["full", "visit-mut"] }
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
index d645695..f433b1a 100644
--- a/LICENSE-APACHE
+++ b/LICENSE-APACHE
@@ -175,28 +175,3 @@
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/METADATA b/METADATA
index b49ad74..69b7fd9 100644
--- a/METADATA
+++ b/METADATA
@@ -1,5 +1,5 @@
name: "pin-project-internal"
-description: "An internal crate to support pin_project - do not use directly"
+description: "Implementation detail of the `pin-project` crate."
third_party {
url {
type: HOMEPAGE
@@ -7,13 +7,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/pin-project-internal/pin-project-internal-0.4.23.crate"
+ value: "https://static.crates.io/crates/pin-project-internal/pin-project-internal-1.0.1.crate"
}
- version: "0.4.23"
+ version: "1.0.1"
license_type: NOTICE
last_upgrade_date {
year: 2020
- month: 7
- day: 27
+ month: 10
+ day: 26
}
}
diff --git a/TEST_MAPPING b/TEST_MAPPING
index fd2153b..787bcd5 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -1,3 +1,4 @@
+// Generated by cargo2android.py for tests in Android.bp
{
"presubmit": [
{
diff --git a/build.rs b/build.rs
deleted file mode 100644
index b4d314c..0000000
--- a/build.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-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 eeb71d7..0fdb9c4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,15 +1,19 @@
-//! An internal crate to support pin_project - **do not use directly**
+//! Implementation detail of the `pin-project` crate. - **do not use directly**
-#![doc(html_root_url = "https://docs.rs/pin-project-internal/0.4.23")]
+#![doc(html_root_url = "https://docs.rs/pin-project-internal/1.0.1")]
#![doc(test(
no_crate_inject,
attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![warn(unsafe_code)]
-#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
+#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
-// mem::take and #[non_exhaustive] requires Rust 1.40
-#![allow(clippy::mem_replace_with_default, clippy::manual_non_exhaustive)]
+// mem::take and #[non_exhaustive] requires Rust 1.40, matches! requires Rust 1.42
+#![allow(
+ clippy::mem_replace_with_default,
+ clippy::manual_non_exhaustive,
+ clippy::match_like_matches_macro
+)]
#![allow(clippy::needless_doctest_main)]
// older compilers require explicit `extern crate`.
@@ -21,12 +25,9 @@ mod utils;
mod pin_project;
mod pinned_drop;
-mod project;
use proc_macro::TokenStream;
-use crate::utils::ProjKind;
-
/// An attribute that creates projection types covering all the fields of
/// struct or enum.
///
@@ -49,23 +50,25 @@ use crate::utils::ProjKind;
/// ```
///
/// By passing an argument with the same name as the method to the attribute,
-/// you can name the projection type returned from the method:
+/// you can name the projection type returned from the method. This allows you
+/// to use pattern matching on the projected types.
///
/// ```rust
-/// use pin_project::pin_project;
-/// use std::pin::Pin;
-///
-/// #[pin_project(project = StructProj)]
-/// struct Struct<T> {
-/// #[pin]
-/// field: T,
+/// # use pin_project::pin_project;
+/// # use std::pin::Pin;
+/// #[pin_project(project = EnumProj)]
+/// enum Enum<T> {
+/// Variant(#[pin] T),
/// }
///
-/// impl<T> Struct<T> {
+/// impl<T> Enum<T> {
/// fn method(self: Pin<&mut Self>) {
-/// let this: StructProj<'_, T> = self.project();
-/// let StructProj { field } = this;
-/// let _: Pin<&mut T> = field;
+/// let this: EnumProj<'_, T> = self.project();
+/// match this {
+/// EnumProj::Variant(x) => {
+/// let _: Pin<&mut T> = x;
+/// }
+/// }
/// }
/// }
/// ```
@@ -73,10 +76,15 @@ use crate::utils::ProjKind;
/// 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
+/// ```text
+/// let this: EnumProj<'_, T> = self.project();
+/// ^^
+/// ```
+///
+/// The visibility of the projected types and projection methods 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)`.
+/// visibility of the projected types and the projection methods is downgraded
+/// to `pub(crate)`.
///
/// # Safety
///
@@ -109,6 +117,7 @@ use crate::utils::ProjKind;
/// ```rust
/// struct MyStruct {}
/// trait MyStructMustNotImplDrop {}
+/// # #[allow(unknown_lints, drop_bounds)]
/// impl<T: Drop> MyStructMustNotImplDrop for T {}
/// impl MyStructMustNotImplDrop for MyStruct {}
/// ```
@@ -122,7 +131,7 @@ use crate::utils::ProjKind;
/// 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
+/// 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.
@@ -148,8 +157,8 @@ use crate::utils::ProjKind;
/// 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.
+/// types. Attempting to use this attribute on a `#[repr(packed)]` type results
+/// in a compile-time error.
///
/// # Examples
///
@@ -220,7 +229,33 @@ use crate::utils::ProjKind;
/// }
/// ```
///
-/// If you want to call the `project()` method multiple times or later use the
+/// When `#[pin_project]` is used on enums, only named projection types and
+/// methods are generated because there is no way to access variants of
+/// projected types without naming it.
+/// For example, in the above example, only the `project` method is generated,
+/// and the `project_ref` method is not generated.
+/// (When `#[pin_project]` is used on structs, both methods are always generated.)
+///
+/// ```rust,compile_fail,E0599
+/// # use pin_project::pin_project;
+/// # use std::pin::Pin;
+/// #
+/// # #[pin_project(project = EnumProj)]
+/// # enum Enum<T, U> {
+/// # Tuple(#[pin] T),
+/// # Struct { field: U },
+/// # Unit,
+/// # }
+/// #
+/// impl<T, U> Enum<T, U> {
+/// fn call_project_ref(self: Pin<&Self>) {
+/// let _this = self.project_ref();
+/// //~^ ERROR no method named `project_ref` found for struct `Pin<&Enum<T, U>>` in the current scope
+/// }
+/// }
+/// ```
+///
+/// If you want to call `.project()` multiple times or later use the
/// original [`Pin`] type, it needs to use [`.as_mut()`][`Pin::as_mut`] to avoid
/// consuming the [`Pin`].
///
@@ -339,14 +374,14 @@ use crate::utils::ProjKind;
/// use std::{fmt::Debug, pin::Pin};
///
/// #[pin_project(PinnedDrop)]
-/// struct Struct<T: Debug, U: Debug> {
+/// struct PrintOnDrop<T: Debug, U: Debug> {
/// #[pin]
/// pinned_field: T,
/// unpin_field: U,
/// }
///
/// #[pinned_drop]
-/// impl<T: Debug, U: Debug> PinnedDrop for Struct<T, U> {
+/// impl<T: Debug, U: Debug> PinnedDrop for PrintOnDrop<T, U> {
/// fn drop(self: Pin<&mut Self>) {
/// println!("Dropping pinned field: {:?}", self.pinned_field);
/// println!("Dropping unpin field: {:?}", self.unpin_field);
@@ -354,17 +389,17 @@ use crate::utils::ProjKind;
/// }
///
/// fn main() {
-/// let _x = Struct { pinned_field: true, unpin_field: 40 };
+/// let _x = PrintOnDrop { pinned_field: true, unpin_field: 40 };
/// }
/// ```
///
-/// See also [`#[pinned_drop]`][`pinned_drop`] attribute.
+/// See also [`#[pinned_drop]`][macro@pinned_drop] attribute.
///
-/// # `project_replace()`
+/// # `project_replace` method
///
-/// In addition to the `project()` and `project_ref()` methods which are always
+/// 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
+/// 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.
///
@@ -407,8 +442,8 @@ use crate::utils::ProjKind;
/// ```
///
/// 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
+/// returned type of the `project_replace` method. This is necessary whenever
+/// destructuring the return type of the `project_replace` method, and work in exactly
/// the same way as the `project` and `project_ref` arguments.
///
/// ```rust
@@ -437,26 +472,25 @@ use crate::utils::ProjKind;
/// [`Pin::as_mut`]: core::pin::Pin::as_mut
/// [`Pin::set`]: core::pin::Pin::set
/// [`Pin`]: core::pin::Pin
-/// [`UnsafeUnpin`]: https://docs.rs/pin-project/0.4/pin_project/trait.UnsafeUnpin.html
-/// [`pinned_drop`]: ./attr.pinned_drop.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
+/// [`UnsafeUnpin`]: https://docs.rs/pin-project/1/pin_project/trait.UnsafeUnpin.html
+/// [drop-guarantee]: core::pin#drop-guarantee
+/// [pin-projection]: core::pin#projections-and-structural-pinning
+/// [pinned-drop]: macro@pin_project#pinned_drop
/// [repr-packed]: https://doc.rust-lang.org/nomicon/other-reprs.html#reprpacked
/// [undefined-behavior]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
-/// [unsafe-unpin]: ./attr.pin_project.html#unsafeunpin
+/// [unsafe-unpin]: macro@pin_project#unsafeunpin
#[proc_macro_attribute]
pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
pin_project::attribute(&args.into(), input.into()).into()
}
-/// An attribute for annotating an impl block that implements [`Drop`].
+/// An attribute used for custom implementations of [`Drop`].
///
-/// This attribute is only needed when you wish to provide a [`Drop`]
-/// impl for your type.
+/// This attribute is used in conjunction with the `PinnedDrop` argument to
+/// the [`#[pin_project]`][macro@pin_project] attribute.
///
-/// This impl block acts just like a normal [`Drop`] impl,
-/// except for the following two:
+/// The impl block annotated with this attribute acts just like a normal
+/// [`Drop`] impl, except for the following two:
///
/// * `drop` method takes [`Pin`]`<&mut Self>`
/// * Name of the trait is `PinnedDrop`.
@@ -475,27 +509,27 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
/// In particular, it will never be called more than once, just like
/// [`Drop::drop`].
///
-/// # Example
+/// # Examples
///
/// ```rust
/// use pin_project::{pin_project, pinned_drop};
/// use std::pin::Pin;
///
/// #[pin_project(PinnedDrop)]
-/// struct Foo {
+/// struct PrintOnDrop {
/// #[pin]
/// field: u8,
/// }
///
/// #[pinned_drop]
-/// impl PinnedDrop for Foo {
+/// impl PinnedDrop for PrintOnDrop {
/// fn drop(self: Pin<&mut Self>) {
/// println!("Dropping: {}", self.field);
/// }
/// }
///
/// fn main() {
-/// let _x = Foo { field: 50 };
+/// let _x = PrintOnDrop { field: 50 };
/// }
/// ```
///
@@ -504,13 +538,13 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
/// # 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
-/// [double dropping is unsound](https://github.com/rust-lang/rust/pull/62360).
+/// This is because destructors can be called multiple times in safe code and
+/// [double dropping is unsound][rust-lang/rust#62360].
///
/// 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,
+/// this crate prevent users from calling `PinnedDrop::drop` in safe code.
///
/// ```rust
/// # use std::pin::Pin;
@@ -523,244 +557,16 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream {
/// Also by using the [`drop`] function just like dropping a type that directly
/// implements [`Drop`], can drop safely a type that implements `PinnedDrop`.
///
+/// [rust-lang/rust#62360]: https://github.com/rust-lang/rust/pull/62360
/// [`Pin`]: core::pin::Pin
-/// [pinned-drop]: ./attr.pin_project.html#pinned_drop
+/// [pinned-drop]: macro@pin_project#pinned_drop
#[proc_macro_attribute]
pub fn pinned_drop(args: TokenStream, input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input);
pinned_drop::attribute(&args.into(), input).into()
}
-/// (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
-///
-/// *The attribute at the expression position is not stable, so you need to use
-/// a dummy `#[project]` attribute for the function.*
-///
-/// ## Examples
-///
-/// ```rust
-/// # #![allow(deprecated)]
-/// use pin_project::{pin_project, project};
-/// use std::pin::Pin;
-///
-/// #[pin_project]
-/// struct Foo<T, U> {
-/// #[pin]
-/// future: T,
-/// field: U,
-/// }
-///
-/// impl<T, U> Foo<T, U> {
-/// #[project] // Nightly does not need a dummy attribute to the function.
-/// fn baz(self: Pin<&mut Self>) {
-/// #[project]
-/// let Foo { future, field } = self.project();
-///
-/// let _: Pin<&mut T> = future;
-/// let _: &mut U = field;
-/// }
-/// }
-/// ```
-///
-/// # `match` expressions
-///
-/// *The attribute at the expression position is not stable, so you need to use
-/// a dummy `#[project]` attribute for the function.*
-///
-/// ## Examples
-///
-/// ```rust
-/// # #![allow(deprecated)]
-/// use pin_project::{pin_project, project};
-/// use std::pin::Pin;
-///
-/// #[pin_project]
-/// enum Enum<A, B, C> {
-/// Tuple(#[pin] A, B),
-/// Struct { field: C },
-/// Unit,
-/// }
-///
-/// 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() {
-/// Enum::Tuple(x, y) => {
-/// let _: Pin<&mut A> = x;
-/// let _: &mut B = y;
-/// }
-/// Enum::Struct { field } => {
-/// let _: &mut C = field;
-/// }
-/// Enum::Unit => {}
-/// }
-/// }
-/// }
-/// ```
-///
-/// # `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
-/// original type, you need to create another (non-`#[project]`) `impl` block.
-///
-/// To call a method implemented in `#[project] impl` block, you need to first
-/// get the projected-type with `let this = self.project();`.
-///
-/// ## Examples
-///
-/// ```rust
-/// # #![allow(deprecated)]
-/// use pin_project::{pin_project, project};
-/// use std::pin::Pin;
-///
-/// #[pin_project]
-/// struct Foo<T, U> {
-/// #[pin]
-/// future: T,
-/// field: U,
-/// }
-///
-/// // impl for the original type
-/// impl<T, U> Foo<T, U> {
-/// fn bar(self: Pin<&mut Self>) {
-/// self.project().baz()
-/// }
-/// }
-///
-/// // impl for the projected type
-/// #[project]
-/// impl<T, U> Foo<T, U> {
-/// fn baz(self) {
-/// let Self { future, field } = self;
-///
-/// let _: Pin<&mut T> = future;
-/// let _: &mut U = field;
-/// }
-/// }
-/// ```
-///
-/// # `use` statements
-///
-/// ## Examples
-///
-/// ```rust
-/// # #![allow(deprecated)]
-/// # mod dox {
-/// use pin_project::pin_project;
-///
-/// #[pin_project]
-/// struct Foo<A> {
-/// #[pin]
-/// field: A,
-/// }
-///
-/// mod bar {
-/// use super::Foo;
-/// use pin_project::project;
-/// use std::pin::Pin;
-///
-/// #[project]
-/// use super::Foo;
-///
-/// #[project]
-/// fn baz<A>(foo: Pin<&mut Foo<A>>) {
-/// #[project]
-/// let Foo { field } = foo.project();
-/// let _: Pin<&mut A> = field;
-/// }
-/// }
-/// # }
-/// ```
-///
-/// [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, ProjKind::Mutable).into()
-}
-
-/// (deprecated) An attribute to provide way to refer to the projected type returned by
-/// `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**
-///
-/// 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, ProjKind::Immutable).into()
-}
-
-/// (deprecated) An attribute to provide way to refer to the projected type returned by
-/// `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]`][`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, ProjKind::Owned).into()
-}
-
-// An internal helper macro. Not public API.
+// 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/args.rs b/src/pin_project/args.rs
new file mode 100644
index 0000000..38e43d4
--- /dev/null
+++ b/src/pin_project/args.rs
@@ -0,0 +1,257 @@
+use proc_macro2::{Span, TokenStream};
+use quote::quote;
+use syn::{
+ parse::{Parse, ParseStream},
+ spanned::Spanned,
+ *,
+};
+
+use super::PIN;
+use crate::utils::{ParseBufferExt, SliceExt};
+
+pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> {
+ // `(__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
+ }
+ })()))
+ }
+ }
+
+ 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] attribute"))
+ } else {
+ // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`.
+ syn::parse2(prev.1.unwrap())
+ }
+}
+
+pub(super) struct Args {
+ /// `PinnedDrop` argument.
+ pub(super) pinned_drop: Option<Span>,
+ /// `UnsafeUnpin` or `!Unpin` argument.
+ pub(super) unpin_impl: UnpinImpl,
+ /// `project = <ident>` argument.
+ pub(super) project: Option<Ident>,
+ /// `project_ref = <ident>` argument.
+ pub(super) project_ref: Option<Ident>,
+ /// `project_replace [= <ident>]` argument.
+ pub(super) project_replace: ProjReplace,
+}
+
+impl Parse for Args {
+ fn parse(input: ParseStream<'_>) -> Result<Self> {
+ mod kw {
+ syn::custom_keyword!(Unpin);
+ }
+
+ /// 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));
+ }
+ 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((value, span))
+ }
+ }
+
+ let mut pinned_drop = None;
+ let mut unsafe_unpin = None;
+ let mut not_unpin = None;
+ let mut project = None;
+ let mut project_ref = None;
+ let mut project_replace_value = None;
+ let mut project_replace_span = None;
+
+ while !input.is_empty() {
+ 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" => {
+ 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" => {
+ project = Some(parse_value(input, &token, project.is_some())?.0);
+ }
+ "project_ref" => {
+ project_ref = Some(parse_value(input, &token, project_ref.is_some())?.0);
+ }
+ "project_replace" => {
+ 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" => {
+ return Err(error!(
+ token,
+ "`Replace` argument was removed, use `project_replace` argument instead"
+ ));
+ }
+ _ => return Err(error!(token, "unexpected argument: {}", token)),
+ }
+ }
+
+ if input.is_empty() {
+ break;
+ }
+ let _: Token![,] = input.parse()?;
+ }
+
+ 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",
+ ));
+ }
+ }
+ let project_replace = match (project_replace_span, project_replace_value) {
+ (None, _) => ProjReplace::None,
+ (Some(span), Some(ident)) => ProjReplace::Named { ident, span },
+ (Some(span), None) => 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",
+ ));
+ }
+ };
+
+ Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace })
+ }
+}
+
+/// `UnsafeUnpin` or `!Unpin` argument.
+#[derive(Clone, Copy)]
+pub(super) enum UnpinImpl {
+ Default,
+ /// `UnsafeUnpin`.
+ Unsafe(Span),
+ /// `!Unpin`.
+ Negative(Span),
+}
+
+/// `project_replace [= <ident>]` argument.
+pub(super) enum ProjReplace {
+ None,
+ /// `project_replace`.
+ Unnamed {
+ span: Span,
+ },
+ /// `project_replace = <ident>`.
+ Named {
+ span: Span,
+ ident: Ident,
+ },
+}
+
+impl ProjReplace {
+ /// Return the span of this argument.
+ pub(super) fn span(&self) -> Option<Span> {
+ match self {
+ ProjReplace::None => None,
+ ProjReplace::Named { span, .. } | ProjReplace::Unnamed { span, .. } => Some(*span),
+ }
+ }
+
+ pub(super) fn ident(&self) -> Option<&Ident> {
+ if let ProjReplace::Named { ident, .. } = self { Some(ident) } else { None }
+ }
+}
diff --git a/src/pin_project/derive.rs b/src/pin_project/derive.rs
index 56a3d47..1a598b7 100644
--- a/src/pin_project/derive.rs
+++ b/src/pin_project/derive.rs
@@ -1,21 +1,22 @@
use proc_macro2::{Delimiter, Group, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
-use syn::{
- parse::{Parse, ParseStream},
- spanned::Spanned,
- visit_mut::VisitMut,
- *,
-};
+use syn::{visit_mut::VisitMut, *};
-use super::PIN;
+use super::{
+ args::{parse_args, Args, ProjReplace, UnpinImpl},
+ PIN,
+};
use crate::utils::{
- determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ParseBufferExt,
- ProjKind, ReplaceReceiver, SliceExt, Variants,
+ determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ReplaceReceiver,
+ SliceExt, Variants,
};
pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> {
let mut input: DeriveInput = syn::parse2(input)?;
+ let mut cx;
+ let mut generate = GenerateTokens::default();
+
let ident = &input.ident;
let ty_generics = input.generics.split_for_impl().1;
let self_ty = parse_quote!(#ident #ty_generics);
@@ -23,20 +24,14 @@ pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> {
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 &input.data {
+ match &input.data {
Data::Struct(data) => {
- // Do this first for a better error message.
- packed_check = Some(cx.ensure_not_packed(&data.fields)?);
- cx.parse_struct(data)?
+ cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Struct)?;
+ parse_struct(&mut cx, &data.fields, &mut generate)?;
}
Data::Enum(data) => {
- // We don't need to check for `#[repr(packed)]`,
- // since it does not apply to enums.
- packed_check = None;
- cx.parse_enum(data)?
+ cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Enum)?;
+ parse_enum(&mut cx, data, &mut generate)?;
}
Data::Union(_) => {
return Err(error!(
@@ -44,371 +39,101 @@ pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> {
"#[pin_project] attribute may only be used on structs or enums"
));
}
- };
-
- let unpin_impl = cx.make_unpin_impl();
- let drop_impl = cx.make_drop_impl();
- 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.
- // However, this prevents Rustdoc from displaying docs for any
- // of our types. In particular, users cannot see the
- // automatically generated `Unpin` impl for the '__UnpinStruct' types
- //
- // Previously, we provided a flag to correctly document the
- // automatically generated `Unpin` impl by using def-site hygiene,
- // but it is now removed.
- //
- // Refs:
- // * https://github.com/rust-lang/rust/issues/63281
- // * https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867
- // * https://github.com/taiki-e/pin-project/pull/70
- #[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
- #drop_impl
- #packed_check
- };
- });
- Ok(items)
-}
-
-fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> {
- if fields.is_empty() {
- let msg = "#[pin_project] attribute may not be used on structs with zero fields";
- if let Fields::Unit = fields { Err(error!(ident, msg)) } else { Err(error!(fields, msg)) }
- } else {
- Ok(())
}
-}
-fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> {
- if variants.is_empty() {
- return Err(Error::new(
- brace_token.span,
- "#[pin_project] attribute may not be used on enums without variants",
- ));
- }
- let has_field = variants.iter().try_fold(false, |has_field, v| {
- if let Some((_, e)) = &v.discriminant {
- Err(error!(e, "#[pin_project] attribute may not be used on enums with discriminants"))
- } else if let Some(attr) = v.attrs.find(PIN) {
- Err(error!(attr, "#[pin] attribute may only be used on fields of structs or variants"))
- } else if v.fields.is_empty() {
- Ok(has_field)
- } else {
- Ok(true)
- }
- })?;
- if has_field {
- Ok(())
- } else {
- Err(error!(variants, "#[pin_project] attribute may not be used on enums with zero fields"))
- }
+ Ok(generate.into_tokens(&cx))
}
-struct Args {
- /// `PinnedDrop` argument.
- pinned_drop: Option<Span>,
- /// `UnsafeUnpin` or `!Unpin` argument.
- unpin_impl: UnpinImpl,
- /// `project = <ident>` argument.
- project: Option<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>`.
- Named {
- span: Span,
- ident: Ident,
- },
+#[derive(Default)]
+struct GenerateTokens {
+ exposed: TokenStream,
+ scoped: TokenStream,
}
-impl ProjReplace {
- fn span(&self) -> Option<Span> {
- match self {
- ProjReplace::None => None,
- ProjReplace::Named { span, .. } | ProjReplace::Unnamed { span, .. } => Some(*span),
+impl GenerateTokens {
+ fn extend(&mut self, expose: bool, tokens: TokenStream) {
+ if expose {
+ self.exposed.extend(tokens);
+ } else {
+ self.scoped.extend(tokens);
}
}
- fn ident(&self) -> Option<&Ident> {
- if let ProjReplace::Named { ident, .. } = self { Some(ident) } else { None }
- }
-}
+ fn into_tokens(self, cx: &Context<'_>) -> TokenStream {
+ let mut tokens = self.exposed;
+ let scoped = self.scoped;
-const DUPLICATE_PIN: &str = "duplicate #[pin] attribute";
-
-impl Args {
- fn get(attrs: &[Attribute]) -> Result<Self> {
- // `(__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
- }
- })()))
- }
- }
+ let unpin_impl = make_unpin_impl(cx);
+ let drop_impl = make_drop_impl(cx);
+ let allowed_lints = global_allowed_lints();
- 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,
+ tokens.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.
+ // However, this prevents Rustdoc from displaying docs for any
+ // of our types. In particular, users cannot see the
+ // automatically generated `Unpin` impl for the '__UnpinStruct' types
+ //
+ // Previously, we provided a flag to correctly document the
+ // automatically generated `Unpin` impl by using def-site hygiene,
+ // but it is now removed.
+ //
+ // Refs:
+ // * https://github.com/rust-lang/rust/issues/63281
+ // * https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867
+ // * https://github.com/taiki-e/pin-project/pull/70
+ #allowed_lints
+ #[allow(clippy::used_underscore_binding)]
+ const _: () = {
+ #scoped
+ #unpin_impl
+ #drop_impl
};
- Err(error!(span, DUPLICATE_PIN))
- } else {
- // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`.
- syn::parse2(prev.1.unwrap())
- }
+ });
+ tokens
}
}
-impl Parse for Args {
- fn parse(input: ParseStream<'_>) -> Result<Self> {
- mod kw {
- syn::custom_keyword!(Unpin);
- }
-
- // 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));
- }
- 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((value, span))
- }
- }
-
- let mut pinned_drop = None;
- let mut unsafe_unpin = None;
- let mut not_unpin = None;
- let mut project = None;
- let mut project_ref = 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![!]) {
- 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" => {
- 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" => {
- project = Some(parse_value(input, &token, project.is_some())?.0);
- }
- "project_ref" => {
- project_ref = Some(parse_value(input, &token, project_ref.is_some())?.0);
- }
- "project_replace" => {
- 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() {
- break;
- }
- let _: Token![,] = input.parse()?;
- }
-
- 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",
- ));
- }
- };
-
- Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace })
+/// Returns attributes that should be applied to all generated code.
+fn global_allowed_lints() -> TokenStream {
+ quote! {
+ #[allow(box_pointers)] // This lint warns use of the `Box` type.
+ #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
+ #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
+ #[allow(unreachable_pub)] // This lint warns `pub` field in private struct.
+ #[allow(clippy::pattern_type_mismatch)]
+ #[allow(clippy::redundant_pub_crate)] // This lint warns `pub(crate)` field in private struct.
}
}
-struct OriginalType<'a> {
- /// Attributes of the original type.
- attrs: &'a [Attribute],
- /// Visibility of the original type.
- vis: &'a Visibility,
- /// Name of the original type.
- ident: &'a Ident,
- /// Generics of the original type.
- generics: &'a Generics,
-}
-
-struct ProjectedType {
- /// Visibility of the projected types.
- vis: Visibility,
- /// Name of the projected type returned by `project` method.
- mut_ident: Ident,
- /// Name of the projected type returned by `project_ref` method.
- ref_ident: Ident,
- /// Name of the projected type returned by `project_replace` method.
- own_ident: Ident,
- /// Lifetime on the generated projected types.
- lifetime: Lifetime,
- /// Generics of the projected types.
- generics: Generics,
- /// `where` clause of the projected types. This has an additional
- /// bound generated by `insert_lifetime_and_bound`
- where_clause: WhereClause,
-}
-
-struct ProjectedVariants {
- proj_variants: TokenStream,
- proj_ref_variants: TokenStream,
- proj_own_variants: TokenStream,
- proj_arms: TokenStream,
- proj_ref_arms: TokenStream,
- proj_own_arms: TokenStream,
-}
-
-#[derive(Default)]
-struct ProjectedFields {
- proj_pat: TokenStream,
- proj_body: TokenStream,
- proj_own_body: TokenStream,
- proj_fields: TokenStream,
- proj_ref_fields: TokenStream,
- proj_own_fields: TokenStream,
+/// Returns attributes used on projected types.
+fn proj_allowed_lints(kind: TypeKind) -> (TokenStream, TokenStream, TokenStream) {
+ let large_enum_variant = if kind == Enum {
+ Some(quote! {
+ #[allow(variant_size_differences)]
+ #[allow(clippy::large_enum_variant)]
+ })
+ } else {
+ None
+ };
+ let global_allowed_lints = global_allowed_lints();
+ let proj_mut = quote! {
+ #[allow(dead_code)] // This lint warns unused fields/variants.
+ #[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}
+ #global_allowed_lints
+ };
+ let proj_ref = quote! {
+ #[allow(dead_code)] // This lint warns unused fields/variants.
+ #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326
+ #global_allowed_lints
+ };
+ let proj_own = quote! {
+ #[allow(dead_code)] // This lint warns unused fields/variants.
+ #large_enum_variant
+ #global_allowed_lints
+ };
+ (proj_mut, proj_ref, proj_own)
}
struct Context<'a> {
@@ -418,6 +143,8 @@ struct Context<'a> {
proj: ProjectedType,
/// Types of the pinned fields.
pinned_fields: Vec<Type>,
+ /// Kind of the original type: struct or enum
+ kind: TypeKind,
/// `PinnedDrop` argument.
pinned_drop: Option<Span>,
@@ -427,28 +154,28 @@ struct Context<'a> {
project: bool,
/// `project_ref` argument.
project_ref: bool,
- /// `project_replace [= <ident>]` or `Replace` argument.
+ /// `project_replace [= <ident>]` argument.
project_replace: ProjReplace,
}
-#[derive(Clone, Copy)]
-enum UnpinImpl {
- Default,
- /// `UnsafeUnpin`.
- Unsafe(Span),
- /// `!Unpin`.
- Negative(Span),
-}
-
impl<'a> Context<'a> {
fn new(
attrs: &'a [Attribute],
vis: &'a Visibility,
ident: &'a Ident,
generics: &'a mut Generics,
+ kind: TypeKind,
) -> Result<Self> {
let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } =
- Args::get(attrs)?;
+ parse_args(attrs)?;
+
+ if let Some(name) = [project.as_ref(), project_ref.as_ref(), project_replace.ident()]
+ .iter()
+ .filter_map(Option::as_ref)
+ .find(|name| **name == ident)
+ {
+ return Err(error!(name, "name `{}` is the same as the original type name", name));
+ }
let mut lifetime_name = String::from("'pin");
determine_lifetime_name(&mut lifetime_name, generics);
@@ -466,10 +193,13 @@ impl<'a> Context<'a> {
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));
+ let own_ident = project_replace
+ .ident()
+ .cloned()
+ .unwrap_or_else(|| format_ident!("__{}ProjectionOwned", ident));
Ok(Self {
+ kind,
pinned_drop,
unpin_impl,
project: project.is_some(),
@@ -477,8 +207,8 @@ impl<'a> Context<'a> {
project_replace,
proj: ProjectedType {
vis: determine_visibility(vis),
- mut_ident: project.unwrap_or_else(|| ProjKind::Mutable.proj_ident(ident)),
- ref_ident: project_ref.unwrap_or_else(|| ProjKind::Immutable.proj_ident(ident)),
+ mut_ident: project.unwrap_or_else(|| format_ident!("__{}Projection", ident)),
+ ref_ident: project_ref.unwrap_or_else(|| format_ident!("__{}ProjectionRef", ident)),
own_ident,
lifetime,
generics: proj_generics,
@@ -488,739 +218,868 @@ impl<'a> Context<'a> {
pinned_fields: Vec::new(),
})
}
+}
- /// 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)
- }
+#[derive(Copy, Clone, Eq, PartialEq)]
+enum TypeKind {
+ Enum,
+ Struct,
+}
- fn parse_struct(
- &mut self,
- DataStruct { fields, .. }: &DataStruct,
- ) -> Result<(TokenStream, TokenStream)> {
- validate_struct(self.orig.ident, fields)?;
+use TypeKind::{Enum, Struct};
- let ProjectedFields {
- proj_pat,
- proj_body,
- proj_fields,
- proj_ref_fields,
- proj_own_fields,
- proj_own_body,
- } = match fields {
- Fields::Named(_) => self.visit_fields(None, fields, Delimiter::Brace)?,
- Fields::Unnamed(_) => self.visit_fields(None, fields, Delimiter::Parenthesis)?,
- Fields::Unit => unreachable!(),
- };
+struct OriginalType<'a> {
+ /// Attributes of the original type.
+ attrs: &'a [Attribute],
+ /// Visibility of the original type.
+ vis: &'a Visibility,
+ /// Name of the original type.
+ ident: &'a Ident,
+ /// Generics of the original type.
+ generics: &'a Generics,
+}
- let proj_ident = &self.proj.mut_ident;
- let proj_ref_ident = &self.proj.ref_ident;
- let proj_own_ident = &self.proj.own_ident;
- let vis = &self.proj.vis;
- let mut orig_generics = self.orig.generics.clone();
- let orig_where_clause = orig_generics.where_clause.take();
- let proj_generics = &self.proj.generics;
- 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!(#proj_where_clause #proj_fields),
- quote!(#proj_where_clause #proj_ref_fields),
- quote!(#orig_where_clause #proj_own_fields),
- ),
- Fields::Unnamed(_) => (
- quote!(#proj_fields #proj_where_clause;),
- quote!(#proj_ref_fields #proj_where_clause;),
- quote!(#proj_own_fields #orig_where_clause;),
- ),
- Fields::Unit => unreachable!(),
- };
+struct ProjectedType {
+ /// Visibility of the projected types.
+ vis: Visibility,
+ /// Name of the projected type returned by `project` method.
+ mut_ident: Ident,
+ /// Name of the projected type returned by `project_ref` method.
+ ref_ident: Ident,
+ /// Name of the projected type returned by `project_replace` method.
+ own_ident: Ident,
+ /// Lifetime on the generated projected types.
+ lifetime: Lifetime,
+ /// Generics of the projected types.
+ generics: Generics,
+ /// `where` clause of the projected types. This has an additional
+ /// bound generated by `insert_lifetime_and_bound`
+ where_clause: WhereClause,
+}
- let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs();
- let mut proj_items = quote! {
- #proj_attrs
- #vis struct #proj_ident #proj_generics #where_clause_fields
- #proj_ref_attrs
- #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields
- };
- if self.project_replace.span().is_some() {
- proj_items.extend(quote! {
- #proj_own_attrs
- #vis struct #proj_own_ident #orig_generics #where_clause_own_fields
- });
+struct ProjectedVariants {
+ proj_variants: TokenStream,
+ proj_ref_variants: TokenStream,
+ proj_own_variants: TokenStream,
+ proj_arms: TokenStream,
+ proj_ref_arms: TokenStream,
+ proj_own_arms: TokenStream,
+}
+
+#[derive(Default)]
+struct ProjectedFields {
+ proj_pat: TokenStream,
+ proj_body: TokenStream,
+ proj_own_body: TokenStream,
+ proj_fields: TokenStream,
+ proj_ref_fields: TokenStream,
+ proj_own_fields: TokenStream,
+}
+
+fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> {
+ if fields.is_empty() {
+ let msg = "#[pin_project] attribute may not be used on structs with zero fields";
+ if let Fields::Unit = fields { Err(error!(ident, msg)) } else { Err(error!(fields, msg)) }
+ } else {
+ Ok(())
+ }
+}
+
+fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> {
+ if variants.is_empty() {
+ return Err(Error::new(
+ brace_token.span,
+ "#[pin_project] attribute may not be used on enums without variants",
+ ));
+ }
+ let has_field = variants.iter().try_fold(false, |has_field, v| {
+ if let Some((_, e)) = &v.discriminant {
+ Err(error!(e, "#[pin_project] attribute may not be used on enums with discriminants"))
+ } else if let Some(attr) = v.attrs.find(PIN) {
+ Err(error!(attr, "#[pin] attribute may only be used on fields of structs or variants"))
+ } else if v.fields.is_empty() {
+ Ok(has_field)
+ } else {
+ Ok(true)
}
+ })?;
+ if has_field {
+ Ok(())
+ } else {
+ Err(error!(variants, "#[pin_project] attribute may not be used on enums with zero fields"))
+ }
+}
- let proj_mut_body = quote! {
- let Self #proj_pat = self.get_unchecked_mut();
- #proj_ident #proj_body
- };
- let proj_ref_body = quote! {
- let Self #proj_pat = self.get_ref();
- #proj_ref_ident #proj_body
- };
- let proj_own_body = quote! {
- let __self_ptr: *mut Self = self.get_unchecked_mut();
- let Self #proj_pat = &mut *__self_ptr;
- #proj_own_body
- };
- let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body);
+fn parse_struct(
+ cx: &mut Context<'_>,
+ fields: &Fields,
+ generate: &mut GenerateTokens,
+) -> Result<()> {
+ // Do this first for a better error message.
+ let packed_check = ensure_not_packed(&cx.orig, fields)?;
+
+ validate_struct(cx.orig.ident, fields)?;
+
+ let ProjectedFields {
+ proj_pat,
+ proj_body,
+ proj_fields,
+ proj_ref_fields,
+ proj_own_fields,
+ proj_own_body,
+ } = match fields {
+ Fields::Named(_) => visit_fields(cx, None, fields, Delimiter::Brace)?,
+ Fields::Unnamed(_) => visit_fields(cx, None, fields, Delimiter::Parenthesis)?,
+ Fields::Unit => unreachable!(),
+ };
- Ok((proj_items, proj_impl))
+ let proj_ident = &cx.proj.mut_ident;
+ let proj_ref_ident = &cx.proj.ref_ident;
+ let proj_own_ident = &cx.proj.own_ident;
+ let vis = &cx.proj.vis;
+ let mut orig_generics = cx.orig.generics.clone();
+ let orig_where_clause = orig_generics.where_clause.take();
+ let proj_generics = &cx.proj.generics;
+ let proj_where_clause = &cx.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!(#proj_where_clause #proj_fields),
+ quote!(#proj_where_clause #proj_ref_fields),
+ quote!(#orig_where_clause #proj_own_fields),
+ ),
+ Fields::Unnamed(_) => (
+ quote!(#proj_fields #proj_where_clause;),
+ quote!(#proj_ref_fields #proj_where_clause;),
+ quote!(#proj_own_fields #orig_where_clause;),
+ ),
+ Fields::Unit => unreachable!(),
+ };
+
+ let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx.kind);
+ generate.extend(cx.project, quote! {
+ #proj_attrs
+ #vis struct #proj_ident #proj_generics #where_clause_fields
+ });
+ generate.extend(cx.project_ref, quote! {
+ #proj_ref_attrs
+ #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields
+ });
+ if cx.project_replace.span().is_some() {
+ generate.extend(cx.project_replace.ident().is_some(), quote! {
+ #proj_own_attrs
+ #vis struct #proj_own_ident #orig_generics #where_clause_own_fields
+ });
+ }
+
+ let proj_mut_body = quote! {
+ let Self #proj_pat = self.get_unchecked_mut();
+ #proj_ident #proj_body
+ };
+ let proj_ref_body = quote! {
+ let Self #proj_pat = self.get_ref();
+ #proj_ref_ident #proj_body
+ };
+ let proj_own_body = quote! {
+ let __self_ptr: *mut Self = self.get_unchecked_mut();
+ let Self #proj_pat = &mut *__self_ptr;
+ #proj_own_body
+ };
+ generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body));
+
+ generate.extend(false, packed_check);
+ Ok(())
+}
+
+fn parse_enum(
+ cx: &mut Context<'_>,
+ DataEnum { brace_token, variants, .. }: &DataEnum,
+ generate: &mut GenerateTokens,
+) -> Result<()> {
+ if let ProjReplace::Unnamed { span } = &cx.project_replace {
+ return Err(Error::new(
+ *span,
+ "`project_replace` argument requires a value when used on enums",
+ ));
}
- fn parse_enum(
- &mut self,
- DataEnum { brace_token, variants, .. }: &DataEnum,
- ) -> Result<(TokenStream, TokenStream)> {
- validate_enum(*brace_token, variants)?;
-
- let ProjectedVariants {
- proj_variants,
- proj_ref_variants,
- proj_own_variants,
- proj_arms,
- proj_ref_arms,
- proj_own_arms,
- } = self.visit_variants(variants)?;
-
- let proj_ident = &self.proj.mut_ident;
- let proj_ref_ident = &self.proj.ref_ident;
- let proj_own_ident = &self.proj.own_ident;
- let vis = &self.proj.vis;
- let mut orig_generics = self.orig.generics.clone();
- let orig_where_clause = orig_generics.where_clause.take();
- let proj_generics = &self.proj.generics;
- let proj_where_clause = &self.proj.where_clause;
-
- let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs();
- let mut proj_items = quote! {
+ // We don't need to check for `#[repr(packed)]`,
+ // since it does not apply to enums.
+
+ validate_enum(*brace_token, variants)?;
+
+ let ProjectedVariants {
+ proj_variants,
+ proj_ref_variants,
+ proj_own_variants,
+ proj_arms,
+ proj_ref_arms,
+ proj_own_arms,
+ } = visit_variants(cx, variants)?;
+
+ let proj_ident = &cx.proj.mut_ident;
+ let proj_ref_ident = &cx.proj.ref_ident;
+ let proj_own_ident = &cx.proj.own_ident;
+ let vis = &cx.proj.vis;
+ let mut orig_generics = cx.orig.generics.clone();
+ let orig_where_clause = orig_generics.where_clause.take();
+ let proj_generics = &cx.proj.generics;
+ let proj_where_clause = &cx.proj.where_clause;
+
+ let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx.kind);
+ if cx.project {
+ generate.extend(true, quote! {
#proj_attrs
#vis enum #proj_ident #proj_generics #proj_where_clause {
#proj_variants
}
+ });
+ }
+ if cx.project_ref {
+ generate.extend(true, quote! {
#proj_ref_attrs
#vis enum #proj_ref_ident #proj_generics #proj_where_clause {
#proj_ref_variants
}
- };
- if self.project_replace.span().is_some() {
- proj_items.extend(quote! {
- #proj_own_attrs
- #vis enum #proj_own_ident #orig_generics #orig_where_clause {
- #proj_own_variants
- }
- });
+ });
+ }
+ if cx.project_replace.ident().is_some() {
+ generate.extend(true, quote! {
+ #proj_own_attrs
+ #vis enum #proj_own_ident #orig_generics #orig_where_clause {
+ #proj_own_variants
+ }
+ });
+ }
+
+ let proj_mut_body = quote! {
+ match self.get_unchecked_mut() {
+ #proj_arms
}
+ };
+ let proj_ref_body = quote! {
+ match self.get_ref() {
+ #proj_ref_arms
+ }
+ };
+ let proj_own_body = quote! {
+ let __self_ptr: *mut Self = self.get_unchecked_mut();
+ match &mut *__self_ptr {
+ #proj_own_arms
+ }
+ };
+ generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body));
- let proj_mut_body = quote! {
- match self.get_unchecked_mut() {
- #proj_arms
- }
+ Ok(())
+}
+
+fn visit_variants(cx: &mut Context<'_>, variants: &Variants) -> Result<ProjectedVariants> {
+ let mut proj_variants = TokenStream::new();
+ let mut proj_ref_variants = TokenStream::new();
+ let mut proj_own_variants = TokenStream::new();
+ let mut proj_arms = TokenStream::new();
+ let mut proj_ref_arms = TokenStream::new();
+ let mut proj_own_arms = TokenStream::new();
+
+ for Variant { ident, fields, .. } in variants {
+ let ProjectedFields {
+ proj_pat,
+ proj_body,
+ proj_fields,
+ proj_ref_fields,
+ proj_own_fields,
+ proj_own_body,
+ } = match fields {
+ Fields::Named(_) => visit_fields(cx, Some(ident), fields, Delimiter::Brace)?,
+ Fields::Unnamed(_) => visit_fields(cx, Some(ident), fields, Delimiter::Parenthesis)?,
+ Fields::Unit => ProjectedFields {
+ proj_own_body: proj_own_body(cx, Some(ident), None, &[]),
+ ..ProjectedFields::default()
+ },
};
- let proj_ref_body = quote! {
- match self.get_ref() {
- #proj_ref_arms
+
+ let orig_ident = cx.orig.ident;
+ let proj_ident = &cx.proj.mut_ident;
+ let proj_ref_ident = &cx.proj.ref_ident;
+ proj_variants.extend(quote! {
+ #ident #proj_fields,
+ });
+ proj_ref_variants.extend(quote! {
+ #ident #proj_ref_fields,
+ });
+ proj_own_variants.extend(quote! {
+ #ident #proj_own_fields,
+ });
+ proj_arms.extend(quote! {
+ #orig_ident::#ident #proj_pat => {
+ #proj_ident::#ident #proj_body
}
- };
- let proj_own_body = quote! {
- let __self_ptr: *mut Self = self.get_unchecked_mut();
- match &mut *__self_ptr {
- #proj_own_arms
+ });
+ proj_ref_arms.extend(quote! {
+ #orig_ident::#ident #proj_pat => {
+ #proj_ref_ident::#ident #proj_body
}
- };
- let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body);
-
- Ok((proj_items, proj_impl))
+ });
+ proj_own_arms.extend(quote! {
+ #orig_ident::#ident #proj_pat => {
+ #proj_own_body
+ }
+ });
}
- fn visit_variants(&mut self, variants: &Variants) -> Result<ProjectedVariants> {
- let mut proj_variants = TokenStream::new();
- let mut proj_ref_variants = TokenStream::new();
- let mut proj_own_variants = TokenStream::new();
- let mut proj_arms = TokenStream::new();
- let mut proj_ref_arms = TokenStream::new();
- let mut proj_own_arms = TokenStream::new();
-
- for Variant { ident, fields, .. } in variants {
- let ProjectedFields {
- proj_pat,
- proj_body,
- proj_fields,
- proj_ref_fields,
- proj_own_fields,
- proj_own_body,
- } = match fields {
- 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()
- },
- };
+ Ok(ProjectedVariants {
+ proj_variants,
+ proj_ref_variants,
+ proj_own_variants,
+ proj_arms,
+ proj_ref_arms,
+ proj_own_arms,
+ })
+}
- let orig_ident = self.orig.ident;
- let proj_ident = &self.proj.mut_ident;
- let proj_ref_ident = &self.proj.ref_ident;
- proj_variants.extend(quote! {
- #ident #proj_fields,
+fn visit_fields(
+ cx: &mut Context<'_>,
+ variant_ident: Option<&Ident>,
+ fields: &Fields,
+ delim: Delimiter,
+) -> Result<ProjectedFields> {
+ 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() {
+ let lifetime = &cx.proj.lifetime;
+ proj_fields.extend(quote! {
+ #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>,
});
- proj_ref_variants.extend(quote! {
- #ident #proj_ref_fields,
+ proj_ref_fields.extend(quote! {
+ #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>,
});
- proj_own_variants.extend(quote! {
- #ident #proj_own_fields,
+ proj_own_fields.extend(quote! {
+ #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>,
});
- proj_arms.extend(quote! {
- #orig_ident::#ident #proj_pat => {
- #proj_ident::#ident #proj_body
- }
+ proj_body.extend(quote! {
+ #ident #colon_token ::pin_project::__private::Pin::new_unchecked(#binding),
});
- proj_ref_arms.extend(quote! {
- #orig_ident::#ident #proj_pat => {
- #proj_ref_ident::#ident #proj_body
- }
+ proj_move.extend(quote! {
+ #ident #colon_token ::pin_project::__private::PhantomData,
});
- proj_own_arms.extend(quote! {
- #orig_ident::#ident #proj_pat => {
- #proj_own_body
- }
+
+ cx.pinned_fields.push(ty.clone());
+ pinned_bindings.push(binding);
+ } else {
+ let lifetime = &cx.proj.lifetime;
+ 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),
});
}
-
- Ok(ProjectedVariants {
- proj_variants,
- proj_ref_variants,
- proj_own_variants,
- proj_arms,
- proj_ref_arms,
- proj_own_arms,
- })
}
- fn visit_fields(
- &mut self,
- variant_ident: Option<&Ident>,
- fields: &Fields,
- delim: Delimiter,
- ) -> Result<ProjectedFields> {
- 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() {
- let lifetime = &self.proj.lifetime;
- 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.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),
- });
- }
- }
+ fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream {
+ Group::new(delim, tokens).into_token_stream()
+ }
- 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 = proj_own_body(cx, variant_ident, Some(proj_move), &pinned_bindings);
+
+ Ok(ProjectedFields {
+ proj_pat,
+ proj_body,
+ proj_own_body,
+ proj_fields,
+ proj_ref_fields,
+ proj_own_fields,
+ })
+}
- 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);
+/// Generates the processing that `project_replace` does for the struct or each variant.
+///
+/// Note: `pinned_fields` must be in declaration order.
+fn proj_own_body(
+ cx: &Context<'_>,
+ variant_ident: Option<&Ident>,
+ proj_move: Option<Group>,
+ pinned_fields: &[Ident],
+) -> TokenStream {
+ let ident = &cx.proj.own_ident;
+ let proj_own = match variant_ident {
+ Some(variant_ident) => quote!(#ident::#variant_ident),
+ None => quote!(#ident),
+ };
- let proj_move = Group::new(delim, proj_move);
- let proj_own_body = self.proj_own_body(variant_ident, Some(proj_move), &pinned_bindings);
+ // The fields of the struct and the active enum variant are dropped
+ // in declaration order.
+ // Refs: https://doc.rust-lang.org/reference/destructors.html
+ let pinned_fields = pinned_fields.iter().rev();
- Ok(ProjectedFields {
- proj_pat,
- proj_body,
- proj_own_body,
- proj_fields,
- proj_ref_fields,
- proj_own_fields,
- })
- }
+ quote! {
+ // First, extract all the unpinned fields.
+ let __result = #proj_own #proj_move;
- /// 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),
+ // 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),
};
- quote! {
- // First, extract all the unpinned fields
- let __result = #proj_own #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),
- };
-
- // 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
+ // 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);
+ )*
}
- }
- /// Creates `Unpin` implementation for original type.
- fn make_unpin_impl(&self) -> TokenStream {
- match self.unpin_impl {
- UnpinImpl::Unsafe(span) => {
- let mut proj_generics = self.proj.generics.clone();
- let orig_ident = self.orig.ident;
- let lifetime = &self.proj.lifetime;
-
- // 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_spanned! { span =>
- impl #impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics
- #where_clause
- {
- }
- }
- }
- UnpinImpl::Negative(span) => {
- let mut proj_generics = self.proj.generics.clone();
- let orig_ident = self.orig.ident;
- let lifetime = &self.proj.lifetime;
-
- proj_generics.make_where_clause().predicates.push(parse_quote! {
- ::pin_project::__private::Wrapper<
- #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();
- quote_spanned! { span =>
- impl #proj_impl_generics ::pin_project::__private::Unpin
- for #orig_ident #ty_generics
- #proj_where_clause
- {
- }
+ // Finally, return the result.
+ __result
+ }
+}
- // 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 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 => {
- let mut full_where_clause = self.orig.generics.where_clause.clone().unwrap();
-
- // Generate a field in our new struct for every
- // pinned field in the original type.
- let fields = self.pinned_fields.iter().enumerate().map(|(i, ty)| {
- let field_ident = format_ident!("__field{}", i);
- quote!(#field_ident: #ty)
- });
-
- // We could try to determine the subset of type parameters
- // and lifetimes that are actually used by the pinned fields
- // (as opposed to those only used by unpinned fields).
- // However, this would be tricky and error-prone, since
- // it's possible for users to create types that would alias
- // with generic parameters (e.g. 'struct T').
- //
- // Instead, we generate a use of every single type parameter
- // and lifetime used in the original struct. For type parameters,
- // we generate code like this:
- //
- // ```rust
- // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {}
- // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {}
- //
- // ...
- // _field: AlwaysUnpin<(A, B, C)>
- // ```
- //
- // This ensures that any unused type parameters
- // don't end up with `Unpin` bounds.
- let lifetime_fields = self.orig.generics.lifetimes().enumerate().map(
- |(i, LifetimeDef { lifetime, .. })| {
- let field_ident = format_ident!("__lifetime{}", i);
- quote!(#field_ident: &#lifetime ())
- },
- );
-
- let orig_ident = self.orig.ident;
- let struct_ident = format_ident!("__{}", orig_ident);
- let vis = self.orig.vis;
- let lifetime = &self.proj.lifetime;
- let type_params = self.orig.generics.type_params().map(|t| &t.ident);
- let proj_generics = &self.proj.generics;
- let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl();
- let (impl_generics, ty_generics, where_clause) =
- self.orig.generics.split_for_impl();
-
- full_where_clause.predicates.push(parse_quote! {
- #struct_ident #proj_ty_generics: ::pin_project::__private::Unpin
- });
-
- quote! {
- // This needs to have the same visibility as the original type,
- // due to the limitations of the 'public in private' error.
- //
- // Our goal is to implement the public trait `Unpin` for
- // a potentially public user type. Because of this, rust
- // requires that any types mentioned in the where clause of
- // our `Unpin` impl also be public. This means that our generated
- // `__UnpinStruct` type must also be public.
- // 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, (#(::pin_project::__private::PhantomData<#type_params>),*)
- >,
-
- #(#fields,)*
- #(#lifetime_fields,)*
- }
+/// Creates `Unpin` implementation for the original type.
+///
+/// The kind of `Unpin` impl generated depends on `unpin_impl` field:
+/// * `UnpinImpl::Unsafe` - Implements `Unpin` via `UnsafeUnpin` impl.
+/// * `UnpinImpl::Negative` - Generates `Unpin` impl with bounds that will never be true.
+/// * `UnpinImpl::Default` - Generates `Unpin` impl that requires `Unpin` for all pinned fields.
+fn make_unpin_impl(cx: &Context<'_>) -> TokenStream {
+ match cx.unpin_impl {
+ UnpinImpl::Unsafe(span) => {
+ let mut proj_generics = cx.proj.generics.clone();
+ let orig_ident = cx.orig.ident;
+ let lifetime = &cx.proj.lifetime;
+
+ // 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
+ });
- impl #proj_impl_generics ::pin_project::__private::Unpin
- for #orig_ident #ty_generics
- #full_where_clause
- {
- }
+ let (impl_generics, _, where_clause) = proj_generics.split_for_impl();
+ let ty_generics = cx.orig.generics.split_for_impl().1;
- // 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 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
- {
- }
+ quote_spanned! { span =>
+ impl #impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics
+ #where_clause
+ {
}
}
}
- }
+ UnpinImpl::Negative(span) => {
+ let mut proj_generics = cx.proj.generics.clone();
+ let orig_ident = cx.orig.ident;
+ let lifetime = &cx.proj.lifetime;
+
+ proj_generics.make_where_clause().predicates.push(parse_quote! {
+ ::pin_project::__private::Wrapper<
+ #lifetime, ::pin_project::__private::PhantomPinned
+ >: ::pin_project::__private::Unpin
+ });
- /// Creates `Drop` implementation for original type.
- fn make_drop_impl(&self) -> TokenStream {
- let ident = self.orig.ident;
- let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl();
+ let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl();
+ let ty_generics = cx.orig.generics.split_for_impl().1;
- 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
+ impl #proj_impl_generics ::pin_project::__private::Unpin
+ for #orig_ident #ty_generics
+ #proj_where_clause
+ {
+ }
+
+ // Generate 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 an `UnsafeUnpin`
+ // impl, they'll get a "conflicting implementations of trait" error when
+ // coherence checks are run.
+ #[doc(hidden)]
+ #unsafety impl #proj_impl_generics ::pin_project::UnsafeUnpin
+ for #orig_ident #ty_generics
+ #proj_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::__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*.
- #unsafety {
- ::pin_project::__private::PinnedDrop::drop(pinned_self);
- }
- }
}
}
- } else {
- // If the user does not provide a `PinnedDrop` impl,
- // we need to ensure that they don't provide a `Drop` impl of their
- // own.
- // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87
+ }
+ UnpinImpl::Default => {
+ let mut full_where_clause = cx.orig.generics.where_clause.clone().unwrap();
+
+ // Generate a field in our new struct for every
+ // pinned field in the original type.
+ let fields = cx.pinned_fields.iter().enumerate().map(|(i, ty)| {
+ let field_ident = format_ident!("__field{}", i);
+ quote!(#field_ident: #ty)
+ });
+
+ // We could try to determine the subset of type parameters
+ // and lifetimes that are actually used by the pinned fields
+ // (as opposed to those only used by unpinned fields).
+ // However, this would be tricky and error-prone, since
+ // it's possible for users to create types that would alias
+ // with generic parameters (e.g. 'struct T').
+ //
+ // Instead, we generate a use of every single type parameter
+ // and lifetime used in the original struct. For type parameters,
+ // we generate code like this:
//
- // We create a new identifier for each struct, so that the traits
- // for different types do not conflict with each other.
+ // ```rust
+ // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {}
+ // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {}
//
- // Another approach would be to provide an empty Drop impl,
- // which would conflict with a user-provided Drop impl.
- // However, this would trigger the compiler's special handling
- // of Drop types (e.g. fields cannot be moved out of a Drop type).
- // This approach prevents the creation of needless Drop impls,
- // giving users more flexibility.
- let trait_ident = format_ident!("{}MustNotImplDrop", ident);
+ // ...
+ // _field: AlwaysUnpin<(A, B, C)>
+ // ```
+ //
+ // This ensures that any unused type parameters
+ // don't end up with `Unpin` bounds.
+ let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map(
+ |(i, LifetimeDef { lifetime, .. })| {
+ let field_ident = format_ident!("__lifetime{}", i);
+ quote!(#field_ident: &#lifetime ())
+ },
+ );
+
+ let orig_ident = cx.orig.ident;
+ let struct_ident = format_ident!("__{}", orig_ident);
+ let vis = cx.orig.vis;
+ let lifetime = &cx.proj.lifetime;
+ let type_params = cx.orig.generics.type_params().map(|t| &t.ident);
+ let proj_generics = &cx.proj.generics;
+ let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl();
+ let (_, ty_generics, where_clause) = cx.orig.generics.split_for_impl();
+
+ full_where_clause.predicates.push(parse_quote! {
+ #struct_ident #proj_ty_generics: ::pin_project::__private::Unpin
+ });
quote! {
- // There are two possible cases:
- // 1. The user type does not implement Drop. In this case,
- // the first blanked impl will not apply to it. This code
- // will compile, as there is only one impl of MustNotImplDrop for the user type
- // 2. The user type does impl Drop. This will make the blanket impl applicable,
- // which will then conflict with the explicit MustNotImplDrop impl below.
- // This will result in a compilation error, which is exactly what we want.
- trait #trait_ident {}
- #[allow(clippy::drop_bounds)]
- 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.
- // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop`
- // impl will not actually be called. Unfortunately, we can't detect this situation
- // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since
- // we don't know what other attirbutes/impl may exist.
+ // This needs to have the same visibility as the original type,
+ // due to the limitations of the 'public in private' error.
//
- // To ensure that users don't accidentally write a non-functional `PinnedDrop`
- // 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
+ // Our goal is to implement the public trait `Unpin` for
+ // a potentially public user type. Because of this, rust
+ // requires that any types mentioned in the where clause of
+ // our `Unpin` impl also be public. This means that our generated
+ // `__UnpinStruct` type must also be public.
+ // However, we ensure that the user can never actually reference
+ // this 'public' type by creating this type in the inside of `const`.
+ #[allow(missing_debug_implementations)]
+ #vis struct #struct_ident #proj_generics #where_clause {
+ __pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<
+ #lifetime, (#(::pin_project::__private::PhantomData<#type_params>),*)
+ >,
+
+ #(#fields,)*
+ #(#lifetime_fields,)*
+ }
+
+ impl #proj_impl_generics ::pin_project::__private::Unpin
+ for #orig_ident #ty_generics
+ #full_where_clause
+ {
+ }
+
+ // Generate 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 an `UnsafeUnpin`
+ // impl, they'll get a "conflicting implementations of trait" error when
+ // coherence checks are run.
+ #[doc(hidden)]
+ unsafe impl #proj_impl_generics ::pin_project::UnsafeUnpin
+ for #orig_ident #ty_generics
+ #full_where_clause
{
- unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {}
}
}
}
}
+}
- /// Creates an implementation of the projection method.
- fn make_proj_impl(
- &self,
- proj_body: &TokenStream,
- proj_ref_body: &TokenStream,
- proj_own_body: &TokenStream,
- ) -> TokenStream {
- let vis = &self.proj.vis;
- let lifetime = &self.proj.lifetime;
- 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;
-
- let orig_ty_generics = self.orig.generics.split_for_impl().1;
- 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.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::__private::Pin<&mut Self>,
- __replacement: Self,
- ) -> #proj_own_ident #orig_ty_generics {
+/// Creates `Drop` implementation for the original type.
+///
+/// The kind of `Drop` impl generated depends on `pinned_drop` field:
+/// * `Some` - implements `Drop` via `PinnedDrop` impl.
+/// * `None` - generates code that ensures that `Drop` trait is not implemented,
+/// instead of generating `Drop` impl.
+fn make_drop_impl(cx: &Context<'_>) -> TokenStream {
+ let ident = cx.orig.ident;
+ let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl();
+
+ if let Some(span) = cx.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) {
#unsafety {
- #proj_own_body
+ // Safety - we're in 'drop', so we know that 'self' will
+ // never move again.
+ let __pinned_self = ::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*.
+ ::pin_project::__private::PinnedDrop::drop(__pinned_self);
}
}
}
- });
+ }
+ } else {
+ // If the user does not provide a `PinnedDrop` impl,
+ // we need to ensure that they don't provide a `Drop` impl of their
+ // own.
+ // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87
+ //
+ // We create a new identifier for each struct, so that the traits
+ // for different types do not conflict with each other.
+ //
+ // Another approach would be to provide an empty Drop impl,
+ // which would conflict with a user-provided Drop impl.
+ // However, this would trigger the compiler's special handling
+ // of Drop types (e.g. fields cannot be moved out of a Drop type).
+ // This approach prevents the creation of needless Drop impls,
+ // giving users more flexibility.
+ let trait_ident = format_ident!("{}MustNotImplDrop", ident);
quote! {
- impl #impl_generics #orig_ident #ty_generics #where_clause {
- #vis fn project<#lifetime>(
- self: ::pin_project::__private::Pin<&#lifetime mut Self>,
- ) -> #proj_ident #proj_ty_generics {
- unsafe {
- #proj_body
- }
- }
- #vis fn project_ref<#lifetime>(
- self: ::pin_project::__private::Pin<&#lifetime Self>,
- ) -> #proj_ref_ident #proj_ty_generics {
- unsafe {
- #proj_ref_body
- }
- }
- #replace_impl
+ // There are two possible cases:
+ // 1. The user type does not implement Drop. In this case,
+ // the first blanked impl will not apply to it. This code
+ // will compile, as there is only one impl of MustNotImplDrop for the user type
+ // 2. The user type does impl Drop. This will make the blanket impl applicable,
+ // which will then conflict with the explicit MustNotImplDrop impl below.
+ // This will result in a compilation error, which is exactly what we want.
+ trait #trait_ident {}
+ #[allow(clippy::drop_bounds, drop_bounds)]
+ impl<T: ::pin_project::__private::Drop> #trait_ident for T {}
+ impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {}
+
+ // Generate a dummy impl of `PinnedDrop`, to ensure that the user cannot implement it.
+ // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop`
+ // impl will not actually be called. Unfortunately, we can't detect this situation
+ // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since
+ // we don't know what other attirbutes/impl may exist.
+ //
+ // To ensure that users don't accidentally write a non-functional `PinnedDrop`
+ // 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.
+ #[doc(hidden)]
+ impl #impl_generics ::pin_project::__private::PinnedDrop for #ident #ty_generics
+ #where_clause
+ {
+ unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {}
}
}
}
+}
- 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(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, .. }))
- if path.is_ident("packed") =>
- {
- return Err(error!(
- repr,
- "#[pin_project] attribute may not be used on #[repr(packed)] types"
- ));
- }
- _ => {}
- }
- }
+/// Creates an implementation of the projection methods.
+///
+/// On structs, both the `project` and `project_ref` methods are always generated,
+/// and the `project_replace` method is only generated if `ProjReplace::span` is `Some`.
+///
+/// On enums, only methods that the returned projected type is named will be generated.
+fn make_proj_impl(
+ cx: &Context<'_>,
+ proj_body: &TokenStream,
+ proj_ref_body: &TokenStream,
+ proj_own_body: &TokenStream,
+) -> TokenStream {
+ let vis = &cx.proj.vis;
+ let lifetime = &cx.proj.lifetime;
+ let orig_ident = cx.orig.ident;
+ let proj_ident = &cx.proj.mut_ident;
+ let proj_ref_ident = &cx.proj.ref_ident;
+ let proj_own_ident = &cx.proj.own_ident;
+
+ let orig_ty_generics = cx.orig.generics.split_for_impl().1;
+ let proj_ty_generics = cx.proj.generics.split_for_impl().1;
+ let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl();
+
+ let mut project = Some(quote! {
+ #vis fn project<#lifetime>(
+ self: ::pin_project::__private::Pin<&#lifetime mut Self>,
+ ) -> #proj_ident #proj_ty_generics {
+ unsafe {
+ #proj_body
+ }
+ }
+ });
+ let mut project_ref = Some(quote! {
+ #[allow(clippy::missing_const_for_fn)]
+ #vis fn project_ref<#lifetime>(
+ self: ::pin_project::__private::Pin<&#lifetime Self>,
+ ) -> #proj_ref_ident #proj_ty_generics {
+ unsafe {
+ #proj_ref_body
+ }
+ }
+ });
+ let mut project_replace = cx.project_replace.span().map(|span| {
+ // It is enough to only set the span of the signature.
+ let sig = quote_spanned! { span =>
+ #vis fn project_replace(
+ self: ::pin_project::__private::Pin<&mut Self>,
+ __replacement: Self,
+ ) -> #proj_own_ident #orig_ty_generics
+ };
+ quote! {
+ #sig {
+ unsafe {
+ #proj_own_body
}
}
}
+ });
- // As proc-macro-derive can't rewrite the structure definition,
- // it's probably no longer necessary, but it keeps it for now.
+ if cx.kind == Enum {
+ if !cx.project {
+ project = None;
+ }
+ if !cx.project_ref {
+ project_ref = None;
+ }
+ if cx.project_replace.ident().is_none() {
+ project_replace = None;
+ }
+ }
- // Workaround for https://github.com/taiki-e/pin-project/issues/32
- // Through the tricky use of proc macros, it's possible to bypass
- // the above check for the `repr` attribute.
- // To ensure that it's impossible to use pin projections on a `#[repr(packed)]`
- // struct, we generate code like this:
- //
- // ```rust
- // #[deny(safe_packed_borrows)]
- // fn assert_not_repr_packed(val: &MyStruct) {
- // let _field1 = &val.field1;
- // let _field2 = &val.field2;
- // ...
- // let _fieldn = &val.fieldn;
- // }
- // ```
- //
- // Taking a reference to a packed field is unsafe, and applying
- // `#[deny(safe_packed_borrows)]` makes sure that doing this without
- // an `unsafe` block (which we deliberately do not generate)
- // is a hard error.
- //
- // If the struct ends up having `#[repr(packed)]` applied somehow,
- // this will generate an (unfriendly) error message. Under all reasonable
- // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate
- // a much nicer error above.
- //
- // There is one exception: If the type of a struct field has 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)]`.
- //
- // Fortunately, this should have no observable consequence - `#[repr(packed)]`
- // is essentially a no-op on such a type. Nevertheless, we include a test
- // to ensure that the compiler doesn't ever try to copy the fields on
- // such a struct when trying to drop it - which is reason we prevent
- // `#[repr(packed)]` in the first place.
- //
- // See also https://github.com/taiki-e/pin-project/pull/34.
- let mut field_refs = vec![];
- match fields {
- Fields::Named(FieldsNamed { named, .. }) => {
- for Field { ident, .. } in named {
- field_refs.push(quote!(&val.#ident;));
- }
- }
- Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
- for (index, _) in unnamed.iter().enumerate() {
- let index = Index::from(index);
- field_refs.push(quote!(&val.#index;));
+ quote! {
+ impl #impl_generics #orig_ident #ty_generics #where_clause {
+ #project
+ #project_ref
+ #project_replace
+ }
+ }
+}
+
+/// Checks that the `[repr(packed)]` attribute is not included.
+///
+/// This currently does two checks:
+/// * Checks the attributes of structs to ensure there is no `[repr(packed)]`.
+/// * Generates a function that borrows fields without an unsafe block and
+/// forbidding `safe_packed_borrows` lint.
+fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenStream> {
+ for meta in orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
+ 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, .. }))
+ if path.is_ident("packed") =>
+ {
+ return Err(error!(
+ repr,
+ "#[pin_project] attribute may not be used on #[repr(packed)] types"
+ ));
+ }
+ _ => {}
+ }
}
}
- Fields::Unit => {}
}
+ }
- let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl();
- let ident = self.orig.ident;
- Ok(quote! {
- #[deny(safe_packed_borrows)]
- fn __assert_not_repr_packed #impl_generics (val: &#ident #ty_generics) #where_clause {
- #(#field_refs)*
+ // As proc-macro-derive can't rewrite the structure definition,
+ // it's probably no longer necessary, but it keeps it for now.
+
+ // Workaround for https://github.com/taiki-e/pin-project/issues/32
+ // Through the tricky use of proc macros, it's possible to bypass
+ // the above check for the `repr` attribute.
+ // To ensure that it's impossible to use pin projections on a `#[repr(packed)]`
+ // struct, we generate code like this:
+ //
+ // ```rust
+ // #[forbid(safe_packed_borrows)]
+ // fn assert_not_repr_packed(val: &MyStruct) {
+ // let _field1 = &val.field1;
+ // let _field2 = &val.field2;
+ // ...
+ // let _fieldn = &val.fieldn;
+ // }
+ // ```
+ //
+ // Taking a reference to a packed field is unsafe, and applying
+ // `#[forbid(safe_packed_borrows)]` makes sure that doing this without
+ // an `unsafe` block (which we deliberately do not generate)
+ // is a hard error.
+ //
+ // If the struct ends up having `#[repr(packed)]` applied somehow,
+ // this will generate an (unfriendly) error message. Under all reasonable
+ // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate
+ // a much nicer error above.
+ //
+ // There is one exception: If the type of a struct field has 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)]`.
+ //
+ // Fortunately, this should have no observable consequence - `#[repr(packed)]`
+ // is essentially a no-op on such a type. Nevertheless, we include a test
+ // to ensure that the compiler doesn't ever try to copy the fields on
+ // such a struct when trying to drop it - which is reason we prevent
+ // `#[repr(packed)]` in the first place.
+ //
+ // See also https://github.com/taiki-e/pin-project/pull/34.
+ let mut field_refs = vec![];
+ match fields {
+ Fields::Named(FieldsNamed { named, .. }) => {
+ for Field { ident, .. } in named {
+ field_refs.push(quote!(&this.#ident));
}
- })
+ }
+ Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
+ for (index, _) in unnamed.iter().enumerate() {
+ let index = Index::from(index);
+ field_refs.push(quote!(&this.#index));
+ }
+ }
+ Fields::Unit => {}
}
+
+ let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl();
+ let ident = orig.ident;
+ Ok(quote! {
+ #[forbid(safe_packed_borrows)]
+ fn __assert_not_repr_packed #impl_generics (this: &#ident #ty_generics) #where_clause {
+ #(let _ = #field_refs;)*
+ }
+ })
}
diff --git a/src/pin_project/mod.rs b/src/pin_project/mod.rs
index 3c8e29e..380b586 100644
--- a/src/pin_project/mod.rs
+++ b/src/pin_project/mod.rs
@@ -1,3 +1,4 @@
+mod args;
mod attribute;
mod derive;
diff --git a/src/pinned_drop.rs b/src/pinned_drop.rs
index 42619e3..86b0f47 100644
--- a/src/pinned_drop.rs
+++ b/src/pinned_drop.rs
@@ -5,12 +5,19 @@ use syn::{spanned::Spanned, visit_mut::VisitMut, *};
use crate::utils::{parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, SliceExt};
pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream {
- if let Err(e) = parse_as_empty(args).and_then(|()| parse(&mut input)) {
+ let res = (|| -> Result<()> {
+ parse_as_empty(args)?;
+ validate_impl(&input)?;
+ expand_impl(&mut input);
+ Ok(())
+ })();
+
+ if let Err(e) = res {
let mut tokens = e.to_compile_error();
if let Type::Path(self_ty) = &*input.self_ty {
let (impl_generics, _, where_clause) = input.generics.split_for_impl();
- // A dummy impl of `PinnedDrop`.
+ // Generate 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.
@@ -20,7 +27,8 @@ pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream
// 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.
+ // the type does not exist (since #[pin_project] can only be used on struct/enum
+ // definitions), so do not generate a dummy impl.
tokens.extend(quote! {
impl #impl_generics ::pin_project::__private::PinnedDrop for #self_ty
#where_clause
@@ -35,36 +43,92 @@ pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream
}
}
-fn parse_method(method: &ImplItemMethod) -> Result<()> {
+/// Validates the signature of given `PinnedDrop` impl.
+fn validate_impl(item: &ItemImpl) -> Result<()> {
+ const INVALID_ITEM: &str =
+ "#[pinned_drop] may only be used on implementation for the `PinnedDrop` trait";
+
+ if let Some(attr) = item.attrs.find("pinned_drop") {
+ return Err(error!(attr, "duplicate #[pinned_drop] attribute"));
+ }
+
+ if let Some((_, path, _)) = &item.trait_ {
+ if !path.is_ident("PinnedDrop") {
+ return Err(error!(path, INVALID_ITEM));
+ }
+ } else {
+ return Err(error!(item.self_ty, INVALID_ITEM));
+ }
+
+ if item.unsafety.is_some() {
+ return Err(error!(item.unsafety, "implementing the trait `PinnedDrop` is not unsafe"));
+ }
+ if item.items.is_empty() {
+ 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().try_for_each(|(i, item)| match item {
+ ImplItem::Const(item) => {
+ Err(error!(item, "const `{}` is not a member of trait `PinnedDrop`", item.ident))
+ }
+ ImplItem::Type(item) => {
+ Err(error!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident))
+ }
+ ImplItem::Method(method) => {
+ validate_sig(&method.sig)?;
+ if i == 0 {
+ Ok(())
+ } else {
+ Err(error!(method, "duplicate definitions with name `drop`"))
+ }
+ }
+ _ => unreachable!("unexpected ImplItem"),
+ })
+}
+
+/// Validates the signature of given `PinnedDrop::drop` method.
+///
+/// The correct signature is: `(mut) self: (<path>::)Pin<&mut Self>`
+fn validate_sig(sig: &Signature) -> Result<()> {
fn get_ty_path(ty: &Type) -> Option<&Path> {
if let Type::Path(TypePath { qself: None, path }) = ty { Some(path) } else { None }
}
const INVALID_ARGUMENT: &str = "method `drop` must take an argument `self: Pin<&mut Self>`";
- if method.sig.ident != "drop" {
+ if sig.ident != "drop" {
return Err(error!(
- method.sig.ident,
- "method `{}` is not a member of trait `PinnedDrop", method.sig.ident,
+ sig.ident,
+ "method `{}` is not a member of trait `PinnedDrop", sig.ident,
));
}
- if let ReturnType::Type(_, ty) = &method.sig.output {
+ if let ReturnType::Type(_, ty) = &sig.output {
match &**ty {
Type::Tuple(ty) if ty.elems.is_empty() => {}
_ => return Err(error!(ty, "method `drop` must return the unit type")),
}
}
- match method.sig.inputs.len() {
+ match sig.inputs.len() {
1 => {}
- 0 => return Err(Error::new(method.sig.paren_token.span, INVALID_ARGUMENT)),
- _ => return Err(error!(method.sig.inputs, INVALID_ARGUMENT)),
+ 0 => return Err(Error::new(sig.paren_token.span, INVALID_ARGUMENT)),
+ _ => return Err(error!(sig.inputs, INVALID_ARGUMENT)),
}
- if let Some(FnArg::Typed(pat)) = method.sig.receiver() {
+ if let Some(FnArg::Typed(arg)) = sig.receiver() {
// (mut) self: <path>
- if let Some(path) = get_ty_path(&pat.ty) {
+ if let Some(path) = get_ty_path(&arg.ty) {
let ty = path.segments.last().unwrap();
if let PathArguments::AngleBracketed(args) = &ty.arguments {
// (mut) self: (<path>::)<ty><&mut <elem>..>
@@ -79,9 +143,9 @@ fn parse_method(method: &ImplItemMethod) -> Result<()> {
&& ty.ident == "Pin"
&& get_ty_path(elem).map_or(false, |path| path.is_ident("Self"))
{
- if method.sig.unsafety.is_some() {
+ if sig.unsafety.is_some() {
return Err(error!(
- method.sig.unsafety,
+ sig.unsafety,
"implementing the method `drop` is not unsafe"
));
}
@@ -92,73 +156,13 @@ fn parse_method(method: &ImplItemMethod) -> Result<()> {
}
}
- Err(error!(method.sig.inputs[0], INVALID_ARGUMENT))
-}
-
-fn parse(item: &mut ItemImpl) -> Result<()> {
- const INVALID_ITEM: &str =
- "#[pinned_drop] may only be used on implementation for the `PinnedDrop` trait";
-
- if let Some(attr) = item.attrs.find("pinned_drop") {
- return Err(error!(attr, "duplicate #[pinned_drop] attribute"));
- }
-
- if let Some((_, path, _)) = &mut item.trait_ {
- if path.is_ident("PinnedDrop") {
- *path = parse_quote_spanned! { path.span() =>
- ::pin_project::__private::PinnedDrop
- };
- } else {
- return Err(error!(path, INVALID_ITEM));
- }
- } else {
- return Err(error!(item.self_ty, INVALID_ITEM));
- }
-
- if item.unsafety.is_some() {
- return Err(error!(item.unsafety, "implementing the trait `PinnedDrop` is not unsafe"));
- }
- if item.items.is_empty() {
- 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()
- .try_for_each(|(i, item)| match item {
- ImplItem::Const(item) => {
- Err(error!(item, "const `{}` is not a member of trait `PinnedDrop`", item.ident))
- }
- ImplItem::Type(item) => {
- Err(error!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident))
- }
- ImplItem::Method(method) => {
- parse_method(method)?;
- if i == 0 {
- Ok(())
- } else {
- Err(error!(method, "duplicate definitions with name `drop`"))
- }
- }
- _ => unreachable!("unexpected ImplItem"),
- })
- .map(|()| expand_item(item))
+ Err(error!(sig.inputs[0], INVALID_ARGUMENT))
}
// from:
//
// fn drop(self: Pin<&mut Self>) {
-// // something
+// // ...
// }
//
// into:
@@ -166,42 +170,51 @@ fn parse(item: &mut ItemImpl) -> Result<()> {
// unsafe fn drop(self: Pin<&mut Self>) {
// fn __drop_inner<T>(__self: Pin<&mut Foo<'_, T>>) {
// fn __drop_inner() {}
-// // something
+// // ...
// }
// __drop_inner(self);
// }
//
-fn expand_item(item: &mut ItemImpl) {
+fn expand_impl(item: &mut ItemImpl) {
+ fn get_arg_pat(arg: &mut FnArg) -> Option<&mut PatIdent> {
+ if let FnArg::Typed(arg) = arg {
+ if let Pat::Ident(ident) = &mut *arg.pat {
+ return Some(ident);
+ }
+ }
+ None
+ }
+
+ let path = &mut item.trait_.as_mut().unwrap().1;
+ *path = parse_quote_spanned! { path.span() =>
+ ::pin_project::__private::PinnedDrop
+ };
+
let method =
if let ImplItem::Method(method) = &mut item.items[0] { method } else { unreachable!() };
- let mut drop_inner = method.clone();
// `fn drop(mut self: Pin<&mut Self>)` -> `fn __drop_inner<T>(mut __self: Pin<&mut Receiver>)`
- let ident = Ident::new("__drop_inner", drop_inner.sig.ident.span());
- // Add a dummy `__drop_inner` function to prevent users call outer `__drop_inner`.
- drop_inner.block.stmts.insert(0, parse_quote!(fn #ident() {}));
- drop_inner.sig.ident = ident;
- drop_inner.sig.generics = item.generics.clone();
- if let FnArg::Typed(arg) = &mut drop_inner.sig.inputs[0] {
- if let Pat::Ident(ident) = &mut *arg.pat {
- prepend_underscore_to_self(&mut ident.ident);
- }
- }
- 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);
+ let drop_inner = {
+ let mut drop_inner = method.clone();
+ let ident = Ident::new("__drop_inner", drop_inner.sig.ident.span());
+ // Add a dummy `__drop_inner` function to prevent users call outer `__drop_inner`.
+ drop_inner.block.stmts.insert(0, parse_quote!(fn #ident() {}));
+ drop_inner.sig.ident = ident;
+ drop_inner.sig.generics = item.generics.clone();
+ let self_pat = get_arg_pat(&mut drop_inner.sig.inputs[0]).unwrap();
+ prepend_underscore_to_self(&mut self_pat.ident);
+ 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);
+ drop_inner
+ };
// `fn drop(mut self: Pin<&mut Self>)` -> `unsafe fn drop(self: Pin<&mut Self>)`
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());
+ let self_pat = get_arg_pat(&mut method.sig.inputs[0]).unwrap();
+ self_pat.mutability = None;
+ let self_token = &self_pat.ident;
method.block.stmts = parse_quote! {
#[allow(clippy::needless_pass_by_value)] // This lint does not warn the receiver.
diff --git a/src/project.rs b/src/project.rs
deleted file mode 100644
index a00e4c2..0000000
--- a/src/project.rs
+++ /dev/null
@@ -1,350 +0,0 @@
-use proc_macro2::{Span, TokenStream};
-use quote::ToTokens;
-use syn::{
- visit_mut::{self, VisitMut},
- *,
-};
-
-use crate::utils::{
- determine_lifetime_name, insert_lifetime, parse_as_empty, ProjKind, SliceExt, VecExt,
-};
-
-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, kind: ProjKind) {
- match expr {
- 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(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;
- continue;
- }
- }
- break;
- }
- }
- _ => {}
- }
-}
-
-fn parse(mut stmt: Stmt, kind: ProjKind) -> Result<TokenStream> {
- match &mut stmt {
- 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)?,
- _ => {}
- }
-
- Ok(stmt.into_token_stream())
-}
-
-struct Context {
- register: Option<(Ident, usize)>,
- replaced: bool,
- kind: ProjKind,
-}
-
-impl Context {
- fn new(kind: ProjKind) -> Self {
- Self { register: None, replaced: false, kind }
- }
-
- fn update(&mut self, ident: &Ident, len: usize) {
- self.register.get_or_insert_with(|| (ident.clone(), len));
- }
-
- fn compare_paths(&self, ident: &Ident, len: usize) -> bool {
- match &self.register {
- Some((i, l)) => *l == len && i == ident,
- None => false,
- }
- }
-
- fn replace_local(&mut self, local: &mut Local) -> Result<()> {
- 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) {
- self.replace_expr_match(expr);
- }
-
- if self.replaced {
- if is_replaceable(&local.pat, false) {
- return Err(error!(
- local.pat,
- "Both initializer expression and pattern are replaceable, \
- you need to split the initializer expression into separate let bindings \
- to avoid ambiguity"
- ));
- }
- } else {
- self.replace_pat(&mut local.pat, false);
- }
-
- Ok(())
- }
-
- fn replace_expr_let(&mut self, expr: &mut ExprLet) {
- self.replace_pat(&mut expr.pat, true)
- }
-
- fn replace_expr_match(&mut self, expr: &mut ExprMatch) {
- expr.arms.iter_mut().for_each(|arm| self.replace_pat(&mut arm.pat, true))
- }
-
- fn replace_pat(&mut self, pat: &mut Pat, allow_pat_path: bool) {
- match pat {
- Pat::Ident(PatIdent { subpat: Some((_, pat)), .. })
- | Pat::Reference(PatReference { pat, .. })
- | Pat::Box(PatBox { pat, .. })
- | Pat::Type(PatType { pat, .. }) => self.replace_pat(pat, allow_pat_path),
-
- Pat::Or(PatOr { cases, .. }) => {
- cases.iter_mut().for_each(|pat| self.replace_pat(pat, allow_pat_path))
- }
-
- Pat::Struct(PatStruct { path, .. }) | Pat::TupleStruct(PatTupleStruct { path, .. }) => {
- self.replace_path(path)
- }
- Pat::Path(PatPath { qself: None, path, .. }) if allow_pat_path => {
- self.replace_path(path)
- }
- _ => {}
- }
- }
-
- fn replace_path(&mut self, path: &mut Path) {
- let len = match path.segments.len() {
- // 1: struct
- // 2: enum
- len @ 1 | len @ 2 => len,
- // other path
- _ => return,
- };
-
- 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.kind);
- }
- }
-}
-
-fn is_replaceable(pat: &Pat, allow_pat_path: bool) -> bool {
- match pat {
- Pat::Ident(PatIdent { subpat: Some((_, pat)), .. })
- | Pat::Reference(PatReference { pat, .. })
- | Pat::Box(PatBox { pat, .. })
- | Pat::Type(PatType { pat, .. }) => is_replaceable(pat, allow_pat_path),
-
- Pat::Or(PatOr { cases, .. }) => cases.iter().any(|pat| is_replaceable(pat, allow_pat_path)),
-
- Pat::Struct(_) | Pat::TupleStruct(_) => true,
- Pat::Path(PatPath { qself: None, .. }) => allow_pat_path,
- _ => false,
- }
-}
-
-fn replace_ident(ident: &mut Ident, kind: ProjKind) {
- *ident = kind.proj_ident(ident);
-}
-
-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 {
- Type::Path(TypePath { qself: None, path }) => path.segments.last_mut().unwrap(),
- _ => return Ok(()),
- };
-
- replace_ident(ident, kind);
-
- let mut lifetime_name = String::from("'pin");
- determine_lifetime_name(&mut lifetime_name, &mut item.generics);
- item.items
- .iter_mut()
- .filter_map(|i| if let ImplItem::Method(i) = i { Some(i) } else { None })
- .for_each(|item| determine_lifetime_name(&mut lifetime_name, &mut item.sig.generics));
- let lifetime = Lifetime::new(&lifetime_name, Span::call_site());
-
- insert_lifetime(&mut item.generics, lifetime.clone());
-
- match arguments {
- PathArguments::None => {
- *arguments = PathArguments::AngleBracketed(parse_quote!(<#lifetime>));
- }
- PathArguments::AngleBracketed(args) => {
- args.args.insert(0, parse_quote!(#lifetime));
- }
- PathArguments::Parenthesized(_) => unreachable!(),
- }
- Ok(())
-}
-
-fn replace_item_fn(item: &mut ItemFn, kind: ProjKind) -> Result<()> {
- struct FnVisitor(Result<()>);
-
- impl FnVisitor {
- fn visit_stmt(&mut self, node: &mut Stmt) -> Result<()> {
- match node {
- Stmt::Expr(expr) | Stmt::Semi(expr, _) => self.visit_expr(expr),
- Stmt::Local(local) => {
- visit_mut::visit_local_mut(self, local);
-
- let mut prev = None;
- 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(),
- kind.method_name(),
- ));
- }
- Context::new(kind).replace_local(local)?;
- }
- }
-
- Ok(())
- }
- // Do not recurse into nested items.
- Stmt::Item(_) => Ok(()),
- }
- }
-
- fn visit_expr(&mut self, node: &mut Expr) -> Result<()> {
- visit_mut::visit_expr_mut(self, node);
- match node {
- Expr::Match(expr) => {
- let mut prev = None;
- 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(),
- kind.method_name(),
- ));
- }
- }
- }
- 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 &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(),
- kind.method_name(),
- ));
- }
- }
- }
- if let Some(kind) = prev {
- replace_expr(node, kind);
- }
- }
- }
- _ => {}
- }
- Ok(())
- }
- }
-
- impl VisitMut for FnVisitor {
- fn visit_stmt_mut(&mut self, node: &mut Stmt) {
- if self.0.is_err() {
- return;
- }
- if let Err(e) = self.visit_stmt(node) {
- self.0 = Err(e)
- }
- }
-
- fn visit_expr_mut(&mut self, node: &mut Expr) {
- if self.0.is_err() {
- return;
- }
- if let Err(e) = self.visit_expr(node) {
- self.0 = Err(e)
- }
- }
-
- fn visit_item_mut(&mut self, _: &mut Item) {
- // Do not recurse into nested items.
- }
- }
-
- if let Some(attr) = item.attrs.find(kind.method_name()) {
- return Err(error!(attr, "duplicate #[{}] attribute", kind.method_name()));
- }
-
- let mut visitor = FnVisitor(Ok(()));
- visitor.visit_block_mut(&mut item.block);
- visitor.0
-}
-
-fn replace_item_use(item: &mut ItemUse, kind: ProjKind) -> Result<()> {
- struct UseTreeVisitor {
- res: Result<()>,
- kind: ProjKind,
- }
-
- impl VisitMut for UseTreeVisitor {
- fn visit_use_tree_mut(&mut self, node: &mut UseTree) {
- if self.res.is_err() {
- return;
- }
-
- match node {
- // Desugar `use tree::<name>` into `tree::__<name>Projection`.
- 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.kind.method_name()
- ));
- }
- UseTree::Rename(rename) => {
- self.res = Err(error!(
- rename,
- "#[{}] attribute may not be used on renamed imports",
- self.kind.method_name()
- ));
- }
- UseTree::Path(_) | UseTree::Group(_) => visit_mut::visit_use_tree_mut(self, node),
- }
- }
- }
-
- 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(()), kind };
- visitor.visit_item_use_mut(item);
- visitor.res
-}
diff --git a/src/utils.rs b/src/utils.rs
index 716d764..2fd8a61 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,5 +1,5 @@
use proc_macro2::{Group, Spacing, Span, TokenStream, TokenTree};
-use quote::{format_ident, quote, quote_spanned, ToTokens};
+use quote::{quote, quote_spanned, ToTokens};
use std::{iter::FromIterator, mem};
use syn::{
parse::{Parse, ParseBuffer, ParseStream},
@@ -25,36 +25,6 @@ macro_rules! parse_quote_spanned {
};
}
-#[derive(Clone, Copy, Eq, PartialEq)]
-pub(crate) enum ProjKind {
- Mutable,
- Immutable,
- Owned,
-}
-
-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 {
- 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.
- pub(crate) fn proj_ident(self, ident: &Ident) -> Ident {
- match self {
- 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.
pub(crate) fn determine_lifetime_name(lifetime_name: &mut String, generics: &mut Generics) {
@@ -106,7 +76,10 @@ pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) {
generics.params.insert(0, LifetimeDef::new(lifetime).into());
}
-/// Determines the visibility of the projected type and projection method.
+/// Determines the visibility of the projected types and projection methods.
+///
+/// If given visibility is `pub`, returned visibility is `pub(crate)`.
+/// Otherwise, returned visibility is the same as given visibility.
pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility {
if let Visibility::Public(token) = vis {
parse_quote_spanned!(token.pub_token.span => pub(crate))
@@ -115,7 +88,8 @@ pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility {
}
}
-/// Check if `tokens` is an empty `TokenStream`.
+/// Checks 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`.
pub(crate) fn parse_as_empty(tokens: &TokenStream) -> Result<()> {
@@ -149,11 +123,11 @@ pub(crate) trait SliceExt {
fn find(&self, ident: &str) -> Option<&Attribute>;
}
-pub(crate) trait VecExt {
- fn find_remove(&mut self, ident: &str) -> Result<Option<Attribute>>;
-}
-
impl SliceExt for [Attribute] {
+ /// # Errors
+ ///
+ /// * There are multiple specified attributes.
+ /// * The `Attribute::tokens` field of the specified attribute is not empty.
fn position_exact(&self, ident: &str) -> Result<Option<usize>> {
self.iter()
.try_fold((0, None), |(i, mut prev), attr| {
@@ -169,13 +143,7 @@ impl SliceExt for [Attribute] {
}
fn find(&self, ident: &str) -> Option<&Attribute> {
- self.iter().position(|attr| attr.path.is_ident(ident)).and_then(|i| self.get(i))
- }
-}
-
-impl VecExt for Vec<Attribute> {
- fn find_remove(&mut self, ident: &str) -> Result<Option<Attribute>> {
- self.position_exact(ident).map(|pos| pos.map(|i| self.remove(i)))
+ self.iter().position(|attr| attr.path.is_ident(ident)).map(|i| &self[i])
}
}