aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorChih-Hung Hsieh <chh@google.com>2020-05-13 16:08:03 -0700
committerChih-Hung Hsieh <chh@google.com>2020-05-13 16:08:03 -0700
commit6f3e9271b123f94f158b1f000c996a558351320f (patch)
treec0d427205c0f0002d86155721cc4f8de67c4fbe5 /tests
parent2e1ed433d835e9f8409237243b8c03b0be6a1310 (diff)
downloadpin-project-6f3e9271b123f94f158b1f000c996a558351320f.tar.gz
Import 'pin-project' package version 0.4.16
* Add OWNERS and Android.bp Bug: 156165390 Test: make Change-Id: I8ace68f978b2725ad91e0f64282003fa453674ca
Diffstat (limited to 'tests')
-rw-r--r--tests/cfg.rs242
-rw-r--r--tests/compiletest.rs15
-rw-r--r--tests/forbid_unsafe.rs108
-rw-r--r--tests/lints.rs126
-rw-r--r--tests/overwriting_core_crate.rs116
-rw-r--r--tests/pin_project.rs809
-rw-r--r--tests/pinned_drop.rs235
-rw-r--r--tests/project.rs297
-rw-r--r--tests/project_if_attr.rs.in44
-rw-r--r--tests/project_ref.rs174
-rw-r--r--tests/project_replace.rs98
-rw-r--r--tests/repr_packed.rs50
-rw-r--r--tests/ui/cfg/cfg_attr-resolve.rs11
-rw-r--r--tests/ui/cfg/cfg_attr-resolve.stderr5
-rw-r--r--tests/ui/cfg/cfg_attr-type-mismatch.rs24
-rw-r--r--tests/ui/cfg/cfg_attr-type-mismatch.stderr23
-rw-r--r--tests/ui/cfg/cfg_attr-unpin.rs21
-rw-r--r--tests/ui/cfg/cfg_attr-unpin.stderr22
-rw-r--r--tests/ui/cfg/packed_sneaky-span-issue-1.rs18
-rw-r--r--tests/ui/cfg/packed_sneaky-span-issue-1.stderr1
-rw-r--r--tests/ui/cfg/packed_sneaky-span-issue-2.rs18
-rw-r--r--tests/ui/cfg/packed_sneaky-span-issue-2.stderr1
-rw-r--r--tests/ui/cfg/packed_sneaky.rs12
-rw-r--r--tests/ui/cfg/packed_sneaky.stderr7
-rw-r--r--tests/ui/cfg/proper_unpin.rs28
-rw-r--r--tests/ui/cfg/proper_unpin.stderr11
-rw-r--r--tests/ui/cfg/unsupported.rs13
-rw-r--r--tests/ui/cfg/unsupported.stderr1
-rw-r--r--tests/ui/not_unpin/assert-not-unpin.rs40
-rw-r--r--tests/ui/not_unpin/assert-not-unpin.stderr83
-rw-r--r--tests/ui/not_unpin/conflict-unpin.rs30
-rw-r--r--tests/ui/not_unpin/conflict-unpin.stderr26
-rw-r--r--tests/ui/not_unpin/impl-unsafe-unpin.rs30
-rw-r--r--tests/ui/not_unpin/impl-unsafe-unpin.stderr32
-rw-r--r--tests/ui/pin_project/add-pin-attr-to-struct.rs19
-rw-r--r--tests/ui/pin_project/add-pin-attr-to-struct.stderr15
-rw-r--r--tests/ui/pin_project/add-pinned-field.rs23
-rw-r--r--tests/ui/pin_project/add-pinned-field.stderr23
-rw-r--r--tests/ui/pin_project/conflict-drop.rs31
-rw-r--r--tests/ui/pin_project/conflict-drop.stderr19
-rw-r--r--tests/ui/pin_project/conflict-unpin.rs37
-rw-r--r--tests/ui/pin_project/conflict-unpin.stderr32
-rw-r--r--tests/ui/pin_project/impl-unsafe-unpin.rs30
-rw-r--r--tests/ui/pin_project/impl-unsafe-unpin.stderr32
-rw-r--r--tests/ui/pin_project/invalid.rs199
-rw-r--r--tests/ui/pin_project/invalid.stderr228
-rw-r--r--tests/ui/pin_project/overlapping_unpin_struct.rs18
-rw-r--r--tests/ui/pin_project/overlapping_unpin_struct.stderr11
-rw-r--r--tests/ui/pin_project/packed-enum.rs20
-rw-r--r--tests/ui/pin_project/packed-enum.stderr30
-rw-r--r--tests/ui/pin_project/packed-name-value.rs20
-rw-r--r--tests/ui/pin_project/packed-name-value.stderr17
-rw-r--r--tests/ui/pin_project/packed.rs25
-rw-r--r--tests/ui/pin_project/packed.stderr17
-rw-r--r--tests/ui/pin_project/packed_sneaky-1.rs33
-rw-r--r--tests/ui/pin_project/packed_sneaky-1.stderr23
-rw-r--r--tests/ui/pin_project/packed_sneaky-2.rs12
-rw-r--r--tests/ui/pin_project/packed_sneaky-2.stderr13
-rw-r--r--tests/ui/pin_project/private_in_public-enum.rs23
-rw-r--r--tests/ui/pin_project/private_in_public-enum.stderr17
-rw-r--r--tests/ui/pin_project/proper_unpin.rs38
-rw-r--r--tests/ui/pin_project/proper_unpin.stderr37
-rw-r--r--tests/ui/pin_project/remove-attr-from-field.rs32
-rw-r--r--tests/ui/pin_project/remove-attr-from-field.stderr21
-rw-r--r--tests/ui/pin_project/remove-attr-from-struct.rs30
-rw-r--r--tests/ui/pin_project/remove-attr-from-struct.stderr63
-rw-r--r--tests/ui/pin_project/safe_packed_borrows.rs21
-rw-r--r--tests/ui/pin_project/safe_packed_borrows.stderr24
-rw-r--r--tests/ui/pin_project/unpin_sneaky.rs11
-rw-r--r--tests/ui/pin_project/unpin_sneaky.stderr11
-rw-r--r--tests/ui/pin_project/visibility.rs52
-rw-r--r--tests/ui/pin_project/visibility.stderr39
-rw-r--r--tests/ui/pinned_drop/call-drop-inner.rs16
-rw-r--r--tests/ui/pinned_drop/call-drop-inner.stderr10
-rw-r--r--tests/ui/pinned_drop/conditional-drop-impl.rs26
-rw-r--r--tests/ui/pinned_drop/conditional-drop-impl.stderr26
-rw-r--r--tests/ui/pinned_drop/forget-pinned-drop-impl.rs9
-rw-r--r--tests/ui/pinned_drop/forget-pinned-drop-impl.stderr7
-rw-r--r--tests/ui/pinned_drop/invalid-self.rs14
-rw-r--r--tests/ui/pinned_drop/invalid-self.stderr25
-rw-r--r--tests/ui/pinned_drop/invalid.rs207
-rw-r--r--tests/ui/pinned_drop/invalid.stderr125
-rw-r--r--tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs15
-rw-r--r--tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr8
-rw-r--r--tests/ui/pinned_drop/self.rs28
-rw-r--r--tests/ui/pinned_drop/self.stderr23
-rw-r--r--tests/ui/pinned_drop/unsafe-call.rs17
-rw-r--r--tests/ui/pinned_drop/unsafe-call.stderr7
-rw-r--r--tests/ui/project/ambiguous-let.rs24
-rw-r--r--tests/ui/project/ambiguous-let.stderr5
-rw-r--r--tests/ui/project/invalid.rs190
-rw-r--r--tests/ui/project/invalid.stderr155
-rw-r--r--tests/ui/project/type-mismatch.rs74
-rw-r--r--tests/ui/project/type-mismatch.stderr16
-rw-r--r--tests/ui/project/use-public.rs15
-rw-r--r--tests/ui/project/use-public.stderr7
-rw-r--r--tests/ui/project/use.rs17
-rw-r--r--tests/ui/project/use.stderr11
-rw-r--r--tests/ui/unsafe_unpin/conflict-unpin.rs30
-rw-r--r--tests/ui/unsafe_unpin/conflict-unpin.stderr35
-rw-r--r--tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs14
-rw-r--r--tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr11
-rw-r--r--tests/ui/unsafe_unpin/proper_unpin.rs41
-rw-r--r--tests/ui/unsafe_unpin/proper_unpin.stderr63
-rw-r--r--tests/ui/unstable-features/README.md5
-rw-r--r--tests/ui/unstable-features/marker_trait_attr-feature-gate.rs19
-rw-r--r--tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr10
-rw-r--r--tests/ui/unstable-features/marker_trait_attr.rs25
-rw-r--r--tests/ui/unstable-features/marker_trait_attr.stderr10
-rw-r--r--tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs19
-rw-r--r--tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr10
-rw-r--r--tests/ui/unstable-features/overlapping_marker_traits.rs29
-rw-r--r--tests/ui/unstable-features/overlapping_marker_traits.stderr18
-rw-r--r--tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs62
-rw-r--r--tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs55
-rw-r--r--tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr35
-rw-r--r--tests/ui/unstable-features/trivial_bounds-bug.rs33
-rw-r--r--tests/ui/unstable-features/trivial_bounds-bug.stderr10
-rw-r--r--tests/ui/unstable-features/trivial_bounds-feature-gate.rs54
-rw-r--r--tests/ui/unstable-features/trivial_bounds-feature-gate.stderr50
-rw-r--r--tests/ui/unstable-features/trivial_bounds.rs34
-rw-r--r--tests/ui/unstable-features/trivial_bounds.stderr17
-rw-r--r--tests/unsafe_unpin.rs53
123 files changed, 5936 insertions, 0 deletions
diff --git a/tests/cfg.rs b/tests/cfg.rs
new file mode 100644
index 0000000..20b8472
--- /dev/null
+++ b/tests/cfg.rs
@@ -0,0 +1,242 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+// Refs: https://doc.rust-lang.org/nightly/reference/attributes.html
+
+use pin_project::pin_project;
+use std::{marker::PhantomPinned, pin::Pin};
+
+fn is_unpin<T: Unpin>() {}
+
+#[cfg(target_os = "linux")]
+struct Linux;
+#[cfg(not(target_os = "linux"))]
+struct Other;
+
+// Use this type to check that `cfg(any())` is working properly.
+// If `cfg(any())` is not working properly, `is_unpin` will fail.
+struct Any(PhantomPinned);
+
+#[test]
+fn cfg() {
+ // structs
+
+ #[pin_project(Replace)]
+ struct SameName {
+ #[cfg(target_os = "linux")]
+ #[pin]
+ inner: Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[pin]
+ inner: Other,
+ #[cfg(any())]
+ #[pin]
+ any: Any,
+ }
+
+ is_unpin::<SameName>();
+
+ #[cfg(target_os = "linux")]
+ let _x = SameName { inner: Linux };
+ #[cfg(not(target_os = "linux"))]
+ let _x = SameName { inner: Other };
+
+ #[pin_project(Replace)]
+ struct DifferentName {
+ #[cfg(target_os = "linux")]
+ #[pin]
+ l: Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[pin]
+ o: Other,
+ #[cfg(any())]
+ #[pin]
+ a: Any,
+ }
+
+ is_unpin::<DifferentName>();
+
+ #[cfg(target_os = "linux")]
+ let _x = DifferentName { l: Linux };
+ #[cfg(not(target_os = "linux"))]
+ let _x = DifferentName { o: Other };
+
+ #[pin_project(Replace)]
+ struct TupleStruct(
+ #[cfg(target_os = "linux")]
+ #[pin]
+ Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[pin]
+ Other,
+ #[cfg(any())]
+ #[pin]
+ Any,
+ );
+
+ is_unpin::<TupleStruct>();
+
+ #[cfg(target_os = "linux")]
+ let _x = TupleStruct(Linux);
+ #[cfg(not(target_os = "linux"))]
+ let _x = TupleStruct(Other);
+
+ // enums
+
+ #[pin_project(Replace)]
+ enum Variant {
+ #[cfg(target_os = "linux")]
+ Inner(#[pin] Linux),
+ #[cfg(not(target_os = "linux"))]
+ Inner(#[pin] Other),
+
+ #[cfg(target_os = "linux")]
+ Linux(#[pin] Linux),
+ #[cfg(not(target_os = "linux"))]
+ Other(#[pin] Other),
+ #[cfg(any())]
+ Any(#[pin] Any),
+ }
+
+ is_unpin::<Variant>();
+
+ #[cfg(target_os = "linux")]
+ let _x = Variant::Inner(Linux);
+ #[cfg(not(target_os = "linux"))]
+ let _x = Variant::Inner(Other);
+
+ #[cfg(target_os = "linux")]
+ let _x = Variant::Linux(Linux);
+ #[cfg(not(target_os = "linux"))]
+ let _x = Variant::Other(Other);
+
+ #[pin_project(Replace)]
+ enum Field {
+ SameName {
+ #[cfg(target_os = "linux")]
+ #[pin]
+ inner: Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[pin]
+ inner: Other,
+ #[cfg(any())]
+ #[pin]
+ any: Any,
+ },
+ DifferentName {
+ #[cfg(target_os = "linux")]
+ #[pin]
+ l: Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[pin]
+ w: Other,
+ #[cfg(any())]
+ #[pin]
+ any: Any,
+ },
+ TupleVariant(
+ #[cfg(target_os = "linux")]
+ #[pin]
+ Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[pin]
+ Other,
+ #[cfg(any())]
+ #[pin]
+ Any,
+ ),
+ }
+
+ is_unpin::<Field>();
+
+ #[cfg(target_os = "linux")]
+ let _x = Field::SameName { inner: Linux };
+ #[cfg(not(target_os = "linux"))]
+ let _x = Field::SameName { inner: Other };
+
+ #[cfg(target_os = "linux")]
+ let _x = Field::DifferentName { l: Linux };
+ #[cfg(not(target_os = "linux"))]
+ let _x = Field::DifferentName { w: Other };
+
+ #[cfg(target_os = "linux")]
+ let _x = Field::TupleVariant(Linux);
+ #[cfg(not(target_os = "linux"))]
+ let _x = Field::TupleVariant(Other);
+}
+
+#[test]
+fn cfg_attr() {
+ #[pin_project(Replace)]
+ struct SameCfg {
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(target_os = "linux", pin)]
+ inner: Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[cfg_attr(not(target_os = "linux"), pin)]
+ inner: Other,
+ #[cfg(any())]
+ #[cfg_attr(any(), pin)]
+ any: Any,
+ }
+
+ is_unpin::<SameCfg>();
+
+ #[cfg(target_os = "linux")]
+ let mut x = SameCfg { inner: Linux };
+ #[cfg(not(target_os = "linux"))]
+ let mut x = SameCfg { inner: Other };
+
+ let x = Pin::new(&mut x).project();
+ #[cfg(target_os = "linux")]
+ let _: Pin<&mut Linux> = x.inner;
+ #[cfg(not(target_os = "linux"))]
+ let _: Pin<&mut Other> = x.inner;
+
+ #[pin_project(Replace)]
+ struct DifferentCfg {
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(target_os = "linux", pin)]
+ inner: Linux,
+ #[cfg(not(target_os = "linux"))]
+ #[cfg_attr(target_os = "linux", pin)]
+ inner: Other,
+ #[cfg(any())]
+ #[cfg_attr(any(), pin)]
+ any: Any,
+ }
+
+ is_unpin::<DifferentCfg>();
+
+ #[cfg(target_os = "linux")]
+ let mut x = DifferentCfg { inner: Linux };
+ #[cfg(not(target_os = "linux"))]
+ let mut x = DifferentCfg { inner: Other };
+
+ let x = Pin::new(&mut x).project();
+ #[cfg(target_os = "linux")]
+ let _: Pin<&mut Linux> = x.inner;
+ #[cfg(not(target_os = "linux"))]
+ let _: &mut Other = x.inner;
+
+ #[cfg_attr(not(any()), pin_project)]
+ struct Foo<T> {
+ #[cfg_attr(not(any()), pin)]
+ inner: T,
+ }
+
+ let mut x = Foo { inner: 0_u8 };
+ let x = Pin::new(&mut x).project();
+ let _: Pin<&mut u8> = x.inner;
+}
+
+#[test]
+fn cfg_attr_any_packed() {
+ // Since `cfg(any())` can never be true, it is okay for this to pass.
+ #[pin_project(Replace)]
+ #[cfg_attr(any(), repr(packed))]
+ struct Struct {
+ #[pin]
+ field: u32,
+ }
+}
diff --git a/tests/compiletest.rs b/tests/compiletest.rs
new file mode 100644
index 0000000..078abaa
--- /dev/null
+++ b/tests/compiletest.rs
@@ -0,0 +1,15 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+
+#[ignore]
+#[test]
+fn ui() {
+ let t = trybuild::TestCases::new();
+ t.compile_fail("tests/ui/cfg/*.rs");
+ t.compile_fail("tests/ui/not_unpin/*.rs");
+ t.compile_fail("tests/ui/pin_project/*.rs");
+ t.compile_fail("tests/ui/pinned_drop/*.rs");
+ t.compile_fail("tests/ui/project/*.rs");
+ t.compile_fail("tests/ui/unsafe_unpin/*.rs");
+ t.compile_fail("tests/ui/unstable-features/*.rs");
+ t.pass("tests/ui/unstable-features/run-pass/*.rs");
+}
diff --git a/tests/forbid_unsafe.rs b/tests/forbid_unsafe.rs
new file mode 100644
index 0000000..4b2e248
--- /dev/null
+++ b/tests/forbid_unsafe.rs
@@ -0,0 +1,108 @@
+#![forbid(unsafe_code)]
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+// default #[pin_project], PinnedDrop, Replace, and !Unpin are completely safe.
+
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+#[pin_project]
+pub struct StructDefault<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project(PinnedDrop)]
+pub struct StructPinnedDrop<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for StructPinnedDrop<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+#[pin_project(Replace)]
+pub struct StructReplace<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+// UnsafeUnpin without UnsafeUnpin impl is also safe
+#[pin_project(UnsafeUnpin)]
+pub struct StructUnsafeUnpin<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project(!Unpin)]
+pub struct StructNotUnpin<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project]
+pub enum EnumDefault<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project(PinnedDrop)]
+pub enum EnumPinnedDrop<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for EnumPinnedDrop<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+#[pin_project(Replace)]
+pub enum EnumReplace<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+// UnsafeUnpin without UnsafeUnpin impl is also safe
+#[pin_project(UnsafeUnpin)]
+pub enum EnumUnsafeUnpin<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project(!Unpin)]
+pub enum EnumNotUnpin<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[test]
+fn test() {}
diff --git a/tests/lints.rs b/tests/lints.rs
new file mode 100644
index 0000000..4009f55
--- /dev/null
+++ b/tests/lints.rs
@@ -0,0 +1,126 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![warn(unused, future_incompatible)]
+#![warn(clippy::all, clippy::pedantic, clippy::nursery)]
+
+use pin_project::{pin_project, pinned_drop, UnsafeUnpin};
+use std::pin::Pin;
+
+#[pin_project]
+pub struct StructDefault<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project(PinnedDrop)]
+pub struct StructPinnedDrop<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for StructPinnedDrop<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+#[pin_project(Replace)]
+pub struct StructReplace<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project(UnsafeUnpin)]
+pub struct StructUnsafeUnpin<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for StructUnsafeUnpin<T, U> {}
+
+#[pin_project(!Unpin)]
+pub struct StructNotUnpin<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project]
+pub struct StructMutMut<'a, T, U> {
+ #[pin]
+ pub pinned: &'a mut T,
+ pub unpinned: &'a mut U,
+}
+
+#[pin_project]
+pub enum EnumDefault<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project(PinnedDrop)]
+pub enum EnumPinnedDrop<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for EnumPinnedDrop<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+#[pin_project(Replace)]
+pub enum EnumReplace<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project(UnsafeUnpin)]
+pub enum EnumUnsafeUnpin<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for EnumUnsafeUnpin<T, U> {}
+
+#[pin_project(!Unpin)]
+pub enum EnumNotUnpin<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project]
+pub enum EnumMutMut<'a, T, U> {
+ Struct {
+ #[pin]
+ pinned: &'a mut T,
+ unpinned: &'a mut U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[allow(clippy::missing_const_for_fn)]
+#[test]
+fn test() {}
diff --git a/tests/overwriting_core_crate.rs b/tests/overwriting_core_crate.rs
new file mode 100644
index 0000000..121104c
--- /dev/null
+++ b/tests/overwriting_core_crate.rs
@@ -0,0 +1,116 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+
+// See https://github.com/rust-lang/pin-utils/pull/26#discussion_r344491597
+//
+// Note: If the proc-macro does not depend on its own items, it may be preferable not to
+// support overwriting the name of core/std crate for compatibility with reexport.
+#[allow(unused_extern_crates)]
+extern crate pin_project as core;
+
+// Dummy module to check that the expansion refers to the crate.
+mod pin_project {}
+
+use ::pin_project::{pin_project, pinned_drop, UnsafeUnpin};
+use std::pin::Pin;
+
+#[pin_project]
+pub struct StructDefault<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project(PinnedDrop)]
+pub struct StructPinnedDrop<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for StructPinnedDrop<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+#[pin_project(Replace)]
+pub struct StructReplace<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project(UnsafeUnpin)]
+pub struct StructUnsafeUnpin<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for StructUnsafeUnpin<T, U> {}
+
+#[pin_project(!Unpin)]
+pub struct StructNotUnpin<T, U> {
+ #[pin]
+ pub pinned: T,
+ pub unpinned: U,
+}
+
+#[pin_project]
+pub enum EnumDefault<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project(PinnedDrop)]
+pub enum EnumPinnedDrop<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for EnumPinnedDrop<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+#[pin_project(Replace)]
+pub enum EnumReplace<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[pin_project(UnsafeUnpin)]
+pub enum EnumUnsafeUnpin<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for EnumUnsafeUnpin<T, U> {}
+
+#[pin_project(!Unpin)]
+pub enum EnumNotUnpin<T, U> {
+ Struct {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ Tuple(#[pin] T, U),
+}
+
+#[test]
+fn test() {}
diff --git a/tests/pin_project.rs b/tests/pin_project.rs
new file mode 100644
index 0000000..c84ca4b
--- /dev/null
+++ b/tests/pin_project.rs
@@ -0,0 +1,809 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+use core::{
+ marker::{PhantomData, PhantomPinned},
+ pin::Pin,
+};
+use pin_project::{pin_project, pinned_drop, UnsafeUnpin};
+
+#[test]
+fn projection() {
+ #[pin_project(Replace)]
+ struct Struct<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ let mut s = Struct { field1: 1, field2: 2 };
+ let mut s_orig = Pin::new(&mut s);
+ let s = s_orig.as_mut().project();
+
+ let x: Pin<&mut i32> = s.field1;
+ assert_eq!(*x, 1);
+
+ let y: &mut i32 = s.field2;
+ assert_eq!(*y, 2);
+
+ assert_eq!(s_orig.as_ref().field1, 1);
+ assert_eq!(s_orig.as_ref().field2, 2);
+
+ let mut s = Struct { field1: 1, field2: 2 };
+
+ let __StructProjection { field1, field2 } = Pin::new(&mut s).project();
+ let _: Pin<&mut i32> = field1;
+ let _: &mut i32 = field2;
+
+ let __StructProjectionRef { field1, field2 } = Pin::new(&s).project_ref();
+ let _: Pin<&i32> = field1;
+ let _: &i32 = field2;
+
+ let mut s = Pin::new(&mut s);
+ let __StructProjectionOwned { field1, field2 } =
+ s.as_mut().project_replace(Struct { field1: 3, field2: 4 });
+ let _: PhantomData<i32> = field1;
+ let _: i32 = field2;
+ assert_eq!(field2, 2);
+ assert_eq!(s.field1, 3);
+ assert_eq!(s.field2, 4);
+
+ #[pin_project(Replace)]
+ struct TupleStruct<T, U>(#[pin] T, U);
+
+ let mut s = TupleStruct(1, 2);
+ let s = Pin::new(&mut s).project();
+
+ let x: Pin<&mut i32> = s.0;
+ assert_eq!(*x, 1);
+
+ let y: &mut i32 = s.1;
+ assert_eq!(*y, 2);
+
+ #[pin_project(Replace)]
+ #[derive(Eq, PartialEq, Debug)]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut e = Enum::Variant1(1, 2);
+ let mut e_orig = Pin::new(&mut e);
+ let e = e_orig.as_mut().project();
+
+ match e {
+ __EnumProjection::Variant1(x, y) => {
+ let x: Pin<&mut i32> = x;
+ assert_eq!(*x, 1);
+
+ let y: &mut i32 = y;
+ assert_eq!(*y, 2);
+ }
+ __EnumProjection::Variant2 { field1, field2 } => {
+ let _x: Pin<&mut i32> = field1;
+ let _y: &mut i32 = field2;
+ }
+ __EnumProjection::None => {}
+ }
+
+ assert_eq!(Pin::into_ref(e_orig).get_ref(), &Enum::Variant1(1, 2));
+
+ let mut e = Enum::Variant2 { field1: 3, field2: 4 };
+ let mut e = Pin::new(&mut e).project();
+
+ match &mut e {
+ __EnumProjection::Variant1(x, y) => {
+ let _x: &mut Pin<&mut i32> = x;
+ let _y: &mut &mut i32 = y;
+ }
+ __EnumProjection::Variant2 { field1, field2 } => {
+ let x: &mut Pin<&mut i32> = field1;
+ assert_eq!(**x, 3);
+
+ let y: &mut &mut i32 = field2;
+ assert_eq!(**y, 4);
+ }
+ __EnumProjection::None => {}
+ }
+
+ if let __EnumProjection::Variant2 { field1, field2 } = e {
+ let x: Pin<&mut i32> = field1;
+ assert_eq!(*x, 3);
+
+ let y: &mut i32 = field2;
+ assert_eq!(*y, 4);
+ }
+}
+
+#[test]
+fn enum_project_set() {
+ #[pin_project(Replace)]
+ #[derive(Eq, PartialEq, Debug)]
+ enum Enum {
+ Variant1(#[pin] u8),
+ Variant2(bool),
+ }
+
+ let mut e = Enum::Variant1(25);
+ let mut e_orig = Pin::new(&mut e);
+ let e_proj = e_orig.as_mut().project();
+
+ match e_proj {
+ __EnumProjection::Variant1(val) => {
+ let new_e = Enum::Variant2(val.as_ref().get_ref() == &25);
+ e_orig.set(new_e);
+ }
+ _ => unreachable!(),
+ }
+
+ assert_eq!(e, Enum::Variant2(true));
+}
+
+#[test]
+fn where_clause() {
+ #[pin_project]
+ struct Struct<T>
+ where
+ T: Copy,
+ {
+ field: T,
+ }
+
+ #[pin_project]
+ struct TupleStruct<T>(T)
+ where
+ T: Copy;
+
+ #[pin_project]
+ enum EnumWhere<T>
+ where
+ T: Copy,
+ {
+ Variant(T),
+ }
+}
+
+#[test]
+fn where_clause_and_associated_type_field() {
+ #[pin_project(Replace)]
+ struct Struct1<I>
+ where
+ I: Iterator,
+ {
+ #[pin]
+ field1: I,
+ field2: I::Item,
+ }
+
+ #[pin_project(Replace)]
+ struct Struct2<I, J>
+ where
+ I: Iterator<Item = J>,
+ {
+ #[pin]
+ field1: I,
+ field2: J,
+ }
+
+ #[pin_project(Replace)]
+ struct Struct3<T>
+ where
+ T: 'static,
+ {
+ field: T,
+ }
+
+ trait Static: 'static {}
+
+ impl<T> Static for Struct3<T> {}
+
+ #[pin_project(Replace)]
+ struct TupleStruct<I>(#[pin] I, I::Item)
+ where
+ I: Iterator;
+
+ #[pin_project(Replace)]
+ enum Enum<I>
+ where
+ I: Iterator,
+ {
+ Variant1(#[pin] I),
+ Variant2(I::Item),
+ }
+}
+
+#[test]
+fn derive_copy() {
+ #[pin_project(Replace)]
+ #[derive(Clone, Copy)]
+ struct Struct<T> {
+ val: T,
+ }
+
+ fn is_copy<T: Copy>() {}
+
+ is_copy::<Struct<u8>>();
+}
+
+#[test]
+fn move_out() {
+ struct NotCopy;
+
+ #[pin_project(Replace)]
+ struct Struct {
+ val: NotCopy,
+ }
+
+ let x = Struct { val: NotCopy };
+ let _val: NotCopy = x.val;
+
+ #[pin_project(Replace)]
+ enum Enum {
+ Variant(NotCopy),
+ }
+
+ let x = Enum::Variant(NotCopy);
+ #[allow(clippy::infallible_destructuring_match)]
+ let _val: NotCopy = match x {
+ Enum::Variant(val) => val,
+ };
+}
+
+#[test]
+fn trait_bounds_on_type_generics() {
+ #[pin_project(Replace)]
+ pub struct Struct1<'a, T: ?Sized> {
+ field: &'a mut T,
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct2<'a, T: ::core::fmt::Debug> {
+ field: &'a mut T,
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct3<'a, T: core::fmt::Debug> {
+ field: &'a mut T,
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct4<'a, T: core::fmt::Debug + core::fmt::Display> {
+ field: &'a mut T,
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct5<'a, T: core::fmt::Debug + ?Sized> {
+ field: &'a mut T,
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct6<'a, T: core::fmt::Debug = [u8; 16]> {
+ field: &'a mut T,
+ }
+
+ let _: Struct6<'_> = Struct6 { field: &mut [0u8; 16] };
+
+ #[pin_project(Replace)]
+ pub struct Struct7<T: 'static> {
+ field: T,
+ }
+
+ trait Static: 'static {}
+
+ impl<T> Static for Struct7<T> {}
+
+ #[pin_project(Replace)]
+ pub struct Struct8<'a, 'b: 'a> {
+ field1: &'a u8,
+ field2: &'b u8,
+ }
+
+ #[pin_project(Replace)]
+ pub struct TupleStruct<'a, T: ?Sized>(&'a mut T);
+
+ #[pin_project(Replace)]
+ enum Enum<'a, T: ?Sized> {
+ Variant(&'a mut T),
+ }
+}
+
+#[test]
+fn overlapping_lifetime_names() {
+ #[pin_project(Replace)]
+ pub struct Struct1<'pin, T> {
+ #[pin]
+ field: &'pin mut T,
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct2<'pin, 'pin_, 'pin__> {
+ #[pin]
+ field: &'pin &'pin_ &'pin__ (),
+ }
+
+ pub trait A<'a> {}
+
+ #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058
+ #[pin_project(Replace)]
+ pub struct HRTB<'pin___, T>
+ where
+ for<'pin> &'pin T: Unpin,
+ T: for<'pin> A<'pin>,
+ for<'pin, 'pin_, 'pin__> &'pin &'pin_ &'pin__ T: Unpin,
+ {
+ #[pin]
+ field: &'pin___ mut T,
+ }
+}
+
+#[test]
+fn combine() {
+ #[pin_project(PinnedDrop, UnsafeUnpin)]
+ pub struct Struct1<T> {
+ #[pin]
+ field: T,
+ }
+
+ #[pinned_drop]
+ impl<T> PinnedDrop for Struct1<T> {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ unsafe impl<T: Unpin> UnsafeUnpin for Struct1<T> {}
+
+ #[pin_project(UnsafeUnpin, Replace)]
+ pub struct Struct2<T> {
+ #[pin]
+ field: T,
+ }
+
+ unsafe impl<T: Unpin> UnsafeUnpin for Struct2<T> {}
+
+ #[pin_project(PinnedDrop, !Unpin)]
+ pub struct Struct3<T> {
+ #[pin]
+ field: T,
+ }
+
+ #[pinned_drop]
+ impl<T> PinnedDrop for Struct3<T> {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(!Unpin, Replace)]
+ pub struct Struct4<T> {
+ #[pin]
+ field: T,
+ }
+}
+
+#[test]
+fn private_type_in_public_type() {
+ #[pin_project(Replace)]
+ pub struct PublicStruct<T> {
+ #[pin]
+ inner: PrivateStruct<T>,
+ }
+
+ struct PrivateStruct<T>(T);
+}
+
+#[test]
+fn lifetime_project() {
+ #[pin_project(Replace)]
+ struct Struct1<T, U> {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ }
+
+ #[pin_project(Replace)]
+ struct Struct2<'a, T, U> {
+ #[pin]
+ pinned: &'a mut T,
+ unpinned: U,
+ }
+
+ #[pin_project(Replace)]
+ enum Enum<T, U> {
+ Variant {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ }
+
+ impl<T, U> Struct1<T, U> {
+ fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> {
+ self.project_ref().pinned
+ }
+ fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
+ self.project().pinned
+ }
+ }
+
+ impl<'b, T, U> Struct2<'b, T, U> {
+ fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a &'b mut T> {
+ self.project_ref().pinned
+ }
+ fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut &'b mut T> {
+ self.project().pinned
+ }
+ }
+
+ impl<T, U> Enum<T, U> {
+ fn get_pin_ref<'a>(self: Pin<&'a Self>) -> Pin<&'a T> {
+ match self.project_ref() {
+ __EnumProjectionRef::Variant { pinned, .. } => pinned,
+ }
+ }
+ fn get_pin_mut<'a>(self: Pin<&'a mut Self>) -> Pin<&'a mut T> {
+ match self.project() {
+ __EnumProjection::Variant { pinned, .. } => pinned,
+ }
+ }
+ }
+}
+
+#[rustversion::since(1.36)] // https://github.com/rust-lang/rust/pull/61207
+#[test]
+fn lifetime_project_elided() {
+ #[pin_project(Replace)]
+ struct Struct1<T, U> {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ }
+
+ #[pin_project(Replace)]
+ struct Struct2<'a, T, U> {
+ #[pin]
+ pinned: &'a mut T,
+ unpinned: U,
+ }
+
+ #[pin_project(Replace)]
+ enum Enum<T, U> {
+ Variant {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ },
+ }
+
+ impl<T, U> Struct1<T, U> {
+ fn get_pin_ref(self: Pin<&Self>) -> Pin<&T> {
+ self.project_ref().pinned
+ }
+ fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
+ self.project().pinned
+ }
+ }
+
+ impl<'b, T, U> Struct2<'b, T, U> {
+ fn get_pin_ref(self: Pin<&Self>) -> Pin<&&'b mut T> {
+ self.project_ref().pinned
+ }
+ fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut &'b mut T> {
+ self.project().pinned
+ }
+ }
+
+ impl<T, U> Enum<T, U> {
+ fn get_pin_ref(self: Pin<&Self>) -> Pin<&T> {
+ match self.project_ref() {
+ __EnumProjectionRef::Variant { pinned, .. } => pinned,
+ }
+ }
+ fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
+ match self.project() {
+ __EnumProjection::Variant { pinned, .. } => pinned,
+ }
+ }
+ }
+}
+
+mod visibility {
+ use pin_project::pin_project;
+
+ #[pin_project(Replace)]
+ pub(crate) struct A {
+ pub b: u8,
+ }
+}
+
+#[test]
+fn visibility() {
+ let mut x = visibility::A { b: 0 };
+ let x = Pin::new(&mut x);
+ let y = x.as_ref().project_ref();
+ let _: &u8 = y.b;
+ let y = x.project();
+ let _: &mut u8 = y.b;
+}
+
+#[test]
+fn trivial_bounds() {
+ #[pin_project(Replace)]
+ pub struct NoGenerics {
+ #[pin]
+ field: PhantomPinned,
+ }
+}
+
+#[test]
+fn dst() {
+ #[pin_project]
+ struct Struct1<T: ?Sized> {
+ x: T,
+ }
+
+ let mut x = Struct1 { x: 0_u8 };
+ let x: Pin<&mut Struct1<dyn core::fmt::Debug>> = Pin::new(&mut x as _);
+ let _y: &mut (dyn core::fmt::Debug) = x.project().x;
+
+ #[pin_project]
+ struct Struct2<T: ?Sized> {
+ #[pin]
+ x: T,
+ }
+
+ let mut x = Struct2 { x: 0_u8 };
+ let x: Pin<&mut Struct2<dyn core::fmt::Debug + Unpin>> = Pin::new(&mut x as _);
+ let _y: Pin<&mut (dyn core::fmt::Debug + Unpin)> = x.project().x;
+
+ #[pin_project(UnsafeUnpin)]
+ struct Struct5<T: ?Sized> {
+ x: T,
+ }
+
+ #[pin_project(UnsafeUnpin)]
+ struct Struct6<T: ?Sized> {
+ #[pin]
+ x: T,
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Struct7<T: ?Sized> {
+ x: T,
+ }
+
+ #[pinned_drop]
+ impl<T: ?Sized> PinnedDrop for Struct7<T> {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Struct8<T: ?Sized> {
+ #[pin]
+ x: T,
+ }
+
+ #[pinned_drop]
+ impl<T: ?Sized> PinnedDrop for Struct8<T> {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(!Unpin)]
+ struct Struct9<T: ?Sized> {
+ x: T,
+ }
+
+ #[pin_project(!Unpin)]
+ struct Struct10<T: ?Sized> {
+ #[pin]
+ x: T,
+ }
+
+ #[pin_project]
+ struct TupleStruct1<T: ?Sized>(T);
+
+ #[pin_project]
+ struct TupleStruct2<T: ?Sized>(#[pin] T);
+
+ #[pin_project(UnsafeUnpin)]
+ struct TupleStruct5<T: ?Sized>(T);
+
+ #[pin_project(UnsafeUnpin)]
+ struct TupleStruct6<T: ?Sized>(#[pin] T);
+
+ #[pin_project(PinnedDrop)]
+ struct TupleStruct7<T: ?Sized>(T);
+
+ #[pinned_drop]
+ impl<T: ?Sized> PinnedDrop for TupleStruct7<T> {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct TupleStruct8<T: ?Sized>(#[pin] T);
+
+ #[pinned_drop]
+ impl<T: ?Sized> PinnedDrop for TupleStruct8<T> {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(!Unpin)]
+ struct TupleStruct9<T: ?Sized>(T);
+
+ #[pin_project(!Unpin)]
+ struct TupleStruct10<T: ?Sized>(#[pin] T);
+}
+
+#[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993
+#[test]
+fn unsized_in_where_clause() {
+ #[pin_project]
+ struct Struct3<T>
+ where
+ T: ?Sized,
+ {
+ x: T,
+ }
+
+ #[pin_project]
+ struct Struct4<T>
+ where
+ T: ?Sized,
+ {
+ #[pin]
+ x: T,
+ }
+
+ #[pin_project]
+ struct TupleStruct3<T>(T)
+ where
+ T: ?Sized;
+
+ #[pin_project]
+ struct TupleStruct4<T>(#[pin] T)
+ where
+ T: ?Sized;
+}
+
+#[test]
+fn dyn_type() {
+ #[pin_project]
+ struct Struct1 {
+ f: dyn core::fmt::Debug,
+ }
+
+ #[pin_project]
+ struct Struct2 {
+ #[pin]
+ f: dyn core::fmt::Debug,
+ }
+
+ #[pin_project]
+ struct Struct3 {
+ f: dyn core::fmt::Debug + Send,
+ }
+
+ #[pin_project]
+ struct Struct4 {
+ #[pin]
+ f: dyn core::fmt::Debug + Send,
+ }
+
+ #[pin_project]
+ struct TupleStruct1(dyn core::fmt::Debug);
+
+ #[pin_project]
+ struct TupleStruct2(#[pin] dyn core::fmt::Debug);
+
+ #[pin_project]
+ struct TupleStruct3(dyn core::fmt::Debug + Send);
+
+ #[pin_project]
+ struct TupleStruct4(#[pin] dyn core::fmt::Debug + Send);
+}
+
+#[test]
+fn self_in_where_clause() {
+ pub trait Trait1 {}
+
+ #[pin_project(Replace)]
+ pub struct Struct1<T>
+ where
+ Self: Trait1,
+ {
+ x: T,
+ }
+
+ impl<T> Trait1 for Struct1<T> {}
+
+ pub trait Trait2 {
+ type Assoc;
+ }
+
+ #[pin_project(Replace)]
+ pub struct Struct2<T>
+ where
+ Self: Trait2<Assoc = Struct1<T>>,
+ <Self as Trait2>::Assoc: Trait1,
+ {
+ x: T,
+ }
+
+ impl<T> Trait2 for Struct2<T> {
+ type Assoc = Struct1<T>;
+ }
+}
+
+#[test]
+fn no_infer_outlives() {
+ trait Bar<X> {
+ type Y;
+ }
+
+ struct Example<A>(A);
+
+ impl<X, T> Bar<X> for Example<T> {
+ type Y = Option<T>;
+ }
+
+ #[pin_project(Replace)]
+ struct Foo<A, B> {
+ _x: <Example<A> as Bar<B>>::Y,
+ }
+}
+
+// https://github.com/rust-lang/rust/issues/47949
+// https://github.com/taiki-e/pin-project/pull/194#discussion_r419098111
+#[test]
+fn project_replace_panic() {
+ use std::panic;
+
+ #[pin_project(Replace)]
+ struct S<T, U> {
+ #[pin]
+ pinned: T,
+ unpinned: U,
+ }
+
+ struct D<'a>(&'a mut bool, bool);
+ impl Drop for D<'_> {
+ fn drop(&mut self) {
+ *self.0 = true;
+ if self.1 {
+ panic!()
+ }
+ }
+ }
+
+ let (mut a, mut b, mut c, mut d) = (false, false, false, false);
+ let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+ let mut x = S { pinned: D(&mut a, true), unpinned: D(&mut b, false) };
+ let _y = Pin::new(&mut x)
+ .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
+ // Previous `x.pinned` was dropped and panicked when `project_replace` is called, so this is unreachable.
+ unreachable!();
+ }));
+ assert!(res.is_err());
+ assert!(a);
+ assert!(b);
+ assert!(c);
+ assert!(d);
+
+ let (mut a, mut b, mut c, mut d) = (false, false, false, false);
+ let res = panic::catch_unwind(panic::AssertUnwindSafe(|| {
+ let mut x = S { pinned: D(&mut a, false), unpinned: D(&mut b, true) };
+ {
+ let _y = Pin::new(&mut x)
+ .project_replace(S { pinned: D(&mut c, false), unpinned: D(&mut d, false) });
+ // `_y` (previous `x.unpinned`) live to the end of this scope, so this is not unreachable,
+ // unreachable!();
+ }
+ unreachable!();
+ }));
+ assert!(res.is_err());
+ assert!(a);
+ assert!(b);
+ assert!(c);
+ assert!(d);
+}
diff --git a/tests/pinned_drop.rs b/tests/pinned_drop.rs
new file mode 100644
index 0000000..b0677e2
--- /dev/null
+++ b/tests/pinned_drop.rs
@@ -0,0 +1,235 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+#[test]
+fn safe_project() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct<'a> {
+ was_dropped: &'a mut bool,
+ #[pin]
+ field: u8,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct<'_> {
+ fn drop(self: Pin<&mut Self>) {
+ **self.project().was_dropped = true;
+ }
+ }
+
+ let mut was_dropped = false;
+ drop(Struct { was_dropped: &mut was_dropped, field: 42 });
+ assert!(was_dropped);
+}
+
+#[test]
+fn mut_self_argument() {
+ #[pin_project(PinnedDrop)]
+ struct Struct {
+ data: usize,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(mut self: Pin<&mut Self>) {
+ let _: &mut _ = &mut self.data;
+ }
+ }
+}
+
+#[test]
+fn self_in_vec() {
+ #[pin_project(PinnedDrop)]
+ struct Struct {
+ data: usize,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(self: Pin<&mut Self>) {
+ let _: Vec<_> = vec![self.data];
+ }
+ }
+}
+
+#[test]
+fn self_in_macro_containing_fn() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct {
+ data: usize,
+ }
+
+ macro_rules! emit {
+ ($($tt:tt)*) => {
+ $($tt)*
+ };
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(self: Pin<&mut Self>) {
+ let _ = emit!({
+ impl Struct {
+ pub fn f(self) {}
+ }
+ });
+ let _ = self.data;
+ }
+ }
+}
+
+#[test]
+fn self_call() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct {
+ data: usize,
+ }
+
+ trait Trait {
+ fn self_ref(&self) {}
+ fn self_pin_ref(self: Pin<&Self>) {}
+ fn self_mut(&mut self) {}
+ fn self_pin_mut(self: Pin<&mut Self>) {}
+ fn assoc_fn(_this: Pin<&mut Self>) {}
+ }
+
+ impl Trait for Struct {}
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(mut self: Pin<&mut Self>) {
+ self.self_ref();
+ self.as_ref().self_pin_ref();
+ self.self_mut();
+ self.as_mut().self_pin_mut();
+ Self::assoc_fn(self.as_mut());
+ <Self>::assoc_fn(self.as_mut());
+ }
+ }
+}
+
+#[test]
+fn self_expr() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct {
+ x: usize,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(mut self: Pin<&mut Self>) {
+ let _: Self = Self { x: 0 };
+ }
+ }
+
+ #[pin_project(PinnedDrop)]
+ pub struct TupleStruct(usize);
+
+ #[pinned_drop]
+ impl PinnedDrop for TupleStruct {
+ fn drop(mut self: Pin<&mut Self>) {
+ let _: Self = Self(0);
+ }
+ }
+}
+
+#[rustversion::since(1.37)]
+#[test]
+fn self_expr_enum() {
+ #[pin_project(PinnedDrop)]
+ pub enum Enum {
+ StructVariant { x: usize },
+ TupleVariant(usize),
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Enum {
+ fn drop(mut self: Pin<&mut Self>) {
+ let _: Self = Self::StructVariant { x: 0 };
+ let _: Self = Self::TupleVariant(0);
+ }
+ }
+}
+
+#[test]
+fn self_pat() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct {
+ x: usize,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ #[allow(irrefutable_let_patterns)]
+ #[allow(clippy::match_single_binding)]
+ fn drop(mut self: Pin<&mut Self>) {
+ match *self {
+ Self { x: _ } => {}
+ }
+ if let Self { x: _ } = *self {}
+ let Self { x: _ } = *self;
+ }
+ }
+
+ #[pin_project(PinnedDrop)]
+ pub struct TupleStruct(usize);
+
+ #[pinned_drop]
+ impl PinnedDrop for TupleStruct {
+ #[allow(irrefutable_let_patterns)]
+ fn drop(mut self: Pin<&mut Self>) {
+ match *self {
+ Self(_) => {}
+ }
+ if let Self(_) = *self {}
+ let Self(_) = *self;
+ }
+ }
+}
+
+#[rustversion::since(1.37)]
+#[test]
+fn self_pat_enum() {
+ #[pin_project(PinnedDrop)]
+ pub enum Enum {
+ StructVariant { x: usize },
+ TupleVariant(usize),
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Enum {
+ fn drop(mut self: Pin<&mut Self>) {
+ match *self {
+ Self::StructVariant { x: _ } => {}
+ Self::TupleVariant(_) => {}
+ }
+ if let Self::StructVariant { x: _ } = *self {}
+ if let Self::TupleVariant(_) = *self {}
+ }
+ }
+}
+
+// See also `ui/pinned_drop/self.rs`.
+#[rustversion::since(1.40)] // https://github.com/rust-lang/rust/pull/64690
+#[test]
+fn self_in_macro_def() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct {
+ x: usize,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(self: Pin<&mut Self>) {
+ macro_rules! t {
+ () => {{
+ let _ = self;
+ }};
+ }
+ t!();
+ }
+ }
+}
diff --git a/tests/project.rs b/tests/project.rs
new file mode 100644
index 0000000..a0f8b07
--- /dev/null
+++ b/tests/project.rs
@@ -0,0 +1,297 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+// Ceurrently, `#[attr] if true {}` doesn't even *parse* on MSRV,
+// which means that it will error even behind a `#[rustversion::since(..)]`
+//
+// This trick makes sure that we don't even attempt to parse
+// the `#[project] if let _` test on MSRV.
+#[rustversion::since(1.43)]
+include!("project_if_attr.rs.in");
+
+use pin_project::{pin_project, project, project_ref, project_replace};
+use std::pin::Pin;
+
+#[project] // Nightly does not need a dummy attribute to the function.
+#[test]
+fn project_stmt_expr() {
+ #[pin_project]
+ struct Struct<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ let mut s = Struct { field1: 1, field2: 2 };
+
+ #[project]
+ let Struct { field1, field2 } = Pin::new(&mut s).project();
+
+ let x: Pin<&mut i32> = field1;
+ assert_eq!(*x, 1);
+
+ let y: &mut i32 = field2;
+ assert_eq!(*y, 2);
+
+ #[pin_project]
+ struct TupleStruct<T, U>(#[pin] T, U);
+
+ let mut s = TupleStruct(1, 2);
+
+ #[project]
+ let TupleStruct(x, y) = Pin::new(&mut s).project();
+
+ let x: Pin<&mut i32> = x;
+ assert_eq!(*x, 1);
+
+ let y: &mut i32 = y;
+ assert_eq!(*y, 2);
+
+ #[pin_project]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut e = Enum::Variant1(1, 2);
+
+ let mut e = Pin::new(&mut e).project();
+
+ #[project]
+ match &mut e {
+ Enum::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ Enum::None => {}
+ }
+
+ #[project]
+ let val = match &mut e {
+ Enum::Variant1(_, _) => true,
+ Enum::Variant2 { .. } => false,
+ Enum::None => false,
+ };
+ assert_eq!(val, true);
+}
+
+#[test]
+fn project_impl() {
+ #[pin_project]
+ struct HasGenerics<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ #[project]
+ impl<T, U> HasGenerics<T, U> {
+ fn a(self) {
+ let Self { field1, field2 } = self;
+
+ let _x: Pin<&mut T> = field1;
+ let _y: &mut U = field2;
+ }
+ }
+
+ #[pin_project]
+ struct NoneGenerics {
+ #[pin]
+ field1: i32,
+ field2: u32,
+ }
+
+ #[project]
+ impl NoneGenerics {}
+
+ #[pin_project]
+ struct HasLifetimes<'a, T, U> {
+ #[pin]
+ field1: &'a mut T,
+ field2: U,
+ }
+
+ #[project]
+ impl<T, U> HasLifetimes<'_, T, U> {}
+
+ #[pin_project]
+ struct HasOverlappingLifetimes<'pin, T, U> {
+ #[pin]
+ field1: &'pin mut T,
+ field2: U,
+ }
+
+ #[allow(single_use_lifetimes)]
+ #[project]
+ impl<'pin, T, U> HasOverlappingLifetimes<'pin, T, U> {}
+
+ #[pin_project]
+ struct HasOverlappingLifetimes2<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ #[allow(single_use_lifetimes)]
+ #[allow(clippy::needless_lifetimes)]
+ #[project]
+ impl<T, U> HasOverlappingLifetimes2<T, U> {
+ fn foo<'pin>(&'pin self) {}
+ }
+}
+
+#[pin_project]
+struct A {
+ #[pin]
+ field: u8,
+}
+
+mod project_use_1 {
+ use crate::A;
+ use core::pin::Pin;
+ use pin_project::project;
+
+ #[project]
+ use crate::A;
+
+ #[project]
+ #[test]
+ fn project_use() {
+ let mut x = A { field: 0 };
+ #[project]
+ let A { field } = Pin::new(&mut x).project();
+ let _: Pin<&mut u8> = field;
+ }
+}
+
+mod project_use_2 {
+ #[project]
+ use crate::A;
+ use pin_project::project;
+
+ #[project]
+ impl A {
+ fn project_use(self) {}
+ }
+}
+
+#[allow(clippy::unnecessary_operation, clippy::unit_arg)]
+#[test]
+#[project]
+fn non_stmt_expr_match() {
+ #[pin_project]
+ enum Enum<A> {
+ Variant(#[pin] A),
+ }
+
+ let mut x = Enum::Variant(1);
+ let x = Pin::new(&mut x).project();
+
+ Some(
+ #[project]
+ match x {
+ Enum::Variant(_x) => {}
+ },
+ );
+}
+
+// https://github.com/taiki-e/pin-project/issues/206
+#[allow(clippy::unnecessary_operation, clippy::unit_arg)]
+#[test]
+#[project]
+fn issue_206() {
+ #[pin_project]
+ enum Enum<A> {
+ Variant(#[pin] A),
+ }
+
+ let mut x = Enum::Variant(1);
+ let x = Pin::new(&mut x).project();
+
+ Some({
+ #[project]
+ match &x {
+ Enum::Variant(_) => {}
+ }
+ });
+
+ #[allow(clippy::never_loop)]
+ loop {
+ let _ = {
+ #[project]
+ match &x {
+ Enum::Variant(_) => {}
+ }
+ };
+ break;
+ }
+}
+
+#[project]
+#[test]
+fn combine() {
+ #[pin_project(Replace)]
+ enum Enum<A> {
+ V1(#[pin] A),
+ V2,
+ }
+
+ let mut x = Enum::V1(1);
+ #[project]
+ match Pin::new(&mut x).project() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_ref]
+ match Pin::new(&x).project_ref() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_replace]
+ match Pin::new(&mut x).project_replace(Enum::V2) {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+}
+
+// FIXME: This should be denied, but allowed for compatibility at this time.
+#[project]
+#[project_ref]
+#[project_replace]
+#[test]
+fn combine_compat() {
+ #[pin_project(Replace)]
+ enum Enum<A> {
+ V1(#[pin] A),
+ V2,
+ }
+
+ let mut x = Enum::V1(1);
+ #[project]
+ match Pin::new(&mut x).project() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_ref]
+ match Pin::new(&x).project_ref() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_replace]
+ match Pin::new(&mut x).project_replace(Enum::V2) {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+}
diff --git a/tests/project_if_attr.rs.in b/tests/project_if_attr.rs.in
new file mode 100644
index 0000000..a8ceeac
--- /dev/null
+++ b/tests/project_if_attr.rs.in
@@ -0,0 +1,44 @@
+#[test]
+#[project]
+fn project_if_let() {
+ #[pin_project]
+ enum Foo<A, B> {
+ Variant1(#[pin] A),
+ Variant2(u8),
+ Variant3 {
+ #[pin] field: B
+ }
+ }
+
+ let mut x: Foo<bool, f32> = Foo::Variant1(true);
+ let x = Pin::new(&mut x).project();
+
+ #[project]
+ if let Foo::Variant1(a) = x {
+ let a: Pin<&mut bool> = a;
+ assert_eq!(*a, true);
+ } else if let Foo::Variant2(_) = x {
+ unreachable!();
+ } else if let Foo::Variant3 { .. } = x {
+ unreachable!();
+ }
+}
+
+#[allow(clippy::unnecessary_operation, clippy::unit_arg)]
+#[test]
+#[project]
+fn non_stmt_expr_if_let() {
+ #[pin_project]
+ enum Enum<A> {
+ Variant(#[pin] A),
+ }
+
+ let mut x = Enum::Variant(1);
+ let x = Pin::new(&mut x).project();
+
+ #[allow(irrefutable_let_patterns)]
+ Some(
+ #[project]
+ if let Enum::Variant(_x) = x {},
+ );
+}
diff --git a/tests/project_ref.rs b/tests/project_ref.rs
new file mode 100644
index 0000000..e38ef83
--- /dev/null
+++ b/tests/project_ref.rs
@@ -0,0 +1,174 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+use pin_project::{pin_project, project_ref};
+use std::pin::Pin;
+
+#[project_ref] // Nightly does not need a dummy attribute to the function.
+#[test]
+fn project_stmt_expr() {
+ #[pin_project]
+ struct Struct<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ let s = Struct { field1: 1, field2: 2 };
+
+ #[project_ref]
+ let Struct { field1, field2 } = Pin::new(&s).project_ref();
+
+ let x: Pin<&i32> = field1;
+ assert_eq!(*x, 1);
+
+ let y: &i32 = field2;
+ assert_eq!(*y, 2);
+
+ // tuple struct
+
+ #[pin_project]
+ struct TupleStruct<T, U>(#[pin] T, U);
+
+ let s = TupleStruct(1, 2);
+
+ #[project_ref]
+ let TupleStruct(x, y) = Pin::new(&s).project_ref();
+
+ let x: Pin<&i32> = x;
+ assert_eq!(*x, 1);
+
+ let y: &i32 = y;
+ assert_eq!(*y, 2);
+
+ #[pin_project]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let e = Enum::Variant1(1, 2);
+
+ let e = Pin::new(&e).project_ref();
+
+ #[project_ref]
+ match &e {
+ Enum::Variant1(x, y) => {
+ let x: &Pin<&i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &&i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: &Pin<&i32> = field1;
+ let _y: &&i32 = field2;
+ }
+ Enum::None => {}
+ }
+
+ #[project_ref]
+ let val = match &e {
+ Enum::Variant1(_, _) => true,
+ Enum::Variant2 { .. } => false,
+ Enum::None => false,
+ };
+ assert_eq!(val, true);
+}
+
+#[test]
+fn project_impl() {
+ #[pin_project]
+ struct HasGenerics<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ #[project_ref]
+ impl<T, U> HasGenerics<T, U> {
+ fn a(self) {
+ let Self { field1, field2 } = self;
+
+ let _x: Pin<&T> = field1;
+ let _y: &U = field2;
+ }
+ }
+
+ #[pin_project]
+ struct NoneGenerics {
+ #[pin]
+ field1: i32,
+ field2: u32,
+ }
+
+ #[project_ref]
+ impl NoneGenerics {}
+
+ #[pin_project]
+ struct HasLifetimes<'a, T, U> {
+ #[pin]
+ field1: &'a mut T,
+ field2: U,
+ }
+
+ #[project_ref]
+ impl<T, U> HasLifetimes<'_, T, U> {}
+
+ #[pin_project]
+ struct HasOverlappingLifetimes<'pin, T, U> {
+ #[pin]
+ field1: &'pin mut T,
+ field2: U,
+ }
+
+ #[allow(single_use_lifetimes)]
+ #[project_ref]
+ impl<'pin, T, U> HasOverlappingLifetimes<'pin, T, U> {}
+
+ #[pin_project]
+ struct HasOverlappingLifetimes2<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ #[allow(single_use_lifetimes)]
+ #[allow(clippy::needless_lifetimes)]
+ #[project_ref]
+ impl<T, U> HasOverlappingLifetimes2<T, U> {
+ fn foo<'pin>(&'pin self) {}
+ }
+}
+
+#[project_ref]
+#[test]
+fn combine() {
+ #[pin_project(Replace)]
+ enum Enum<A> {
+ V1(#[pin] A),
+ V2,
+ }
+
+ let mut x = Enum::V1(1);
+ #[project]
+ match Pin::new(&mut x).project() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_ref]
+ match Pin::new(&x).project_ref() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_replace]
+ match Pin::new(&mut x).project_replace(Enum::V2) {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+}
diff --git a/tests/project_replace.rs b/tests/project_replace.rs
new file mode 100644
index 0000000..9c8a5ab
--- /dev/null
+++ b/tests/project_replace.rs
@@ -0,0 +1,98 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+use pin_project::{pin_project, project_replace};
+use std::{marker::PhantomData, pin::Pin};
+
+#[project_replace] // Nightly does not need a dummy attribute to the function.
+#[test]
+fn project_replace_stmt_expr() {
+ #[pin_project(Replace)]
+ struct Struct<T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ }
+
+ let mut s = Struct { field1: 1, field2: 2 };
+
+ #[project_replace]
+ let Struct { field1, field2 } =
+ Pin::new(&mut s).project_replace(Struct { field1: 42, field2: 43 });
+
+ let _x: PhantomData<i32> = field1;
+
+ let y: i32 = field2;
+ assert_eq!(y, 2);
+
+ // tuple struct
+
+ #[pin_project(Replace)]
+ struct TupleStruct<T, U>(#[pin] T, U);
+
+ let mut s = TupleStruct(1, 2);
+
+ #[project_replace]
+ let TupleStruct(x, y) = Pin::new(&mut s).project_replace(TupleStruct(42, 43));
+
+ let _x: PhantomData<i32> = x;
+ let y: i32 = y;
+ assert_eq!(y, 2);
+
+ #[pin_project(Replace)]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut e = Enum::Variant1(1, 2);
+
+ let e = Pin::new(&mut e).project_replace(Enum::None);
+
+ #[project_replace]
+ match e {
+ Enum::Variant1(x, y) => {
+ let _x: PhantomData<i32> = x;
+ let y: i32 = y;
+ assert_eq!(y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: PhantomData<i32> = field1;
+ let _y: i32 = field2;
+ panic!()
+ }
+ Enum::None => panic!(),
+ }
+}
+
+#[project_replace]
+#[test]
+fn combine() {
+ #[pin_project(Replace)]
+ enum Enum<A> {
+ V1(#[pin] A),
+ V2,
+ }
+
+ let mut x = Enum::V1(1);
+ #[project]
+ match Pin::new(&mut x).project() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_ref]
+ match Pin::new(&x).project_ref() {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+ #[project_replace]
+ match Pin::new(&mut x).project_replace(Enum::V2) {
+ Enum::V1(_) => {}
+ Enum::V2 => unreachable!(),
+ }
+}
diff --git a/tests/repr_packed.rs b/tests/repr_packed.rs
new file mode 100644
index 0000000..ca56959
--- /dev/null
+++ b/tests/repr_packed.rs
@@ -0,0 +1,50 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+#![deny(safe_packed_borrows)]
+
+use std::cell::Cell;
+
+// Ensure that the compiler doesn't copy the fields
+// of #[repr(packed)] types during drop, if the field has alignment 1
+// (that is, any reference to the field is guaranteed to have proper alignment)
+// We are currently unable to statically prevent the usage of #[pin_project]
+// on #[repr(packed)] types composed entirely of fields of alignment 1.
+// This shouldn't lead to undefined behavior, as long as the compiler doesn't
+// try to move the field anyway during drop.
+//
+// This tests validates that the compiler is doing what we expect.
+#[test]
+fn weird_repr_packed() {
+ // We keep track of the field address during
+ // drop using a thread local, to avoid changing
+ // the layout of our #[repr(packed)] type.
+ thread_local! {
+ static FIELD_ADDR: Cell<usize> = Cell::new(0);
+ }
+
+ #[repr(packed)]
+ struct Foo {
+ field: u8,
+ }
+
+ impl Drop for Foo {
+ fn drop(&mut self) {
+ FIELD_ADDR.with(|f| {
+ f.set(&self.field as *const u8 as usize);
+ })
+ }
+ }
+
+ #[allow(clippy::let_and_return)]
+ let field_addr = {
+ // We let this field drop by going out of scope,
+ // rather than explicitly calling drop(foo).
+ // Calling drop(foo) causes 'foo' to be moved
+ // into the 'drop' function, resulting in a different
+ // address.
+ let x = Foo { field: 27 };
+ let field_addr = &x.field as *const u8 as usize;
+ field_addr
+ };
+ assert_eq!(field_addr, FIELD_ADDR.with(|f| f.get()));
+}
diff --git a/tests/ui/cfg/cfg_attr-resolve.rs b/tests/ui/cfg/cfg_attr-resolve.rs
new file mode 100644
index 0000000..e16f3e8
--- /dev/null
+++ b/tests/ui/cfg/cfg_attr-resolve.rs
@@ -0,0 +1,11 @@
+use std::pin::Pin;
+
+#[cfg_attr(any(), pin_project::pin_project)]
+struct Foo<T> {
+ inner: T,
+}
+
+fn main() {
+ let mut x = Foo { inner: 0_u8 };
+ let _x = Pin::new(&mut x).project(); //~ ERROR E0599
+}
diff --git a/tests/ui/cfg/cfg_attr-resolve.stderr b/tests/ui/cfg/cfg_attr-resolve.stderr
new file mode 100644
index 0000000..45af3ae
--- /dev/null
+++ b/tests/ui/cfg/cfg_attr-resolve.stderr
@@ -0,0 +1,5 @@
+error[E0599]: no method named `project` found for struct `std::pin::Pin<&mut Foo<u8>>` in the current scope
+ --> $DIR/cfg_attr-resolve.rs:10:31
+ |
+10 | let _x = Pin::new(&mut x).project(); //~ ERROR E0599
+ | ^^^^^^^ method not found in `std::pin::Pin<&mut Foo<u8>>`
diff --git a/tests/ui/cfg/cfg_attr-type-mismatch.rs b/tests/ui/cfg/cfg_attr-type-mismatch.rs
new file mode 100644
index 0000000..2807c87
--- /dev/null
+++ b/tests/ui/cfg/cfg_attr-type-mismatch.rs
@@ -0,0 +1,24 @@
+use pin_project::pin_project;
+use std::pin::Pin;
+
+#[cfg_attr(not(any()), pin_project)]
+struct Foo<T> {
+ #[cfg_attr(any(), pin)]
+ inner: T,
+}
+
+#[cfg_attr(not(any()), pin_project)]
+struct Bar<T> {
+ #[cfg_attr(not(any()), pin)]
+ inner: T,
+}
+
+fn main() {
+ let mut x = Foo { inner: 0_u8 };
+ let x = Pin::new(&mut x).project();
+ let _: Pin<&mut u8> = x.inner; //~ ERROR E0308
+
+ let mut x = Bar { inner: 0_u8 };
+ let x = Pin::new(&mut x).project();
+ let _: &mut u8 = x.inner; //~ ERROR E0308
+}
diff --git a/tests/ui/cfg/cfg_attr-type-mismatch.stderr b/tests/ui/cfg/cfg_attr-type-mismatch.stderr
new file mode 100644
index 0000000..2868299
--- /dev/null
+++ b/tests/ui/cfg/cfg_attr-type-mismatch.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+ --> $DIR/cfg_attr-type-mismatch.rs:19:27
+ |
+19 | let _: Pin<&mut u8> = x.inner; //~ ERROR E0308
+ | ------------ ^^^^^^^ expected struct `std::pin::Pin`, found `&mut u8`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `std::pin::Pin<&mut u8>`
+ found mutable reference `&mut u8`
+
+error[E0308]: mismatched types
+ --> $DIR/cfg_attr-type-mismatch.rs:23:22
+ |
+23 | let _: &mut u8 = x.inner; //~ ERROR E0308
+ | ------- ^^^^^^^
+ | | |
+ | | expected `&mut u8`, found struct `std::pin::Pin`
+ | | help: consider mutably borrowing here: `&mut x.inner`
+ | expected due to this
+ |
+ = note: expected mutable reference `&mut u8`
+ found struct `std::pin::Pin<&mut u8>`
diff --git a/tests/ui/cfg/cfg_attr-unpin.rs b/tests/ui/cfg/cfg_attr-unpin.rs
new file mode 100644
index 0000000..7b88205
--- /dev/null
+++ b/tests/ui/cfg/cfg_attr-unpin.rs
@@ -0,0 +1,21 @@
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[cfg_attr(any(), pin_project)]
+struct Foo<T> {
+ inner: T,
+}
+
+#[cfg_attr(not(any()), pin_project)]
+struct Bar<T> {
+ #[cfg_attr(not(any()), pin)]
+ inner: T,
+}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Foo<PhantomPinned>>(); // ERROR E0277
+ is_unpin::<Bar<()>>(); // Ok
+ is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
+}
diff --git a/tests/ui/cfg/cfg_attr-unpin.stderr b/tests/ui/cfg/cfg_attr-unpin.stderr
new file mode 100644
index 0000000..ce31c24
--- /dev/null
+++ b/tests/ui/cfg/cfg_attr-unpin.stderr
@@ -0,0 +1,22 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/cfg_attr-unpin.rs:18:5
+ |
+15 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+18 | is_unpin::<Foo<PhantomPinned>>(); // ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `Foo<std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Foo<std::marker::PhantomPinned>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/cfg_attr-unpin.rs:20:5
+ |
+15 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+20 | is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<std::marker::PhantomPinned>`
diff --git a/tests/ui/cfg/packed_sneaky-span-issue-1.rs b/tests/ui/cfg/packed_sneaky-span-issue-1.rs
new file mode 100644
index 0000000..3776dac
--- /dev/null
+++ b/tests/ui/cfg/packed_sneaky-span-issue-1.rs
@@ -0,0 +1,18 @@
+use auxiliary_macros::hidden_repr;
+use pin_project::pin_project;
+
+//~ ERROR may not be used on #[repr(packed)] types
+// span is lost.
+// Refs: https://github.com/rust-lang/rust/issues/43081
+#[pin_project]
+#[hidden_repr(packed)]
+struct Foo {
+ #[cfg(not(any()))]
+ #[pin]
+ field: u32,
+ #[cfg(any())]
+ #[pin]
+ field: u8,
+}
+
+fn main() {}
diff --git a/tests/ui/cfg/packed_sneaky-span-issue-1.stderr b/tests/ui/cfg/packed_sneaky-span-issue-1.stderr
new file mode 100644
index 0000000..f4d7dee
--- /dev/null
+++ b/tests/ui/cfg/packed_sneaky-span-issue-1.stderr
@@ -0,0 +1 @@
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
diff --git a/tests/ui/cfg/packed_sneaky-span-issue-2.rs b/tests/ui/cfg/packed_sneaky-span-issue-2.rs
new file mode 100644
index 0000000..aa65d33
--- /dev/null
+++ b/tests/ui/cfg/packed_sneaky-span-issue-2.rs
@@ -0,0 +1,18 @@
+use auxiliary_macros::hidden_repr;
+use pin_project::pin_project;
+
+//~ ERROR may not be used on #[repr(packed)] types
+// span is lost.
+// Refs: https://github.com/rust-lang/rust/issues/43081
+#[pin_project]
+#[hidden_repr(packed)]
+struct Foo {
+ #[cfg(any())]
+ #[pin]
+ field: u32,
+ #[cfg(not(any()))]
+ #[pin]
+ field: u8,
+}
+
+fn main() {}
diff --git a/tests/ui/cfg/packed_sneaky-span-issue-2.stderr b/tests/ui/cfg/packed_sneaky-span-issue-2.stderr
new file mode 100644
index 0000000..f4d7dee
--- /dev/null
+++ b/tests/ui/cfg/packed_sneaky-span-issue-2.stderr
@@ -0,0 +1 @@
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
diff --git a/tests/ui/cfg/packed_sneaky.rs b/tests/ui/cfg/packed_sneaky.rs
new file mode 100644
index 0000000..3305ed3
--- /dev/null
+++ b/tests/ui/cfg/packed_sneaky.rs
@@ -0,0 +1,12 @@
+use auxiliary_macros::hidden_repr_cfg_not_any;
+use pin_project::pin_project;
+
+// `#[hidden_repr_cfg_not_any(packed)]` generates `#[cfg_attr(not(any()), repr(packed))]`.
+#[pin_project]
+#[hidden_repr_cfg_not_any(packed)] //~ ERROR may not be used on #[repr(packed)] types
+struct Foo {
+ #[pin]
+ field: u32,
+}
+
+fn main() {}
diff --git a/tests/ui/cfg/packed_sneaky.stderr b/tests/ui/cfg/packed_sneaky.stderr
new file mode 100644
index 0000000..5910cf4
--- /dev/null
+++ b/tests/ui/cfg/packed_sneaky.stderr
@@ -0,0 +1,7 @@
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed_sneaky.rs:6:1
+ |
+6 | #[hidden_repr_cfg_not_any(packed)] //~ ERROR may not be used on #[repr(packed)] types
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/cfg/proper_unpin.rs b/tests/ui/cfg/proper_unpin.rs
new file mode 100644
index 0000000..b7bb04d
--- /dev/null
+++ b/tests/ui/cfg/proper_unpin.rs
@@ -0,0 +1,28 @@
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project]
+struct Foo<T> {
+ #[cfg(any())]
+ #[pin]
+ inner: T,
+ #[cfg(not(any()))]
+ inner: T,
+}
+
+#[pin_project]
+struct Bar<T> {
+ #[cfg(any())]
+ inner: T,
+ #[cfg(not(any()))]
+ #[pin]
+ inner: T,
+}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Foo<PhantomPinned>>(); // Ok
+ is_unpin::<Bar<()>>(); // Ok
+ is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
+}
diff --git a/tests/ui/cfg/proper_unpin.stderr b/tests/ui/cfg/proper_unpin.stderr
new file mode 100644
index 0000000..407d900
--- /dev/null
+++ b/tests/ui/cfg/proper_unpin.stderr
@@ -0,0 +1,11 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:27:5
+ |
+22 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+27 | is_unpin::<Bar<PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `__SCOPE_Bar::__Bar<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<std::marker::PhantomPinned>`
diff --git a/tests/ui/cfg/unsupported.rs b/tests/ui/cfg/unsupported.rs
new file mode 100644
index 0000000..5205307
--- /dev/null
+++ b/tests/ui/cfg/unsupported.rs
@@ -0,0 +1,13 @@
+use pin_project::pin_project;
+
+//~ ERROR may not be used on structs with zero fields
+// span is lost.
+// Refs: https://github.com/rust-lang/rust/issues/43081
+#[pin_project]
+struct Struct {
+ #[cfg(any())]
+ #[pin]
+ f: u8,
+}
+
+fn main() {}
diff --git a/tests/ui/cfg/unsupported.stderr b/tests/ui/cfg/unsupported.stderr
new file mode 100644
index 0000000..0ee8676
--- /dev/null
+++ b/tests/ui/cfg/unsupported.stderr
@@ -0,0 +1 @@
+error: #[pin_project] attribute may not be used on structs with zero fields
diff --git a/tests/ui/not_unpin/assert-not-unpin.rs b/tests/ui/not_unpin/assert-not-unpin.rs
new file mode 100644
index 0000000..b8f8238
--- /dev/null
+++ b/tests/ui/not_unpin/assert-not-unpin.rs
@@ -0,0 +1,40 @@
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+struct Inner<T> {
+ val: T,
+}
+
+#[pin_project(!Unpin)]
+struct Foo<T, U> {
+ #[pin]
+ inner: Inner<T>,
+ other: U,
+}
+
+#[pin_project(!Unpin)]
+struct TrivialBounds {
+ #[pin]
+ field1: PhantomPinned,
+}
+
+#[pin_project(!Unpin)]
+struct Bar<'a, T, U> {
+ #[pin]
+ inner: &'a mut Inner<T>,
+ other: U,
+}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Foo<(), ()>>(); //~ ERROR E0277
+ is_unpin::<Foo<PhantomPinned, ()>>(); //~ ERROR E0277
+ is_unpin::<Foo<(), PhantomPinned>>(); //~ ERROR E0277
+ is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+
+ is_unpin::<TrivialBounds>(); //~ ERROR E0277
+
+ is_unpin::<Bar<'_, (), ()>>(); //~ ERROR E0277
+ is_unpin::<Bar<'_, PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+}
diff --git a/tests/ui/not_unpin/assert-not-unpin.stderr b/tests/ui/not_unpin/assert-not-unpin.stderr
new file mode 100644
index 0000000..5e323fc
--- /dev/null
+++ b/tests/ui/not_unpin/assert-not-unpin.stderr
@@ -0,0 +1,83 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:31:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+31 | is_unpin::<Foo<(), ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), ()>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:32:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+32 | is_unpin::<Foo<PhantomPinned, ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned, ()>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:33:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+33 | is_unpin::<Foo<(), PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<(), std::marker::PhantomPinned>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:34:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+34 | is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned, std::marker::PhantomPinned>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:36:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+36 | is_unpin::<TrivialBounds>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:38:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+38 | is_unpin::<Bar<'_, (), ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<'_, (), ()>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/assert-not-unpin.rs:39:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+39 | is_unpin::<Bar<'_, PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `pin_project::__private::Wrapper<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>`
diff --git a/tests/ui/not_unpin/conflict-unpin.rs b/tests/ui/not_unpin/conflict-unpin.rs
new file mode 100644
index 0000000..f259f6c
--- /dev/null
+++ b/tests/ui/not_unpin/conflict-unpin.rs
@@ -0,0 +1,30 @@
+use pin_project::pin_project;
+
+#[pin_project(!Unpin)] //~ ERROR E0119
+struct Foo<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T, U> Unpin for Foo<T, U> where T: Unpin {}
+
+#[pin_project(!Unpin)] //~ ERROR E0119
+struct Bar<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T, U> Unpin for Bar<T, U> {}
+
+#[pin_project(!Unpin)] //~ ERROR E0119
+struct Baz<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T: Unpin, U: Unpin> Unpin for Baz<T, U> {}
+
+fn main() {}
diff --git a/tests/ui/not_unpin/conflict-unpin.stderr b/tests/ui/not_unpin/conflict-unpin.stderr
new file mode 100644
index 0000000..7407bdf
--- /dev/null
+++ b/tests/ui/not_unpin/conflict-unpin.stderr
@@ -0,0 +1,26 @@
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`:
+ --> $DIR/conflict-unpin.rs:3:16
+ |
+3 | #[pin_project(!Unpin)] //~ ERROR E0119
+ | ^^^^^ conflicting implementation for `Foo<_, _>`
+...
+10 | impl<T, U> Unpin for Foo<T, U> where T: Unpin {}
+ | --------------------------------------------- first implementation here
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`:
+ --> $DIR/conflict-unpin.rs:12:16
+ |
+12 | #[pin_project(!Unpin)] //~ ERROR E0119
+ | ^^^^^ conflicting implementation for `Bar<_, _>`
+...
+19 | impl<T, U> Unpin for Bar<T, U> {}
+ | ------------------------------ first implementation here
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`:
+ --> $DIR/conflict-unpin.rs:21:16
+ |
+21 | #[pin_project(!Unpin)] //~ ERROR E0119
+ | ^^^^^ conflicting implementation for `Baz<_, _>`
+...
+28 | impl<T: Unpin, U: Unpin> Unpin for Baz<T, U> {}
+ | -------------------------------------------- first implementation here
diff --git a/tests/ui/not_unpin/impl-unsafe-unpin.rs b/tests/ui/not_unpin/impl-unsafe-unpin.rs
new file mode 100644
index 0000000..625dc29
--- /dev/null
+++ b/tests/ui/not_unpin/impl-unsafe-unpin.rs
@@ -0,0 +1,30 @@
+use pin_project::{pin_project, UnsafeUnpin};
+
+#[pin_project(!Unpin)] //~ ERROR E0119
+struct Foo<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+unsafe impl<T, U> UnsafeUnpin for Foo<T, U> where T: Unpin {}
+
+#[pin_project(!Unpin)] //~ ERROR E0119
+struct Bar<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+unsafe impl<T, U> UnsafeUnpin for Bar<T, U> {}
+
+#[pin_project(!Unpin)] //~ ERROR E0119
+struct Baz<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for Baz<T, U> {}
+
+fn main() {}
diff --git a/tests/ui/not_unpin/impl-unsafe-unpin.stderr b/tests/ui/not_unpin/impl-unsafe-unpin.stderr
new file mode 100644
index 0000000..ba80d5e
--- /dev/null
+++ b/tests/ui/not_unpin/impl-unsafe-unpin.stderr
@@ -0,0 +1,32 @@
+error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Foo<_, _>`:
+ --> $DIR/impl-unsafe-unpin.rs:3:1
+ |
+3 | #[pin_project(!Unpin)] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>`
+...
+10 | unsafe impl<T, U> UnsafeUnpin for Foo<T, U> where T: Unpin {}
+ | ---------------------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Bar<_, _>`:
+ --> $DIR/impl-unsafe-unpin.rs:12:1
+ |
+12 | #[pin_project(!Unpin)] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>`
+...
+19 | unsafe impl<T, U> UnsafeUnpin for Bar<T, U> {}
+ | ------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Baz<_, _>`:
+ --> $DIR/impl-unsafe-unpin.rs:21:1
+ |
+21 | #[pin_project(!Unpin)] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>`
+...
+28 | unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for Baz<T, U> {}
+ | --------------------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pin_project/add-pin-attr-to-struct.rs b/tests/ui/pin_project/add-pin-attr-to-struct.rs
new file mode 100644
index 0000000..f5364fc
--- /dev/null
+++ b/tests/ui/pin_project/add-pin-attr-to-struct.rs
@@ -0,0 +1,19 @@
+use auxiliary_macros::add_pin_attr;
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project]
+#[add_pin_attr(struct)] //~ ERROR duplicate #[pin] attribute
+struct Foo {
+ #[pin]
+ field: PhantomPinned,
+}
+
+#[add_pin_attr(struct)] //~ ERROR #[pin] attribute may only be used on fields of structs or variants
+#[pin_project]
+struct Bar {
+ #[pin]
+ field: PhantomPinned,
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/add-pin-attr-to-struct.stderr b/tests/ui/pin_project/add-pin-attr-to-struct.stderr
new file mode 100644
index 0000000..c2adaea
--- /dev/null
+++ b/tests/ui/pin_project/add-pin-attr-to-struct.stderr
@@ -0,0 +1,15 @@
+error: duplicate #[pin] attribute
+ --> $DIR/add-pin-attr-to-struct.rs:6:1
+ |
+6 | #[add_pin_attr(struct)] //~ ERROR duplicate #[pin] attribute
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: #[pin] attribute may only be used on fields of structs or variants
+ --> $DIR/add-pin-attr-to-struct.rs:12:1
+ |
+12 | #[add_pin_attr(struct)] //~ ERROR #[pin] attribute may only be used on fields of structs or variants
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pin_project/add-pinned-field.rs b/tests/ui/pin_project/add-pinned-field.rs
new file mode 100644
index 0000000..76394cf
--- /dev/null
+++ b/tests/ui/pin_project/add-pinned-field.rs
@@ -0,0 +1,23 @@
+use auxiliary_macros::add_pinned_field;
+use pin_project::pin_project;
+
+fn is_unpin<T: Unpin>() {}
+
+#[pin_project]
+#[add_pinned_field]
+struct Foo {
+ #[pin]
+ field: u32,
+}
+
+#[add_pinned_field]
+#[pin_project]
+struct Bar {
+ #[pin]
+ field: u32,
+}
+
+fn main() {
+ is_unpin::<Foo>(); //~ ERROR E0277
+ is_unpin::<Bar>(); //~ ERROR E0277
+}
diff --git a/tests/ui/pin_project/add-pinned-field.stderr b/tests/ui/pin_project/add-pinned-field.stderr
new file mode 100644
index 0000000..db07a74
--- /dev/null
+++ b/tests/ui/pin_project/add-pinned-field.stderr
@@ -0,0 +1,23 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/add-pinned-field.rs:21:5
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+21 | is_unpin::<Foo>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/add-pinned-field.rs:22:5
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+22 | is_unpin::<Bar>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^ within `__SCOPE_Bar::__Bar<'_>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `__SCOPE_Bar::__Bar<'_>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Bar`
diff --git a/tests/ui/pin_project/conflict-drop.rs b/tests/ui/pin_project/conflict-drop.rs
new file mode 100644
index 0000000..c965184
--- /dev/null
+++ b/tests/ui/pin_project/conflict-drop.rs
@@ -0,0 +1,31 @@
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+#[pin_project] //~ ERROR E0119
+struct Foo<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T, U> Drop for Foo<T, U> {
+ fn drop(&mut self) {}
+}
+
+#[pin_project(PinnedDrop)] //~ ERROR E0119
+struct Bar<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+#[pinned_drop]
+impl<T, U> PinnedDrop for Bar<T, U> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+impl<T, U> Drop for Bar<T, U> {
+ fn drop(&mut self) {}
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/conflict-drop.stderr b/tests/ui/pin_project/conflict-drop.stderr
new file mode 100644
index 0000000..f97d060
--- /dev/null
+++ b/tests/ui/pin_project/conflict-drop.stderr
@@ -0,0 +1,19 @@
+error[E0119]: conflicting implementations of trait `__SCOPE_Foo::FooMustNotImplDrop` for type `Foo<_, _>`:
+ --> $DIR/conflict-drop.rs:4:1
+ |
+4 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^
+ | |
+ | first implementation here
+ | conflicting implementation for `Foo<_, _>`
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `std::ops::Drop` for type `Bar<_, _>`:
+ --> $DIR/conflict-drop.rs:15:15
+ |
+15 | #[pin_project(PinnedDrop)] //~ ERROR E0119
+ | ^^^^^^^^^^ conflicting implementation for `Bar<_, _>`
+...
+27 | impl<T, U> Drop for Bar<T, U> {
+ | ----------------------------- first implementation here
diff --git a/tests/ui/pin_project/conflict-unpin.rs b/tests/ui/pin_project/conflict-unpin.rs
new file mode 100644
index 0000000..0c48d27
--- /dev/null
+++ b/tests/ui/pin_project/conflict-unpin.rs
@@ -0,0 +1,37 @@
+use pin_project::pin_project;
+
+// The same implementation.
+
+#[pin_project] //~ ERROR E0119
+struct Foo<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+// conflicting implementations
+impl<T, U> Unpin for Foo<T, U> where T: Unpin {} // Conditional Unpin impl
+
+// The implementation that under different conditions.
+
+#[pin_project] //~ ERROR E0119
+struct Bar<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+// conflicting implementations
+impl<T, U> Unpin for Bar<T, U> {} // Non-conditional Unpin impl
+
+#[pin_project] //~ ERROR E0119
+struct Baz<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+// conflicting implementations
+impl<T: Unpin, U: Unpin> Unpin for Baz<T, U> {} // Conditional Unpin impl
+
+fn main() {}
diff --git a/tests/ui/pin_project/conflict-unpin.stderr b/tests/ui/pin_project/conflict-unpin.stderr
new file mode 100644
index 0000000..0d6f439
--- /dev/null
+++ b/tests/ui/pin_project/conflict-unpin.stderr
@@ -0,0 +1,32 @@
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`:
+ --> $DIR/conflict-unpin.rs:5:1
+ |
+5 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>`
+...
+13 | impl<T, U> Unpin for Foo<T, U> where T: Unpin {} // Conditional Unpin impl
+ | --------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`:
+ --> $DIR/conflict-unpin.rs:17:1
+ |
+17 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>`
+...
+25 | impl<T, U> Unpin for Bar<T, U> {} // Non-conditional Unpin impl
+ | ------------------------------ first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`:
+ --> $DIR/conflict-unpin.rs:27:1
+ |
+27 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>`
+...
+35 | impl<T: Unpin, U: Unpin> Unpin for Baz<T, U> {} // Conditional Unpin impl
+ | -------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pin_project/impl-unsafe-unpin.rs b/tests/ui/pin_project/impl-unsafe-unpin.rs
new file mode 100644
index 0000000..94af322
--- /dev/null
+++ b/tests/ui/pin_project/impl-unsafe-unpin.rs
@@ -0,0 +1,30 @@
+use pin_project::{pin_project, UnsafeUnpin};
+
+#[pin_project] //~ ERROR E0119
+struct Foo<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+unsafe impl<T, U> UnsafeUnpin for Foo<T, U> where T: Unpin {}
+
+#[pin_project] //~ ERROR E0119
+struct Bar<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+unsafe impl<T, U> UnsafeUnpin for Bar<T, U> {}
+
+#[pin_project] //~ ERROR E0119
+struct Baz<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for Baz<T, U> {}
+
+fn main() {}
diff --git a/tests/ui/pin_project/impl-unsafe-unpin.stderr b/tests/ui/pin_project/impl-unsafe-unpin.stderr
new file mode 100644
index 0000000..78545c2
--- /dev/null
+++ b/tests/ui/pin_project/impl-unsafe-unpin.stderr
@@ -0,0 +1,32 @@
+error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Foo<_, _>`:
+ --> $DIR/impl-unsafe-unpin.rs:3:1
+ |
+3 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>`
+...
+10 | unsafe impl<T, U> UnsafeUnpin for Foo<T, U> where T: Unpin {}
+ | ---------------------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Bar<_, _>`:
+ --> $DIR/impl-unsafe-unpin.rs:12:1
+ |
+12 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>`
+...
+19 | unsafe impl<T, U> UnsafeUnpin for Bar<T, U> {}
+ | ------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `pin_project::UnsafeUnpin` for type `Baz<_, _>`:
+ --> $DIR/impl-unsafe-unpin.rs:21:1
+ |
+21 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>`
+...
+28 | unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for Baz<T, U> {}
+ | --------------------------------------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pin_project/invalid.rs b/tests/ui/pin_project/invalid.rs
new file mode 100644
index 0000000..1d05608
--- /dev/null
+++ b/tests/ui/pin_project/invalid.rs
@@ -0,0 +1,199 @@
+mod pin_argument {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ struct Struct {
+ #[pin()] //~ ERROR unexpected token
+ field: (),
+ }
+
+ #[pin_project]
+ struct TupleStruct(#[pin(foo)] ()); //~ ERROR unexpected token
+
+ #[pin_project]
+ enum EnumTuple {
+ V(#[pin(foo)] ()), //~ ERROR unexpected token
+ }
+
+ #[pin_project]
+ enum EnumStruct {
+ V {
+ #[pin(foo)] //~ ERROR unexpected token
+ field: (),
+ },
+ }
+}
+
+mod pin_attribute {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ struct DuplicateStruct {
+ #[pin]
+ #[pin] //~ ERROR duplicate #[pin] attribute
+ field: (),
+ }
+
+ #[pin_project]
+ struct DuplicateTupleStruct(
+ #[pin]
+ #[pin]
+ (),
+ //~^^ ERROR duplicate #[pin] attribute
+ );
+
+ #[pin_project]
+ enum DuplicateEnumTuple {
+ V(
+ #[pin]
+ #[pin]
+ (),
+ //~^^ ERROR duplicate #[pin] attribute
+ ),
+ }
+
+ #[pin_project]
+ enum DuplicateEnumStruct {
+ V {
+ #[pin]
+ #[pin] //~ ERROR duplicate #[pin] attribute
+ field: (),
+ },
+ }
+}
+
+mod pin_item {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ #[pin] //~ ERROR may only be used on fields of structs or variants
+ struct Struct {
+ #[pin]
+ field: (),
+ }
+
+ #[pin_project]
+ enum Variant {
+ #[pin] //~ ERROR may only be used on fields of structs or variants
+ V(()),
+ }
+
+ #[pin_project]
+ #[pin] //~ ERROR may only be used on fields of structs or variants
+ enum Enum {
+ V(()),
+ }
+}
+
+mod pin_project_argument {
+ use pin_project::pin_project;
+
+ #[pin_project(UnsafeUnpin,,)] //~ ERROR expected identifier
+ struct Unexpected1(#[pin] ());
+
+ #[pin_project(Foo)] //~ ERROR unexpected argument
+ struct Unexpected2(#[pin] ());
+
+ #[pin_project(,UnsafeUnpin)] //~ ERROR expected identifier
+ struct Unexpected3(#[pin] ());
+
+ #[pin_project()] // Ok
+ struct Unexpected4(#[pin] ());
+
+ #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument
+ struct DuplicatePinnedDrop(#[pin] ());
+
+ #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument
+ struct DuplicateReplace(#[pin] ());
+
+ #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument
+ struct DuplicateUnsafeUnpin(#[pin] ());
+
+ #[pin_project(!Unpin, !Unpin)] //~ ERROR duplicate `!Unpin` argument
+ struct DuplicateNotUnpin(#[pin] ());
+
+ #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument
+ struct Duplicate3(#[pin] ());
+
+ #[pin_project(PinnedDrop, UnsafeUnpin, PinnedDrop, UnsafeUnpin)] //~ ERROR duplicate `PinnedDrop` argument
+ struct Duplicate4(#[pin] ());
+
+ #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive
+ struct PinnedDropWithReplace1(#[pin] ());
+
+ #[pin_project(Replace, UnsafeUnpin, PinnedDrop)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive
+ struct PinnedDropWithReplace2(#[pin] ());
+
+ #[pin_project(UnsafeUnpin, !Unpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive
+ struct UnsafeUnpinWithNotUnpin1(#[pin] ());
+
+ #[pin_project(!Unpin, PinnedDrop, UnsafeUnpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive
+ struct UnsafeUnpinWithNotUnpin2(#[pin] ());
+
+ #[pin_project(!)] //~ ERROR unexpected end of input, expected `Unpin`
+ struct NotUnpin1(#[pin] ());
+
+ #[pin_project(Unpin)] //~ ERROR unexpected argument
+ struct NotUnpin2(#[pin] ());
+}
+
+mod pin_project_attribute {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ #[pin_project] //~ ERROR duplicate #[pin_project] attribute
+ struct Duplicate(#[pin] ());
+}
+
+mod pin_project_item {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ struct Struct {} //~ ERROR may not be used on structs with zero fields
+
+ #[pin_project]
+ struct TupleStruct(); //~ ERROR may not be used on structs with zero fields
+
+ #[pin_project]
+ struct UnitStruct; //~ ERROR may not be used on structs with zero fields
+
+ #[pin_project]
+ enum EnumEmpty {} //~ ERROR may not be used on enums without variants
+
+ #[pin_project]
+ enum EnumDiscriminant {
+ V = 2, //~ ERROR may not be used on enums with discriminants
+ }
+
+ #[pin_project]
+ enum EnumZeroFields {
+ Unit, //~ ERROR may not be used on enums with zero fields
+ Tuple(),
+ Struct {},
+ }
+
+ #[pin_project]
+ union Union {
+ //~^ ERROR may only be used on structs or enums
+ f: (),
+ }
+}
+
+// #[repr(packed)] is always detected first, even on unsupported structs.
+mod pin_project_item_packed {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ #[repr(packed)]
+ struct Struct {} //~ ERROR may not be used on #[repr(packed)] types
+
+ #[pin_project]
+ #[repr(packed)]
+ struct TupleStruct(); //~ ERROR may not be used on #[repr(packed)] types
+
+ #[pin_project]
+ #[repr(packed)]
+ struct UnitStruct; //~ ERROR may not be used on #[repr(packed)] types
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/invalid.stderr b/tests/ui/pin_project/invalid.stderr
new file mode 100644
index 0000000..95b57f0
--- /dev/null
+++ b/tests/ui/pin_project/invalid.stderr
@@ -0,0 +1,228 @@
+error: unexpected token: ()
+ --> $DIR/invalid.rs:6:14
+ |
+6 | #[pin()] //~ ERROR unexpected token
+ | ^^
+
+error: unexpected token: (foo)
+ --> $DIR/invalid.rs:11:29
+ |
+11 | struct TupleStruct(#[pin(foo)] ()); //~ ERROR unexpected token
+ | ^^^^^
+
+error: unexpected token: (foo)
+ --> $DIR/invalid.rs:15:16
+ |
+15 | V(#[pin(foo)] ()), //~ ERROR unexpected token
+ | ^^^^^
+
+error: unexpected token: (foo)
+ --> $DIR/invalid.rs:21:18
+ |
+21 | #[pin(foo)] //~ ERROR unexpected token
+ | ^^^^^
+
+error: duplicate #[pin] attribute
+ --> $DIR/invalid.rs:33:9
+ |
+33 | #[pin] //~ ERROR duplicate #[pin] attribute
+ | ^^^^^^
+
+error: duplicate #[pin] attribute
+ --> $DIR/invalid.rs:40:9
+ |
+40 | #[pin]
+ | ^^^^^^
+
+error: duplicate #[pin] attribute
+ --> $DIR/invalid.rs:49:13
+ |
+49 | #[pin]
+ | ^^^^^^
+
+error: duplicate #[pin] attribute
+ --> $DIR/invalid.rs:59:13
+ |
+59 | #[pin] //~ ERROR duplicate #[pin] attribute
+ | ^^^^^^
+
+error: #[pin] attribute may only be used on fields of structs or variants
+ --> $DIR/invalid.rs:69:5
+ |
+69 | #[pin] //~ ERROR may only be used on fields of structs or variants
+ | ^^^^^^
+
+error: #[pin] attribute may only be used on fields of structs or variants
+ --> $DIR/invalid.rs:77:9
+ |
+77 | #[pin] //~ ERROR may only be used on fields of structs or variants
+ | ^^^^^^
+
+error: #[pin] attribute may only be used on fields of structs or variants
+ --> $DIR/invalid.rs:82:5
+ |
+82 | #[pin] //~ ERROR may only be used on fields of structs or variants
+ | ^^^^^^
+
+error: expected identifier
+ --> $DIR/invalid.rs:91:31
+ |
+91 | #[pin_project(UnsafeUnpin,,)] //~ ERROR expected identifier
+ | ^
+
+error: unexpected argument: Foo
+ --> $DIR/invalid.rs:94:19
+ |
+94 | #[pin_project(Foo)] //~ ERROR unexpected argument
+ | ^^^
+
+error: expected identifier
+ --> $DIR/invalid.rs:97:19
+ |
+97 | #[pin_project(,UnsafeUnpin)] //~ ERROR expected identifier
+ | ^
+
+error: duplicate `PinnedDrop` argument
+ --> $DIR/invalid.rs:103:31
+ |
+103 | #[pin_project(PinnedDrop, PinnedDrop)] //~ ERROR duplicate `PinnedDrop` argument
+ | ^^^^^^^^^^
+
+error: duplicate `Replace` argument
+ --> $DIR/invalid.rs:106:28
+ |
+106 | #[pin_project(Replace, Replace)] //~ ERROR duplicate `Replace` argument
+ | ^^^^^^^
+
+error: duplicate `UnsafeUnpin` argument
+ --> $DIR/invalid.rs:109:32
+ |
+109 | #[pin_project(UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument
+ | ^^^^^^^^^^^
+
+error: duplicate `!Unpin` argument
+ --> $DIR/invalid.rs:112:27
+ |
+112 | #[pin_project(!Unpin, !Unpin)] //~ ERROR duplicate `!Unpin` argument
+ | ^^^^^^
+
+error: duplicate `UnsafeUnpin` argument
+ --> $DIR/invalid.rs:115:44
+ |
+115 | #[pin_project(PinnedDrop, UnsafeUnpin, UnsafeUnpin)] //~ ERROR duplicate `UnsafeUnpin` argument
+ | ^^^^^^^^^^^
+
+error: duplicate `PinnedDrop` argument
+ --> $DIR/invalid.rs:118:44
+ |
+118 | #[pin_project(PinnedDrop, UnsafeUnpin, PinnedDrop, UnsafeUnpin)] //~ ERROR duplicate `PinnedDrop` argument
+ | ^^^^^^^^^^
+
+error: arguments `PinnedDrop` and `Replace` are mutually exclusive
+ --> $DIR/invalid.rs:121:19
+ |
+121 | #[pin_project(PinnedDrop, Replace)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive
+ | ^^^^^^^^^^
+
+error: arguments `PinnedDrop` and `Replace` are mutually exclusive
+ --> $DIR/invalid.rs:124:41
+ |
+124 | #[pin_project(Replace, UnsafeUnpin, PinnedDrop)] //~ ERROR arguments `PinnedDrop` and `Replace` are mutually exclusive
+ | ^^^^^^^^^^
+
+error: arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive
+ --> $DIR/invalid.rs:127:19
+ |
+127 | #[pin_project(UnsafeUnpin, !Unpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive
+ | ^^^^^^^^^^^
+
+error: arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive
+ --> $DIR/invalid.rs:130:39
+ |
+130 | #[pin_project(!Unpin, PinnedDrop, UnsafeUnpin)] //~ ERROR arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive
+ | ^^^^^^^^^^^
+
+error: unexpected end of input, expected `Unpin`
+ --> $DIR/invalid.rs:133:5
+ |
+133 | #[pin_project(!)] //~ ERROR unexpected end of input, expected `Unpin`
+ | ^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: unexpected argument: Unpin
+ --> $DIR/invalid.rs:136:19
+ |
+136 | #[pin_project(Unpin)] //~ ERROR unexpected argument
+ | ^^^^^
+
+error: duplicate #[pin_project] attribute
+ --> $DIR/invalid.rs:144:5
+ |
+144 | #[pin_project] //~ ERROR duplicate #[pin_project] attribute
+ | ^^^^^^^^^^^^^^
+
+error: #[pin_project] attribute may not be used on structs with zero fields
+ --> $DIR/invalid.rs:152:19
+ |
+152 | struct Struct {} //~ ERROR may not be used on structs with zero fields
+ | ^^
+
+error: #[pin_project] attribute may not be used on structs with zero fields
+ --> $DIR/invalid.rs:155:23
+ |
+155 | struct TupleStruct(); //~ ERROR may not be used on structs with zero fields
+ | ^^
+
+error: #[pin_project] attribute may not be used on structs with zero fields
+ --> $DIR/invalid.rs:158:12
+ |
+158 | struct UnitStruct; //~ ERROR may not be used on structs with zero fields
+ | ^^^^^^^^^^
+
+error: #[pin_project] attribute may not be used on enums without variants
+ --> $DIR/invalid.rs:161:20
+ |
+161 | enum EnumEmpty {} //~ ERROR may not be used on enums without variants
+ | ^^
+
+error: #[pin_project] attribute may not be used on enums with discriminants
+ --> $DIR/invalid.rs:165:13
+ |
+165 | V = 2, //~ ERROR may not be used on enums with discriminants
+ | ^
+
+error: #[pin_project] attribute may not be used on enums with zero fields
+ --> $DIR/invalid.rs:170:9
+ |
+170 | / Unit, //~ ERROR may not be used on enums with zero fields
+171 | | Tuple(),
+172 | | Struct {},
+ | |__________________^
+
+error: #[pin_project] attribute may only be used on structs or enums
+ --> $DIR/invalid.rs:176:5
+ |
+176 | / union Union {
+177 | | //~^ ERROR may only be used on structs or enums
+178 | | f: (),
+179 | | }
+ | |_____^
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/invalid.rs:187:12
+ |
+187 | #[repr(packed)]
+ | ^^^^^^
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/invalid.rs:191:12
+ |
+191 | #[repr(packed)]
+ | ^^^^^^
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/invalid.rs:195:12
+ |
+195 | #[repr(packed)]
+ | ^^^^^^
diff --git a/tests/ui/pin_project/overlapping_unpin_struct.rs b/tests/ui/pin_project/overlapping_unpin_struct.rs
new file mode 100644
index 0000000..00fef3c
--- /dev/null
+++ b/tests/ui/pin_project/overlapping_unpin_struct.rs
@@ -0,0 +1,18 @@
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project]
+struct Foo<T> {
+ #[pin]
+ inner: T,
+}
+
+struct __Foo {}
+
+impl Unpin for __Foo {}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Foo<PhantomPinned>>(); //~ ERROR E0277
+}
diff --git a/tests/ui/pin_project/overlapping_unpin_struct.stderr b/tests/ui/pin_project/overlapping_unpin_struct.stderr
new file mode 100644
index 0000000..d0fd4a9
--- /dev/null
+++ b/tests/ui/pin_project/overlapping_unpin_struct.stderr
@@ -0,0 +1,11 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/overlapping_unpin_struct.rs:17:5
+ |
+14 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+17 | is_unpin::<Foo<PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned>`
diff --git a/tests/ui/pin_project/packed-enum.rs b/tests/ui/pin_project/packed-enum.rs
new file mode 100644
index 0000000..9d4a4c3
--- /dev/null
+++ b/tests/ui/pin_project/packed-enum.rs
@@ -0,0 +1,20 @@
+use pin_project::pin_project;
+
+#[repr(packed)] //~ ERROR E0517
+enum E1 {
+ V(()),
+}
+
+#[pin_project]
+#[repr(packed)] //~ ERROR E0517
+enum E2 {
+ V(()),
+}
+
+#[repr(packed)] //~ ERROR E0517
+#[pin_project]
+enum E3 {
+ V(()),
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/packed-enum.stderr b/tests/ui/pin_project/packed-enum.stderr
new file mode 100644
index 0000000..0a5d31b
--- /dev/null
+++ b/tests/ui/pin_project/packed-enum.stderr
@@ -0,0 +1,30 @@
+error[E0517]: attribute should be applied to struct or union
+ --> $DIR/packed-enum.rs:3:8
+ |
+3 | #[repr(packed)] //~ ERROR E0517
+ | ^^^^^^
+4 | / enum E1 {
+5 | | V(()),
+6 | | }
+ | |_- not a struct or union
+
+error[E0517]: attribute should be applied to struct or union
+ --> $DIR/packed-enum.rs:9:8
+ |
+9 | #[repr(packed)] //~ ERROR E0517
+ | ^^^^^^
+10 | / enum E2 {
+11 | | V(()),
+12 | | }
+ | |_- not a struct or union
+
+error[E0517]: attribute should be applied to struct or union
+ --> $DIR/packed-enum.rs:14:8
+ |
+14 | #[repr(packed)] //~ ERROR E0517
+ | ^^^^^^
+15 | #[pin_project]
+16 | / enum E3 {
+17 | | V(()),
+18 | | }
+ | |_- not a struct or union
diff --git a/tests/ui/pin_project/packed-name-value.rs b/tests/ui/pin_project/packed-name-value.rs
new file mode 100644
index 0000000..ed819ca
--- /dev/null
+++ b/tests/ui/pin_project/packed-name-value.rs
@@ -0,0 +1,20 @@
+use pin_project::pin_project;
+
+#[repr(packed = "")] //~ ERROR E0552
+struct S1 {
+ f: (),
+}
+
+#[pin_project]
+#[repr(packed = "")] //~ ERROR E0552
+struct S2 {
+ f: (),
+}
+
+#[repr(packed = "")] //~ ERROR E0552
+#[pin_project]
+struct S3 {
+ f: (),
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/packed-name-value.stderr b/tests/ui/pin_project/packed-name-value.stderr
new file mode 100644
index 0000000..a3e2571
--- /dev/null
+++ b/tests/ui/pin_project/packed-name-value.stderr
@@ -0,0 +1,17 @@
+error[E0552]: unrecognized representation hint
+ --> $DIR/packed-name-value.rs:3:8
+ |
+3 | #[repr(packed = "")] //~ ERROR E0552
+ | ^^^^^^^^^^^
+
+error[E0552]: unrecognized representation hint
+ --> $DIR/packed-name-value.rs:9:8
+ |
+9 | #[repr(packed = "")] //~ ERROR E0552
+ | ^^^^^^^^^^^
+
+error[E0552]: unrecognized representation hint
+ --> $DIR/packed-name-value.rs:14:8
+ |
+14 | #[repr(packed = "")] //~ ERROR E0552
+ | ^^^^^^^^^^^
diff --git a/tests/ui/pin_project/packed.rs b/tests/ui/pin_project/packed.rs
new file mode 100644
index 0000000..86f3ecf
--- /dev/null
+++ b/tests/ui/pin_project/packed.rs
@@ -0,0 +1,25 @@
+use pin_project::pin_project;
+
+#[pin_project]
+#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types
+struct A {
+ #[pin]
+ field: u8,
+}
+
+// Test putting 'repr' before the 'pin_project' attribute
+#[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types
+#[pin_project]
+struct B {
+ #[pin]
+ field: u8,
+}
+
+#[pin_project]
+#[repr(packed(2))] //~ ERROR may not be used on #[repr(packed)] types
+struct C {
+ #[pin]
+ field: u32,
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/packed.stderr b/tests/ui/pin_project/packed.stderr
new file mode 100644
index 0000000..969faea
--- /dev/null
+++ b/tests/ui/pin_project/packed.stderr
@@ -0,0 +1,17 @@
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed.rs:4:8
+ |
+4 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types
+ | ^^^^^^
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed.rs:11:8
+ |
+11 | #[repr(packed, C)] //~ ERROR may not be used on #[repr(packed)] types
+ | ^^^^^^
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed.rs:19:8
+ |
+19 | #[repr(packed(2))] //~ ERROR may not be used on #[repr(packed)] types
+ | ^^^^^^^^^
diff --git a/tests/ui/pin_project/packed_sneaky-1.rs b/tests/ui/pin_project/packed_sneaky-1.rs
new file mode 100644
index 0000000..dcf5464
--- /dev/null
+++ b/tests/ui/pin_project/packed_sneaky-1.rs
@@ -0,0 +1,33 @@
+use auxiliary_macros::hidden_repr;
+use pin_project::{pin_project, pinned_drop, UnsafeUnpin};
+use std::pin::Pin;
+
+#[pin_project] //~ ERROR may not be used on #[repr(packed)] types
+#[hidden_repr(packed)]
+struct A {
+ #[pin]
+ field: u32,
+}
+
+#[pin_project(UnsafeUnpin)] //~ ERROR may not be used on #[repr(packed)] types
+#[hidden_repr(packed)]
+struct C {
+ #[pin]
+ field: u32,
+}
+
+unsafe impl UnsafeUnpin for C {}
+
+#[pin_project(PinnedDrop)] //~ ERROR may not be used on #[repr(packed)] types
+#[hidden_repr(packed)]
+struct D {
+ #[pin]
+ field: u32,
+}
+
+#[pinned_drop]
+impl PinnedDrop for D {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/packed_sneaky-1.stderr b/tests/ui/pin_project/packed_sneaky-1.stderr
new file mode 100644
index 0000000..06a4f62
--- /dev/null
+++ b/tests/ui/pin_project/packed_sneaky-1.stderr
@@ -0,0 +1,23 @@
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed_sneaky-1.rs:6:1
+ |
+6 | #[hidden_repr(packed)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed_sneaky-1.rs:13:1
+ |
+13 | #[hidden_repr(packed)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed_sneaky-1.rs:22:1
+ |
+22 | #[hidden_repr(packed)]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pin_project/packed_sneaky-2.rs b/tests/ui/pin_project/packed_sneaky-2.rs
new file mode 100644
index 0000000..d162706
--- /dev/null
+++ b/tests/ui/pin_project/packed_sneaky-2.rs
@@ -0,0 +1,12 @@
+use auxiliary_macros::hidden_repr_macro;
+use pin_project::pin_project;
+
+hidden_repr_macro! { //~ ERROR may not be used on #[repr(packed)] types
+ #[pin_project]
+ struct B {
+ #[pin]
+ field: u32,
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/packed_sneaky-2.stderr b/tests/ui/pin_project/packed_sneaky-2.stderr
new file mode 100644
index 0000000..d653a4d
--- /dev/null
+++ b/tests/ui/pin_project/packed_sneaky-2.stderr
@@ -0,0 +1,13 @@
+error: #[pin_project] attribute may not be used on #[repr(packed)] types
+ --> $DIR/packed_sneaky-2.rs:4:1
+ |
+4 | / hidden_repr_macro! { //~ ERROR may not be used on #[repr(packed)] types
+5 | | #[pin_project]
+6 | | struct B {
+7 | | #[pin]
+8 | | field: u32,
+9 | | }
+10 | | }
+ | |_^
+ |
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pin_project/private_in_public-enum.rs b/tests/ui/pin_project/private_in_public-enum.rs
new file mode 100644
index 0000000..cbffa20
--- /dev/null
+++ b/tests/ui/pin_project/private_in_public-enum.rs
@@ -0,0 +1,23 @@
+// Even if allows private_in_public, these are errors.
+
+#![allow(private_in_public)]
+
+pub enum PublicEnum {
+ Variant(PrivateEnum), //~ ERROR E0446
+}
+
+enum PrivateEnum {
+ Variant(u8),
+}
+
+mod foo {
+ pub(crate) enum CrateEnum {
+ Variant(PrivateEnum), //~ ERROR E0446
+ }
+
+ enum PrivateEnum {
+ Variant(u8),
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/private_in_public-enum.stderr b/tests/ui/pin_project/private_in_public-enum.stderr
new file mode 100644
index 0000000..6f2988f
--- /dev/null
+++ b/tests/ui/pin_project/private_in_public-enum.stderr
@@ -0,0 +1,17 @@
+error[E0446]: private type `PrivateEnum` in public interface
+ --> $DIR/private_in_public-enum.rs:6:13
+ |
+6 | Variant(PrivateEnum), //~ ERROR E0446
+ | ^^^^^^^^^^^ can't leak private type
+...
+9 | enum PrivateEnum {
+ | - `PrivateEnum` declared as private
+
+error[E0446]: private type `foo::PrivateEnum` in public interface
+ --> $DIR/private_in_public-enum.rs:15:17
+ |
+15 | Variant(PrivateEnum), //~ ERROR E0446
+ | ^^^^^^^^^^^ can't leak private type
+...
+18 | enum PrivateEnum {
+ | - `foo::PrivateEnum` declared as private
diff --git a/tests/ui/pin_project/proper_unpin.rs b/tests/ui/pin_project/proper_unpin.rs
new file mode 100644
index 0000000..e61789b
--- /dev/null
+++ b/tests/ui/pin_project/proper_unpin.rs
@@ -0,0 +1,38 @@
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+struct Inner<T> {
+ val: T,
+}
+
+#[pin_project]
+struct Foo<T, U> {
+ #[pin]
+ inner: Inner<T>,
+ other: U,
+}
+
+#[pin_project]
+struct TrivialBounds {
+ #[pin]
+ field1: PhantomPinned,
+}
+
+#[pin_project]
+struct Bar<'a, T, U> {
+ #[pin]
+ inner: &'a mut Inner<T>,
+ other: U,
+}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Foo<PhantomPinned, ()>>(); //~ ERROR E0277
+ is_unpin::<Foo<(), PhantomPinned>>(); // Ok
+ is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+
+ is_unpin::<TrivialBounds>(); //~ ERROR E0277
+
+ is_unpin::<Bar<'_, PhantomPinned, PhantomPinned>>(); // Ok
+}
diff --git a/tests/ui/pin_project/proper_unpin.stderr b/tests/ui/pin_project/proper_unpin.stderr
new file mode 100644
index 0000000..9142887
--- /dev/null
+++ b/tests/ui/pin_project/proper_unpin.stderr
@@ -0,0 +1,37 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:31:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+31 | is_unpin::<Foo<PhantomPinned, ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, ()>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Inner<std::marker::PhantomPinned>`
+ = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, ()>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned, ()>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:33:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+33 | is_unpin::<Foo<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Inner<std::marker::PhantomPinned>`
+ = note: required because it appears within the type `__SCOPE_Foo::__Foo<'_, std::marker::PhantomPinned, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Foo<std::marker::PhantomPinned, std::marker::PhantomPinned>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:35:5
+ |
+28 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+35 | is_unpin::<TrivialBounds>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ within `__SCOPE_TrivialBounds::__TrivialBounds<'_>`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `__SCOPE_TrivialBounds::__TrivialBounds<'_>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds`
diff --git a/tests/ui/pin_project/remove-attr-from-field.rs b/tests/ui/pin_project/remove-attr-from-field.rs
new file mode 100644
index 0000000..eebd3cd
--- /dev/null
+++ b/tests/ui/pin_project/remove-attr-from-field.rs
@@ -0,0 +1,32 @@
+use auxiliary_macros::remove_attr;
+use pin_project::pin_project;
+use std::{marker::PhantomPinned, pin::Pin};
+
+fn is_unpin<T: Unpin>() {}
+
+#[pin_project]
+#[remove_attr(field)]
+struct Foo {
+ #[pin]
+ field: PhantomPinned,
+}
+
+#[remove_attr(field)]
+#[pin_project]
+struct Bar {
+ #[pin]
+ field: PhantomPinned,
+}
+
+fn main() {
+ is_unpin::<Foo>();
+ is_unpin::<Bar>();
+
+ let mut x = Foo { field: PhantomPinned };
+ let x = Pin::new(&mut x).project();
+ let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308
+
+ let mut x = Bar { field: PhantomPinned };
+ let x = Pin::new(&mut x).project();
+ let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308
+}
diff --git a/tests/ui/pin_project/remove-attr-from-field.stderr b/tests/ui/pin_project/remove-attr-from-field.stderr
new file mode 100644
index 0000000..15195e7
--- /dev/null
+++ b/tests/ui/pin_project/remove-attr-from-field.stderr
@@ -0,0 +1,21 @@
+error[E0308]: mismatched types
+ --> $DIR/remove-attr-from-field.rs:27:38
+ |
+27 | let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308
+ | ----------------------- ^^^^^^^ expected struct `std::pin::Pin`, found `&mut std::marker::PhantomPinned`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `std::pin::Pin<&mut std::marker::PhantomPinned>`
+ found mutable reference `&mut std::marker::PhantomPinned`
+
+error[E0308]: mismatched types
+ --> $DIR/remove-attr-from-field.rs:31:38
+ |
+31 | let _: Pin<&mut PhantomPinned> = x.field; //~ ERROR E0308
+ | ----------------------- ^^^^^^^ expected struct `std::pin::Pin`, found `&mut std::marker::PhantomPinned`
+ | |
+ | expected due to this
+ |
+ = note: expected struct `std::pin::Pin<&mut std::marker::PhantomPinned>`
+ found mutable reference `&mut std::marker::PhantomPinned`
diff --git a/tests/ui/pin_project/remove-attr-from-struct.rs b/tests/ui/pin_project/remove-attr-from-struct.rs
new file mode 100644
index 0000000..b395a42
--- /dev/null
+++ b/tests/ui/pin_project/remove-attr-from-struct.rs
@@ -0,0 +1,30 @@
+use auxiliary_macros::remove_attr;
+use pin_project::pin_project;
+use std::{marker::PhantomPinned, pin::Pin};
+
+fn is_unpin<T: Unpin>() {}
+
+#[pin_project]
+#[remove_attr(struct)]
+struct Foo {
+ #[pin] //~ ERROR cannot find attribute `pin` in this scope
+ field: PhantomPinned,
+}
+
+#[remove_attr(struct)]
+#[pin_project]
+struct Bar {
+ #[pin] //~ ERROR cannot find attribute `pin` in this scope
+ field: PhantomPinned,
+}
+
+fn main() {
+ is_unpin::<Foo>(); //~ ERROR E0277
+ is_unpin::<Bar>(); //~ ERROR E0277
+
+ let mut x = Foo { field: PhantomPinned };
+ let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599
+
+ let mut x = Bar { field: PhantomPinned };
+ let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599
+}
diff --git a/tests/ui/pin_project/remove-attr-from-struct.stderr b/tests/ui/pin_project/remove-attr-from-struct.stderr
new file mode 100644
index 0000000..3173248
--- /dev/null
+++ b/tests/ui/pin_project/remove-attr-from-struct.stderr
@@ -0,0 +1,63 @@
+error: cannot find attribute `pin` in this scope
+ --> $DIR/remove-attr-from-struct.rs:10:7
+ |
+10 | #[pin] //~ ERROR cannot find attribute `pin` in this scope
+ | ^^^
+
+error: cannot find attribute `pin` in this scope
+ --> $DIR/remove-attr-from-struct.rs:17:7
+ |
+17 | #[pin] //~ ERROR cannot find attribute `pin` in this scope
+ | ^^^
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/remove-attr-from-struct.rs:22:5
+ |
+5 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+22 | is_unpin::<Foo>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^ within `Foo`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Foo`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/remove-attr-from-struct.rs:23:5
+ |
+5 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+23 | is_unpin::<Bar>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^ within `Bar`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Bar`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/remove-attr-from-struct.rs:26:14
+ |
+26 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599
+ | ^^^^^^^^ within `Foo`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Foo`
+ = note: required by `std::pin::Pin::<P>::new`
+
+error[E0599]: no method named `project` found for struct `std::pin::Pin<&mut Foo>` in the current scope
+ --> $DIR/remove-attr-from-struct.rs:26:31
+ |
+26 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599
+ | ^^^^^^^ method not found in `std::pin::Pin<&mut Foo>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/remove-attr-from-struct.rs:29:14
+ |
+29 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599
+ | ^^^^^^^^ within `Bar`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `Bar`
+ = note: required by `std::pin::Pin::<P>::new`
+
+error[E0599]: no method named `project` found for struct `std::pin::Pin<&mut Bar>` in the current scope
+ --> $DIR/remove-attr-from-struct.rs:29:31
+ |
+29 | let _x = Pin::new(&mut x).project(); //~ ERROR E0277,E0599
+ | ^^^^^^^ method not found in `std::pin::Pin<&mut Bar>`
diff --git a/tests/ui/pin_project/safe_packed_borrows.rs b/tests/ui/pin_project/safe_packed_borrows.rs
new file mode 100644
index 0000000..c1a7d55
--- /dev/null
+++ b/tests/ui/pin_project/safe_packed_borrows.rs
@@ -0,0 +1,21 @@
+#![deny(safe_packed_borrows)]
+
+// Refs: https://github.com/rust-lang/rust/issues/46043
+
+#[repr(packed)]
+struct A {
+ field: u32,
+}
+
+#[repr(packed(2))]
+struct B {
+ field: u32,
+}
+
+fn main() {
+ let a = A { field: 1 };
+ &a.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block
+
+ let b = B { field: 1 };
+ &b.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block
+}
diff --git a/tests/ui/pin_project/safe_packed_borrows.stderr b/tests/ui/pin_project/safe_packed_borrows.stderr
new file mode 100644
index 0000000..7b4cc08
--- /dev/null
+++ b/tests/ui/pin_project/safe_packed_borrows.stderr
@@ -0,0 +1,24 @@
+error: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
+ --> $DIR/safe_packed_borrows.rs:17:5
+ |
+17 | &a.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block
+ | ^^^^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/safe_packed_borrows.rs:1:9
+ |
+1 | #![deny(safe_packed_borrows)]
+ | ^^^^^^^^^^^^^^^^^^^
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
+ = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior
+
+error: borrow of packed field is unsafe and requires unsafe function or block (error E0133)
+ --> $DIR/safe_packed_borrows.rs:20:5
+ |
+20 | &b.field; //~ ERROR borrow of packed field is unsafe and requires unsafe function or block
+ | ^^^^^^^^
+ |
+ = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
+ = note: for more information, see issue #46043 <https://github.com/rust-lang/rust/issues/46043>
+ = note: fields of packed structs might be misaligned: dereferencing a misaligned pointer or even just creating a misaligned reference is undefined behavior
diff --git a/tests/ui/pin_project/unpin_sneaky.rs b/tests/ui/pin_project/unpin_sneaky.rs
new file mode 100644
index 0000000..3ccb1a9
--- /dev/null
+++ b/tests/ui/pin_project/unpin_sneaky.rs
@@ -0,0 +1,11 @@
+use pin_project::pin_project;
+
+#[pin_project]
+struct Foo {
+ #[pin]
+ inner: u8,
+}
+
+impl Unpin for __Foo {} //~ ERROR E0412,E0321
+
+fn main() {}
diff --git a/tests/ui/pin_project/unpin_sneaky.stderr b/tests/ui/pin_project/unpin_sneaky.stderr
new file mode 100644
index 0000000..0637a66
--- /dev/null
+++ b/tests/ui/pin_project/unpin_sneaky.stderr
@@ -0,0 +1,11 @@
+error[E0412]: cannot find type `__Foo` in this scope
+ --> $DIR/unpin_sneaky.rs:9:16
+ |
+9 | impl Unpin for __Foo {} //~ ERROR E0412,E0321
+ | ^^^^^ not found in this scope
+
+error[E0321]: cross-crate traits with a default impl, like `std::marker::Unpin`, can only be implemented for a struct/enum type, not `[type error]`
+ --> $DIR/unpin_sneaky.rs:9:1
+ |
+9 | impl Unpin for __Foo {} //~ ERROR E0412,E0321
+ | ^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
diff --git a/tests/ui/pin_project/visibility.rs b/tests/ui/pin_project/visibility.rs
new file mode 100644
index 0000000..4f0cb1b
--- /dev/null
+++ b/tests/ui/pin_project/visibility.rs
@@ -0,0 +1,52 @@
+mod pub_ {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ pub struct Default(());
+
+ #[pin_project(Replace)]
+ pub struct Replace(());
+}
+pub mod pub_use {
+ #[rustfmt::skip]
+ pub use crate::pub_::__DefaultProjection; //~ ERROR E0365
+ #[rustfmt::skip]
+ pub use crate::pub_::__DefaultProjectionRef; //~ ERROR E0365
+ #[rustfmt::skip]
+ pub use crate::pub_::__ReplaceProjection; //~ ERROR E0365
+ #[rustfmt::skip]
+ pub use crate::pub_::__ReplaceProjectionOwned; //~ ERROR E0365
+ #[rustfmt::skip]
+ pub use crate::pub_::__ReplaceProjectionRef; //~ ERROR E0365
+
+ // Confirm that the visibility of the original type is not changed.
+ pub use crate::pub_::{Default, Replace};
+}
+pub mod pub_use2 {
+ // Ok
+ #[allow(unused_imports)]
+ pub(crate) use crate::pub_::{
+ __DefaultProjection, __DefaultProjectionRef, __ReplaceProjection, __ReplaceProjectionOwned,
+ __ReplaceProjectionRef,
+ };
+}
+
+mod pub_crate {
+ use pin_project::pin_project;
+
+ #[pin_project]
+ pub(crate) struct Default(());
+
+ #[pin_project(Replace)]
+ pub(crate) struct Replace(());
+}
+pub mod pub_crate_use {
+ // Ok
+ #[allow(unused_imports)]
+ pub(crate) use crate::pub_crate::{
+ __DefaultProjection, __DefaultProjectionRef, __ReplaceProjection, __ReplaceProjectionOwned,
+ __ReplaceProjectionRef,
+ };
+}
+
+fn main() {}
diff --git a/tests/ui/pin_project/visibility.stderr b/tests/ui/pin_project/visibility.stderr
new file mode 100644
index 0000000..1ea60d7
--- /dev/null
+++ b/tests/ui/pin_project/visibility.stderr
@@ -0,0 +1,39 @@
+error[E0365]: `__DefaultProjection` is private, and cannot be re-exported
+ --> $DIR/visibility.rs:12:13
+ |
+12 | pub use crate::pub_::__DefaultProjection; //~ ERROR E0365
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__DefaultProjection`
+ |
+ = note: consider declaring type or module `__DefaultProjection` with `pub`
+
+error[E0365]: `__DefaultProjectionRef` is private, and cannot be re-exported
+ --> $DIR/visibility.rs:14:13
+ |
+14 | pub use crate::pub_::__DefaultProjectionRef; //~ ERROR E0365
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__DefaultProjectionRef`
+ |
+ = note: consider declaring type or module `__DefaultProjectionRef` with `pub`
+
+error[E0365]: `__ReplaceProjection` is private, and cannot be re-exported
+ --> $DIR/visibility.rs:16:13
+ |
+16 | pub use crate::pub_::__ReplaceProjection; //~ ERROR E0365
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__ReplaceProjection`
+ |
+ = note: consider declaring type or module `__ReplaceProjection` with `pub`
+
+error[E0365]: `__ReplaceProjectionOwned` is private, and cannot be re-exported
+ --> $DIR/visibility.rs:18:13
+ |
+18 | pub use crate::pub_::__ReplaceProjectionOwned; //~ ERROR E0365
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__ReplaceProjectionOwned`
+ |
+ = note: consider declaring type or module `__ReplaceProjectionOwned` with `pub`
+
+error[E0365]: `__ReplaceProjectionRef` is private, and cannot be re-exported
+ --> $DIR/visibility.rs:20:13
+ |
+20 | pub use crate::pub_::__ReplaceProjectionRef; //~ ERROR E0365
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ re-export of private `__ReplaceProjectionRef`
+ |
+ = note: consider declaring type or module `__ReplaceProjectionRef` with `pub`
diff --git a/tests/ui/pinned_drop/call-drop-inner.rs b/tests/ui/pinned_drop/call-drop-inner.rs
new file mode 100644
index 0000000..c953acb
--- /dev/null
+++ b/tests/ui/pinned_drop/call-drop-inner.rs
@@ -0,0 +1,16 @@
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+#[pin_project(PinnedDrop)]
+struct Struct {
+ dropped: bool,
+}
+
+#[pinned_drop]
+impl PinnedDrop for Struct {
+ fn drop(mut self: Pin<&mut Self>) {
+ __drop_inner(__self);
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/call-drop-inner.stderr b/tests/ui/pinned_drop/call-drop-inner.stderr
new file mode 100644
index 0000000..eb55ce7
--- /dev/null
+++ b/tests/ui/pinned_drop/call-drop-inner.stderr
@@ -0,0 +1,10 @@
+error[E0061]: this function takes 0 arguments but 1 argument was supplied
+ --> $DIR/call-drop-inner.rs:12:9
+ |
+9 | #[pinned_drop]
+ | -------------- defined here
+...
+12 | __drop_inner(__self);
+ | ^^^^^^^^^^^^ ------ supplied 1 argument
+ | |
+ | expected 0 arguments
diff --git a/tests/ui/pinned_drop/conditional-drop-impl.rs b/tests/ui/pinned_drop/conditional-drop-impl.rs
new file mode 100644
index 0000000..42d18b7
--- /dev/null
+++ b/tests/ui/pinned_drop/conditional-drop-impl.rs
@@ -0,0 +1,26 @@
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+// In `Drop` impl, the implementor must specify the same requirement as type definition.
+
+struct DropImpl<T> {
+ field: T,
+}
+
+impl<T: Unpin> Drop for DropImpl<T> {
+ //~^ ERROR E0367
+ fn drop(&mut self) {}
+}
+
+#[pin_project(PinnedDrop)] //~ ERROR E0277
+struct PinnedDropImpl<T> {
+ #[pin]
+ field: T,
+}
+
+#[pinned_drop]
+impl<T: Unpin> PinnedDrop for PinnedDropImpl<T> {
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/conditional-drop-impl.stderr b/tests/ui/pinned_drop/conditional-drop-impl.stderr
new file mode 100644
index 0000000..ad8fb69
--- /dev/null
+++ b/tests/ui/pinned_drop/conditional-drop-impl.stderr
@@ -0,0 +1,26 @@
+error[E0367]: `Drop` impl requires `T: std::marker::Unpin` but the struct it is implemented for does not
+ --> $DIR/conditional-drop-impl.rs:10:9
+ |
+10 | impl<T: Unpin> Drop for DropImpl<T> {
+ | ^^^^^
+ |
+note: the implementor must specify the same requirement
+ --> $DIR/conditional-drop-impl.rs:6:1
+ |
+6 | / struct DropImpl<T> {
+7 | | field: T,
+8 | | }
+ | |_^
+
+error[E0277]: `T` cannot be unpinned
+ --> $DIR/conditional-drop-impl.rs:15:15
+ |
+15 | #[pin_project(PinnedDrop)] //~ ERROR E0277
+ | ^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `T`
+ |
+ = note: required because of the requirements on the impl of `pin_project::__private::PinnedDrop` for `PinnedDropImpl<T>`
+ = note: required by `pin_project::__private::PinnedDrop::drop`
+help: consider restricting type parameter `T`
+ |
+16 | struct PinnedDropImpl<T: std::marker::Unpin> {
+ | ^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/pinned_drop/forget-pinned-drop-impl.rs b/tests/ui/pinned_drop/forget-pinned-drop-impl.rs
new file mode 100644
index 0000000..6c9f718
--- /dev/null
+++ b/tests/ui/pinned_drop/forget-pinned-drop-impl.rs
@@ -0,0 +1,9 @@
+use pin_project::pin_project;
+
+#[pin_project(PinnedDrop)] //~ ERROR E0277
+struct Struct {
+ #[pin]
+ field: u8,
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/forget-pinned-drop-impl.stderr b/tests/ui/pinned_drop/forget-pinned-drop-impl.stderr
new file mode 100644
index 0000000..67bdbe1
--- /dev/null
+++ b/tests/ui/pinned_drop/forget-pinned-drop-impl.stderr
@@ -0,0 +1,7 @@
+error[E0277]: the trait bound `Struct: pin_project::__private::PinnedDrop` is not satisfied
+ --> $DIR/forget-pinned-drop-impl.rs:3:15
+ |
+3 | #[pin_project(PinnedDrop)] //~ ERROR E0277
+ | ^^^^^^^^^^ the trait `pin_project::__private::PinnedDrop` is not implemented for `Struct`
+ |
+ = note: required by `pin_project::__private::PinnedDrop::drop`
diff --git a/tests/ui/pinned_drop/invalid-self.rs b/tests/ui/pinned_drop/invalid-self.rs
new file mode 100644
index 0000000..73d3b43
--- /dev/null
+++ b/tests/ui/pinned_drop/invalid-self.rs
@@ -0,0 +1,14 @@
+// by-ref binding `ref (mut) self` and sub-patterns `@` are not allowed in receivers (rejected by rustc).
+
+use std::pin::Pin;
+
+struct Struct {}
+
+impl Struct {
+ fn take_ref_self(ref self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self`
+ fn take_ref_mut_self(ref mut self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self`
+
+ fn self_subpat(self @ Struct {}: Self) {} //~ ERROR expected one of `)`, `,`, or `:`, found `@`
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/invalid-self.stderr b/tests/ui/pinned_drop/invalid-self.stderr
new file mode 100644
index 0000000..a43e91d
--- /dev/null
+++ b/tests/ui/pinned_drop/invalid-self.stderr
@@ -0,0 +1,25 @@
+error: expected identifier, found keyword `self`
+ --> $DIR/invalid-self.rs:8:26
+ |
+8 | fn take_ref_self(ref self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self`
+ | ^^^^ expected identifier, found keyword
+
+error: expected identifier, found keyword `self`
+ --> $DIR/invalid-self.rs:9:34
+ |
+9 | fn take_ref_mut_self(ref mut self: Pin<&mut Self>) {} //~ ERROR expected identifier, found keyword `self`
+ | ^^^^ expected identifier, found keyword
+
+error: expected parameter name, found `@`
+ --> $DIR/invalid-self.rs:11:25
+ |
+11 | fn self_subpat(self @ Struct {}: Self) {} //~ ERROR expected one of `)`, `,`, or `:`, found `@`
+ | ^ expected parameter name
+
+error: expected one of `)`, `,`, or `:`, found `@`
+ --> $DIR/invalid-self.rs:11:25
+ |
+11 | fn self_subpat(self @ Struct {}: Self) {} //~ ERROR expected one of `)`, `,`, or `:`, found `@`
+ | -^ expected one of `)`, `,`, or `:`
+ | |
+ | help: missing `,`
diff --git a/tests/ui/pinned_drop/invalid.rs b/tests/ui/pinned_drop/invalid.rs
new file mode 100644
index 0000000..9ff5de2
--- /dev/null
+++ b/tests/ui/pinned_drop/invalid.rs
@@ -0,0 +1,207 @@
+mod argument {
+ use pin_project::{pin_project, pinned_drop};
+ use std::pin::Pin;
+
+ #[pin_project(PinnedDrop)]
+ struct UnexpectedArg1(());
+
+ #[pinned_drop(foo)] //~ ERROR unexpected token
+ impl PinnedDrop for UnexpectedArg1 {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct UnexpectedArg2(());
+
+ #[pinned_drop()] // Ok
+ impl PinnedDrop for UnexpectedArg2 {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+}
+
+mod attribute {
+ use pin_project::{pin_project, pinned_drop};
+
+ #[pin_project(PinnedDrop)]
+ struct Duplicate(());
+
+ #[pinned_drop]
+ #[pinned_drop] //~ ERROR duplicate #[pinned_drop] attribute
+ impl PinnedDrop for Duplicate {
+ fn drop(self: Pin<&mut Self>) {}
+ }
+}
+
+mod item {
+ use pin_project::{pin_project, pinned_drop};
+
+ #[pin_project(PinnedDrop)]
+ struct TraitImpl(());
+
+ #[pinned_drop]
+ impl Drop for TraitImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait
+
+ #[pin_project(PinnedDrop)]
+ struct InherentImpl(());
+
+ #[pinned_drop]
+ impl InherentImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait
+
+ #[pinned_drop]
+ fn drop(_: Pin<&mut ()>) {} //~ ERROR expected `impl`
+}
+
+mod unsafety {
+ use pin_project::{pin_project, pinned_drop};
+
+ #[pin_project(PinnedDrop)]
+ struct Impl(());
+
+ #[pinned_drop]
+ unsafe impl PinnedDrop for Impl {
+ //~^ ERROR implementing the trait `PinnedDrop` is not unsafe
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Method(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Method {
+ unsafe fn drop(self: Pin<&mut Self>) {} //~ ERROR implementing the method `drop` is not unsafe
+ }
+}
+
+mod assoc_item {
+ use pin_project::{pin_project, pinned_drop};
+
+ #[pin_project(PinnedDrop)]
+ struct Empty(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Empty {} //~ ERROR not all trait items implemented, missing: `drop`
+
+ #[pin_project(PinnedDrop)]
+ struct Const1(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Const1 {
+ const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop`
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Const2(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Const2 {
+ fn drop(self: Pin<&mut Self>) {}
+ const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Type1(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Type1 {
+ type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop`
+ fn drop(self: Pin<&mut Self>) {}
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Type2(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Type2 {
+ fn drop(self: Pin<&mut Self>) {}
+ type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct Duplicate(());
+
+ #[pinned_drop]
+ impl PinnedDrop for Duplicate {
+ fn drop(self: Pin<&mut Self>) {}
+ fn drop(self: Pin<&mut Self>) {} //~ ERROR duplicate definitions with name `drop`
+ }
+}
+
+mod method {
+ use pin_project::{pin_project, pinned_drop};
+ use std::pin::Pin;
+
+ #[pin_project(PinnedDrop)]
+ struct RetUnit(());
+
+ #[pinned_drop]
+ impl PinnedDrop for RetUnit {
+ fn drop(self: Pin<&mut Self>) -> () {} // Ok
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct RetTy(());
+
+ #[pinned_drop]
+ impl PinnedDrop for RetTy {
+ fn drop(self: Pin<&mut Self>) -> Self {} //~ ERROR method `drop` must return the unit type
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct NoArg(());
+
+ #[pinned_drop]
+ impl PinnedDrop for NoArg {
+ fn drop() {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct MultiArg(());
+
+ #[pinned_drop]
+ impl PinnedDrop for MultiArg {
+ fn drop(self: Pin<&mut Self>, _: ()) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct InvalidArg1(());
+
+ #[pinned_drop]
+ impl PinnedDrop for InvalidArg1 {
+ fn drop(&mut self) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct InvalidArg2(());
+
+ #[pinned_drop]
+ impl PinnedDrop for InvalidArg2 {
+ fn drop(_: Pin<&mut Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct InvalidArg3(());
+
+ #[pinned_drop]
+ impl PinnedDrop for InvalidArg3 {
+ fn drop(self: Pin<&Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct InvalidArg4(());
+
+ #[pinned_drop]
+ impl PinnedDrop for InvalidArg4 {
+ fn drop(self: Pin<&mut ()>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ }
+
+ #[pin_project(PinnedDrop)]
+ struct InvalidName(());
+
+ #[pinned_drop]
+ impl PinnedDrop for InvalidName {
+ fn pinned_drop(&mut self) {} //~ ERROR method `pinned_drop` is not a member of trait `PinnedDrop
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/invalid.stderr b/tests/ui/pinned_drop/invalid.stderr
new file mode 100644
index 0000000..8046903
--- /dev/null
+++ b/tests/ui/pinned_drop/invalid.stderr
@@ -0,0 +1,125 @@
+error: unexpected token: foo
+ --> $DIR/invalid.rs:8:19
+ |
+8 | #[pinned_drop(foo)] //~ ERROR unexpected token
+ | ^^^
+
+error: duplicate #[pinned_drop] attribute
+ --> $DIR/invalid.rs:29:5
+ |
+29 | #[pinned_drop] //~ ERROR duplicate #[pinned_drop] attribute
+ | ^^^^^^^^^^^^^^
+
+error: #[pinned_drop] may only be used on implementation for the `PinnedDrop` trait
+ --> $DIR/invalid.rs:42:10
+ |
+42 | impl Drop for TraitImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait
+ | ^^^^
+
+error: #[pinned_drop] may only be used on implementation for the `PinnedDrop` trait
+ --> $DIR/invalid.rs:48:10
+ |
+48 | impl InherentImpl {} //~ ERROR may only be used on implementation for the `PinnedDrop` trait
+ | ^^^^^^^^^^^^
+
+error: expected `impl`
+ --> $DIR/invalid.rs:51:5
+ |
+51 | fn drop(_: Pin<&mut ()>) {} //~ ERROR expected `impl`
+ | ^^
+
+error: implementing the trait `PinnedDrop` is not unsafe
+ --> $DIR/invalid.rs:61:5
+ |
+61 | unsafe impl PinnedDrop for Impl {
+ | ^^^^^^
+
+error: implementing the method `drop` is not unsafe
+ --> $DIR/invalid.rs:71:9
+ |
+71 | unsafe fn drop(self: Pin<&mut Self>) {} //~ ERROR implementing the method `drop` is not unsafe
+ | ^^^^^^
+
+error: not all trait items implemented, missing: `drop`
+ --> $DIR/invalid.rs:82:5
+ |
+82 | impl PinnedDrop for Empty {} //~ ERROR not all trait items implemented, missing: `drop`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: const `A` is not a member of trait `PinnedDrop`
+ --> $DIR/invalid.rs:89:9
+ |
+89 | const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop`
+ | ^^^^^^^^^^^^^^^^
+
+error: const `A` is not a member of trait `PinnedDrop`
+ --> $DIR/invalid.rs:99:9
+ |
+99 | const A: u8 = 0; //~ ERROR const `A` is not a member of trait `PinnedDrop`
+ | ^^^^^^^^^^^^^^^^
+
+error: type `A` is not a member of trait `PinnedDrop`
+ --> $DIR/invalid.rs:107:9
+ |
+107 | type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop`
+ | ^^^^^^^^^^^^
+
+error: type `A` is not a member of trait `PinnedDrop`
+ --> $DIR/invalid.rs:117:9
+ |
+117 | type A = u8; //~ ERROR type `A` is not a member of trait `PinnedDrop`
+ | ^^^^^^^^^^^^
+
+error: duplicate definitions with name `drop`
+ --> $DIR/invalid.rs:126:9
+ |
+126 | fn drop(self: Pin<&mut Self>) {} //~ ERROR duplicate definitions with name `drop`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: method `drop` must return the unit type
+ --> $DIR/invalid.rs:147:42
+ |
+147 | fn drop(self: Pin<&mut Self>) -> Self {} //~ ERROR method `drop` must return the unit type
+ | ^^^^
+
+error: method `drop` must take an argument `self: Pin<&mut Self>`
+ --> $DIR/invalid.rs:155:16
+ |
+155 | fn drop() {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ | ^^
+
+error: method `drop` must take an argument `self: Pin<&mut Self>`
+ --> $DIR/invalid.rs:163:17
+ |
+163 | fn drop(self: Pin<&mut Self>, _: ()) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: method `drop` must take an argument `self: Pin<&mut Self>`
+ --> $DIR/invalid.rs:171:17
+ |
+171 | fn drop(&mut self) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ | ^^^^^^^^^
+
+error: method `drop` must take an argument `self: Pin<&mut Self>`
+ --> $DIR/invalid.rs:179:17
+ |
+179 | fn drop(_: Pin<&mut Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ | ^^^^^^^^^^^^^^^^^
+
+error: method `drop` must take an argument `self: Pin<&mut Self>`
+ --> $DIR/invalid.rs:187:17
+ |
+187 | fn drop(self: Pin<&Self>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ | ^^^^^^^^^^^^^^^^
+
+error: method `drop` must take an argument `self: Pin<&mut Self>`
+ --> $DIR/invalid.rs:195:17
+ |
+195 | fn drop(self: Pin<&mut ()>) {} //~ ERROR method `drop` must take an argument `self: Pin<&mut Self>`
+ | ^^^^^^^^^^^^^^^^^^
+
+error: method `pinned_drop` is not a member of trait `PinnedDrop
+ --> $DIR/invalid.rs:203:12
+ |
+203 | fn pinned_drop(&mut self) {} //~ ERROR method `pinned_drop` is not a member of trait `PinnedDrop
+ | ^^^^^^^^^^^
diff --git a/tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs
new file mode 100644
index 0000000..1241b5b
--- /dev/null
+++ b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.rs
@@ -0,0 +1,15 @@
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+#[pin_project]
+struct Foo {
+ #[pin]
+ field: u8,
+}
+
+#[pinned_drop]
+impl PinnedDrop for Foo { //~ ERROR E0119
+ fn drop(self: Pin<&mut Self>) {}
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr
new file mode 100644
index 0000000..7353dc4
--- /dev/null
+++ b/tests/ui/pinned_drop/pinned-drop-no-attr-arg.stderr
@@ -0,0 +1,8 @@
+error[E0119]: conflicting implementations of trait `pin_project::__private::PinnedDrop` for type `Foo`:
+ --> $DIR/pinned-drop-no-attr-arg.rs:11:1
+ |
+4 | #[pin_project]
+ | -------------- first implementation here
+...
+11 | impl PinnedDrop for Foo { //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo`
diff --git a/tests/ui/pinned_drop/self.rs b/tests/ui/pinned_drop/self.rs
new file mode 100644
index 0000000..cd53b04
--- /dev/null
+++ b/tests/ui/pinned_drop/self.rs
@@ -0,0 +1,28 @@
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+fn self_in_macro_def() {
+ #[pin_project(PinnedDrop)]
+ pub struct Struct {
+ x: usize,
+ }
+
+ #[pinned_drop]
+ impl PinnedDrop for Struct {
+ fn drop(self: Pin<&mut Self>) {
+ macro_rules! t {
+ () => {{
+ let _ = self; //~ ERROR can't capture dynamic environment in a fn item
+
+ fn f(self: ()) {
+ //~^ ERROR `self` parameter is only allowed in associated functions
+ let _ = self;
+ }
+ }};
+ }
+ t!();
+ }
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/self.stderr b/tests/ui/pinned_drop/self.stderr
new file mode 100644
index 0000000..3ba333b
--- /dev/null
+++ b/tests/ui/pinned_drop/self.stderr
@@ -0,0 +1,23 @@
+error: `self` parameter is only allowed in associated functions
+ --> $DIR/self.rs:17:26
+ |
+17 | fn f(self: ()) {
+ | ^^^^ not semantically valid as function parameter
+...
+23 | t!();
+ | ----- in this macro invocation
+ |
+ = note: associated functions are those in `impl` or `trait` definitions
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0434]: can't capture dynamic environment in a fn item
+ --> $DIR/self.rs:15:29
+ |
+15 | let _ = self; //~ ERROR can't capture dynamic environment in a fn item
+ | ^^^^
+...
+23 | t!();
+ | ----- in this macro invocation
+ |
+ = help: use the `|| { ... }` closure form instead
+ = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/pinned_drop/unsafe-call.rs b/tests/ui/pinned_drop/unsafe-call.rs
new file mode 100644
index 0000000..2f400c1
--- /dev/null
+++ b/tests/ui/pinned_drop/unsafe-call.rs
@@ -0,0 +1,17 @@
+use pin_project::{pin_project, pinned_drop};
+use std::pin::Pin;
+
+#[pin_project(PinnedDrop)]
+struct Struct {
+ #[pin]
+ field: u8,
+}
+
+#[pinned_drop]
+impl PinnedDrop for Struct {
+ fn drop(self: Pin<&mut Self>) {
+ self.project().field.get_unchecked_mut(); //~ ERROR call to unsafe function is unsafe and requires unsafe function or block [E0133]
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/pinned_drop/unsafe-call.stderr b/tests/ui/pinned_drop/unsafe-call.stderr
new file mode 100644
index 0000000..4e8e00b
--- /dev/null
+++ b/tests/ui/pinned_drop/unsafe-call.stderr
@@ -0,0 +1,7 @@
+error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+ --> $DIR/unsafe-call.rs:13:9
+ |
+13 | self.project().field.get_unchecked_mut(); //~ ERROR call to unsafe function is unsafe and requires unsafe function or block [E0133]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
+ |
+ = note: consult the function's documentation for information on how to avoid undefined behavior
diff --git a/tests/ui/project/ambiguous-let.rs b/tests/ui/project/ambiguous-let.rs
new file mode 100644
index 0000000..a706749
--- /dev/null
+++ b/tests/ui/project/ambiguous-let.rs
@@ -0,0 +1,24 @@
+use pin_project::{pin_project, project};
+
+#[pin_project]
+enum Enum<A, B> {
+ A(#[pin] A),
+ B(B),
+}
+
+struct Struct<T>(T);
+
+#[project]
+fn foo() {
+ let mut foo: Enum<bool, bool> = Enum::A(true);
+
+ #[project]
+ let Struct(x) = match Pin::new(&mut foo).project() {
+ //~^ ERROR Both initializer expression and pattern are replaceable, you need to split the initializer expression into separate let bindings to avoid ambiguity
+ Enum::A(_) => Struct(true),
+ Enum::B(_) => unreachable!(),
+ };
+ assert!(x);
+}
+
+fn main() {}
diff --git a/tests/ui/project/ambiguous-let.stderr b/tests/ui/project/ambiguous-let.stderr
new file mode 100644
index 0000000..e6552c8
--- /dev/null
+++ b/tests/ui/project/ambiguous-let.stderr
@@ -0,0 +1,5 @@
+error: Both initializer expression and pattern are replaceable, you need to split the initializer expression into separate let bindings to avoid ambiguity
+ --> $DIR/ambiguous-let.rs:16:9
+ |
+16 | let Struct(x) = match Pin::new(&mut foo).project() {
+ | ^^^^^^^^^
diff --git a/tests/ui/project/invalid.rs b/tests/ui/project/invalid.rs
new file mode 100644
index 0000000..07e9970
--- /dev/null
+++ b/tests/ui/project/invalid.rs
@@ -0,0 +1,190 @@
+mod argument {
+ use pin_project::{pin_project, project};
+
+ #[pin_project]
+ struct A(#[pin] ());
+
+ #[project]
+ fn unexpected_local1() {
+ let mut x = A(());
+ #[project()] //~ ERROR unexpected token
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project]
+ fn unexpected_local1() {
+ let mut x = A(());
+ #[project(foo)] //~ ERROR unexpected token
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project]
+ fn unexpected_expr1() {
+ let mut x = A(());
+ #[project()] //~ ERROR unexpected token
+ match Pin::new(&mut x).project() {
+ A(_) => {}
+ }
+ }
+
+ #[project]
+ fn unexpected_expr1() {
+ let mut x = A(());
+ #[project(foo)] //~ ERROR unexpected token
+ match Pin::new(&mut x).project() {
+ A(_) => {}
+ }
+ }
+
+ #[project()] // Ok
+ fn unexpected_item1() {}
+
+ #[project(foo)] //~ ERROR unexpected token
+ fn unexpected_item2() {}
+}
+
+mod attribute {
+ use pin_project::{pin_project, project, project_ref, project_replace};
+
+ #[pin_project(Replace)]
+ struct A(#[pin] ());
+
+ #[project]
+ fn duplicate_stmt_project() {
+ let mut x = A(());
+ #[project]
+ #[project] //~ ERROR duplicate #[project] attribute
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_ref]
+ fn duplicate_stmt_project_ref() {
+ let mut x = A(());
+ #[project_ref]
+ #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_replace]
+ fn duplicate_stmt_project_replace() {
+ let mut x = A(());
+ #[project_replace]
+ #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project]
+ fn combine_stmt_project1() {
+ let mut x = A(());
+ #[project]
+ #[project_ref] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project]
+ fn combine_stmt_project2() {
+ let mut x = A(());
+ #[project]
+ #[project_replace] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project]
+ fn combine_stmt_project3() {
+ let mut x = A(());
+ #[project_ref]
+ #[project_replace] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_ref]
+ fn combine_stmt_project_ref1() {
+ let mut x = A(());
+ #[project]
+ #[project_ref] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_ref]
+ fn combine_stmt_project_ref2() {
+ let mut x = A(());
+ #[project]
+ #[project_replace] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_ref]
+ fn combine_stmt_project_ref3() {
+ let mut x = A(());
+ #[project_ref]
+ #[project_replace] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_replace]
+ fn combine_stmt_project_replace1() {
+ let mut x = A(());
+ #[project]
+ #[project_ref] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_replace]
+ fn combine_stmt_project_replace2() {
+ let mut x = A(());
+ #[project]
+ #[project_replace] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project_replace]
+ fn combine_stmt_project_replace3() {
+ let mut x = A(());
+ #[project_ref]
+ #[project_replace] //~ ERROR are mutually exclusive
+ let A(_) = Pin::new(&mut x).project();
+ }
+
+ #[project]
+ #[project] //~ ERROR duplicate #[project] attribute
+ fn duplicate_fn_project() {}
+
+ #[project_ref]
+ #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ fn duplicate_fn_project_ref() {}
+
+ #[project_replace]
+ #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ fn duplicate_fn_project_replace() {}
+
+ #[project]
+ #[project] //~ ERROR duplicate #[project] attribute
+ impl A {}
+
+ #[project_ref]
+ #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ impl A {}
+
+ #[project_replace]
+ #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ impl A {}
+
+ #[allow(unused_imports)]
+ mod use_ {
+ use pin_project::{project, project_ref, project_replace};
+
+ #[project]
+ #[project] //~ ERROR duplicate #[project] attribute
+ use super::A;
+
+ #[project_ref]
+ #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ use super::A;
+
+ #[project_replace]
+ #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ use super::A;
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/project/invalid.stderr b/tests/ui/project/invalid.stderr
new file mode 100644
index 0000000..287cac8
--- /dev/null
+++ b/tests/ui/project/invalid.stderr
@@ -0,0 +1,155 @@
+error: unexpected token: ()
+ --> $DIR/invalid.rs:10:18
+ |
+10 | #[project()] //~ ERROR unexpected token
+ | ^^
+
+error: unexpected token: (foo)
+ --> $DIR/invalid.rs:17:18
+ |
+17 | #[project(foo)] //~ ERROR unexpected token
+ | ^^^^^
+
+error: unexpected token: ()
+ --> $DIR/invalid.rs:24:18
+ |
+24 | #[project()] //~ ERROR unexpected token
+ | ^^
+
+error: unexpected token: (foo)
+ --> $DIR/invalid.rs:33:18
+ |
+33 | #[project(foo)] //~ ERROR unexpected token
+ | ^^^^^
+
+error: unexpected token: foo
+ --> $DIR/invalid.rs:42:15
+ |
+42 | #[project(foo)] //~ ERROR unexpected token
+ | ^^^
+
+error: duplicate #[project] attribute
+ --> $DIR/invalid.rs:56:9
+ |
+56 | #[project] //~ ERROR duplicate #[project] attribute
+ | ^^^^^^^^^^
+
+error: duplicate #[project_ref] attribute
+ --> $DIR/invalid.rs:64:9
+ |
+64 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ | ^^^^^^^^^^^^^^
+
+error: duplicate #[project_replace] attribute
+ --> $DIR/invalid.rs:72:9
+ |
+72 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ | ^^^^^^^^^^^^^^^^^^
+
+error: attributes `project_ref` and `project` are mutually exclusive
+ --> $DIR/invalid.rs:79:9
+ |
+79 | #[project]
+ | ^^^^^^^^^^
+
+error: attributes `project` and `project_replace` are mutually exclusive
+ --> $DIR/invalid.rs:88:9
+ |
+88 | #[project_replace] //~ ERROR are mutually exclusive
+ | ^^^^^^^^^^^^^^^^^^
+
+error: attributes `project_ref` and `project_replace` are mutually exclusive
+ --> $DIR/invalid.rs:96:9
+ |
+96 | #[project_replace] //~ ERROR are mutually exclusive
+ | ^^^^^^^^^^^^^^^^^^
+
+error: attributes `project_ref` and `project` are mutually exclusive
+ --> $DIR/invalid.rs:103:9
+ |
+103 | #[project]
+ | ^^^^^^^^^^
+
+error: attributes `project` and `project_replace` are mutually exclusive
+ --> $DIR/invalid.rs:112:9
+ |
+112 | #[project_replace] //~ ERROR are mutually exclusive
+ | ^^^^^^^^^^^^^^^^^^
+
+error: attributes `project_ref` and `project_replace` are mutually exclusive
+ --> $DIR/invalid.rs:120:9
+ |
+120 | #[project_replace] //~ ERROR are mutually exclusive
+ | ^^^^^^^^^^^^^^^^^^
+
+error: attributes `project_ref` and `project` are mutually exclusive
+ --> $DIR/invalid.rs:127:9
+ |
+127 | #[project]
+ | ^^^^^^^^^^
+
+error: attributes `project` and `project_replace` are mutually exclusive
+ --> $DIR/invalid.rs:136:9
+ |
+136 | #[project_replace] //~ ERROR are mutually exclusive
+ | ^^^^^^^^^^^^^^^^^^
+
+error: attributes `project_ref` and `project_replace` are mutually exclusive
+ --> $DIR/invalid.rs:144:9
+ |
+144 | #[project_replace] //~ ERROR are mutually exclusive
+ | ^^^^^^^^^^^^^^^^^^
+
+error: duplicate #[project] attribute
+ --> $DIR/invalid.rs:149:5
+ |
+149 | #[project] //~ ERROR duplicate #[project] attribute
+ | ^^^^^^^^^^
+
+error: duplicate #[project_ref] attribute
+ --> $DIR/invalid.rs:153:5
+ |
+153 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ | ^^^^^^^^^^^^^^
+
+error: duplicate #[project_replace] attribute
+ --> $DIR/invalid.rs:157:5
+ |
+157 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ | ^^^^^^^^^^^^^^^^^^
+
+error: duplicate #[project] attribute
+ --> $DIR/invalid.rs:161:5
+ |
+161 | #[project] //~ ERROR duplicate #[project] attribute
+ | ^^^^^^^^^^
+
+error: duplicate #[project_ref] attribute
+ --> $DIR/invalid.rs:165:5
+ |
+165 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ | ^^^^^^^^^^^^^^
+
+error: duplicate #[project_replace] attribute
+ --> $DIR/invalid.rs:169:5
+ |
+169 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ | ^^^^^^^^^^^^^^^^^^
+
+error: duplicate #[project] attribute
+ --> $DIR/invalid.rs:177:9
+ |
+177 | #[project] //~ ERROR duplicate #[project] attribute
+ | ^^^^^^^^^^
+
+error: duplicate #[project_ref] attribute
+ --> $DIR/invalid.rs:181:9
+ |
+181 | #[project_ref] //~ ERROR duplicate #[project_ref] attribute
+ | ^^^^^^^^^^^^^^
+
+error: duplicate #[project_replace] attribute
+ --> $DIR/invalid.rs:185:9
+ |
+185 | #[project_replace] //~ ERROR duplicate #[project_replace] attribute
+ | ^^^^^^^^^^^^^^^^^^
diff --git a/tests/ui/project/type-mismatch.rs b/tests/ui/project/type-mismatch.rs
new file mode 100644
index 0000000..41a70eb
--- /dev/null
+++ b/tests/ui/project/type-mismatch.rs
@@ -0,0 +1,74 @@
+#![feature(proc_macro_hygiene, stmt_expr_attributes)]
+
+use pin_project::{pin_project, project};
+use std::pin::Pin;
+
+#[project]
+fn type_mismatch() {
+ #[pin_project]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut foo = Enum::Variant1(1, 2);
+ let mut foo = Pin::new(&mut foo).project();
+
+ #[project]
+ match &mut foo {
+ Enum::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ None => {} //~ ERROR mismatched types
+ }
+}
+
+//~ ERROR mismatched types
+// span is lost.
+// Refs: https://github.com/rust-lang/rust/issues/43081
+fn type_mismatch_span_issue() {
+ #[pin_project]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut foo = Enum::Variant1(1, 2);
+ let mut foo = Pin::new(&mut foo).project();
+
+ #[project]
+ match &mut foo {
+ Enum::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ None => {}
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/project/type-mismatch.stderr b/tests/ui/project/type-mismatch.stderr
new file mode 100644
index 0000000..b4c97d5
--- /dev/null
+++ b/tests/ui/project/type-mismatch.stderr
@@ -0,0 +1,16 @@
+error[E0308]: mismatched types
+ --> $DIR/type-mismatch.rs:35:9
+ |
+23 | match &mut foo {
+ | -------- this expression has type `&mut type_mismatch::__EnumProjection<'_, {integer}, {integer}, _, _>`
+...
+35 | None => {} //~ ERROR mismatched types
+ | ^^^^ expected enum `type_mismatch::__EnumProjection`, found enum `std::option::Option`
+ |
+ = note: expected enum `type_mismatch::__EnumProjection<'_, {integer}, {integer}, _, _>`
+ found enum `std::option::Option<_>`
+
+error[E0308]: mismatched types
+ |
+ = note: expected enum `type_mismatch_span_issue::__EnumProjection<'_, {integer}, {integer}, _, _>`
+ found enum `std::option::Option<_>`
diff --git a/tests/ui/project/use-public.rs b/tests/ui/project/use-public.rs
new file mode 100644
index 0000000..23c9b89
--- /dev/null
+++ b/tests/ui/project/use-public.rs
@@ -0,0 +1,15 @@
+use pin_project::pin_project;
+
+#[pin_project]
+struct A {
+ field: u8,
+}
+
+pub mod b {
+ use pin_project::project;
+
+ #[project]
+ pub use crate::A; //~ ERROR E0365
+}
+
+fn main() {}
diff --git a/tests/ui/project/use-public.stderr b/tests/ui/project/use-public.stderr
new file mode 100644
index 0000000..7919d65
--- /dev/null
+++ b/tests/ui/project/use-public.stderr
@@ -0,0 +1,7 @@
+error[E0365]: `__AProjection` is private, and cannot be re-exported
+ --> $DIR/use-public.rs:12:13
+ |
+12 | pub use crate::A; //~ ERROR E0365
+ | ^^^^^^^^ re-export of private `__AProjection`
+ |
+ = note: consider declaring type or module `__AProjection` with `pub`
diff --git a/tests/ui/project/use.rs b/tests/ui/project/use.rs
new file mode 100644
index 0000000..d4b02c1
--- /dev/null
+++ b/tests/ui/project/use.rs
@@ -0,0 +1,17 @@
+use pin_project::pin_project;
+
+#[pin_project]
+struct A {
+ field: u8,
+}
+
+mod b {
+ use pin_project::project;
+
+ #[project]
+ use crate::A as B; //~ ERROR #[project] attribute may not be used on renamed imports
+ #[project]
+ use crate::*; //~ ERROR #[project] attribute may not be used on glob imports
+}
+
+fn main() {}
diff --git a/tests/ui/project/use.stderr b/tests/ui/project/use.stderr
new file mode 100644
index 0000000..07d0241
--- /dev/null
+++ b/tests/ui/project/use.stderr
@@ -0,0 +1,11 @@
+error: #[project] attribute may not be used on renamed imports
+ --> $DIR/use.rs:12:16
+ |
+12 | use crate::A as B; //~ ERROR #[project] attribute may not be used on renamed imports
+ | ^^^^^^
+
+error: #[project] attribute may not be used on glob imports
+ --> $DIR/use.rs:14:16
+ |
+14 | use crate::*; //~ ERROR #[project] attribute may not be used on glob imports
+ | ^
diff --git a/tests/ui/unsafe_unpin/conflict-unpin.rs b/tests/ui/unsafe_unpin/conflict-unpin.rs
new file mode 100644
index 0000000..e0c8a7b
--- /dev/null
+++ b/tests/ui/unsafe_unpin/conflict-unpin.rs
@@ -0,0 +1,30 @@
+use pin_project::pin_project;
+
+#[pin_project(UnsafeUnpin)] //~ ERROR E0119
+struct Foo<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T, U> Unpin for Foo<T, U> where T: Unpin {}
+
+#[pin_project(UnsafeUnpin)] //~ ERROR E0119
+struct Bar<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T, U> Unpin for Bar<T, U> {}
+
+#[pin_project(UnsafeUnpin)] //~ ERROR E0119
+struct Baz<T, U> {
+ #[pin]
+ future: T,
+ field: U,
+}
+
+impl<T: Unpin, U: Unpin> Unpin for Baz<T, U> {}
+
+fn main() {}
diff --git a/tests/ui/unsafe_unpin/conflict-unpin.stderr b/tests/ui/unsafe_unpin/conflict-unpin.stderr
new file mode 100644
index 0000000..62de016
--- /dev/null
+++ b/tests/ui/unsafe_unpin/conflict-unpin.stderr
@@ -0,0 +1,35 @@
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Foo<_, _>`:
+ --> $DIR/conflict-unpin.rs:3:1
+ |
+3 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Foo<_, _>`
+...
+10 | impl<T, U> Unpin for Foo<T, U> where T: Unpin {}
+ | --------------------------------------------- first implementation here
+ |
+ = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Foo<_, _>>` in future versions
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Bar<_, _>`:
+ --> $DIR/conflict-unpin.rs:12:1
+ |
+12 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bar<_, _>`
+...
+19 | impl<T, U> Unpin for Bar<T, U> {}
+ | ------------------------------ first implementation here
+ |
+ = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Bar<_, _>>` in future versions
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Baz<_, _>`:
+ --> $DIR/conflict-unpin.rs:21:1
+ |
+21 | #[pin_project(UnsafeUnpin)] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Baz<_, _>`
+...
+28 | impl<T: Unpin, U: Unpin> Unpin for Baz<T, U> {}
+ | -------------------------------------------- first implementation here
+ |
+ = note: upstream crates may add a new impl of trait `pin_project::UnsafeUnpin` for type `pin_project::__private::Wrapper<'_, Baz<_, _>>` in future versions
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs
new file mode 100644
index 0000000..429d60f
--- /dev/null
+++ b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.rs
@@ -0,0 +1,14 @@
+use pin_project::pin_project;
+
+#[pin_project(UnsafeUnpin)]
+struct Struct<T, U> {
+ #[pin]
+ inner: T,
+ other: U,
+}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Struct<(), ()>>(); //~ ERROR E0277
+}
diff --git a/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr
new file mode 100644
index 0000000..0baefe3
--- /dev/null
+++ b/tests/ui/unsafe_unpin/not-implement-unsafe-unpin.stderr
@@ -0,0 +1,11 @@
+error[E0277]: the trait bound `Struct<(), ()>: pin_project::UnsafeUnpin` is not satisfied
+ --> $DIR/not-implement-unsafe-unpin.rs:13:16
+ |
+10 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+13 | is_unpin::<Struct<(), ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^ the trait `pin_project::UnsafeUnpin` is not implemented for `Struct<(), ()>`
+ |
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Struct<(), ()>>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Struct<(), ()>`
diff --git a/tests/ui/unsafe_unpin/proper_unpin.rs b/tests/ui/unsafe_unpin/proper_unpin.rs
new file mode 100644
index 0000000..6573aec
--- /dev/null
+++ b/tests/ui/unsafe_unpin/proper_unpin.rs
@@ -0,0 +1,41 @@
+use pin_project::{pin_project, UnsafeUnpin};
+use std::marker::PhantomPinned;
+
+fn is_unpin<T: Unpin>() {}
+
+#[pin_project(UnsafeUnpin)]
+struct Blah<T, U> {
+ field1: U,
+ #[pin]
+ field2: T,
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for Blah<T, U> {}
+
+#[pin_project(UnsafeUnpin)]
+struct TrivialBounds {
+ #[pin]
+ field1: PhantomPinned,
+}
+
+#[pin_project(UnsafeUnpin)]
+struct OverlappingLifetimeNames<'pin, T, U> {
+ #[pin]
+ field1: U,
+ #[pin]
+ field2: Option<T>,
+ field3: &'pin (),
+}
+
+unsafe impl<T: Unpin, U: Unpin> UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {}
+
+fn main() {
+ is_unpin::<Blah<PhantomPinned, ()>>(); //~ ERROR E0277
+ is_unpin::<Blah<(), PhantomPinned>>(); // Ok
+ is_unpin::<Blah<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+
+ is_unpin::<TrivialBounds>(); //~ ERROR E0277
+
+ is_unpin::<OverlappingLifetimeNames<'_, PhantomPinned, ()>>(); //~ ERROR E0277
+ is_unpin::<OverlappingLifetimeNames<'_, (), PhantomPinned>>(); //~ ERROR E0277
+}
diff --git a/tests/ui/unsafe_unpin/proper_unpin.stderr b/tests/ui/unsafe_unpin/proper_unpin.stderr
new file mode 100644
index 0000000..410dd0e
--- /dev/null
+++ b/tests/ui/unsafe_unpin/proper_unpin.stderr
@@ -0,0 +1,63 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:33:5
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+33 | is_unpin::<Blah<PhantomPinned, ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah<std::marker::PhantomPinned, ()>`
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah<std::marker::PhantomPinned, ()>>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah<std::marker::PhantomPinned, ()>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:35:5
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+35 | is_unpin::<Blah<PhantomPinned, PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `Blah<std::marker::PhantomPinned, std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, Blah<std::marker::PhantomPinned, std::marker::PhantomPinned>>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `Blah<std::marker::PhantomPinned, std::marker::PhantomPinned>`
+
+error[E0277]: the trait bound `TrivialBounds: pin_project::UnsafeUnpin` is not satisfied
+ --> $DIR/proper_unpin.rs:37:16
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+37 | is_unpin::<TrivialBounds>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^ the trait `pin_project::UnsafeUnpin` is not implemented for `TrivialBounds`
+ |
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, TrivialBounds>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `TrivialBounds`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:39:5
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+39 | is_unpin::<OverlappingLifetimeNames<'_, PhantomPinned, ()>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>`
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `OverlappingLifetimeNames<'_, std::marker::PhantomPinned, ()>`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/proper_unpin.rs:40:5
+ |
+4 | fn is_unpin<T: Unpin>() {}
+ | ----- required by this bound in `is_unpin`
+...
+40 | is_unpin::<OverlappingLifetimeNames<'_, (), PhantomPinned>>(); //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>`
+ = note: required because of the requirements on the impl of `pin_project::UnsafeUnpin` for `pin_project::__private::Wrapper<'_, OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>>`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `OverlappingLifetimeNames<'_, (), std::marker::PhantomPinned>`
diff --git a/tests/ui/unstable-features/README.md b/tests/ui/unstable-features/README.md
new file mode 100644
index 0000000..b9215b6
--- /dev/null
+++ b/tests/ui/unstable-features/README.md
@@ -0,0 +1,5 @@
+# UI tests for unstable features
+
+These tests check how the guarantees and features provided by pin-project interact with unstable language features.
+
+The names of the files contained in this directory need to begin with the name of the feature.
diff --git a/tests/ui/unstable-features/marker_trait_attr-feature-gate.rs b/tests/ui/unstable-features/marker_trait_attr-feature-gate.rs
new file mode 100644
index 0000000..fa4b01e
--- /dev/null
+++ b/tests/ui/unstable-features/marker_trait_attr-feature-gate.rs
@@ -0,0 +1,19 @@
+// NB: If you change this test, change 'marker_trait_attr.rs' at the same time.
+
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project] //~ ERROR E0119
+struct Struct<T> {
+ #[pin]
+ x: T,
+}
+
+// unsound Unpin impl
+impl<T> Unpin for Struct<T> {}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Struct<PhantomPinned>>()
+}
diff --git a/tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr b/tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr
new file mode 100644
index 0000000..bab534b
--- /dev/null
+++ b/tests/ui/unstable-features/marker_trait_attr-feature-gate.stderr
@@ -0,0 +1,10 @@
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`:
+ --> $DIR/marker_trait_attr-feature-gate.rs:6:1
+ |
+6 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>`
+...
+13 | impl<T> Unpin for Struct<T> {}
+ | --------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/unstable-features/marker_trait_attr.rs b/tests/ui/unstable-features/marker_trait_attr.rs
new file mode 100644
index 0000000..0b8b30a
--- /dev/null
+++ b/tests/ui/unstable-features/marker_trait_attr.rs
@@ -0,0 +1,25 @@
+// NB: If you change this test, change 'marker_trait_attr-feature-gate.rs' at the same time.
+
+// marker_trait_attr
+// Tracking issue: https://github.com/rust-lang/rust/issues/29864
+#![feature(marker_trait_attr)]
+
+// See https://github.com/taiki-e/pin-project/issues/105#issuecomment-535355974
+
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project] //~ ERROR E0119
+struct Struct<T> {
+ #[pin]
+ x: T,
+}
+
+// unsound Unpin impl
+impl<T> Unpin for Struct<T> {}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Struct<PhantomPinned>>()
+}
diff --git a/tests/ui/unstable-features/marker_trait_attr.stderr b/tests/ui/unstable-features/marker_trait_attr.stderr
new file mode 100644
index 0000000..9b3ec57
--- /dev/null
+++ b/tests/ui/unstable-features/marker_trait_attr.stderr
@@ -0,0 +1,10 @@
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`:
+ --> $DIR/marker_trait_attr.rs:12:1
+ |
+12 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>`
+...
+19 | impl<T> Unpin for Struct<T> {}
+ | --------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs
new file mode 100644
index 0000000..0bd4a32
--- /dev/null
+++ b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.rs
@@ -0,0 +1,19 @@
+// NB: If you change this test, change 'overlapping_marker_traits.rs' at the same time.
+
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project] //~ ERROR E0119
+struct Struct<T> {
+ #[pin]
+ x: T,
+}
+
+// unsound Unpin impl
+impl<T> Unpin for Struct<T> {}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Struct<PhantomPinned>>()
+}
diff --git a/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr
new file mode 100644
index 0000000..4a8e238
--- /dev/null
+++ b/tests/ui/unstable-features/overlapping_marker_traits-feature-gate.stderr
@@ -0,0 +1,10 @@
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`:
+ --> $DIR/overlapping_marker_traits-feature-gate.rs:6:1
+ |
+6 | #[pin_project] //~ ERROR E0119
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>`
+...
+13 | impl<T> Unpin for Struct<T> {}
+ | --------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/unstable-features/overlapping_marker_traits.rs b/tests/ui/unstable-features/overlapping_marker_traits.rs
new file mode 100644
index 0000000..27d37a3
--- /dev/null
+++ b/tests/ui/unstable-features/overlapping_marker_traits.rs
@@ -0,0 +1,29 @@
+// NB: If you change this test, change 'overlapping_marker_traits-feature-gate.rs' at the same time.
+
+// This feature could break the guarantee for Unpin provided by pin-project,
+// but was removed in https://github.com/rust-lang/rust/pull/68544 (nightly-2020-02-06).
+// Refs:
+// * https://github.com/rust-lang/rust/issues/29864#issuecomment-515780867.
+// * https://github.com/taiki-e/pin-project/issues/105
+
+// overlapping_marker_traits
+// Tracking issue: https://github.com/rust-lang/rust/issues/29864
+#![feature(overlapping_marker_traits)]
+
+use pin_project::pin_project;
+use std::marker::PhantomPinned;
+
+#[pin_project]
+struct Struct<T> {
+ #[pin]
+ x: T,
+}
+
+// unsound Unpin impl
+impl<T> Unpin for Struct<T> {}
+
+fn is_unpin<T: Unpin>() {}
+
+fn main() {
+ is_unpin::<Struct<PhantomPinned>>()
+}
diff --git a/tests/ui/unstable-features/overlapping_marker_traits.stderr b/tests/ui/unstable-features/overlapping_marker_traits.stderr
new file mode 100644
index 0000000..91aaf6c
--- /dev/null
+++ b/tests/ui/unstable-features/overlapping_marker_traits.stderr
@@ -0,0 +1,18 @@
+error[E0557]: feature has been removed
+ --> $DIR/overlapping_marker_traits.rs:11:12
+ |
+11 | #![feature(overlapping_marker_traits)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ feature has been removed
+ |
+ = note: removed in favor of `#![feature(marker_trait_attr)]`
+
+error[E0119]: conflicting implementations of trait `std::marker::Unpin` for type `Struct<_>`:
+ --> $DIR/overlapping_marker_traits.rs:16:1
+ |
+16 | #[pin_project]
+ | ^^^^^^^^^^^^^^ conflicting implementation for `Struct<_>`
+...
+23 | impl<T> Unpin for Struct<T> {}
+ | --------------------------- first implementation here
+ |
+ = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
diff --git a/tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs b/tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs
new file mode 100644
index 0000000..8ad8e41
--- /dev/null
+++ b/tests/ui/unstable-features/run-pass/stmt_expr_attributes.rs
@@ -0,0 +1,62 @@
+// NB: If you change this test, change 'stmt_expr_attributes-feature-gate.rs' at the same time.
+
+// proc_macro_hygiene
+// Tracking issue: https://github.com/rust-lang/rust/issues/54727
+#![feature(proc_macro_hygiene)]
+// stmt_expr_attributes
+// Tracking issue: https://github.com/rust-lang/rust/issues/15701
+#![feature(stmt_expr_attributes)]
+
+use pin_project::{pin_project, project};
+use std::pin::Pin;
+
+fn project_stmt_expr_nightly() {
+ #[pin_project]
+ enum Baz<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut baz = Baz::Variant1(1, 2);
+
+ let mut baz = Pin::new(&mut baz).project();
+
+ #[project]
+ match &mut baz {
+ Baz::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Baz::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ Baz::None => {}
+ }
+
+ let () = #[project]
+ match &mut baz {
+ Baz::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Baz::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ Baz::None => {}
+ };
+}
+
+fn main() {}
diff --git a/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs
new file mode 100644
index 0000000..8226723
--- /dev/null
+++ b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.rs
@@ -0,0 +1,55 @@
+// NB: If you change this test, change 'stmt_expr_attributes.rs' at the same time.
+
+use pin_project::{pin_project, project};
+use std::pin::Pin;
+
+fn project_stmt_expr_nightly() {
+ #[pin_project]
+ enum Enum<A, B, C, D> {
+ Variant1(#[pin] A, B),
+ Variant2 {
+ #[pin]
+ field1: C,
+ field2: D,
+ },
+ None,
+ }
+
+ let mut baz = Enum::Variant1(1, 2);
+
+ let mut baz = Pin::new(&mut baz).project();
+
+ #[project] //~ ERROR E0658
+ match &mut baz {
+ Enum::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ Enum::None => {}
+ }
+
+ let () = #[project] //~ ERROR E0658
+ match &mut baz {
+ Enum::Variant1(x, y) => {
+ let x: &mut Pin<&mut i32> = x;
+ assert_eq!(**x, 1);
+
+ let y: &mut &mut i32 = y;
+ assert_eq!(**y, 2);
+ }
+ Enum::Variant2 { field1, field2 } => {
+ let _x: &mut Pin<&mut i32> = field1;
+ let _y: &mut &mut i32 = field2;
+ }
+ Enum::None => {}
+ };
+}
+
+fn main() {}
diff --git a/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr
new file mode 100644
index 0000000..6510ec7
--- /dev/null
+++ b/tests/ui/unstable-features/stmt_expr_attributes-feature-gate.stderr
@@ -0,0 +1,35 @@
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/stmt_expr_attributes-feature-gate.rs:22:5
+ |
+22 | #[project] //~ ERROR E0658
+ | ^^^^^^^^^^
+ |
+ = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+ = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: attributes on expressions are experimental
+ --> $DIR/stmt_expr_attributes-feature-gate.rs:38:14
+ |
+38 | let () = #[project] //~ ERROR E0658
+ | ^^^^^^^^^^
+ |
+ = note: see issue #15701 <https://github.com/rust-lang/rust/issues/15701> for more information
+ = help: add `#![feature(stmt_expr_attributes)]` to the crate attributes to enable
+
+error[E0658]: custom attributes cannot be applied to expressions
+ --> $DIR/stmt_expr_attributes-feature-gate.rs:22:5
+ |
+22 | #[project] //~ ERROR E0658
+ | ^^^^^^^^^^
+ |
+ = note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information
+ = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
+
+error[E0658]: custom attributes cannot be applied to expressions
+ --> $DIR/stmt_expr_attributes-feature-gate.rs:38:14
+ |
+38 | let () = #[project] //~ ERROR E0658
+ | ^^^^^^^^^^
+ |
+ = note: see issue #54727 <https://github.com/rust-lang/rust/issues/54727> for more information
+ = help: add `#![feature(proc_macro_hygiene)]` to the crate attributes to enable
diff --git a/tests/ui/unstable-features/trivial_bounds-bug.rs b/tests/ui/unstable-features/trivial_bounds-bug.rs
new file mode 100644
index 0000000..2ec4960
--- /dev/null
+++ b/tests/ui/unstable-features/trivial_bounds-bug.rs
@@ -0,0 +1,33 @@
+// NB: If you change this test, change 'trivial_bounds-feature-gate.rs' at the same time.
+
+// trivial_bounds
+// Tracking issue: https://github.com/rust-lang/rust/issues/48214
+#![feature(trivial_bounds)]
+
+mod phantom_pinned {
+ use std::marker::{PhantomData, PhantomPinned};
+
+ struct A(PhantomPinned);
+
+ // bug of trivial_bounds?
+ impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277
+
+ struct Wrapper<T>(T);
+
+ impl<T> Unpin for Wrapper<T> where T: Unpin {}
+
+ struct B(PhantomPinned);
+
+ impl Unpin for B where Wrapper<PhantomPinned>: Unpin {} // Ok
+
+ struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T);
+
+ impl<T> Unpin for WrapperWithLifetime<'_, T> where T: Unpin {}
+
+ struct C(PhantomPinned);
+
+ // Ok
+ impl<'a> Unpin for C where WrapperWithLifetime<'a, PhantomPinned>: Unpin {}
+}
+
+fn main() {}
diff --git a/tests/ui/unstable-features/trivial_bounds-bug.stderr b/tests/ui/unstable-features/trivial_bounds-bug.stderr
new file mode 100644
index 0000000..7ddf10c
--- /dev/null
+++ b/tests/ui/unstable-features/trivial_bounds-bug.stderr
@@ -0,0 +1,10 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/trivial_bounds-bug.rs:13:43
+ |
+13 | impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277
+ | ^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ ::: /Users/taiki/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore/marker.rs:736:1
+ |
+736 | pub auto trait Unpin {}
+ | -------------------- required by this bound in `std::marker::Unpin`
diff --git a/tests/ui/unstable-features/trivial_bounds-feature-gate.rs b/tests/ui/unstable-features/trivial_bounds-feature-gate.rs
new file mode 100644
index 0000000..0453a3f
--- /dev/null
+++ b/tests/ui/unstable-features/trivial_bounds-feature-gate.rs
@@ -0,0 +1,54 @@
+// NB: If you change this test, change 'trivial_bounds.rs' at the same time.
+
+mod phantom_pinned {
+ use std::marker::{PhantomData, PhantomPinned};
+
+ struct A(PhantomPinned);
+
+ impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277
+
+ struct Wrapper<T>(T);
+
+ impl<T> Unpin for Wrapper<T> where T: Unpin {}
+
+ struct B(PhantomPinned);
+
+ impl Unpin for B where Wrapper<PhantomPinned>: Unpin {} //~ ERROR E0277
+
+ struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T);
+
+ impl<T> Unpin for WrapperWithLifetime<'_, T> where T: Unpin {}
+
+ struct C(PhantomPinned);
+
+ impl<'a> Unpin for C where WrapperWithLifetime<'a, PhantomPinned>: Unpin {}
+ // Ok
+}
+
+mod inner {
+ use std::marker::{PhantomData, PhantomPinned};
+
+ struct Inner(PhantomPinned);
+
+ struct A(Inner);
+
+ impl Unpin for A where Inner: Unpin {} //~ ERROR E0277
+
+ struct Wrapper<T>(T);
+
+ impl<T> Unpin for Wrapper<T> where T: Unpin {}
+
+ struct B(Inner);
+
+ impl Unpin for B where Wrapper<Inner>: Unpin {} //~ ERROR E0277
+
+ struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T);
+
+ impl<T> Unpin for WrapperWithLifetime<'_, T> where T: Unpin {}
+
+ struct C(Inner);
+
+ impl<'a> Unpin for C where WrapperWithLifetime<'a, Inner>: Unpin {} // Ok
+}
+
+fn main() {}
diff --git a/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr b/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr
new file mode 100644
index 0000000..31196a2
--- /dev/null
+++ b/tests/ui/unstable-features/trivial_bounds-feature-gate.stderr
@@ -0,0 +1,50 @@
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/trivial_bounds-feature-gate.rs:8:5
+ |
+8 | impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = help: see issue #48214
+ = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/trivial_bounds-feature-gate.rs:8:43
+ |
+8 | impl Unpin for A where PhantomPinned: Unpin {} //~ ERROR E0277
+ | ^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ ::: /Users/taiki/.rustup/toolchains/nightly-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore/marker.rs:736:1
+ |
+736 | pub auto trait Unpin {}
+ | -------------------- required by this bound in `std::marker::Unpin`
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/trivial_bounds-feature-gate.rs:16:5
+ |
+16 | impl Unpin for B where Wrapper<PhantomPinned>: Unpin {} //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `phantom_pinned::Wrapper<std::marker::PhantomPinned>`
+ = help: see issue #48214
+ = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/trivial_bounds-feature-gate.rs:35:5
+ |
+35 | impl Unpin for A where Inner: Unpin {} //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `inner::Inner`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `inner::Inner`
+ = help: see issue #48214
+ = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
+
+error[E0277]: `std::marker::PhantomPinned` cannot be unpinned
+ --> $DIR/trivial_bounds-feature-gate.rs:43:5
+ |
+43 | impl Unpin for B where Wrapper<Inner>: Unpin {} //~ ERROR E0277
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ within `inner::Inner`, the trait `std::marker::Unpin` is not implemented for `std::marker::PhantomPinned`
+ |
+ = note: required because it appears within the type `inner::Inner`
+ = note: required because of the requirements on the impl of `std::marker::Unpin` for `inner::Wrapper<inner::Inner>`
+ = help: see issue #48214
+ = help: add `#![feature(trivial_bounds)]` to the crate attributes to enable
diff --git a/tests/ui/unstable-features/trivial_bounds.rs b/tests/ui/unstable-features/trivial_bounds.rs
new file mode 100644
index 0000000..680effe
--- /dev/null
+++ b/tests/ui/unstable-features/trivial_bounds.rs
@@ -0,0 +1,34 @@
+// NB: If you change this test, change 'trivial_bounds-feature-gate.rs' at the same time.
+
+// trivial_bounds
+// Tracking issue: https://github.com/rust-lang/rust/issues/48214
+#![feature(trivial_bounds)]
+#![deny(trivial_bounds)]
+
+use std::marker::{PhantomData, PhantomPinned};
+
+fn inner() {
+ struct Inner(PhantomPinned);
+
+ struct A(Inner);
+
+ impl Unpin for A where Inner: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters
+
+ struct Wrapper<T>(T);
+
+ impl<T> Unpin for Wrapper<T> where T: Unpin {}
+
+ struct B(Inner);
+
+ impl Unpin for B where Wrapper<Inner>: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters
+
+ struct WrapperWithLifetime<'a, T>(PhantomData<&'a ()>, T);
+
+ impl<T> Unpin for WrapperWithLifetime<'_, T> where T: Unpin {}
+
+ struct C(Inner);
+
+ impl<'a> Unpin for C where WrapperWithLifetime<'a, Inner>: Unpin {} // Ok
+}
+
+fn main() {}
diff --git a/tests/ui/unstable-features/trivial_bounds.stderr b/tests/ui/unstable-features/trivial_bounds.stderr
new file mode 100644
index 0000000..03d0161
--- /dev/null
+++ b/tests/ui/unstable-features/trivial_bounds.stderr
@@ -0,0 +1,17 @@
+error: Trait bound inner::Inner: std::marker::Unpin does not depend on any type or lifetime parameters
+ --> $DIR/trivial_bounds.rs:15:35
+ |
+15 | impl Unpin for A where Inner: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters
+ | ^^^^^
+ |
+note: the lint level is defined here
+ --> $DIR/trivial_bounds.rs:6:9
+ |
+6 | #![deny(trivial_bounds)]
+ | ^^^^^^^^^^^^^^
+
+error: Trait bound inner::Wrapper<inner::Inner>: std::marker::Unpin does not depend on any type or lifetime parameters
+ --> $DIR/trivial_bounds.rs:23:44
+ |
+23 | impl Unpin for B where Wrapper<Inner>: Unpin {} //~ ERROR std::marker::Unpin does not depend on any type or lifetime parameters
+ | ^^^^^
diff --git a/tests/unsafe_unpin.rs b/tests/unsafe_unpin.rs
new file mode 100644
index 0000000..5e0e7cf
--- /dev/null
+++ b/tests/unsafe_unpin.rs
@@ -0,0 +1,53 @@
+#![warn(rust_2018_idioms, single_use_lifetimes)]
+#![allow(dead_code)]
+
+use pin_project::{pin_project, UnsafeUnpin};
+use std::{marker::PhantomPinned, pin::Pin};
+
+fn is_unpin<T: Unpin>() {}
+
+#[pin_project(UnsafeUnpin)]
+pub struct Blah<T, U> {
+ field1: U,
+ #[pin]
+ field2: T,
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for Blah<T, U> {}
+
+#[pin_project(UnsafeUnpin)]
+pub struct OverlappingLifetimeNames<'pin, T, U> {
+ #[pin]
+ field1: T,
+ field2: U,
+ field3: &'pin (),
+}
+
+unsafe impl<T: Unpin, U> UnsafeUnpin for OverlappingLifetimeNames<'_, T, U> {}
+
+#[test]
+fn unsafe_unpin() {
+ is_unpin::<Blah<(), PhantomPinned>>();
+ is_unpin::<OverlappingLifetimeNames<'_, (), ()>>();
+}
+
+#[test]
+fn trivial_bounds() {
+ #[pin_project(UnsafeUnpin)]
+ pub struct NotImplementUnsafUnpin {
+ #[pin]
+ field: PhantomPinned,
+ }
+}
+
+#[test]
+fn test() {
+ let mut x = OverlappingLifetimeNames { field1: 0, field2: 1, field3: &() };
+ let x = Pin::new(&mut x);
+ let y = x.as_ref().project_ref();
+ let _: Pin<&u8> = y.field1;
+ let _: &u8 = y.field2;
+ let y = x.project();
+ let _: Pin<&mut u8> = y.field1;
+ let _: &mut u8 = y.field2;
+}