use proc_macro2::{Delimiter, Group, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
visit_mut::VisitMut,
*,
};
use super::PIN;
use crate::utils::{
determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ParseBufferExt,
ProjKind, ReplaceReceiver, SliceExt, Variants,
};
pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> {
let mut input: DeriveInput = syn::parse2(input)?;
let ident = &input.ident;
let ty_generics = input.generics.split_for_impl().1;
let self_ty = parse_quote!(#ident #ty_generics);
let mut visitor = ReplaceReceiver(&self_ty);
visitor.visit_generics_mut(&mut input.generics);
visitor.visit_data_mut(&mut input.data);
let mut cx = Context::new(&input.attrs, &input.vis, &input.ident, &mut input.generics)?;
let packed_check;
let (mut items, scoped_items) = match &input.data {
Data::Struct(data) => {
packed_check = Some(cx.ensure_not_packed(&data.fields)?);
cx.parse_struct(data)?
}
Data::Enum(data) => {
packed_check = None;
cx.parse_enum(data)?
}
Data::Union(_) => {
return Err(error!(
input,
"#[pin_project] attribute may only be used on structs or enums"
));
}
};
let unpin_impl = cx.make_unpin_impl();
let drop_impl = cx.make_drop_impl();
let dummy_const = if cfg!(underscore_consts) {
format_ident!("_")
} else {
format_ident!("__SCOPE_{}", ident)
};
items.extend(quote! {
#[doc(hidden)]
#[allow(non_upper_case_globals)]
#[allow(single_use_lifetimes)]
#[allow(clippy::used_underscore_binding)]
const #dummy_const: () = {
#scoped_items
#unpin_impl
#drop_impl
#packed_check
};
});
Ok(items)
}
fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> {
if fields.is_empty() {
let msg = "#[pin_project] attribute may not be used on structs with zero fields";
if let Fields::Unit = fields { Err(error!(ident, msg)) } else { Err(error!(fields, msg)) }
} else {
Ok(())
}
}
fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> {
if variants.is_empty() {
return Err(Error::new(
brace_token.span,
"#[pin_project] attribute may not be used on enums without variants",
));
}
let has_field = variants.iter().try_fold(false, |has_field, v| {
if let Some((_, e)) = &v.discriminant {
Err(error!(e, "#[pin_project] attribute may not be used on enums with discriminants"))
} else if let Some(attr) = v.attrs.find(PIN) {
Err(error!(attr, "#[pin] attribute may only be used on fields of structs or variants"))
} else if v.fields.is_empty() {
Ok(has_field)
} else {
Ok(true)
}
})?;
if has_field {
Ok(())
} else {
Err(error!(variants, "#[pin_project] attribute may not be used on enums with zero fields"))
}
}
struct Args {
pinned_drop: Option<Span>,
unpin_impl: UnpinImpl,
project: Option<Ident>,
project_ref: Option<Ident>,
project_replace: ProjReplace,
}
enum ProjReplace {
None,
Unnamed {
span: Span,
},
Named {
span: Span,
ident: Ident,
},
}
impl ProjReplace {
fn span(&self) -> Option<Span> {
match self {
ProjReplace::None => None,
ProjReplace::Named { span, .. } | ProjReplace::Unnamed { span, .. } => Some(*span),
}
}
fn ident(&self) -> Option<&Ident> {
if let ProjReplace::Named { ident, .. } = self { Some(ident) } else { None }
}
}
const DUPLICATE_PIN: &str = "duplicate #[pin] attribute";
impl Args {
fn get(attrs: &[Attribute]) -> Result<Self> {
struct Input(Option<TokenStream>);
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> Result<Self> {
Ok(Self((|| {
let content = input.parenthesized().ok()?;
let private = content.parse::<Ident>().ok()?;
if private == "__private" {
content.parenthesized().ok()?.parse::<TokenStream>().ok()
} else {
None
}
})()))
}
}
if let Some(attr) = attrs.find("pin_project") {
return Err(error!(attr, "duplicate #[pin_project] attribute"));
}
let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN));
let prev = if let Some(attr) = attrs.next() {
(attr, syn::parse2::<Input>(attr.tokens.clone()).unwrap().0)
} else {
return Err(error!(TokenStream::new(), "#[pin_project] attribute has been removed"));
};
if let Some(attr) = attrs.next() {
let (prev_attr, prev_res) = &prev;
let res = syn::parse2::<Input>(attr.tokens.clone()).unwrap().0;
let span = match (&prev_res, res) {
(Some(_), _) => attr,
(_, Some(_)) => prev_attr,
(None, None) => prev_attr,
};
Err(error!(span, DUPLICATE_PIN))
} else {
syn::parse2(prev.1.unwrap())
}
}
}
impl Parse for Args {
fn parse(input: ParseStream<'_>) -> Result<Self> {
mod kw {
syn::custom_keyword!(Unpin);
}
fn parse_value(
input: ParseStream<'_>,
name: &Ident,
has_prev: bool,
) -> Result<(Ident, TokenStream)> {
if input.is_empty() {
return Err(error!(name, "expected `{0} = <identifier>`, found `{0}`", name));
}
let eq_token: Token![=] = input.parse()?;
if input.is_empty() {
let span = quote!(#name #eq_token);
return Err(error!(span, "expected `{0} = <identifier>`, found `{0} =`", name));
}
let value: Ident = input.parse()?;
let span = quote!(#name #value);
if has_prev {
Err(error!(span, "duplicate `{}` argument", name))
} else {
Ok((value, span))
}
}
let mut pinned_drop = None;
let mut unsafe_unpin = None;
let mut not_unpin = None;
let mut project = None;
let mut project_ref = None;
let mut replace = None;
let mut project_replace_value = None;
let mut project_replace_span = None;
while !input.is_empty() {
if input.peek(Token![!]) {
let bang: Token![!] = input.parse()?;
if input.is_empty() {
return Err(error!(bang, "expected `!Unpin`, found `!`"));
}
let unpin: kw::Unpin = input.parse()?;
let span = quote!(#bang #unpin);
if not_unpin.replace(span.span()).is_some() {
return Err(error!(span, "duplicate `!Unpin` argument"));
}
} else {
let token = input.parse::<Ident>()?;
match &*token.to_string() {
"PinnedDrop" => {
if pinned_drop.replace(token.span()).is_some() {
return Err(error!(token, "duplicate `PinnedDrop` argument"));
}
}
"UnsafeUnpin" => {
if unsafe_unpin.replace(token.span()).is_some() {
return Err(error!(token, "duplicate `UnsafeUnpin` argument"));
}
}
"project" => {
project = Some(parse_value(input, &token, project.is_some())?.0);
}
"project_ref" => {
project_ref = Some(parse_value(input, &token, project_ref.is_some())?.0);
}
"project_replace" => {
if input.peek(Token![=]) {
let (value, span) =
parse_value(input, &token, project_replace_span.is_some())?;
project_replace_value = Some(value);
project_replace_span = Some(span.span());
} else if project_replace_span.is_some() {
return Err(error!(token, "duplicate `project_replace` argument"));
} else {
project_replace_span = Some(token.span());
}
}
"Replace" => {
if replace.replace(token.span()).is_some() {
return Err(error!(token, "duplicate `Replace` argument"));
}
}
_ => return Err(error!(token, "unexpected argument: {}", token)),
}
}
if input.is_empty() {
break;
}
let _: Token![,] = input.parse()?;
}
if project.is_some() || project_ref.is_some() {
if project == project_ref {
return Err(error!(
project_ref,
"name `{}` is already specified by `project` argument",
project_ref.as_ref().unwrap()
));
}
if let Some(ident) = &project_replace_value {
if project == project_replace_value {
return Err(error!(
ident,
"name `{}` is already specified by `project` argument", ident
));
} else if project_ref == project_replace_value {
return Err(error!(
ident,
"name `{}` is already specified by `project_ref` argument", ident
));
}
}
}
if let Some(span) = pinned_drop {
if project_replace_span.is_some() {
return Err(Error::new(
span,
"arguments `PinnedDrop` and `project_replace` are mutually exclusive",
));
} else if replace.is_some() {
return Err(Error::new(
span,
"arguments `PinnedDrop` and `Replace` are mutually exclusive",
));
}
}
let project_replace = match (project_replace_span, project_replace_value, replace) {
(None, _, None) => ProjReplace::None,
(Some(span), Some(ident), _) => ProjReplace::Named { ident, span },
(Some(span), ..) | (None, _, Some(span)) => ProjReplace::Unnamed { span },
};
let unpin_impl = match (unsafe_unpin, not_unpin) {
(None, None) => UnpinImpl::Default,
(Some(span), None) => UnpinImpl::Unsafe(span),
(None, Some(span)) => UnpinImpl::Negative(span),
(Some(span), Some(_)) => {
return Err(Error::new(
span,
"arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive",
));
}
};
Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace })
}
}
struct OriginalType<'a> {
attrs: &'a [Attribute],
vis: &'a Visibility,
ident: &'a Ident,
generics: &'a Generics,
}
struct ProjectedType {
vis: Visibility,
mut_ident: Ident,
ref_ident: Ident,
own_ident: Ident,
lifetime: Lifetime,
generics: Generics,
where_clause: WhereClause,
}
struct ProjectedVariants {
proj_variants: TokenStream,
proj_ref_variants: TokenStream,
proj_own_variants: TokenStream,
proj_arms: TokenStream,
proj_ref_arms: TokenStream,
proj_own_arms: TokenStream,
}
#[derive(Default)]
struct ProjectedFields {
proj_pat: TokenStream,
proj_body: TokenStream,
proj_own_body: TokenStream,
proj_fields: TokenStream,
proj_ref_fields: TokenStream,
proj_own_fields: TokenStream,
}
struct Context<'a> {
orig: OriginalType<'a>,
proj: ProjectedType,
pinned_fields: Vec<Type>,
pinned_drop: Option<Span>,
unpin_impl: UnpinImpl,
project: bool,
project_ref: bool,
project_replace: ProjReplace,
}
#[derive(Clone, Copy)]
enum UnpinImpl {
Default,
Unsafe(Span),
Negative(Span),
}
impl<'a> Context<'a> {
fn new(
attrs: &'a [Attribute],
vis: &'a Visibility,
ident: &'a Ident,
generics: &'a mut Generics,
) -> Result<Self> {
let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } =
Args::get(attrs)?;
let mut lifetime_name = String::from("'pin");
determine_lifetime_name(&mut lifetime_name, generics);
let lifetime = Lifetime::new(&lifetime_name, Span::call_site());
let ty_generics = generics.split_for_impl().1;
let ty_generics_as_generics = parse_quote!(#ty_generics);
let mut proj_generics = generics.clone();
let pred = insert_lifetime_and_bound(
&mut proj_generics,
lifetime.clone(),
&ty_generics_as_generics,
ident,
);
let mut where_clause = generics.make_where_clause().clone();
where_clause.predicates.push(pred);
let own_ident =
project_replace.ident().cloned().unwrap_or_else(|| ProjKind::Owned.proj_ident(ident));
Ok(Self {
pinned_drop,
unpin_impl,
project: project.is_some(),
project_ref: project_ref.is_some(),
project_replace,
proj: ProjectedType {
vis: determine_visibility(vis),
mut_ident: project.unwrap_or_else(|| ProjKind::Mutable.proj_ident(ident)),
ref_ident: project_ref.unwrap_or_else(|| ProjKind::Immutable.proj_ident(ident)),
own_ident,
lifetime,
generics: proj_generics,
where_clause,
},
orig: OriginalType { attrs, vis, ident, generics },
pinned_fields: Vec::new(),
})
}
fn proj_attrs(&self) -> (TokenStream, TokenStream, TokenStream) {
let doc_attr = quote!(#[doc(hidden)]);
let doc_proj = if self.project { None } else { Some(&doc_attr) };
let doc_proj_ref = if self.project_ref { None } else { Some(&doc_attr) };
let doc_proj_own =
if self.project_replace.ident().is_some() { None } else { Some(&doc_attr) };
let proj_mut = quote! {
#doc_proj
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::mut_mut)]
#[allow(clippy::type_repetition_in_bounds)]
};
let proj_ref = quote! {
#doc_proj_ref
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(clippy::type_repetition_in_bounds)]
};
let proj_own = quote! {
#doc_proj_own
#[allow(dead_code)]
#[allow(single_use_lifetimes)]
#[allow(unreachable_pub)]
};
(proj_mut, proj_ref, proj_own)
}
fn parse_struct(
&mut self,
DataStruct { fields, .. }: &DataStruct,
) -> Result<(TokenStream, TokenStream)> {
validate_struct(self.orig.ident, fields)?;
let ProjectedFields {
proj_pat,
proj_body,
proj_fields,
proj_ref_fields,
proj_own_fields,
proj_own_body,
} = match fields {
Fields::Named(_) => self.visit_fields(None, fields, Delimiter::Brace)?,
Fields::Unnamed(_) => self.visit_fields(None, fields, Delimiter::Parenthesis)?,
Fields::Unit => unreachable!(),
};
let proj_ident = &self.proj.mut_ident;
let proj_ref_ident = &self.proj.ref_ident;
let proj_own_ident = &self.proj.own_ident;
let vis = &self.proj.vis;
let mut orig_generics = self.orig.generics.clone();
let orig_where_clause = orig_generics.where_clause.take();
let proj_generics = &self.proj.generics;
let proj_where_clause = &self.proj.where_clause;
let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields {
Fields::Named(_) => (
quote!(#proj_where_clause #proj_fields),
quote!(#proj_where_clause #proj_ref_fields),
quote!(#orig_where_clause #proj_own_fields),
),
Fields::Unnamed(_) => (
quote!(#proj_fields #proj_where_clause;),
quote!(#proj_ref_fields #proj_where_clause;),
quote!(#proj_own_fields #orig_where_clause;),
),
Fields::Unit => unreachable!(),
};
let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs();
let mut proj_items = quote! {
#proj_attrs
#vis struct #proj_ident #proj_generics #where_clause_fields
#proj_ref_attrs
#vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields
};
if self.project_replace.span().is_some() {
proj_items.extend(quote! {
#proj_own_attrs
#vis struct #proj_own_ident #orig_generics #where_clause_own_fields
});
}
let proj_mut_body = quote! {
let Self #proj_pat = self.get_unchecked_mut();
#proj_ident #proj_body
};
let proj_ref_body = quote! {
let Self #proj_pat = self.get_ref();
#proj_ref_ident #proj_body
};
let proj_own_body = quote! {
let __self_ptr: *mut Self = self.get_unchecked_mut();
let Self #proj_pat = &mut *__self_ptr;
#proj_own_body
};
let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body);
Ok((proj_items, proj_impl))
}
fn parse_enum(
&mut self,
DataEnum { brace_token, variants, .. }: &DataEnum,
) -> Result<(TokenStream, TokenStream)> {
validate_enum(*brace_token, variants)?;
let ProjectedVariants {
proj_variants,
proj_ref_variants,
proj_own_variants,
proj_arms,
proj_ref_arms,
proj_own_arms,
} = self.visit_variants(variants)?;
let proj_ident = &self.proj.mut_ident;
let proj_ref_ident = &self.proj.ref_ident;
let proj_own_ident = &self.proj.own_ident;
let vis = &self.proj.vis;
let mut orig_generics = self.orig.generics.clone();
let orig_where_clause = orig_generics.where_clause.take();
let proj_generics = &self.proj.generics;
let proj_where_clause = &self.proj.where_clause;
let (proj_attrs, proj_ref_attrs, proj_own_attrs) = self.proj_attrs();
let mut proj_items = quote! {
#proj_attrs
#vis enum #proj_ident #proj_generics #proj_where_clause {
#proj_variants
}
#proj_ref_attrs
#vis enum #proj_ref_ident #proj_generics #proj_where_clause {
#proj_ref_variants
}
};
if self.project_replace.span().is_some() {
proj_items.extend(quote! {
#proj_own_attrs
#vis enum #proj_own_ident #orig_generics #orig_where_clause {
#proj_own_variants
}
});
}
let proj_mut_body = quote! {
match self.get_unchecked_mut() {
#proj_arms
}
};
let proj_ref_body = quote! {
match self.get_ref() {
#proj_ref_arms
}
};
let proj_own_body = quote! {
let __self_ptr: *mut Self = self.get_unchecked_mut();
match &mut *__self_ptr {
#proj_own_arms
}
};
let proj_impl = self.make_proj_impl(&proj_mut_body, &proj_ref_body, &proj_own_body);
Ok((proj_items, proj_impl))
}
fn visit_variants(&mut self, variants: &Variants) -> Result<ProjectedVariants> {
let mut proj_variants = TokenStream::new();
let mut proj_ref_variants = TokenStream::new();
let mut proj_own_variants = TokenStream::new();
let mut proj_arms = TokenStream::new();
let mut proj_ref_arms = TokenStream::new();
let mut proj_own_arms = TokenStream::new();
for Variant { ident, fields, .. } in variants {
let ProjectedFields {
proj_pat,
proj_body,
proj_fields,
proj_ref_fields,
proj_own_fields,
proj_own_body,
} = match fields {
Fields::Named(_) => self.visit_fields(Some(ident), fields, Delimiter::Brace)?,
Fields::Unnamed(_) => {
self.visit_fields(Some(ident), fields, Delimiter::Parenthesis)?
}
Fields::Unit => ProjectedFields {
proj_own_body: self.proj_own_body(Some(ident), None, &[]),
..ProjectedFields::default()
},
};
let orig_ident = self.orig.ident;
let proj_ident = &self.proj.mut_ident;
let proj_ref_ident = &self.proj.ref_ident;
proj_variants.extend(quote! {
#ident #proj_fields,
});
proj_ref_variants.extend(quote! {
#ident #proj_ref_fields,
});
proj_own_variants.extend(quote! {
#ident #proj_own_fields,
});
proj_arms.extend(quote! {
#orig_ident::#ident #proj_pat => {
#proj_ident::#ident #proj_body
}
});
proj_ref_arms.extend(quote! {
#orig_ident::#ident #proj_pat => {
#proj_ref_ident::#ident #proj_body
}
});
proj_own_arms.extend(quote! {
#orig_ident::#ident #proj_pat => {
#proj_own_body
}
});
}
Ok(ProjectedVariants {
proj_variants,
proj_ref_variants,
proj_own_variants,
proj_arms,
proj_ref_arms,
proj_own_arms,
})
}
fn visit_fields(
&mut self,
variant_ident: Option<&Ident>,
fields: &Fields,
delim: Delimiter,
) -> Result<ProjectedFields> {
let mut proj_pat = TokenStream::new();
let mut proj_body = TokenStream::new();
let mut proj_fields = TokenStream::new();
let mut proj_ref_fields = TokenStream::new();
let mut proj_own_fields = TokenStream::new();
let mut proj_move = TokenStream::new();
let mut pinned_bindings = Vec::with_capacity(fields.len());
for (i, Field { attrs, vis, ident, colon_token, ty, .. }) in fields.iter().enumerate() {
let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i));
proj_pat.extend(quote!(#binding,));
if attrs.position_exact(PIN)?.is_some() {
let lifetime = &self.proj.lifetime;
proj_fields.extend(quote! {
#vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>,
});
proj_ref_fields.extend(quote! {
#vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime (#ty)>,
});
proj_own_fields.extend(quote! {
#vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>,
});
proj_body.extend(quote! {
#ident #colon_token ::pin_project::__private::Pin::new_unchecked(#binding),
});
proj_move.extend(quote! {
#ident #colon_token ::pin_project::__private::PhantomData,
});
self.pinned_fields.push(ty.clone());
pinned_bindings.push(binding);
} else {
let lifetime = &self.proj.lifetime;
proj_fields.extend(quote! {
#vis #ident #colon_token &#lifetime mut (#ty),
});
proj_ref_fields.extend(quote! {
#vis #ident #colon_token &#lifetime (#ty),
});
proj_own_fields.extend(quote! {
#vis #ident #colon_token #ty,
});
proj_body.extend(quote! {
#binding,
});
proj_move.extend(quote! {
#ident #colon_token ::pin_project::__private::ptr::read(#binding),
});
}
}
fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream {
Group::new(delim, tokens).into_token_stream()
}
let proj_pat = surround(delim, proj_pat);
let proj_body = surround(delim, proj_body);
let proj_fields = surround(delim, proj_fields);
let proj_ref_fields = surround(delim, proj_ref_fields);
let proj_own_fields = surround(delim, proj_own_fields);
let proj_move = Group::new(delim, proj_move);
let proj_own_body = self.proj_own_body(variant_ident, Some(proj_move), &pinned_bindings);
Ok(ProjectedFields {
proj_pat,
proj_body,
proj_own_body,
proj_fields,
proj_ref_fields,
proj_own_fields,
})
}
fn proj_own_body(
&self,
variant_ident: Option<&'a Ident>,
proj_move: Option<Group>,
pinned_fields: &[Ident],
) -> TokenStream {
let ident = &self.proj.own_ident;
let proj_own = match variant_ident {
Some(variant_ident) => quote!(#ident::#variant_ident),
None => quote!(#ident),
};
let pinned_fields = pinned_fields.iter().rev();
quote! {
let __result = #proj_own #proj_move;
let __guard = ::pin_project::__private::UnsafeOverwriteGuard {
target: __self_ptr,
value: ::pin_project::__private::ManuallyDrop::new(__replacement),
};
{
#(
let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(#pinned_fields);
)*
}
__result
}
}
fn make_unpin_impl(&self) -> TokenStream {
match self.unpin_impl {
UnpinImpl::Unsafe(span) => {
let mut proj_generics = self.proj.generics.clone();
let orig_ident = self.orig.ident;
let lifetime = &self.proj.lifetime;
proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span =>
::pin_project::__private::Wrapper<#lifetime, Self>: ::pin_project::UnsafeUnpin
});
let (impl_generics, _, where_clause) = proj_generics.split_for_impl();
let ty_generics = self.orig.generics.split_for_impl().1;
quote_spanned! { span =>
impl #impl_generics ::pin_project::__private::Unpin for #orig_ident #ty_generics
#where_clause
{
}
}
}
UnpinImpl::Negative(span) => {
let mut proj_generics = self.proj.generics.clone();
let orig_ident = self.orig.ident;
let lifetime = &self.proj.lifetime;
proj_generics.make_where_clause().predicates.push(parse_quote! {
::pin_project::__private::Wrapper<
#lifetime, ::pin_project::__private::PhantomPinned
>: ::pin_project::__private::Unpin
});
let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl();
let (impl_generics, ty_generics, orig_where_clause) =
self.orig.generics.split_for_impl();
let unsafety = <Token![unsafe]>::default();
quote_spanned! { span =>
impl #proj_impl_generics ::pin_project::__private::Unpin
for #orig_ident #ty_generics
#proj_where_clause
{
}
#unsafety impl #impl_generics ::pin_project::UnsafeUnpin
for #orig_ident #ty_generics
#orig_where_clause
{
}
}
}
UnpinImpl::Default => {
let mut full_where_clause = self.orig.generics.where_clause.clone().unwrap();
let fields = self.pinned_fields.iter().enumerate().map(|(i, ty)| {
let field_ident = format_ident!("__field{}", i);
quote!(#field_ident: #ty)
});
let lifetime_fields = self.orig.generics.lifetimes().enumerate().map(
|(i, LifetimeDef { lifetime, .. })| {
let field_ident = format_ident!("__lifetime{}", i);
quote!(#field_ident: &#lifetime ())
},
);
let orig_ident = self.orig.ident;
let struct_ident = format_ident!("__{}", orig_ident);
let vis = self.orig.vis;
let lifetime = &self.proj.lifetime;
let type_params = self.orig.generics.type_params().map(|t| &t.ident);
let proj_generics = &self.proj.generics;
let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl();
let (impl_generics, ty_generics, where_clause) =
self.orig.generics.split_for_impl();
full_where_clause.predicates.push(parse_quote! {
#struct_ident #proj_ty_generics: ::pin_project::__private::Unpin
});
quote! {
#vis struct #struct_ident #proj_generics #where_clause {
__pin_project_use_generics: ::pin_project::__private::AlwaysUnpin<
#lifetime, (#(::pin_project::__private::PhantomData<#type_params>),*)
>,
#(#fields,)*
#(#lifetime_fields,)*
}
impl #proj_impl_generics ::pin_project::__private::Unpin
for #orig_ident #ty_generics
#full_where_clause
{
}
unsafe impl #impl_generics ::pin_project::UnsafeUnpin
for #orig_ident #ty_generics
#where_clause
{
}
}
}
}
}
fn make_drop_impl(&self) -> TokenStream {
let ident = self.orig.ident;
let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl();
if let Some(span) = self.pinned_drop {
let unsafety = <Token![unsafe]>::default();
quote_spanned! { span =>
impl #impl_generics ::pin_project::__private::Drop for #ident #ty_generics
#where_clause
{
fn drop(&mut self) {
let pinned_self = #unsafety {
::pin_project::__private::Pin::new_unchecked(self)
};
#unsafety {
::pin_project::__private::PinnedDrop::drop(pinned_self);
}
}
}
}
} else {
let trait_ident = format_ident!("{}MustNotImplDrop", ident);
quote! {
trait #trait_ident {}
#[allow(clippy::drop_bounds, drop_bounds)]
impl<T: ::pin_project::__private::Drop> #trait_ident for T {}
impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {}
impl #impl_generics ::pin_project::__private::PinnedDrop for #ident #ty_generics
#where_clause
{
unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {}
}
}
}
}
fn make_proj_impl(
&self,
proj_body: &TokenStream,
proj_ref_body: &TokenStream,
proj_own_body: &TokenStream,
) -> TokenStream {
let vis = &self.proj.vis;
let lifetime = &self.proj.lifetime;
let orig_ident = self.orig.ident;
let proj_ident = &self.proj.mut_ident;
let proj_ref_ident = &self.proj.ref_ident;
let proj_own_ident = &self.proj.own_ident;
let orig_ty_generics = self.orig.generics.split_for_impl().1;
let proj_ty_generics = self.proj.generics.split_for_impl().1;
let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl();
let replace_impl = self.project_replace.span().map(|span| {
let unsafety = <Token![unsafe]>::default();
quote_spanned! { span =>
#vis fn project_replace(
self: ::pin_project::__private::Pin<&mut Self>,
__replacement: Self,
) -> #proj_own_ident #orig_ty_generics {
#unsafety {
#proj_own_body
}
}
}
});
quote! {
impl #impl_generics #orig_ident #ty_generics #where_clause {
#vis fn project<#lifetime>(
self: ::pin_project::__private::Pin<&#lifetime mut Self>,
) -> #proj_ident #proj_ty_generics {
unsafe {
#proj_body
}
}
#vis fn project_ref<#lifetime>(
self: ::pin_project::__private::Pin<&#lifetime Self>,
) -> #proj_ref_ident #proj_ty_generics {
unsafe {
#proj_ref_body
}
}
#replace_impl
}
}
}
fn ensure_not_packed(&self, fields: &Fields) -> Result<TokenStream> {
for meta in self.orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) {
if let Meta::List(list) = meta {
if list.path.is_ident("repr") {
for repr in list.nested.iter() {
match repr {
NestedMeta::Meta(Meta::Path(path))
| NestedMeta::Meta(Meta::List(MetaList { path, .. }))
if path.is_ident("packed") =>
{
return Err(error!(
repr,
"#[pin_project] attribute may not be used on #[repr(packed)] types"
));
}
_ => {}
}
}
}
}
}
let mut field_refs = vec![];
match fields {
Fields::Named(FieldsNamed { named, .. }) => {
for Field { ident, .. } in named {
field_refs.push(quote!(&val.#ident;));
}
}
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
for (index, _) in unnamed.iter().enumerate() {
let index = Index::from(index);
field_refs.push(quote!(&val.#index;));
}
}
Fields::Unit => {}
}
let (impl_generics, ty_generics, where_clause) = self.orig.generics.split_for_impl();
let ident = self.orig.ident;
Ok(quote! {
#[forbid(safe_packed_borrows)]
fn __assert_not_repr_packed #impl_generics (val: &#ident #ty_generics) #where_clause {
#(#field_refs)*
}
})
}
}