diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:04:13 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-05-10 07:04:13 +0000 |
commit | c7a97c61c946d1f946a8149253769274d068d4a6 (patch) | |
tree | 6b24ddd0d0285a3b0e3ee80782f20a11ca2703dc | |
parent | 9dc72f48683fbc16c68a1e531f38daae431e63a1 (diff) | |
parent | 0adf6dae3163b2719eb1baee269a652ba0e96698 (diff) | |
download | pin-project-internal-android13-mainline-extservices-release.tar.gz |
Snap for 8564071 from 0adf6dae3163b2719eb1baee269a652ba0e96698 to mainline-extservices-releaseaml_ext_331814220aml_ext_331412000aml_ext_331312000aml_ext_331112010aml_ext_331012020android13-mainline-extservices-release
Change-Id: I1202bdb7d505566c9cdf32f88408993b525b9087
-rw-r--r-- | .cargo_vcs_info.json | 7 | ||||
-rw-r--r-- | Android.bp | 27 | ||||
-rw-r--r-- | Cargo.toml | 13 | ||||
-rw-r--r-- | Cargo.toml.orig | 4 | ||||
-rw-r--r-- | METADATA | 8 | ||||
-rw-r--r-- | TEST_MAPPING | 8 | ||||
-rw-r--r-- | cargo2android.json | 4 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/pin_project/args.rs | 56 | ||||
-rw-r--r-- | src/pin_project/attribute.rs | 15 | ||||
-rw-r--r-- | src/pin_project/derive.rs | 230 | ||||
-rw-r--r-- | src/pinned_drop.rs | 46 | ||||
-rw-r--r-- | src/utils.rs | 45 |
13 files changed, 250 insertions, 229 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json index 6cfdedc..60d79af 100644 --- a/.cargo_vcs_info.json +++ b/.cargo_vcs_info.json @@ -1,5 +1,6 @@ { "git": { - "sha1": "a51d39fcdb31fe78cc61c0053ead2beb65a4156d" - } -} + "sha1": "0273e6ecd64057f47c3b2ada6fb4e5c37357c185" + }, + "path_in_vcs": "pin-project-internal" +}
\ No newline at end of file @@ -1,4 +1,4 @@ -// This file is generated by cargo2android.py --run --dependencies --tests. +// This file is generated by cargo2android.py --config cargo2android.json. // Do not modify this file as changes will be overridden on upgrade. package { @@ -42,6 +42,8 @@ license { rust_proc_macro { name: "libpin_project_internal", crate_name: "pin_project_internal", + cargo_env_compat: true, + cargo_pkg_version: "1.0.10", srcs: ["src/lib.rs"], edition: "2018", rustlibs: [ @@ -50,26 +52,3 @@ rust_proc_macro { "libsyn", ], } - -rust_test_host { - name: "pin-project-internal_host_test_src_lib", - crate_name: "pin_project_internal", - srcs: ["src/lib.rs"], - test_suites: ["general-tests"], - auto_gen_config: true, - test_options: { - unit_test: true, - }, - edition: "2018", - rustlibs: [ - "libproc_macro2", - "libquote", - "libsyn", - ], -} - -// dependent_library ["feature_list"] -// proc-macro2-1.0.26 "default,proc-macro" -// quote-1.0.9 "default,proc-macro" -// syn-1.0.68 "clone-impls,default,derive,full,parsing,printing,proc-macro,quote,visit-mut" -// unicode-xid-0.2.1 "default" @@ -3,20 +3,17 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "pin-project-internal" -version = "1.0.6" -authors = ["Taiki Endo <te316e89@gmail.com>"] +version = "1.0.10" description = "Implementation detail of the `pin-project` crate.\n" -documentation = "https://docs.rs/pin-project-internal" keywords = ["pin", "macros", "attribute"] categories = ["no-std", "rust-patterns"] license = "Apache-2.0 OR MIT" diff --git a/Cargo.toml.orig b/Cargo.toml.orig index 707c32b..78c2f44 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -1,11 +1,9 @@ [package] name = "pin-project-internal" -version = "1.0.6" -authors = ["Taiki Endo <te316e89@gmail.com>"] +version = "1.0.10" edition = "2018" license = "Apache-2.0 OR MIT" repository = "https://github.com/taiki-e/pin-project" -documentation = "https://docs.rs/pin-project-internal" keywords = ["pin", "macros", "attribute"] categories = ["no-std", "rust-patterns"] description = """ @@ -7,13 +7,13 @@ third_party { } url { type: ARCHIVE - value: "https://static.crates.io/crates/pin-project-internal/pin-project-internal-1.0.6.crate" + value: "https://static.crates.io/crates/pin-project-internal/pin-project-internal-1.0.10.crate" } - version: "1.0.6" + version: "1.0.10" license_type: NOTICE last_upgrade_date { - year: 2021 - month: 4 + year: 2022 + month: 3 day: 1 } } diff --git a/TEST_MAPPING b/TEST_MAPPING deleted file mode 100644 index 7e10dd0..0000000 --- a/TEST_MAPPING +++ /dev/null @@ -1,8 +0,0 @@ -// Generated by update_crate_tests.py for tests that depend on this crate. -{ - "presubmit": [ - { - "name": "futures-util_device_test_src_lib" - } - ] -} diff --git a/cargo2android.json b/cargo2android.json new file mode 100644 index 0000000..341300b --- /dev/null +++ b/cargo2android.json @@ -0,0 +1,4 @@ +{ + "run": true, + "tests": true +}
\ No newline at end of file @@ -8,8 +8,8 @@ ) ))] #![warn(unsafe_code)] -#![warn(future_incompatible, rust_2018_idioms, single_use_lifetimes, unreachable_pub)] -#![warn(clippy::all, clippy::default_trait_access)] +#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)] +#![warn(clippy::default_trait_access, clippy::wildcard_imports)] #![allow(clippy::needless_doctest_main)] // older compilers require explicit `extern crate`. @@ -29,9 +29,9 @@ use proc_macro::TokenStream; /// /// This attribute creates projection types according to the following rules: /// -/// * For the fields that use `#[pin]` attribute, create the pinned reference to +/// - For the fields that use `#[pin]` attribute, create the pinned reference to /// the field. -/// * For the other fields, create a normal reference to the field. +/// - For the other fields, create a normal reference to the field. /// /// And the following methods are implemented on the original type: /// @@ -351,8 +351,8 @@ use proc_macro::TokenStream; /// This impl block acts just like a normal [`Drop`] impl, /// except for the following two: /// -/// * `drop` method takes [`Pin`]`<&mut Self>` -/// * Name of the trait is `PinnedDrop`. +/// - `drop` method takes [`Pin`]`<&mut Self>` +/// - Name of the trait is `PinnedDrop`. /// /// ```rust /// # use std::pin::Pin; @@ -495,8 +495,8 @@ pub fn pin_project(args: TokenStream, input: TokenStream) -> TokenStream { /// The impl block annotated with this attribute acts just like a normal /// [`Drop`] impl, except for the following two: /// -/// * `drop` method takes [`Pin`]`<&mut Self>` -/// * Name of the trait is `PinnedDrop`. +/// - `drop` method takes [`Pin`]`<&mut Self>` +/// - Name of the trait is `PinnedDrop`. /// /// ```rust /// # use std::pin::Pin; diff --git a/src/pin_project/args.rs b/src/pin_project/args.rs index c321260..d0d4f36 100644 --- a/src/pin_project/args.rs +++ b/src/pin_project/args.rs @@ -28,7 +28,7 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { } if let Some(attr) = attrs.find("pin_project") { - return Err(error!(attr, "duplicate #[pin_project] attribute")); + bail!(attr, "duplicate #[pin_project] attribute"); } let mut attrs = attrs.iter().filter(|attr| attr.path.is_ident(PIN)); @@ -37,7 +37,7 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { (attr, syn::parse2::<Input>(attr.tokens.clone()).unwrap().0) } else { // This only fails if another macro removes `#[pin]`. - return Err(error!(TokenStream::new(), "#[pin_project] attribute has been removed")); + bail!(TokenStream::new(), "#[pin_project] attribute has been removed"); }; if let Some(attr) = attrs.next() { @@ -51,11 +51,10 @@ pub(super) fn parse_args(attrs: &[Attribute]) -> Result<Args> { (Some(_), _) => attr, (None, _) => prev_attr, }; - Err(error!(span, "duplicate #[pin] attribute")) - } else { - // This `unwrap` only fails if another macro removes `#[pin]` and inserts own `#[pin]`. - syn::parse2(prev.1.unwrap()) + 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 { @@ -84,20 +83,19 @@ impl Parse for Args { has_prev: bool, ) -> Result<(Ident, TokenStream)> { if input.is_empty() { - return Err(error!(name, "expected `{0} = <identifier>`, found `{0}`", name)); + bail!(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)); + bail!(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)) + bail!(span, "duplicate `{}` argument", name); } + Ok((value, span)) } let mut pinned_drop = None; @@ -112,24 +110,24 @@ impl Parse for Args { if input.peek(Token![!]) { let bang: Token![!] = input.parse()?; if input.is_empty() { - return Err(error!(bang, "expected `!Unpin`, found `!`")); + bail!(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")); + bail!(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")); + bail!(token, "duplicate `PinnedDrop` argument"); } } "UnsafeUnpin" => { if unsafe_unpin.replace(token.span()).is_some() { - return Err(error!(token, "duplicate `UnsafeUnpin` argument")); + bail!(token, "duplicate `UnsafeUnpin` argument"); } } "project" => { @@ -145,18 +143,18 @@ impl Parse for Args { 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")); + bail!(token, "duplicate `project_replace` argument"); } else { project_replace_span = Some(token.span()); } } "Replace" => { - return Err(error!( + bail!( token, "`Replace` argument was removed, use `project_replace` argument instead" - )); + ); } - _ => return Err(error!(token, "unexpected argument: {}", token)), + _ => bail!(token, "unexpected argument: {}", token), } } @@ -168,23 +166,17 @@ impl Parse for Args { if project.is_some() || project_ref.is_some() { if project == project_ref { - return Err(error!( + 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 { - return Err(error!( - ident, - "name `{}` is already specified by `project` argument", ident - )); + bail!(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 - )); + bail!(ident, "name `{}` is already specified by `project_ref` argument", ident); } } } @@ -253,6 +245,10 @@ impl ProjReplace { } pub(super) fn ident(&self) -> Option<&Ident> { - if let Self::Named { ident, .. } = self { Some(ident) } else { None } + if let Self::Named { ident, .. } = self { + Some(ident) + } else { + None + } } } diff --git a/src/pin_project/attribute.rs b/src/pin_project/attribute.rs index 92ed547..c8811cb 100644 --- a/src/pin_project/attribute.rs +++ b/src/pin_project/attribute.rs @@ -17,9 +17,9 @@ use crate::utils::SliceExt; // // 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 +// - `#[derive(InternalDerive)]` - An internal helper macro that does the above // processing. -// * `#[pin(__private(#args))]` - Pass the argument of `#[pin_project]` to +// - `#[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> { @@ -51,16 +51,15 @@ impl Parse for Input { 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. - Err(error!( + bail!( input.parse::<TokenStream>()?, "#[pin_project] attribute may only be used on structs or enums" - )) + ); } else if let Some(attr) = attrs.find(PIN) { - Err(error!(attr, "#[pin] attribute may only be used on fields of structs or variants")) + bail!(attr, "#[pin] attribute may only be used on fields of structs or variants"); } else if let Some(attr) = attrs.find("pin_project") { - Err(error!(attr, "duplicate #[pin_project] attribute")) - } else { - Ok(Self { attrs, body: input.parse()? }) + bail!(attr, "duplicate #[pin_project] attribute"); } + Ok(Self { attrs, body: input.parse()? }) } } diff --git a/src/pin_project/derive.rs b/src/pin_project/derive.rs index 82539ca..3e578f7 100644 --- a/src/pin_project/derive.rs +++ b/src/pin_project/derive.rs @@ -3,7 +3,7 @@ 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, NestedMeta, Result, Token, Type, Variant, Visibility, WhereClause, + MetaList, MetaNameValue, NestedMeta, Result, Token, Type, Variant, Visibility, WhereClause, }; use super::{ @@ -38,10 +38,7 @@ pub(super) fn parse_derive(input: TokenStream) -> Result<TokenStream> { parse_enum(&mut cx, data, &mut generate)?; } Data::Union(_) => { - return Err(error!( - input, - "#[pin_project] attribute may only be used on structs or enums" - )); + bail!(input, "#[pin_project] attribute may only be used on structs or enums"); } } @@ -83,13 +80,17 @@ impl GenerateTokens { // 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 + // - 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 @@ -112,12 +113,13 @@ fn global_allowed_lints() -> TokenStream { #[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(kind: TypeKind) -> (TokenStream, TokenStream, TokenStream) { - let large_enum_variant = if kind == Enum { +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)] @@ -126,20 +128,22 @@ fn proj_allowed_lints(kind: TypeKind) -> (TokenStream, TokenStream, TokenStream) 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! { - #global_allowed_lints + #proj_mut_allowed_lints #[allow(dead_code)] // This lint warns unused fields/variants. #[allow(clippy::mut_mut)] // This lint warns `&mut &mut <ty>`. - #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326} }; + let proj_ref_allowed_lints = if cx.project_ref { Some(&global_allowed_lints) } else { None }; let proj_ref = quote! { - #global_allowed_lints + #proj_ref_allowed_lints #[allow(dead_code)] // This lint warns unused fields/variants. #[allow(clippy::ref_option_ref)] // This lint warns `&Option<&<ty>>`. - #[allow(clippy::type_repetition_in_bounds)] // https://github.com/rust-lang/rust-clippy/issues/4326 }; + let proj_own_allowed_lints = + if cx.project_replace.ident().is_some() { Some(&global_allowed_lints) } else { None }; let proj_own = quote! { - #global_allowed_lints + #proj_own_allowed_lints #[allow(dead_code)] // This lint warns unused fields/variants. #large_enum_variant }; @@ -152,7 +156,7 @@ struct Context<'a> { /// The projected types. proj: ProjectedType, /// Types of the pinned fields. - pinned_fields: Vec<Type>, + pinned_fields: Vec<&'a Type>, /// Kind of the original type: struct or enum kind: TypeKind, @@ -184,7 +188,7 @@ impl<'a> Context<'a> { .filter_map(Option::as_ref) .find(|name| **name == ident) { - return Err(error!(name, "name `{}` is the same as the original type name", name)); + bail!(name, "name `{}` is the same as the original type name", name); } let mut lifetime_name = String::from("'pin"); @@ -289,10 +293,12 @@ struct ProjectedFields { 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(()) + if let Fields::Unit = fields { + bail!(ident, msg) + } + bail!(fields, msg) } + Ok(()) } fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { @@ -304,9 +310,9 @@ fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { } 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")) + bail!(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")) + bail!(attr, "#[pin] attribute may only be used on fields of structs or variants"); } else if v.fields.is_empty() { Ok(has_field) } else { @@ -316,17 +322,17 @@ fn validate_enum(brace_token: token::Brace, variants: &Variants) -> Result<()> { if has_field { Ok(()) } else { - Err(error!(variants, "#[pin_project] attribute may not be used on enums with zero fields")) + bail!(variants, "#[pin_project] attribute may not be used on enums with zero fields"); } } -fn parse_struct( - cx: &mut Context<'_>, - fields: &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, fields)?; + let packed_check = ensure_not_packed(&cx.orig, Some(fields))?; validate_struct(cx.orig.ident, fields)?; @@ -368,7 +374,7 @@ fn parse_struct( Fields::Unit => unreachable!(), }; - let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx.kind); + 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 @@ -402,9 +408,9 @@ fn parse_struct( Ok(()) } -fn parse_enum( - cx: &mut Context<'_>, - DataEnum { brace_token, variants, .. }: &DataEnum, +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 { @@ -414,8 +420,12 @@ fn parse_enum( )); } - // We don't need to check for `#[repr(packed)]`, - // since it does not apply to 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)?; @@ -437,7 +447,7 @@ fn parse_enum( 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.kind); + let (proj_attrs, proj_ref_attrs, proj_own_attrs) = proj_allowed_lints(cx); if cx.project { generate.extend(true, quote! { #proj_attrs @@ -483,7 +493,7 @@ fn parse_enum( Ok(()) } -fn visit_variants(cx: &mut Context<'_>, variants: &Variants) -> Result<ProjectedVariants> { +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(); @@ -540,10 +550,10 @@ fn visit_variants(cx: &mut Context<'_>, variants: &Variants) -> Result<Projected }) } -fn visit_fields( - cx: &mut Context<'_>, +fn visit_fields<'a>( + cx: &mut Context<'a>, variant_ident: Option<&Ident>, - fields: &Fields, + fields: &'a Fields, delim: Delimiter, ) -> Result<ProjectedFields> { fn surround(delim: Delimiter, tokens: TokenStream) -> TokenStream { @@ -561,8 +571,8 @@ fn visit_fields( 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() { - let lifetime = &cx.proj.lifetime; proj_fields.extend(quote! { #vis #ident #colon_token ::pin_project::__private::Pin<&#lifetime mut (#ty)>, }); @@ -573,16 +583,15 @@ fn visit_fields( #vis #ident #colon_token ::pin_project::__private::PhantomData<#ty>, }); proj_body.extend(quote! { - #ident #colon_token ::pin_project::__private::Pin::new_unchecked(#binding), + #ident #colon_token _pin_project::__private::Pin::new_unchecked(#binding), }); proj_move.extend(quote! { - #ident #colon_token ::pin_project::__private::PhantomData, + #ident #colon_token _pin_project::__private::PhantomData, }); - cx.pinned_fields.push(ty.clone()); + cx.pinned_fields.push(ty); pinned_bindings.push(binding); } else { - let lifetime = &cx.proj.lifetime; proj_fields.extend(quote! { #vis #ident #colon_token &#lifetime mut (#ty), }); @@ -596,7 +605,7 @@ fn visit_fields( #binding, }); proj_move.extend(quote! { - #ident #colon_token ::pin_project::__private::ptr::read(#binding), + #ident #colon_token _pin_project::__private::ptr::read(#binding), }); } } @@ -608,7 +617,7 @@ fn visit_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); + let proj_own_body = proj_own_body(cx, variant_ident, Some(&proj_move), &pinned_bindings); Ok(ProjectedFields { proj_pat, @@ -626,7 +635,7 @@ fn visit_fields( fn proj_own_body( cx: &Context<'_>, variant_ident: Option<&Ident>, - proj_move: Option<Group>, + proj_move: Option<&Group>, pinned_fields: &[Ident], ) -> TokenStream { let ident = &cx.proj.own_ident; @@ -651,7 +660,7 @@ fn proj_own_body( // if any of the destructors panic. { #( - let __guard = ::pin_project::__private::UnsafeDropInPlaceGuard(#pinned_fields); + let __guard = _pin_project::__private::UnsafeDropInPlaceGuard::new(#pinned_fields); )* } @@ -663,9 +672,9 @@ fn proj_own_body( /// 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. +/// - `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) => { @@ -675,14 +684,14 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { // 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 + _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 + impl #impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics #where_clause { } @@ -694,9 +703,9 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { 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 + _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(); @@ -706,7 +715,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { // call-site span. let unsafety = <Token![unsafe]>::default(); quote_spanned! { span => - impl #proj_impl_generics ::pin_project::__private::Unpin + impl #proj_impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics #proj_where_clause { @@ -719,7 +728,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { // 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 + #unsafety impl #proj_impl_generics _pin_project::UnsafeUnpin for #orig_ident #ty_generics #proj_where_clause { @@ -774,7 +783,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { 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 + #struct_ident #proj_ty_generics: _pin_project::__private::Unpin }); quote! { @@ -790,15 +799,15 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { // 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>),*) + __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 + impl #proj_impl_generics _pin_project::__private::Unpin for #orig_ident #ty_generics #full_where_clause { @@ -811,7 +820,7 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { // 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 + unsafe impl #proj_impl_generics _pin_project::UnsafeUnpin for #orig_ident #ty_generics #full_where_clause { @@ -824,8 +833,8 @@ fn make_unpin_impl(cx: &Context<'_>) -> TokenStream { /// 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, +/// - `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; @@ -836,18 +845,18 @@ fn make_drop_impl(cx: &Context<'_>) -> TokenStream { // call-site span. let unsafety = <Token![unsafe]>::default(); quote_spanned! { span => - impl #impl_generics ::pin_project::__private::Drop for #ident #ty_generics + 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); + 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); + _pin_project::__private::PinnedDrop::drop(__pinned_self); } } } @@ -879,24 +888,24 @@ fn make_drop_impl(cx: &Context<'_>) -> TokenStream { // 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<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 attirbutes/impl may exist. + // 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 + impl #impl_generics _pin_project::__private::PinnedDrop for #ident #ty_generics #where_clause { - unsafe fn drop(self: ::pin_project::__private::Pin<&mut Self>) {} + unsafe fn drop(self: _pin_project::__private::Pin<&mut Self>) {} } } } @@ -927,7 +936,7 @@ fn make_proj_impl( let mut project = Some(quote! { #vis fn project<#lifetime>( - self: ::pin_project::__private::Pin<&#lifetime mut Self>, + self: _pin_project::__private::Pin<&#lifetime mut Self>, ) -> #proj_ident #proj_ty_generics { unsafe { #proj_body @@ -937,7 +946,7 @@ fn make_proj_impl( let mut project_ref = Some(quote! { #[allow(clippy::missing_const_for_fn)] #vis fn project_ref<#lifetime>( - self: ::pin_project::__private::Pin<&#lifetime Self>, + self: _pin_project::__private::Pin<&#lifetime Self>, ) -> #proj_ref_ident #proj_ty_generics { unsafe { #proj_ref_body @@ -948,7 +957,7 @@ fn make_proj_impl( // It is enough to only set the span of the signature. let sig = quote_spanned! { span => #vis fn project_replace( - self: ::pin_project::__private::Pin<&mut Self>, + self: _pin_project::__private::Pin<&mut Self>, __replacement: Self, ) -> #proj_own_ident #orig_ty_generics }; @@ -959,10 +968,10 @@ fn make_proj_impl( // 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 { - target: __self_ptr, - value: ::pin_project::__private::ManuallyDrop::new(__replacement), - }; + let __guard = _pin_project::__private::UnsafeOverwriteGuard::new( + __self_ptr, + __replacement, + ); #proj_own_body } @@ -994,10 +1003,10 @@ fn make_proj_impl( /// 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 `safe_packed_borrows` lint. -fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenStream> { +/// - 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") { @@ -1005,22 +1014,36 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenSt 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" - )); + | 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(..) => {} } } } } } - // As proc-macro-derive can't rewrite the structure definition, - // it's probably no longer necessary, but it keeps it for now. + 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 @@ -1029,7 +1052,7 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenSt // struct, we generate code like this: // // ```rust - // #[forbid(safe_packed_borrows)] + // #[forbid(unaligned_references)] // fn assert_not_repr_packed(val: &MyStruct) { // let _field1 = &val.field1; // let _field2 = &val.field2; @@ -1038,10 +1061,8 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenSt // } // ``` // - // Taking a reference to a packed field is unsafe, and applying - // `#[forbid(safe_packed_borrows)]` makes sure that doing this without - // an `unsafe` block (which we deliberately do not generate) - // is a hard error. + // 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 @@ -1061,6 +1082,21 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenSt // `#[repr(packed)]` in the first place. // // See also https://github.com/taiki-e/pin-project/pull/34. + // + // Note: + // - pin-project v0.4.3 or later (#135, v0.4.0-v0.4.2 are already yanked for + // another reason) is internally proc-macro-derive, so they are not + // affected by the problem that the struct definition is rewritten by + // another macro after the #[pin_project] is expanded. + // So this is probably no longer necessary, but it keeps it for now. + // + // - 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, .. }) => { @@ -1080,7 +1116,7 @@ fn ensure_not_packed(orig: &OriginalType<'_>, fields: &Fields) -> Result<TokenSt let (impl_generics, ty_generics, where_clause) = orig.generics.split_for_impl(); let ident = orig.ident; Ok(quote! { - #[forbid(safe_packed_borrows)] + #[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/src/pinned_drop.rs b/src/pinned_drop.rs index ecf52dd..912989d 100644 --- a/src/pinned_drop.rs +++ b/src/pinned_drop.rs @@ -53,47 +53,44 @@ fn validate_impl(item: &ItemImpl) -> Result<()> { "#[pinned_drop] may only be used on implementation for the `PinnedDrop` trait"; if let Some(attr) = item.attrs.find("pinned_drop") { - return Err(error!(attr, "duplicate #[pinned_drop] attribute")); + bail!(attr, "duplicate #[pinned_drop] attribute"); } if let Some((_, path, _)) = &item.trait_ { if !path.is_ident("PinnedDrop") { - return Err(error!(path, INVALID_ITEM)); + bail!(path, INVALID_ITEM); } } else { - return Err(error!(item.self_ty, INVALID_ITEM)); + bail!(item.self_ty, INVALID_ITEM); } if item.unsafety.is_some() { - return Err(error!(item.unsafety, "implementing the trait `PinnedDrop` is not unsafe")); + bail!(item.unsafety, "implementing the trait `PinnedDrop` is not unsafe"); } if item.items.is_empty() { - return Err(error!(item, "not all trait items implemented, missing: `drop`")); + bail!(item, "not all trait items implemented, missing: `drop`"); } match &*item.self_ty { Type::Path(_) => {} ty => { - return Err(error!( - ty, - "implementing the trait `PinnedDrop` on this type is unsupported" - )); + bail!(ty, "implementing the trait `PinnedDrop` on this type is unsupported"); } } item.items.iter().enumerate().try_for_each(|(i, item)| match item { ImplItem::Const(item) => { - Err(error!(item, "const `{}` is not a member of trait `PinnedDrop`", item.ident)) + bail!(item, "const `{}` is not a member of trait `PinnedDrop`", item.ident) } ImplItem::Type(item) => { - Err(error!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident)) + bail!(item, "type `{}` is not a member of trait `PinnedDrop`", item.ident) } ImplItem::Method(method) => { validate_sig(&method.sig)?; if i == 0 { Ok(()) } else { - Err(error!(method, "duplicate definitions with name `drop`")) + bail!(method, "duplicate definitions with name `drop`") } } _ => unreachable!("unexpected ImplItem"), @@ -105,29 +102,30 @@ fn validate_impl(item: &ItemImpl) -> Result<()> { /// The correct signature is: `(mut) self: (<path>::)Pin<&mut Self>` fn validate_sig(sig: &Signature) -> Result<()> { fn get_ty_path(ty: &Type) -> Option<&Path> { - if let Type::Path(TypePath { qself: None, path }) = ty { Some(path) } else { None } + if let Type::Path(TypePath { qself: None, path }) = ty { + Some(path) + } else { + None + } } const INVALID_ARGUMENT: &str = "method `drop` must take an argument `self: Pin<&mut Self>`"; if sig.ident != "drop" { - return Err(error!( - sig.ident, - "method `{}` is not a member of trait `PinnedDrop", sig.ident, - )); + bail!(sig.ident, "method `{}` is not a member of trait `PinnedDrop", sig.ident,); } if let ReturnType::Type(_, ty) = &sig.output { match &**ty { Type::Tuple(ty) if ty.elems.is_empty() => {} - _ => return Err(error!(ty, "method `drop` must return the unit type")), + _ => bail!(ty, "method `drop` must return the unit type"), } } match sig.inputs.len() { 1 => {} 0 => return Err(Error::new(sig.paren_token.span, INVALID_ARGUMENT)), - _ => return Err(error!(sig.inputs, INVALID_ARGUMENT)), + _ => bail!(sig.inputs, INVALID_ARGUMENT), } if let Some(FnArg::Typed(arg)) = sig.receiver() { @@ -148,10 +146,7 @@ fn validate_sig(sig: &Signature) -> Result<()> { && get_ty_path(elem).map_or(false, |path| path.is_ident("Self")) { if sig.unsafety.is_some() { - return Err(error!( - sig.unsafety, - "implementing the method `drop` is not unsafe" - )); + bail!(sig.unsafety, "implementing the method `drop` is not unsafe"); } return Ok(()); } @@ -160,7 +155,7 @@ fn validate_sig(sig: &Signature) -> Result<()> { } } - Err(error!(sig.inputs[0], INVALID_ARGUMENT)) + bail!(sig.inputs[0], INVALID_ARGUMENT) } // from: @@ -189,6 +184,9 @@ fn expand_impl(item: &mut ItemImpl) { 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; *path = parse_quote_spanned! { path.span() => ::pin_project::__private::PinnedDrop diff --git a/src/utils.rs b/src/utils.rs index 37f35ba..27373ef 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -15,12 +15,18 @@ use syn::{ pub(crate) type Variants = Punctuated<Variant, Token![,]>; -macro_rules! error { - ($span:expr, $msg:expr) => { - syn::Error::new_spanned(&$span, $msg) +macro_rules! format_err { + ($span:expr, $msg:expr $(,)?) => { + syn::Error::new_spanned(&$span as &dyn quote::ToTokens, &$msg as &dyn std::fmt::Display) }; ($span:expr, $($tt:tt)*) => { - error!($span, format!($($tt)*)) + format_err!($span, format!($($tt)*)) + }; +} + +macro_rules! bail { + ($($tt:tt)*) => { + return Err(format_err!($($tt)*)) }; } @@ -98,7 +104,11 @@ pub(crate) fn determine_visibility(vis: &Visibility) -> Visibility { /// 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 { Err(error!(tokens, "unexpected token: {}", tokens)) } + if tokens.is_empty() { + Ok(()) + } else { + bail!(tokens, "unexpected token: `{}`", tokens) + } } pub(crate) fn respan<T>(node: &T, span: Span) -> T @@ -131,14 +141,14 @@ pub(crate) trait SliceExt { impl SliceExt for [Attribute] { /// # Errors /// - /// * There are multiple specified attributes. - /// * The `Attribute::tokens` field of the specified attribute is not empty. + /// - There are multiple specified attributes. + /// - The `Attribute::tokens` field of the specified attribute is not empty. 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 prev.replace(i).is_some() { - return Err(error!(attr, "duplicate #[{}] attribute", ident)); + bail!(attr, "duplicate #[{}] attribute", ident); } parse_as_empty(&attr.tokens)?; } @@ -176,7 +186,9 @@ impl<'a> ParseBufferExt<'a> for ParseBuffer<'a> { // visitors // Replace `self`/`Self` with `__self`/`self_ty`. -// Based on https://github.com/dtolnay/async-trait/blob/0.1.35/src/receiver.rs +// Based on: +// - https://github.com/dtolnay/async-trait/blob/0.1.35/src/receiver.rs +// - https://github.com/dtolnay/async-trait/commit/6029cbf375c562ca98fa5748e9d950a8ff93b0e7 pub(crate) struct ReplaceReceiver<'a>(pub(crate) &'a TypePath); @@ -259,7 +271,7 @@ impl ReplaceReceiver<'_> { match iter.peek() { Some(TokenTree::Punct(p)) if p.as_char() == ':' => { let span = ident.span(); - out.extend(quote_spanned!(span=> <#self_ty>)) + out.extend(quote_spanned!(span=> <#self_ty>)); } _ => out.extend(quote!(#self_ty)), } @@ -313,7 +325,6 @@ impl VisitMut for ReplaceReceiver<'_> { // `Self::method` -> `<Receiver>::method` fn visit_expr_path_mut(&mut self, expr: &mut ExprPath) { if expr.qself.is_none() { - prepend_underscore_to_self(&mut expr.path.segments[0].ident); self.self_to_qself(&mut expr.qself, &mut expr.path); } visit_mut::visit_expr_path_mut(self, expr); @@ -341,11 +352,21 @@ impl VisitMut for ReplaceReceiver<'_> { visit_mut::visit_pat_tuple_struct_mut(self, pat); } + fn visit_path_mut(&mut self, path: &mut Path) { + if path.segments.len() == 1 { + // Replace `self`, but not `self::function`. + prepend_underscore_to_self(&mut path.segments[0].ident); + } + for segment in &mut path.segments { + self.visit_path_arguments_mut(&mut segment.arguments); + } + } + fn visit_item_mut(&mut self, item: &mut Item) { match item { // Visit `macro_rules!` because locally defined macros can refer to `self`. Item::Macro(item) if item.mac.path.is_ident("macro_rules") => { - self.visit_macro_mut(&mut item.mac) + self.visit_macro_mut(&mut item.mac); } // Otherwise, do not recurse into nested items. _ => {} |