diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/pin-project-internal/src/pin_project | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/pin-project-internal/src/pin_project')
4 files changed, 1463 insertions, 0 deletions
diff --git a/third_party/rust/pin-project-internal/src/pin_project/args.rs b/third_party/rust/pin-project-internal/src/pin_project/args.rs new file mode 100644 index 0000000000..d0d4f362f5 --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/args.rs @@ -0,0 +1,254 @@ +use proc_macro2::{Span, TokenStream}; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + spanned::Spanned, + Attribute, Error, Ident, Result, Token, +}; + +use super::PIN; +use crate::utils::{ParseBufferExt, SliceExt}; + +pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { + // `(__private(<args>))` -> `<args>` + struct Input(Option<TokenStream>); + + impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result<Self> { + Ok(Self((|| { + let content = input.parenthesized().ok()?; + let private = content.parse::<Ident>().ok()?; + if private == "__private" { + content.parenthesized().ok()?.parse::<TokenStream>().ok() + } else { + None + } + })())) + } + } + + if let Some(attr) = attrs.find("pin_project") { + bail!(attr, "duplicate #[pin_project] attribute"); + } + + let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); + + let prev = if let Some(attr) = attrs.next() { + (attr, syn::parse2::<Input>(attr.tokens.clone()).unwrap().0) + } else { + // This only fails if another macro removes `#[pin]`. + bail!(TokenStream::new(), "#[pin_project] attribute has been removed"); + }; + + if let Some(attr) = attrs.next() { + let (prev_attr, prev_res) = &prev; + // As the `#[pin]` attribute generated by `#[pin_project]` + // has the same span as `#[pin_project]`, it is possible + // that a useless error message will be generated. + // So, use the span of `prev_attr` if it is not a valid attribute. + let res = syn::parse2::<Input>(attr.tokens.clone()).unwrap().0; + let span = match (prev_res, res) { + (Some(_), _) => attr, + (None, _) => prev_attr, + }; + bail!(span, "duplicate #[pin] attribute"); + } + // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`. + syn::parse2(prev.1.unwrap()) +} + +pub(super) struct Args { + /// `PinnedDrop` argument. + pub(super) pinned_drop: Option<Span>, + /// `UnsafeUnpin` or `!Unpin` argument. + pub(super) unpin_impl: UnpinImpl, + /// `project = <ident>` argument. + pub(super) project: Option<Ident>, + /// `project_ref = <ident>` argument. + pub(super) project_ref: Option<Ident>, + /// `project_replace [= <ident>]` argument. + pub(super) project_replace: ProjReplace, +} + +impl Parse for Args { + fn parse(input: ParseStream<'_>) -> Result<Self> { + mod kw { + syn::custom_keyword!(Unpin); + } + + /// Parses `= <value>` in `<name> = <value>` and returns value and span of name-value pair. + fn parse_value( + input: ParseStream<'_>, + name: &Ident, + has_prev: bool, + ) -> Result<(Ident, TokenStream)> { + if input.is_empty() { + bail!(name, "expected `{0} = <identifier>`, found `{0}`", name); + } + let eq_token: Token![=] = input.parse()?; + if input.is_empty() { + let span = quote!(#name #eq_token); + bail!(span, "expected `{0} = <identifier>`, found `{0} =`", name); + } + let value: Ident = input.parse()?; + let span = quote!(#name #value); + if has_prev { + bail!(span, "duplicate `{}` argument", name); + } + Ok((value, span)) + } + + let mut pinned_drop = None; + let mut unsafe_unpin = None; + let mut not_unpin = None; + let mut project = None; + let mut project_ref = None; + let mut project_replace_value = None; + let mut project_replace_span = None; + + while !input.is_empty() { + if input.peek(Token![!]) { + let bang: Token![!] = input.parse()?; + if input.is_empty() { + bail!(bang, "expected `!Unpin`, found `!`"); + } + let unpin: kw::Unpin = input.parse()?; + let span = quote!(#bang #unpin); + if not_unpin.replace(span.span()).is_some() { + bail!(span, "duplicate `!Unpin` argument"); + } + } else { + let token = input.parse::<Ident>()?; + match &*token.to_string() { + "PinnedDrop" => { + if pinned_drop.replace(token.span()).is_some() { + bail!(token, "duplicate `PinnedDrop` argument"); + } + } + "UnsafeUnpin" => { + if unsafe_unpin.replace(token.span()).is_some() { + bail!(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() { + bail!(token, "duplicate `project_replace` argument"); + } else { + project_replace_span = Some(token.span()); + } + } + "Replace" => { + bail!( + token, + "`Replace` argument was removed, use `project_replace` argument instead" + ); + } + _ => bail!(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 { + bail!( + 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 { + bail!(ident, "name `{}` is already specified by `project` argument", ident); + } else if project_ref == project_replace_value { + bail!(ident, "name `{}` is already specified by `project_ref` argument", ident); + } + } + } + + if let Some(span) = pinned_drop { + if project_replace_span.is_some() { + return Err(Error::new( + span, + "arguments `PinnedDrop` and `project_replace` are mutually exclusive", + )); + } + } + let project_replace = match (project_replace_span, project_replace_value) { + (None, _) => ProjReplace::None, + (Some(span), Some(ident)) => ProjReplace::Named { ident, span }, + (Some(span), None) => ProjReplace::Unnamed { span }, + }; + let unpin_impl = match (unsafe_unpin, not_unpin) { + (None, None) => UnpinImpl::Default, + (Some(span), None) => UnpinImpl::Unsafe(span), + (None, Some(span)) => UnpinImpl::Negative(span), + (Some(span), Some(_)) => { + return Err(Error::new( + span, + "arguments `UnsafeUnpin` and `!Unpin` are mutually exclusive", + )); + } + }; + + Ok(Self { pinned_drop, unpin_impl, project, project_ref, project_replace }) + } +} + +/// `UnsafeUnpin` or `!Unpin` argument. +#[derive(Clone, Copy)] +pub(super) enum UnpinImpl { + Default, + /// `UnsafeUnpin`. + Unsafe(Span), + /// `!Unpin`. + Negative(Span), +} + +/// `project_replace [= <ident>]` argument. +pub(super) enum ProjReplace { + None, + /// `project_replace`. + Unnamed { + span: Span, + }, + /// `project_replace = <ident>`. + #[allow(dead_code)] // false positive that fixed in Rust 1.38 + Named { + span: Span, + ident: Ident, + }, +} + +impl ProjReplace { + /// Return the span of this argument. + pub(super) fn span(&self) -> Option<Span> { + match self { + Self::None => None, + Self::Named { span, .. } | Self::Unnamed { span, .. } => Some(*span), + } + } + + pub(super) fn ident(&self) -> Option<&Ident> { + if let Self::Named { ident, .. } = self { + Some(ident) + } else { + None + } + } +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/attribute.rs b/third_party/rust/pin-project-internal/src/pin_project/attribute.rs new file mode 100644 index 0000000000..7adfc07d59 --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/attribute.rs @@ -0,0 +1,65 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::{ + parse::{Parse, ParseStream}, + Attribute, Result, Token, Visibility, +}; + +use super::PIN; +use crate::utils::SliceExt; + +// To generate the correct `Unpin` implementation and the projection methods, +// we need to collect the types of the pinned fields. +// However, since proc-macro-attribute is applied before `cfg` and `cfg_attr` +// on fields, we cannot be collecting field types properly at this timing. +// So instead of generating the `Unpin` implementation and the projection +// methods here, delegate their processing to proc-macro-derive. +// +// At this stage, only attributes are parsed and the following attributes are +// added to the attributes of the item. +// - `#[derive(InternalDerive)]` - An internal helper macro that does the above +// processing. +// - `#[pin(__private(#args))]` - Pass the argument of `#[pin_project]` to +// proc-macro-derive (`InternalDerive`). + +pub(super) fn parse_attribute(args: &TokenStream, input: TokenStream) -> Result<TokenStream> { + let Input { attrs, body } = syn::parse2(input)?; + + Ok(quote! { + #(#attrs)* + #[derive(::pin_project::__private::__PinProjectInternalDerive)] + // Use `__private` to prevent users from trying to control `InternalDerive` + // manually. `__private` does not guarantee compatibility between patch + // versions, so it should be sufficient for this purpose in most cases. + #[pin(__private(#args))] + #body + }) +} + +#[allow(dead_code)] // false positive that fixed in Rust 1.39 +struct Input { + attrs: Vec<Attribute>, + body: TokenStream, +} + +impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result<Self> { + let attrs = input.call(Attribute::parse_outer)?; + + let ahead = input.fork(); + let _vis: Visibility = ahead.parse()?; + if !ahead.peek(Token![struct]) && !ahead.peek(Token![enum]) { + // If we check this only on proc-macro-derive, it may generate unhelpful error + // messages. So it is preferable to be able to detect it here. + bail!( + input.parse::<TokenStream>()?, + "#[pin_project] attribute may only be used on structs or enums" + ); + } else if let Some(attr) = attrs.find(PIN) { + bail!(attr, "#[pin] attribute may only be used on fields of structs or variants"); + } else if let Some(attr) = attrs.find("pin_project") { + bail!(attr, "duplicate #[pin_project] attribute"); + } + Ok(Self { attrs, body: input.parse()? }) + } +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/derive.rs b/third_party/rust/pin-project-internal/src/pin_project/derive.rs new file mode 100644 index 0000000000..fd2375dc3b --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/derive.rs @@ -0,0 +1,1127 @@ +use proc_macro2::{Delimiter, Group, Span, TokenStream}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; +use syn::{ + parse_quote, token, visit_mut::VisitMut, Attribute, Data, DataEnum, DeriveInput, Error, Field, + Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, Lifetime, LifetimeDef, Meta, + MetaList, MetaNameValue, NestedMeta, Result, Token, Type, Variant, Visibility, WhereClause, +}; + +use super::{ + args::{parse_args, Args, ProjReplace, UnpinImpl}, + PIN, +}; +use crate::utils::{ + determine_lifetime_name, determine_visibility, insert_lifetime_and_bound, ReplaceReceiver, + SliceExt, Variants, +}; + +pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { + let mut input: DeriveInput = syn::parse2(input)?; + + let mut cx; + let mut generate = GenerateTokens::default(); + + let ident = &input.ident; + let ty_generics = input.generics.split_for_impl().1; + let self_ty = parse_quote!(#ident #ty_generics); + let mut visitor = ReplaceReceiver(&self_ty); + visitor.visit_generics_mut(&mut input.generics); + visitor.visit_data_mut(&mut input.data); + + match &input.data { + Data::Struct(data) => { + cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Struct)?; + parse_struct(&mut cx, &data.fields, &mut generate)?; + } + Data::Enum(data) => { + cx = Context::new(&input.attrs, &input.vis, ident, &mut input.generics, Enum)?; + parse_enum(&mut cx, data, &mut generate)?; + } + Data::Union(_) => { + bail!(input, "#[pin_project] attribute may only be used on structs or enums"); + } + } + + Ok(generate.into_tokens(&cx)) +} + +#[derive(Default)] +struct GenerateTokens { + exposed: TokenStream, + scoped: TokenStream, +} + +impl GenerateTokens { + fn extend(&mut self, expose: bool, tokens: TokenStream) { + if expose { + self.exposed.extend(tokens); + } else { + self.scoped.extend(tokens); + } + } + + fn into_tokens(self, cx: &Context<'_>) -> TokenStream { + let mut tokens = self.exposed; + let scoped = self.scoped; + + let unpin_impl = make_unpin_impl(cx); + let drop_impl = make_drop_impl(cx); + let allowed_lints = global_allowed_lints(); + + tokens.extend(quote! { + // All items except projected types are generated inside a `const` scope. + // This makes it impossible for user code to refer to these types. + // However, this prevents Rustdoc from displaying docs for any + // of our types. In particular, users cannot see the + // automatically generated `Unpin` impl for the '__UnpinStruct' types + // + // Previously, we provided a flag to correctly document the + // automatically generated `Unpin` impl by using def-site hygiene, + // but it is now removed. + // + // Refs: + // - https://github.com/rust-lang/rust/issues/63281 + // - https://github.com/taiki-e/pin-project/pull/53#issuecomment-525906867 + // - https://github.com/taiki-e/pin-project/pull/70 + #allowed_lints + #[allow(unused_qualifications)] + #[allow(clippy::semicolon_if_nothing_returned)] + #[allow(clippy::use_self)] + #[allow(clippy::used_underscore_binding)] + const _: () = { + #[allow(unused_extern_crates)] + extern crate pin_project as _pin_project; + #scoped + #unpin_impl + #drop_impl + }; + }); + tokens + } +} + +/// Returns attributes that should be applied to all generated code. +fn global_allowed_lints() -> TokenStream { + quote! { + #[allow(box_pointers)] // This lint warns use of the `Box` type. + #[allow(deprecated)] + #[allow(explicit_outlives_requirements)] // https://github.com/rust-lang/rust/issues/60993 + #[allow(single_use_lifetimes)] // https://github.com/rust-lang/rust/issues/55058 + #[allow(unreachable_pub)] // This lint warns `pub` field in private struct. + #[allow(unused_tuple_struct_fields)] + // This lint warns of `clippy::*` generated by external macros. + // We allow this lint for compatibility with older compilers. + #[allow(clippy::unknown_clippy_lints)] + #[allow(clippy::pattern_type_mismatch)] + #[allow(clippy::redundant_pub_crate)] // This lint warns `pub(crate)` field in private struct. + #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 + } +} + +/// Returns attributes used on projected types. +fn proj_allowed_lints(cx: &Context<'_>) -> (TokenStream, TokenStream, TokenStream) { + let large_enum_variant = if cx.kind == Enum { + Some(quote! { + #[allow(variant_size_differences)] + #[allow(clippy::large_enum_variant)] + }) + } else { + None + }; + let global_allowed_lints = global_allowed_lints(); + let proj_mut_allowed_lints = if cx.project { Some(&global_allowed_lints) } else { None }; + let proj_mut = quote! { + #proj_mut_allowed_lints + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. + }; + let proj_ref_allowed_lints = if cx.project_ref { Some(&global_allowed_lints) } else { None }; + let proj_ref = quote! { + #proj_ref_allowed_lints + #[allow(dead_code)] // This lint warns unused fields/variants. + #[allow(clippy::ref_option_ref)] // This lint warns `&Option<&<ty>>`. + }; + let proj_own_allowed_lints = + if cx.project_replace.ident().is_some() { Some(&global_allowed_lints) } else { None }; + let proj_own = quote! { + #proj_own_allowed_lints + #[allow(dead_code)] // This lint warns unused fields/variants. + #large_enum_variant + }; + (proj_mut, proj_ref, proj_own) +} + +struct Context<'a> { + /// The original type. + orig: OriginalType<'a>, + /// The projected types. + proj: ProjectedType, + /// Types of the pinned fields. + pinned_fields: Vec<&'a Type>, + /// Kind of the original type: struct or enum + kind: TypeKind, + + /// `PinnedDrop` argument. + pinned_drop: Option<Span>, + /// `UnsafeUnpin` or `!Unpin` argument. + unpin_impl: UnpinImpl, + /// `project` argument. + project: bool, + /// `project_ref` argument. + project_ref: bool, + /// `project_replace [= <ident>]` argument. + project_replace: ProjReplace, +} + +impl<'a> Context<'a> { + fn new( + attrs: &'a [Attribute], + vis: &'a Visibility, + ident: &'a Ident, + generics: &'a mut Generics, + kind: TypeKind, + ) -> Result<Self> { + let Args { pinned_drop, unpin_impl, project, project_ref, project_replace } = + parse_args(attrs)?; + + if let Some(name) = [project.as_ref(), project_ref.as_ref(), project_replace.ident()] + .iter() + .filter_map(Option::as_ref) + .find(|name| **name == ident) + { + bail!(name, "name `{}` is the same as the original type name", name); + } + + let mut lifetime_name = String::from("'pin"); + determine_lifetime_name(&mut lifetime_name, generics); + 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(|| format_ident!("__{}ProjectionOwned", ident)); + + Ok(Self { + kind, + 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(|| format_ident!("__{}Projection", ident)), + ref_ident: project_ref.unwrap_or_else(|| format_ident!("__{}ProjectionRef", ident)), + own_ident, + lifetime, + generics: proj_generics, + where_clause, + }, + orig: OriginalType { attrs, vis, ident, generics }, + pinned_fields: Vec::new(), + }) + } +} + +#[derive(Copy, Clone, Eq, PartialEq)] +enum TypeKind { + Enum, + Struct, +} + +use TypeKind::{Enum, Struct}; + +struct OriginalType<'a> { + /// Attributes of the original type. + attrs: &'a [Attribute], + /// Visibility of the original type. + vis: &'a Visibility, + /// Name of the original type. + ident: &'a Ident, + /// Generics of the original type. + generics: &'a Generics, +} + +struct ProjectedType { + /// Visibility of the projected types. + vis: Visibility, + /// Name of the projected type returned by `project` method. + mut_ident: Ident, + /// Name of the projected type returned by `project_ref` method. + ref_ident: Ident, + /// Name of the projected type returned by `project_replace` method. + own_ident: Ident, + /// Lifetime on the generated projected types. + lifetime: Lifetime, + /// Generics of the projected types. + generics: Generics, + /// `where` clause of the projected types. This has an additional + /// bound generated by `insert_lifetime_and_bound` + where_clause: WhereClause, +} + +struct ProjectedVariants { + proj_variants: TokenStream, + proj_ref_variants: TokenStream, + proj_own_variants: TokenStream, + proj_arms: TokenStream, + proj_ref_arms: TokenStream, + proj_own_arms: TokenStream, +} + +#[derive(Default)] +struct ProjectedFields { + proj_pat: TokenStream, + proj_body: TokenStream, + proj_own_body: TokenStream, + proj_fields: TokenStream, + proj_ref_fields: TokenStream, + proj_own_fields: TokenStream, +} + +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 { + bail!(ident, msg) + } + bail!(fields, msg) + } + 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 { + bail!(e, "#[pin_project] attribute may not be used on enums with discriminants"); + } else if let Some(attr) = v.attrs.find(PIN) { + bail!(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 { + bail!(variants, "#[pin_project] attribute may not be used on enums with zero fields"); + } +} + +fn parse_struct<'a>( + cx: &mut Context<'a>, + fields: &'a Fields, + generate: &mut GenerateTokens, +) -> Result<()> { + // Do this first for a better error message. + let packed_check = ensure_not_packed(&cx.orig, Some(fields))?; + + validate_struct(cx.orig.ident, fields)?; + + let ProjectedFields { + proj_pat, + proj_body, + proj_fields, + proj_ref_fields, + proj_own_fields, + proj_own_body, + } = match fields { + Fields::Named(_) => visit_fields(cx, None, fields, Delimiter::Brace)?, + Fields::Unnamed(_) => visit_fields(cx, None, fields, Delimiter::Parenthesis)?, + Fields::Unit => unreachable!(), + }; + + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + let proj_own_ident = &cx.proj.own_ident; + let vis = &cx.proj.vis; + let mut orig_generics = cx.orig.generics.clone(); + let orig_where_clause = orig_generics.where_clause.take(); + let proj_generics = &cx.proj.generics; + let proj_where_clause = &cx.proj.where_clause; + + // For tuple structs, we need to generate `(T1, T2) where Foo: Bar` + // For non-tuple structs, we need to generate `where Foo: Bar { field1: T }` + let (where_clause_fields, where_clause_ref_fields, where_clause_own_fields) = match fields { + Fields::Named(_) => ( + quote!(#proj_where_clause #proj_fields), + quote!(#proj_where_clause #proj_ref_fields), + quote!(#orig_where_clause #proj_own_fields), + ), + Fields::Unnamed(_) => ( + quote!(#proj_fields #proj_where_clause;), + quote!(#proj_ref_fields #proj_where_clause;), + quote!(#proj_own_fields #orig_where_clause;), + ), + Fields::Unit => unreachable!(), + }; + + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); + generate.extend(cx.project, quote! { + #proj_attrs + #vis struct #proj_ident #proj_generics #where_clause_fields + }); + generate.extend(cx.project_ref, quote! { + #proj_ref_attrs + #vis struct #proj_ref_ident #proj_generics #where_clause_ref_fields + }); + if cx.project_replace.span().is_some() { + generate.extend(cx.project_replace.ident().is_some(), quote! { + #proj_own_attrs + #vis struct #proj_own_ident #orig_generics #where_clause_own_fields + }); + } + + let proj_mut_body = quote! { + let Self #proj_pat = self.get_unchecked_mut(); + #proj_ident #proj_body + }; + let proj_ref_body = quote! { + let Self #proj_pat = self.get_ref(); + #proj_ref_ident #proj_body + }; + let proj_own_body = quote! { + let Self #proj_pat = &mut *__self_ptr; + #proj_own_body + }; + generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); + + generate.extend(false, packed_check); + Ok(()) +} + +fn parse_enum<'a>( + cx: &mut Context<'a>, + DataEnum { brace_token, variants, .. }: &'a DataEnum, + generate: &mut GenerateTokens, +) -> Result<()> { + if let ProjReplace::Unnamed { span } = &cx.project_replace { + return Err(Error::new( + *span, + "`project_replace` argument requires a value when used on enums", + )); + } + + // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. + // However, we should not rely on the behavior of rustc that rejects this. + // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 + // + // Do this first for a better error message. + ensure_not_packed(&cx.orig, None)?; + + validate_enum(*brace_token, variants)?; + + let ProjectedVariants { + proj_variants, + proj_ref_variants, + proj_own_variants, + proj_arms, + proj_ref_arms, + proj_own_arms, + } = visit_variants(cx, variants)?; + + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + let proj_own_ident = &cx.proj.own_ident; + let vis = &cx.proj.vis; + let mut orig_generics = cx.orig.generics.clone(); + let orig_where_clause = orig_generics.where_clause.take(); + let proj_generics = &cx.proj.generics; + let proj_where_clause = &cx.proj.where_clause; + + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); + if cx.project { + generate.extend(true, quote! { + #proj_attrs + #vis enum #proj_ident #proj_generics #proj_where_clause { + #proj_variants + } + }); + } + if cx.project_ref { + generate.extend(true, quote! { + #proj_ref_attrs + #vis enum #proj_ref_ident #proj_generics #proj_where_clause { + #proj_ref_variants + } + }); + } + if cx.project_replace.ident().is_some() { + generate.extend(true, quote! { + #proj_own_attrs + #vis enum #proj_own_ident #orig_generics #orig_where_clause { + #proj_own_variants + } + }); + } + + let proj_mut_body = quote! { + match self.get_unchecked_mut() { + #proj_arms + } + }; + let proj_ref_body = quote! { + match self.get_ref() { + #proj_ref_arms + } + }; + let proj_own_body = quote! { + match &mut *__self_ptr { + #proj_own_arms + } + }; + generate.extend(false, make_proj_impl(cx, &proj_mut_body, &proj_ref_body, &proj_own_body)); + + Ok(()) +} + +fn visit_variants<'a>(cx: &mut Context<'a>, variants: &'a Variants) -> Result<ProjectedVariants> { + let mut proj_variants = TokenStream::new(); + let mut proj_ref_variants = TokenStream::new(); + let mut proj_own_variants = TokenStream::new(); + let mut proj_arms = TokenStream::new(); + let mut proj_ref_arms = TokenStream::new(); + let mut proj_own_arms = TokenStream::new(); + + for Variant { ident, fields, .. } in variants { + let ProjectedFields { + proj_pat, + proj_body, + proj_fields, + proj_ref_fields, + proj_own_fields, + proj_own_body, + } = match fields { + Fields::Named(_) => visit_fields(cx, Some(ident), fields, Delimiter::Brace)?, + Fields::Unnamed(_) => visit_fields(cx, Some(ident), fields, Delimiter::Parenthesis)?, + Fields::Unit => ProjectedFields { + proj_own_body: proj_own_body(cx, Some(ident), None, &[]), + ..Default::default() + }, + }; + + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + proj_variants.extend(quote! { + #ident #proj_fields, + }); + proj_ref_variants.extend(quote! { + #ident #proj_ref_fields, + }); + proj_own_variants.extend(quote! { + #ident #proj_own_fields, + }); + proj_arms.extend(quote! { + Self::#ident #proj_pat => #proj_ident::#ident #proj_body, + }); + proj_ref_arms.extend(quote! { + Self::#ident #proj_pat => #proj_ref_ident::#ident #proj_body, + }); + proj_own_arms.extend(quote! { + Self::#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<'a>( + cx: &mut Context<'a>, + variant_ident: Option<&Ident>, + fields: &'a Fields, + delim: Delimiter, +) -> Result<ProjectedFields> { + fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { + Group::new(delim, tokens).into_token_stream() + } + + 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,)); + let lifetime = &cx.proj.lifetime; + if attrs.position_exact(PIN)?.is_some() { + 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, + }); + + cx.pinned_fields.push(ty); + pinned_bindings.push(binding); + } else { + 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), + }); + } + } + + let proj_pat = surround(delim, proj_pat); + let proj_body = surround(delim, proj_body); + let proj_fields = surround(delim, proj_fields); + let proj_ref_fields = surround(delim, proj_ref_fields); + let proj_own_fields = surround(delim, proj_own_fields); + + let proj_move = Group::new(delim, proj_move); + let proj_own_body = proj_own_body(cx, variant_ident, Some(&proj_move), &pinned_bindings); + + Ok(ProjectedFields { + proj_pat, + proj_body, + proj_own_body, + proj_fields, + proj_ref_fields, + proj_own_fields, + }) +} + +/// Generates the processing that `project_replace` does for the struct or each variant. +/// +/// Note: `pinned_fields` must be in declaration order. +fn proj_own_body( + cx: &Context<'_>, + variant_ident: Option<&Ident>, + proj_move: Option<&Group>, + pinned_fields: &[Ident], +) -> TokenStream { + let ident = &cx.proj.own_ident; + let proj_own = match variant_ident { + Some(variant_ident) => quote!(#ident::#variant_ident), + None => quote!(#ident), + }; + + // The fields of the struct and the active enum variant are dropped + // in declaration order. + // Refs: https://doc.rust-lang.org/reference/destructors.html + let pinned_fields = pinned_fields.iter().rev(); + + quote! { + // First, extract all the unpinned fields. + let __result = #proj_own #proj_move; + + // Now create guards to drop all the pinned fields. + // + // Due to a compiler bug (https://github.com/rust-lang/rust/issues/47949) + // this must be in its own scope, or else `__result` will not be dropped + // if any of the destructors panic. + { + #( + let __guard = _pin_project::__private::UnsafeDropInPlaceGuard::new(#pinned_fields); + )* + } + + // Finally, return the result. + __result + } +} + +/// Creates `Unpin` implementation for the original type. +/// +/// The kind of `Unpin` impl generated depends on `unpin_impl` field: +/// - `UnpinImpl::Unsafe` - Implements `Unpin` via `UnsafeUnpin` impl. +/// - `UnpinImpl::Negative` - Generates `Unpin` impl with bounds that will never be true. +/// - `UnpinImpl::Default` - Generates `Unpin` impl that requires `Unpin` for all pinned fields. +fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { + match cx.unpin_impl { + UnpinImpl::Unsafe(span) => { + let mut proj_generics = cx.proj.generics.clone(); + let orig_ident = cx.orig.ident; + let lifetime = &cx.proj.lifetime; + + // Make the error message highlight `UnsafeUnpin` argument. + proj_generics.make_where_clause().predicates.push(parse_quote_spanned! { span => + _pin_project::__private::Wrapper<#lifetime, Self>: _pin_project::UnsafeUnpin + }); + + let (impl_generics, _, where_clause) = proj_generics.split_for_impl(); + let ty_generics = cx.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 = cx.proj.generics.clone(); + let orig_ident = cx.orig.ident; + let lifetime = &cx.proj.lifetime; + + proj_generics.make_where_clause().predicates.push(parse_quote! { + _pin_project::__private::Wrapper< + #lifetime, _pin_project::__private::PhantomPinned + >: _pin_project::__private::Unpin + }); + + let (proj_impl_generics, _, proj_where_clause) = proj_generics.split_for_impl(); + let ty_generics = cx.orig.generics.split_for_impl().1; + + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); + quote_spanned! { span => + impl #proj_impl_generics _pin_project::__private::Unpin + for #orig_ident #ty_generics + #proj_where_clause + { + } + + // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` + // impl, they'll get a "conflicting implementations of trait" error when + // coherence checks are run. + #[doc(hidden)] + #unsafety impl #proj_impl_generics _pin_project::UnsafeUnpin + for #orig_ident #ty_generics + #proj_where_clause + { + } + } + } + UnpinImpl::Default => { + let mut full_where_clause = cx.orig.generics.where_clause.clone().unwrap(); + + // Generate a field in our new struct for every + // pinned field in the original type. + let fields = cx.pinned_fields.iter().enumerate().map(|(i, ty)| { + let field_ident = format_ident!("__field{}", i); + quote!(#field_ident: #ty) + }); + + // We could try to determine the subset of type parameters + // and lifetimes that are actually used by the pinned fields + // (as opposed to those only used by unpinned fields). + // However, this would be tricky and error-prone, since + // it's possible for users to create types that would alias + // with generic parameters (e.g. 'struct T'). + // + // Instead, we generate a use of every single type parameter + // and lifetime used in the original struct. For type parameters, + // we generate code like this: + // + // ```rust + // struct AlwaysUnpin<T: ?Sized>(PhantomData<T>) {} + // impl<T: ?Sized> Unpin for AlwaysUnpin<T> {} + // + // ... + // _field: AlwaysUnpin<(A, B, C)> + // ``` + // + // This ensures that any unused type parameters + // don't end up with `Unpin` bounds. + let lifetime_fields = cx.orig.generics.lifetimes().enumerate().map( + |(i, LifetimeDef { lifetime, .. })| { + let field_ident = format_ident!("__lifetime{}", i); + quote!(#field_ident: &#lifetime ()) + }, + ); + + let orig_ident = cx.orig.ident; + let struct_ident = format_ident!("__{}", orig_ident); + let vis = cx.orig.vis; + let lifetime = &cx.proj.lifetime; + let type_params = cx.orig.generics.type_params().map(|t| &t.ident); + let proj_generics = &cx.proj.generics; + let (proj_impl_generics, proj_ty_generics, _) = proj_generics.split_for_impl(); + let (_, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); + + full_where_clause.predicates.push(parse_quote! { + #struct_ident #proj_ty_generics: _pin_project::__private::Unpin + }); + + quote! { + // This needs to have the same visibility as the original type, + // due to the limitations of the 'public in private' error. + // + // Our goal is to implement the public trait `Unpin` for + // a potentially public user type. Because of this, rust + // requires that any types mentioned in the where clause of + // our `Unpin` impl also be public. This means that our generated + // `__UnpinStruct` type must also be public. + // However, we ensure that the user can never actually reference + // this 'public' type by creating this type in the inside of `const`. + #[allow(missing_debug_implementations)] + #vis struct #struct_ident #proj_generics #where_clause { + __pin_project_use_generics: _pin_project::__private::AlwaysUnpin< + #lifetime, (#(_pin_project::__private::PhantomData<#type_params>),*) + >, + + #(#fields,)* + #(#lifetime_fields,)* + } + + impl #proj_impl_generics _pin_project::__private::Unpin + for #orig_ident #ty_generics + #full_where_clause + { + } + + // Generate a dummy impl of `UnsafeUnpin`, to ensure that the user cannot implement it. + // + // To ensure that users don't accidentally write a non-functional `UnsafeUnpin` + // impls, we emit one ourselves. If the user ends up writing an `UnsafeUnpin` + // impl, they'll get a "conflicting implementations of trait" error when + // coherence checks are run. + #[doc(hidden)] + unsafe impl #proj_impl_generics _pin_project::UnsafeUnpin + for #orig_ident #ty_generics + #full_where_clause + { + } + } + } + } +} + +/// Creates `Drop` implementation for the original type. +/// +/// The kind of `Drop` impl generated depends on `pinned_drop` field: +/// - `Some` - implements `Drop` via `PinnedDrop` impl. +/// - `None` - generates code that ensures that `Drop` trait is not implemented, +/// instead of generating `Drop` impl. +fn make_drop_impl(cx: &Context<'_>) -> TokenStream { + let ident = cx.orig.ident; + let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); + + if let Some(span) = cx.pinned_drop { + // For interoperability with `forbid(unsafe_code)`, `unsafe` token should be + // call-site span. + let unsafety = <Token![unsafe]>::default(); + quote_spanned! { span => + impl #impl_generics _pin_project::__private::Drop for #ident #ty_generics + #where_clause + { + fn drop(&mut self) { + #unsafety { + // Safety - we're in 'drop', so we know that 'self' will + // never move again. + let __pinned_self = _pin_project::__private::Pin::new_unchecked(self); + // We call `pinned_drop` only once. Since `PinnedDrop::drop` + // is an unsafe method and a private API, it is never called again in safe + // code *unless the user uses a maliciously crafted macro*. + _pin_project::__private::PinnedDrop::drop(__pinned_self); + } + } + } + } + } else { + // If the user does not provide a `PinnedDrop` impl, + // we need to ensure that they don't provide a `Drop` impl of their + // own. + // Based on https://github.com/upsuper/assert-impl/blob/f503255b292ab0ba8d085b657f4065403cfa46eb/src/lib.rs#L80-L87 + // + // We create a new identifier for each struct, so that the traits + // for different types do not conflict with each other. + // + // Another approach would be to provide an empty Drop impl, + // which would conflict with a user-provided Drop impl. + // However, this would trigger the compiler's special handling + // of Drop types (e.g. fields cannot be moved out of a Drop type). + // This approach prevents the creation of needless Drop impls, + // giving users more flexibility. + let trait_ident = format_ident!("{}MustNotImplDrop", ident); + + quote! { + // There are two possible cases: + // 1. The user type does not implement Drop. In this case, + // the first blanked impl will not apply to it. This code + // will compile, as there is only one impl of MustNotImplDrop for the user type + // 2. The user type does impl Drop. This will make the blanket impl applicable, + // which will then conflict with the explicit MustNotImplDrop impl below. + // This will result in a compilation error, which is exactly what we want. + trait #trait_ident {} + #[allow(clippy::drop_bounds, drop_bounds)] + impl<T: _pin_project::__private::Drop> #trait_ident for T {} + impl #impl_generics #trait_ident for #ident #ty_generics #where_clause {} + + // Generate a dummy impl of `PinnedDrop`, to ensure that the user cannot implement it. + // Since the user did not pass `PinnedDrop` to `#[pin_project]`, any `PinnedDrop` + // impl will not actually be called. Unfortunately, we can't detect this situation + // directly from either the `#[pin_project]` or `#[pinned_drop]` attributes, since + // we don't know what other attributes/impl may exist. + // + // To ensure that users don't accidentally write a non-functional `PinnedDrop` + // impls, we emit one ourselves. If the user ends up writing a `PinnedDrop` impl, + // they'll get a "conflicting implementations of trait" error when coherence + // checks are run. + #[doc(hidden)] + impl #impl_generics _pin_project::__private::PinnedDrop for #ident #ty_generics + #where_clause + { + unsafe fn drop(self: _pin_project::__private::Pin<&mut Self>) {} + } + } + } +} + +/// Creates an implementation of the projection methods. +/// +/// On structs, both the `project` and `project_ref` methods are always generated, +/// and the `project_replace` method is only generated if `ProjReplace::span` is `Some`. +/// +/// On enums, only methods that the returned projected type is named will be generated. +fn make_proj_impl( + cx: &Context<'_>, + proj_body: &TokenStream, + proj_ref_body: &TokenStream, + proj_own_body: &TokenStream, +) -> TokenStream { + let vis = &cx.proj.vis; + let lifetime = &cx.proj.lifetime; + let orig_ident = cx.orig.ident; + let proj_ident = &cx.proj.mut_ident; + let proj_ref_ident = &cx.proj.ref_ident; + let proj_own_ident = &cx.proj.own_ident; + + let orig_ty_generics = cx.orig.generics.split_for_impl().1; + let proj_ty_generics = cx.proj.generics.split_for_impl().1; + let (impl_generics, ty_generics, where_clause) = cx.orig.generics.split_for_impl(); + // TODO: For enums and project_replace, dead_code warnings should not be + // allowed because methods are not generated unless explicitly specified. + // However, there is currently no good way to allow warnings for generated + // code, so we allow warnings for all methods for now. + let allow_dead_code = quote! { #[allow(dead_code)] }; + + let mut project = Some(quote! { + #allow_dead_code + #vis fn project<#lifetime>( + self: _pin_project::__private::Pin<&#lifetime mut Self>, + ) -> #proj_ident #proj_ty_generics { + unsafe { + #proj_body + } + } + }); + let mut project_ref = Some(quote! { + #allow_dead_code + #[allow(clippy::missing_const_for_fn)] + #vis fn project_ref<#lifetime>( + self: _pin_project::__private::Pin<&#lifetime Self>, + ) -> #proj_ref_ident #proj_ty_generics { + unsafe { + #proj_ref_body + } + } + }); + let mut project_replace = cx.project_replace.span().map(|span| { + // It is enough to only set the span of the signature. + let sig = quote_spanned! { span => + #allow_dead_code + #vis fn project_replace( + self: _pin_project::__private::Pin<&mut Self>, + __replacement: Self, + ) -> #proj_own_ident #orig_ty_generics + }; + quote! { + #sig { + unsafe { + let __self_ptr: *mut Self = self.get_unchecked_mut(); + + // Destructors will run in reverse order, so next create a guard to overwrite + // `self` with the replacement value without calling destructors. + let __guard = _pin_project::__private::UnsafeOverwriteGuard::new( + __self_ptr, + __replacement, + ); + + #proj_own_body + } + } + } + }); + + if cx.kind == Enum { + if !cx.project { + project = None; + } + if !cx.project_ref { + project_ref = None; + } + if cx.project_replace.ident().is_none() { + project_replace = None; + } + } + + quote! { + impl #impl_generics #orig_ident #ty_generics #where_clause { + #project + #project_ref + #project_replace + } + } +} + +/// Checks that the `[repr(packed)]` attribute is not included. +/// +/// This currently does two checks: +/// - Checks the attributes of structs to ensure there is no `[repr(packed)]`. +/// - Generates a function that borrows fields without an unsafe block and +/// forbidding `unaligned_references` lint. +fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result<TokenStream> { + for meta in orig.attrs.iter().filter_map(|attr| attr.parse_meta().ok()) { + if let Meta::List(list) = meta { + if list.path.is_ident("repr") { + for repr in list.nested.iter() { + match repr { + NestedMeta::Meta(Meta::Path(path)) + | NestedMeta::Meta(Meta::List(MetaList { path, .. })) + | NestedMeta::Meta(Meta::NameValue(MetaNameValue { path, .. })) => { + if path.is_ident("packed") { + let msg = if fields.is_none() { + // #[repr(packed)] cannot be apply on enums and will be rejected by rustc. + // However, we should not rely on the behavior of rustc that rejects this. + // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 + "#[repr(packed)] attribute should be applied to a struct or union" + } else if let NestedMeta::Meta(Meta::NameValue(..)) = repr { + // #[repr(packed = "")] is not valid format of #[repr(packed)] and will be + // rejected by rustc. + // However, we should not rely on the behavior of rustc that rejects this. + // https://github.com/taiki-e/pin-project/pull/324#discussion_r612388001 + "#[repr(packed)] attribute should not be name-value pair" + } else { + "#[pin_project] attribute may not be used on #[repr(packed)] types" + }; + bail!(repr, msg); + } + } + NestedMeta::Lit(..) => {} + } + } + } + } + } + + let fields = match fields { + Some(fields) => fields, + None => return Ok(TokenStream::new()), + }; + + // Workaround for https://github.com/taiki-e/pin-project/issues/32 + // Through the tricky use of proc macros, it's possible to bypass + // the above check for the `repr` attribute. + // To ensure that it's impossible to use pin projections on a `#[repr(packed)]` + // struct, we generate code like this: + // + // ```rust + // #[forbid(unaligned_references)] + // fn assert_not_repr_packed(val: &MyStruct) { + // let _field1 = &val.field1; + // let _field2 = &val.field2; + // ... + // let _fieldn = &val.fieldn; + // } + // ``` + // + // Taking a reference to a packed field is UB, and applying + // `#[forbid(unaligned_references)]` makes sure that doing this is a hard error. + // + // If the struct ends up having `#[repr(packed)]` applied somehow, + // this will generate an (unfriendly) error message. Under all reasonable + // circumstances, we'll detect the `#[repr(packed)]` attribute, and generate + // a much nicer error above. + // + // There is one exception: If the type of a struct field has an alignment of 1 + // (e.g. u8), it is always safe to take a reference to it, even if the struct + // is `#[repr(packed)]`. If the struct is composed entirely of types of + // alignment 1, our generated method will not trigger an error if the + // struct is `#[repr(packed)]`. + // + // Fortunately, this should have no observable consequence - `#[repr(packed)]` + // is essentially a no-op on such a type. Nevertheless, we include a test + // to ensure that the compiler doesn't ever try to copy the fields on + // such a struct when trying to drop it - which is reason we prevent + // `#[repr(packed)]` in the first place. + // + // See also https://github.com/taiki-e/pin-project/pull/34. + // + // Note: + // - Lint-based tricks aren't perfect, but they're much better than nothing: + // https://github.com/taiki-e/pin-project-lite/issues/26 + // + // - Enable both unaligned_references and safe_packed_borrows lints + // because unaligned_references lint does not exist in older compilers: + // https://github.com/taiki-e/pin-project-lite/pull/55 + // https://github.com/rust-lang/rust/pull/82525 + let mut field_refs = vec![]; + match fields { + Fields::Named(FieldsNamed { named, .. }) => { + for Field { ident, .. } in named { + field_refs.push(quote!(&this.#ident)); + } + } + Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { + for (index, _) in unnamed.iter().enumerate() { + let index = Index::from(index); + field_refs.push(quote!(&this.#index)); + } + } + Fields::Unit => {} + } + + let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl(); + let ident = orig.ident; + Ok(quote! { + #[forbid(unaligned_references, safe_packed_borrows)] + fn __assert_not_repr_packed #impl_generics (this: &#ident #ty_generics) #where_clause { + #(let _ = #field_refs;)* + } + }) +} diff --git a/third_party/rust/pin-project-internal/src/pin_project/mod.rs b/third_party/rust/pin-project-internal/src/pin_project/mod.rs new file mode 100644 index 0000000000..2dce78f37f --- /dev/null +++ b/third_party/rust/pin-project-internal/src/pin_project/mod.rs @@ -0,0 +1,17 @@ +mod args; +mod attribute; +mod derive; + +use proc_macro2::TokenStream; +use syn::Error; + +/// The annotation for pinned type. +const PIN: &str = "pin"; + +pub(crate) fn attribute(args: &TokenStream, input: TokenStream) -> TokenStream { + attribute::parse_attribute(args, input).unwrap_or_else(Error::into_compile_error) +} + +pub(crate) fn derive(input: TokenStream) -> TokenStream { + derive::parse_derive(input).unwrap_or_else(Error::into_compile_error) +} |