diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-05-26 23:54:06 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-05-26 23:54:06 +0000 |
commit | fc5427bddf3519c15510fbf933063cf3a035da16 (patch) | |
tree | 9cbb0fb46dab16628c26e4667f91ac6db791b4dd | |
parent | 8cded5437e359c67fe7523fb3fd22ed3c256036d (diff) | |
parent | 2f79acbc52e50e5b980a835c6f6b807cf11449ab (diff) | |
download | pin-project-internal-fc5427bddf3519c15510fbf933063cf3a035da16.tar.gz |
Snap for 10214594 from 2f79acbc52e50e5b980a835c6f6b807cf11449ab to sdk-release
Change-Id: I646c2af8d80af123645cca7094658ea197680cdf
-rw-r--r-- | patches/syn-2.diff | 378 | ||||
-rw-r--r-- | src/pin_project/args.rs | 11 | ||||
-rw-r--r-- | src/pin_project/derive.rs | 67 | ||||
-rw-r--r-- | src/pinned_drop.rs | 59 | ||||
-rw-r--r-- | src/utils.rs | 33 |
5 files changed, 455 insertions, 93 deletions
diff --git a/patches/syn-2.diff b/patches/syn-2.diff new file mode 100644 index 0000000..143c988 --- /dev/null +++ b/patches/syn-2.diff @@ -0,0 +1,378 @@ +diff --git a/src/pin_project/args.rs b/src/pin_project/args.rs +index d0d4f36..d242ae7 100644 +--- a/src/pin_project/args.rs ++++ b/src/pin_project/args.rs +@@ -16,10 +16,9 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { + impl Parse for Input { + fn parse(input: ParseStream<'_>) -> Result<Self> { + Ok(Self((|| { +- let content = input.parenthesized().ok()?; +- let private = content.parse::<Ident>().ok()?; ++ let private = input.parse::<Ident>().ok()?; + if private == "__private" { +- content.parenthesized().ok()?.parse::<TokenStream>().ok() ++ input.parenthesized().ok()?.parse::<TokenStream>().ok() + } else { + None + } +@@ -31,10 +30,10 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { + bail!(attr, "duplicate #[pin_project] attribute"); + } + +- let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); ++ 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) ++ (attr, syn::parse2::<Input>(attr.meta.require_list()?.tokens.clone())?.0) + } else { + // This only fails if another macro removes `#[pin]`. + bail!(TokenStream::new(), "#[pin_project] attribute has been removed"); +@@ -46,7 +45,7 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { + // 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 res = syn::parse2::<Input>(attr.meta.require_list()?.tokens.clone())?.0; + let span = match (prev_res, res) { + (Some(_), _) => attr, + (None, _) => prev_attr, +diff --git a/src/pin_project/derive.rs b/src/pin_project/derive.rs +index fd2375d..d2b2b5e 100644 +--- a/src/pin_project/derive.rs ++++ b/src/pin_project/derive.rs +@@ -1,9 +1,9 @@ + 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, ++ parse_quote, punctuated::Punctuated, token, visit_mut::VisitMut, Attribute, Data, DataEnum, ++ DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, ++ Lifetime, LifetimeParam, Meta, Result, Token, Type, Variant, Visibility, WhereClause, + }; + + use super::{ +@@ -235,7 +235,7 @@ impl<'a> Context<'a> { + } + } + +-#[derive(Copy, Clone, Eq, PartialEq)] ++#[derive(Copy, Clone, PartialEq)] + enum TypeKind { + Enum, + Struct, +@@ -305,7 +305,7 @@ fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> { + fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { + if variants.is_empty() { + return Err(Error::new( +- brace_token.span, ++ brace_token.span.join(), + "#[pin_project] attribute may not be used on enums without variants", + )); + } +@@ -569,7 +569,9 @@ fn visit_fields<'a>( + 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() { ++ for (i, Field { attrs, vis, ident, colon_token, ty, mutability: _ }) in ++ fields.iter().enumerate() ++ { + let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i)); + proj_pat.extend(quote!(#binding,)); + let lifetime = &cx.proj.lifetime; +@@ -768,7 +770,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { + // 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, .. })| { ++ |(i, LifetimeParam { lifetime, .. })| { + let field_ident = format_ident!("__lifetime{}", i); + quote!(#field_ident: &#lifetime ()) + }, +@@ -1016,33 +1018,26 @@ fn make_proj_impl( + /// - 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 { ++ for attr in orig.attrs { ++ if let Meta::List(ref list) = attr.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(..) => {} ++ for repr in list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? { ++ if repr.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 repr.require_name_value().is_ok() { ++ // #[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); + } + } + } +@@ -1063,10 +1058,10 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result + // ```rust + // #[forbid(unaligned_references)] + // fn assert_not_repr_packed(val: &MyStruct) { +- // let _field1 = &val.field1; +- // let _field2 = &val.field2; ++ // let _field_1 = &val.field_1; ++ // let _field_2 = &val.field_2; + // ... +- // let _fieldn = &val.fieldn; ++ // let _field_n = &val.field_n; + // } + // ``` + // +diff --git a/src/pinned_drop.rs b/src/pinned_drop.rs +index 912989d..d30ecbe 100644 +--- a/src/pinned_drop.rs ++++ b/src/pinned_drop.rs +@@ -1,16 +1,18 @@ + use proc_macro2::TokenStream; + use quote::{format_ident, quote, ToTokens}; + use syn::{ +- parse_quote, spanned::Spanned, visit_mut::VisitMut, Error, FnArg, GenericArgument, ImplItem, +- ItemImpl, Pat, PatIdent, Path, PathArguments, Result, ReturnType, Signature, Token, Type, +- TypePath, TypeReference, ++ parse_quote, spanned::Spanned, token::Colon, visit_mut::VisitMut, Error, FnArg, ++ GenericArgument, Ident, ImplItem, ItemImpl, Pat, PatIdent, PatType, Path, PathArguments, ++ Result, ReturnType, Signature, Token, Type, TypePath, TypeReference, + }; + +-use crate::utils::{parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, SliceExt}; ++use crate::utils::{ReplaceReceiver, SliceExt}; + + pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream { + let res = (|| -> Result<()> { +- parse_as_empty(args)?; ++ if !args.is_empty() { ++ bail!(args, "unexpected argument: `{}`", args) ++ } + validate_impl(&input)?; + expand_impl(&mut input); + Ok(()) +@@ -85,7 +87,7 @@ fn validate_impl(item: &ItemImpl) -> Result<()> { + ImplItem::Type(item) => { + bail!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident) + } +- ImplItem::Method(method) => { ++ ImplItem::Fn(method) => { + validate_sig(&method.sig)?; + if i == 0 { + Ok(()) +@@ -124,14 +126,15 @@ fn validate_sig(sig: &Signature) -> Result<()> { + + match sig.inputs.len() { + 1 => {} +- 0 => return Err(Error::new(sig.paren_token.span, INVALID_ARGUMENT)), ++ 0 => return Err(Error::new(sig.paren_token.span.join(), INVALID_ARGUMENT)), + _ => bail!(sig.inputs, INVALID_ARGUMENT), + } + +- if let Some(FnArg::Typed(arg)) = sig.receiver() { ++ if let Some(arg) = sig.receiver() { + // (mut) self: <path> + if let Some(path) = get_ty_path(&arg.ty) { +- let ty = path.segments.last().unwrap(); ++ let ty = ++ path.segments.last().expect("Type paths should always have at least one segment"); + if let PathArguments::AngleBracketed(args) = &ty.arguments { + // (mut) self: (<path>::)<ty><&mut <elem>..> + if let Some(GenericArgument::Type(Type::Reference(TypeReference { +@@ -175,25 +178,16 @@ fn validate_sig(sig: &Signature) -> Result<()> { + // } + // + fn expand_impl(item: &mut ItemImpl) { +- fn get_arg_pat(arg: &mut FnArg) -> Option<&mut PatIdent> { +- if let FnArg::Typed(arg) = arg { +- if let Pat::Ident(ident) = &mut *arg.pat { +- return Some(ident); +- } +- } +- None +- } +- + // `PinnedDrop` is a private trait and should not appear in docs. + item.attrs.push(parse_quote!(#[doc(hidden)])); + +- let path = &mut item.trait_.as_mut().unwrap().1; ++ let path = &mut item.trait_.as_mut().expect("unexpected inherent impl").1; + *path = parse_quote_spanned! { path.span() => + ::pin_project::__private::PinnedDrop + }; + + let method = +- if let ImplItem::Method(method) = &mut item.items[0] { method } else { unreachable!() }; ++ if let ImplItem::Fn(method) = &mut item.items[0] { method } else { unreachable!() }; + + // `fn drop(mut self: Pin<&mut Self>)` -> `fn __drop_inner<T>(mut __self: Pin<&mut Receiver>)` + let drop_inner = { +@@ -203,8 +197,20 @@ fn expand_impl(item: &mut ItemImpl) { + drop_inner.block.stmts.insert(0, parse_quote!(fn #ident() {})); + drop_inner.sig.ident = ident; + drop_inner.sig.generics = item.generics.clone(); +- let self_pat = get_arg_pat(&mut drop_inner.sig.inputs[0]).unwrap(); +- prepend_underscore_to_self(&mut self_pat.ident); ++ let receiver = drop_inner.sig.receiver().expect("drop() should have a receiver").clone(); ++ let pat = Box::new(Pat::Ident(PatIdent { ++ attrs: Vec::new(), ++ by_ref: None, ++ mutability: receiver.mutability, ++ ident: Ident::new("__self", receiver.self_token.span()), ++ subpat: None, ++ })); ++ drop_inner.sig.inputs[0] = FnArg::Typed(PatType { ++ attrs: receiver.attrs, ++ pat, ++ colon_token: Colon::default(), ++ ty: receiver.ty, ++ }); + let self_ty = if let Type::Path(ty) = &*item.self_ty { ty } else { unreachable!() }; + let mut visitor = ReplaceReceiver(self_ty); + visitor.visit_signature_mut(&mut drop_inner.sig); +@@ -214,9 +220,12 @@ fn expand_impl(item: &mut ItemImpl) { + + // `fn drop(mut self: Pin<&mut Self>)` -> `unsafe fn drop(self: Pin<&mut Self>)` + method.sig.unsafety = Some(<Token![unsafe]>::default()); +- let self_pat = get_arg_pat(&mut method.sig.inputs[0]).unwrap(); +- self_pat.mutability = None; +- let self_token = &self_pat.ident; ++ let self_token = if let FnArg::Receiver(ref mut rec) = method.sig.inputs[0] { ++ rec.mutability = None; ++ &rec.self_token ++ } else { ++ panic!("drop() should have a receiver") ++ }; + + method.block.stmts = parse_quote! { + #[allow(clippy::needless_pass_by_value)] // This lint does not warn the receiver. +diff --git a/src/utils.rs b/src/utils.rs +index 27373ef..3f4ec85 100644 +--- a/src/utils.rs ++++ b/src/utils.rs +@@ -8,7 +8,7 @@ use syn::{ + punctuated::Punctuated, + token, + visit_mut::{self, VisitMut}, +- Attribute, ExprPath, ExprStruct, Generics, Ident, Item, Lifetime, LifetimeDef, Macro, PatPath, ++ Attribute, ExprPath, ExprStruct, Generics, Ident, Item, Lifetime, LifetimeParam, Macro, + PatStruct, PatTupleStruct, Path, PathArguments, PredicateType, QSelf, Result, Token, Type, + TypeParamBound, TypePath, Variant, Visibility, WherePredicate, + }; +@@ -42,7 +42,7 @@ pub(crate) fn determine_lifetime_name(lifetime_name: &mut String, generics: &mut + struct CollectLifetimes(Vec<String>); + + impl VisitMut for CollectLifetimes { +- fn visit_lifetime_def_mut(&mut self, def: &mut LifetimeDef) { ++ fn visit_lifetime_param_mut(&mut self, def: &mut LifetimeParam) { + self.0.push(def.lifetime.to_string()); + } + } +@@ -84,7 +84,7 @@ pub(crate) fn insert_lifetime_and_bound( + pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { + generics.lt_token.get_or_insert_with(<Token![<]>::default); + generics.gt_token.get_or_insert_with(<Token![>]>::default); +- generics.params.insert(0, LifetimeDef::new(lifetime).into()); ++ generics.params.insert(0, LifetimeParam::new(lifetime).into()); + } + + /// Determines the visibility of the projected types and projection methods. +@@ -93,24 +93,12 @@ pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { + /// Otherwise, returned visibility is the same as given visibility. + pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility { + if let Visibility::Public(token) = vis { +- parse_quote_spanned!(token.pub_token.span => pub(crate)) ++ parse_quote_spanned!(token.span => pub(crate)) + } else { + vis.clone() + } + } + +-/// Checks if `tokens` is an empty `TokenStream`. +-/// +-/// This is almost equivalent to `syn::parse2::<Nothing>()`, but produces +-/// a better error message and does not require ownership of `tokens`. +-pub(crate) fn parse_as_empty(tokens: &TokenStream) -> Result<()> { +- if tokens.is_empty() { +- Ok(()) +- } else { +- bail!(tokens, "unexpected token: `{}`", tokens) +- } +-} +- + pub(crate) fn respan<T>(node: &T, span: Span) -> T + where + T: ToTokens + Parse, +@@ -146,11 +134,11 @@ impl SliceExt for [Attribute] { + fn position_exact(&self, ident: &str) -> Result<Option<usize>> { + self.iter() + .try_fold((0, None), |(i, mut prev), attr| { +- if attr.path.is_ident(ident) { ++ if attr.path().is_ident(ident) { + if prev.replace(i).is_some() { + bail!(attr, "duplicate #[{}] attribute", ident); + } +- parse_as_empty(&attr.tokens)?; ++ attr.meta.require_path_only()?; + } + Ok((i + 1, prev)) + }) +@@ -158,7 +146,7 @@ impl SliceExt for [Attribute] { + } + + fn find(&self, ident: &str) -> Option<&Attribute> { +- self.iter().position(|attr| attr.path.is_ident(ident)).map(|i| &self[i]) ++ self.iter().position(|attr| attr.path().is_ident(ident)).map(|i| &self[i]) + } + } + +@@ -335,13 +323,6 @@ impl VisitMut for ReplaceReceiver<'_> { + visit_mut::visit_expr_struct_mut(self, expr); + } + +- fn visit_pat_path_mut(&mut self, pat: &mut PatPath) { +- if pat.qself.is_none() { +- self.self_to_qself(&mut pat.qself, &mut pat.path); +- } +- visit_mut::visit_pat_path_mut(self, pat); +- } +- + fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) { + self.self_to_expr_path(&mut pat.path); + visit_mut::visit_pat_struct_mut(self, pat); diff --git a/src/pin_project/args.rs b/src/pin_project/args.rs index d0d4f36..d242ae7 100644 --- a/src/pin_project/args.rs +++ b/src/pin_project/args.rs @@ -16,10 +16,9 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { impl Parse for Input { fn parse(input: ParseStream<'_>) -> Result<Self> { Ok(Self((|| { - let content = input.parenthesized().ok()?; - let private = content.parse::<Ident>().ok()?; + let private = input.parse::<Ident>().ok()?; if private == "__private" { - content.parenthesized().ok()?.parse::<TokenStream>().ok() + input.parenthesized().ok()?.parse::<TokenStream>().ok() } else { None } @@ -31,10 +30,10 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { bail!(attr, "duplicate #[pin_project] attribute"); } - let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); + 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) + (attr, syn::parse2::<Input>(attr.meta.require_list()?.tokens.clone())?.0) } else { // This only fails if another macro removes `#[pin]`. bail!(TokenStream::new(), "#[pin_project] attribute has been removed"); @@ -46,7 +45,7 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { // 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 res = syn::parse2::<Input>(attr.meta.require_list()?.tokens.clone())?.0; let span = match (prev_res, res) { (Some(_), _) => attr, (None, _) => prev_attr, diff --git a/src/pin_project/derive.rs b/src/pin_project/derive.rs index fd2375d..d2b2b5e 100644 --- a/src/pin_project/derive.rs +++ b/src/pin_project/derive.rs @@ -1,9 +1,9 @@ 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, + parse_quote, punctuated::Punctuated, token, visit_mut::VisitMut, Attribute, Data, DataEnum, + DeriveInput, Error, Field, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident, Index, + Lifetime, LifetimeParam, Meta, Result, Token, Type, Variant, Visibility, WhereClause, }; use super::{ @@ -235,7 +235,7 @@ impl<'a> Context<'a> { } } -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, PartialEq)] enum TypeKind { Enum, Struct, @@ -305,7 +305,7 @@ fn validate_struct(ident: &Ident, fields: &Fields) -> Result<()> { fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { if variants.is_empty() { return Err(Error::new( - brace_token.span, + brace_token.span.join(), "#[pin_project] attribute may not be used on enums without variants", )); } @@ -569,7 +569,9 @@ fn visit_fields<'a>( 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() { + for (i, Field { attrs, vis, ident, colon_token, ty, mutability: _ }) in + fields.iter().enumerate() + { let binding = ident.clone().unwrap_or_else(|| format_ident!("_{}", i)); proj_pat.extend(quote!(#binding,)); let lifetime = &cx.proj.lifetime; @@ -768,7 +770,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { // 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, .. })| { + |(i, LifetimeParam { lifetime, .. })| { let field_ident = format_ident!("__lifetime{}", i); quote!(#field_ident: &#lifetime ()) }, @@ -1016,33 +1018,26 @@ fn make_proj_impl( /// - 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 { + for attr in orig.attrs { + if let Meta::List(ref list) = attr.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(..) => {} + for repr in list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)? { + if repr.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 repr.require_name_value().is_ok() { + // #[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); } } } @@ -1063,10 +1058,10 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: Option<&Fields>) -> Result // ```rust // #[forbid(unaligned_references)] // fn assert_not_repr_packed(val: &MyStruct) { - // let _field1 = &val.field1; - // let _field2 = &val.field2; + // let _field_1 = &val.field_1; + // let _field_2 = &val.field_2; // ... - // let _fieldn = &val.fieldn; + // let _field_n = &val.field_n; // } // ``` // diff --git a/src/pinned_drop.rs b/src/pinned_drop.rs index 912989d..d30ecbe 100644 --- a/src/pinned_drop.rs +++ b/src/pinned_drop.rs @@ -1,16 +1,18 @@ use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; use syn::{ - parse_quote, spanned::Spanned, visit_mut::VisitMut, Error, FnArg, GenericArgument, ImplItem, - ItemImpl, Pat, PatIdent, Path, PathArguments, Result, ReturnType, Signature, Token, Type, - TypePath, TypeReference, + parse_quote, spanned::Spanned, token::Colon, visit_mut::VisitMut, Error, FnArg, + GenericArgument, Ident, ImplItem, ItemImpl, Pat, PatIdent, PatType, Path, PathArguments, + Result, ReturnType, Signature, Token, Type, TypePath, TypeReference, }; -use crate::utils::{parse_as_empty, prepend_underscore_to_self, ReplaceReceiver, SliceExt}; +use crate::utils::{ReplaceReceiver, SliceExt}; pub(crate) fn attribute(args: &TokenStream, mut input: ItemImpl) -> TokenStream { let res = (|| -> Result<()> { - parse_as_empty(args)?; + if !args.is_empty() { + bail!(args, "unexpected argument: `{}`", args) + } validate_impl(&input)?; expand_impl(&mut input); Ok(()) @@ -85,7 +87,7 @@ fn validate_impl(item: &ItemImpl) -> Result<()> { ImplItem::Type(item) => { bail!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident) } - ImplItem::Method(method) => { + ImplItem::Fn(method) => { validate_sig(&method.sig)?; if i == 0 { Ok(()) @@ -124,14 +126,15 @@ fn validate_sig(sig: &Signature) -> Result<()> { match sig.inputs.len() { 1 => {} - 0 => return Err(Error::new(sig.paren_token.span, INVALID_ARGUMENT)), + 0 => return Err(Error::new(sig.paren_token.span.join(), INVALID_ARGUMENT)), _ => bail!(sig.inputs, INVALID_ARGUMENT), } - if let Some(FnArg::Typed(arg)) = sig.receiver() { + if let Some(arg) = sig.receiver() { // (mut) self: <path> if let Some(path) = get_ty_path(&arg.ty) { - let ty = path.segments.last().unwrap(); + let ty = + path.segments.last().expect("Type paths should always have at least one segment"); if let PathArguments::AngleBracketed(args) = &ty.arguments { // (mut) self: (<path>::)<ty><&mut <elem>..> if let Some(GenericArgument::Type(Type::Reference(TypeReference { @@ -175,25 +178,16 @@ fn validate_sig(sig: &Signature) -> Result<()> { // } // fn expand_impl(item: &mut ItemImpl) { - fn get_arg_pat(arg: &mut FnArg) -> Option<&mut PatIdent> { - if let FnArg::Typed(arg) = arg { - if let Pat::Ident(ident) = &mut *arg.pat { - return Some(ident); - } - } - None - } - // `PinnedDrop` is a private trait and should not appear in docs. item.attrs.push(parse_quote!(#[doc(hidden)])); - let path = &mut item.trait_.as_mut().unwrap().1; + let path = &mut item.trait_.as_mut().expect("unexpected inherent impl").1; *path = parse_quote_spanned! { path.span() => ::pin_project::__private::PinnedDrop }; let method = - if let ImplItem::Method(method) = &mut item.items[0] { method } else { unreachable!() }; + if let ImplItem::Fn(method) = &mut item.items[0] { method } else { unreachable!() }; // `fn drop(mut self: Pin<&mut Self>)` -> `fn __drop_inner<T>(mut __self: Pin<&mut Receiver>)` let drop_inner = { @@ -203,8 +197,20 @@ fn expand_impl(item: &mut ItemImpl) { drop_inner.block.stmts.insert(0, parse_quote!(fn #ident() {})); drop_inner.sig.ident = ident; drop_inner.sig.generics = item.generics.clone(); - let self_pat = get_arg_pat(&mut drop_inner.sig.inputs[0]).unwrap(); - prepend_underscore_to_self(&mut self_pat.ident); + let receiver = drop_inner.sig.receiver().expect("drop() should have a receiver").clone(); + let pat = Box::new(Pat::Ident(PatIdent { + attrs: Vec::new(), + by_ref: None, + mutability: receiver.mutability, + ident: Ident::new("__self", receiver.self_token.span()), + subpat: None, + })); + drop_inner.sig.inputs[0] = FnArg::Typed(PatType { + attrs: receiver.attrs, + pat, + colon_token: Colon::default(), + ty: receiver.ty, + }); let self_ty = if let Type::Path(ty) = &*item.self_ty { ty } else { unreachable!() }; let mut visitor = ReplaceReceiver(self_ty); visitor.visit_signature_mut(&mut drop_inner.sig); @@ -214,9 +220,12 @@ fn expand_impl(item: &mut ItemImpl) { // `fn drop(mut self: Pin<&mut Self>)` -> `unsafe fn drop(self: Pin<&mut Self>)` method.sig.unsafety = Some(<Token![unsafe]>::default()); - let self_pat = get_arg_pat(&mut method.sig.inputs[0]).unwrap(); - self_pat.mutability = None; - let self_token = &self_pat.ident; + let self_token = if let FnArg::Receiver(ref mut rec) = method.sig.inputs[0] { + rec.mutability = None; + &rec.self_token + } else { + panic!("drop() should have a receiver") + }; method.block.stmts = parse_quote! { #[allow(clippy::needless_pass_by_value)] // This lint does not warn the receiver. diff --git a/src/utils.rs b/src/utils.rs index 27373ef..3f4ec85 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -8,7 +8,7 @@ use syn::{ punctuated::Punctuated, token, visit_mut::{self, VisitMut}, - Attribute, ExprPath, ExprStruct, Generics, Ident, Item, Lifetime, LifetimeDef, Macro, PatPath, + Attribute, ExprPath, ExprStruct, Generics, Ident, Item, Lifetime, LifetimeParam, Macro, PatStruct, PatTupleStruct, Path, PathArguments, PredicateType, QSelf, Result, Token, Type, TypeParamBound, TypePath, Variant, Visibility, WherePredicate, }; @@ -42,7 +42,7 @@ pub(crate) fn determine_lifetime_name(lifetime_name: &mut String, generics: &mut struct CollectLifetimes(Vec<String>); impl VisitMut for CollectLifetimes { - fn visit_lifetime_def_mut(&mut self, def: &mut LifetimeDef) { + fn visit_lifetime_param_mut(&mut self, def: &mut LifetimeParam) { self.0.push(def.lifetime.to_string()); } } @@ -84,7 +84,7 @@ pub(crate) fn insert_lifetime_and_bound( pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { generics.lt_token.get_or_insert_with(<Token![<]>::default); generics.gt_token.get_or_insert_with(<Token![>]>::default); - generics.params.insert(0, LifetimeDef::new(lifetime).into()); + generics.params.insert(0, LifetimeParam::new(lifetime).into()); } /// Determines the visibility of the projected types and projection methods. @@ -93,24 +93,12 @@ pub(crate) fn insert_lifetime(generics: &mut Generics, lifetime: Lifetime) { /// Otherwise, returned visibility is the same as given visibility. pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility { if let Visibility::Public(token) = vis { - parse_quote_spanned!(token.pub_token.span => pub(crate)) + parse_quote_spanned!(token.span => pub(crate)) } else { vis.clone() } } -/// Checks if `tokens` is an empty `TokenStream`. -/// -/// This is almost equivalent to `syn::parse2::<Nothing>()`, but produces -/// a better error message and does not require ownership of `tokens`. -pub(crate) fn parse_as_empty(tokens: &TokenStream) -> Result<()> { - if tokens.is_empty() { - Ok(()) - } else { - bail!(tokens, "unexpected token: `{}`", tokens) - } -} - pub(crate) fn respan<T>(node: &T, span: Span) -> T where T: ToTokens + Parse, @@ -146,11 +134,11 @@ impl SliceExt for [Attribute] { fn position_exact(&self, ident: &str) -> Result<Option<usize>> { self.iter() .try_fold((0, None), |(i, mut prev), attr| { - if attr.path.is_ident(ident) { + if attr.path().is_ident(ident) { if prev.replace(i).is_some() { bail!(attr, "duplicate #[{}] attribute", ident); } - parse_as_empty(&attr.tokens)?; + attr.meta.require_path_only()?; } Ok((i + 1, prev)) }) @@ -158,7 +146,7 @@ impl SliceExt for [Attribute] { } fn find(&self, ident: &str) -> Option<&Attribute> { - self.iter().position(|attr| attr.path.is_ident(ident)).map(|i| &self[i]) + self.iter().position(|attr| attr.path().is_ident(ident)).map(|i| &self[i]) } } @@ -335,13 +323,6 @@ impl VisitMut for ReplaceReceiver<'_> { visit_mut::visit_expr_struct_mut(self, expr); } - fn visit_pat_path_mut(&mut self, pat: &mut PatPath) { - if pat.qself.is_none() { - self.self_to_qself(&mut pat.qself, &mut pat.path); - } - visit_mut::visit_pat_path_mut(self, pat); - } - fn visit_pat_struct_mut(&mut self, pat: &mut PatStruct) { self.self_to_expr_path(&mut pat.path); visit_mut::visit_pat_struct_mut(self, pat); |