diff options
Diffstat (limited to 'tests/project.rs')
-rw-r--r-- | tests/project.rs | 297 |
1 files changed, 297 insertions, 0 deletions
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!(), + } +} |