Repository: dtolnay/syn
Pull request #714 (2019-10-27 15:13:15)

Add a receiver getter to Signature, and clarify parsing behaviour
The current behaviour of receiver parsing in syn is a bit complex, but I don't see a way to simplify it without making some breaking changes. I think we'd want the receiver would be a separate member of `Signature`, like `variadic`, and would support both the shorthand and typed forms. The remaining arguments would then be stored in `Signature` directly as a `Punctuated<PatType, Token![,]>`.


Patch:
diff --git a/.travis.yml b/.travis.yml
index c0db3497..897adb34 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -70,6 +70,8 @@ matrix:
       install:
         - rustup target add "${TARGET}"
   allow_failures:
+    - rust: nightly
+      name: Codegen # temporarily broken: unresolved import `crossbeam_utils::atomic`
     - rust: nightly
       name: Clippy
   fast_finish: true
diff --git a/Cargo.toml b/Cargo.toml
index 15fa46bf..49534146 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "syn"
-version = "1.0.6" # don't forget to update html_root_url and syn.json
+version = "1.0.5" # don't forget to update html_root_url and syn.json
 authors = ["David Tolnay <dtolnay@gmail.com>"]
 license = "MIT OR Apache-2.0"
 description = "Parser for Rust source code"
diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml
index 4c4e6b1b..051e8505 100644
--- a/codegen/Cargo.toml
+++ b/codegen/Cargo.toml
@@ -24,7 +24,3 @@ toml = "0.4.10"
 [workspace]
 # Prefer that `cargo clean` in syn's directory does not require a rebuild of
 # rustfmt in the codegen directory.
-
-[patch.crates-io]
-# work around https://github.com/rust-lang/rust/issues/65424
-crossbeam-utils = { git = "https://github.com/crossbeam-rs/crossbeam" }
diff --git a/src/data.rs b/src/data.rs
index 2554506c..be436798 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -93,24 +93,6 @@ impl Fields {
             Fields::Unnamed(f) => f.unnamed.iter_mut(),
         }
     }
-
-    /// Returns the number of fields.
-    pub fn len(&self) -> usize {
-        match self {
-            Fields::Unit => 0,
-            Fields::Named(f) => f.named.len(),
-            Fields::Unnamed(f) => f.unnamed.len(),
-        }
-    }
-
-    /// Returns `true` if there are zero fields.
-    pub fn is_empty(&self) -> bool {
-        match self {
-            Fields::Unit => true,
-            Fields::Named(f) => f.named.is_empty(),
-            Fields::Unnamed(f) => f.unnamed.is_empty(),
-        }
-    }
 }
 
 impl IntoIterator for Fields {
diff --git a/src/item.rs b/src/item.rs
index 3bb67b05..b7e276e7 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -1,5 +1,5 @@
 use super::*;
-use crate::derive::{Data, DataEnum, DataStruct, DataUnion, DeriveInput};
+use crate::derive::{Data, DeriveInput};
 use crate::punctuated::Punctuated;
 use proc_macro2::TokenStream;
 
@@ -496,53 +496,6 @@ impl From<DeriveInput> for Item {
     }
 }
 
-impl From<ItemStruct> for DeriveInput {
-    fn from(input: ItemStruct) -> DeriveInput {
-        DeriveInput {
-            attrs: input.attrs,
-            vis: input.vis,
-            ident: input.ident,
-            generics: input.generics,
-            data: Data::Struct(DataStruct {
-                struct_token: input.struct_token,
-                fields: input.fields,
-                semi_token: input.semi_token,
-            }),
-        }
-    }
-}
-
-impl From<ItemEnum> for DeriveInput {
-    fn from(input: ItemEnum) -> DeriveInput {
-        DeriveInput {
-            attrs: input.attrs,
-            vis: input.vis,
-            ident: input.ident,
-            generics: input.generics,
-            data: Data::Enum(DataEnum {
-                enum_token: input.enum_token,
-                brace_token: input.brace_token,
-                variants: input.variants,
-            }),
-        }
-    }
-}
-
-impl From<ItemUnion> for DeriveInput {
-    fn from(input: ItemUnion) -> DeriveInput {
-        DeriveInput {
-            attrs: input.attrs,
-            vis: input.vis,
-            ident: input.ident,
-            generics: input.generics,
-            data: Data::Union(DataUnion {
-                union_token: input.union_token,
-                fields: input.fields,
-            }),
-        }
-    }
-}
-
 ast_enum_of_structs! {
     /// A suffix of an import tree in a `use` item: `Type as Renamed` or `*`.
     ///
@@ -1064,6 +1017,24 @@ ast_struct! {
     }
 }
 
+impl Signature {
+    /// A method's `self` receiver, such as `&self` or `self: Box<Self>`.
+    pub fn receiver(&self) -> Option<&FnArg> {
+        let arg = self.inputs.first()?;
+        match arg {
+            FnArg::Receiver(_) => Some(arg),
+            FnArg::Typed(PatType { pat, .. }) => {
+                if let Pat::Ident(PatIdent { ident, .. }) = &**pat {
+                    if ident == "self" {
+                        return Some(arg);
+                    }
+                }
+                None
+            }
+        }
+    }
+}
+
 ast_enum_of_structs! {
     /// An argument in a function signature: the `n: usize` in `fn f(n: usize)`.
     ///
@@ -1071,6 +1042,9 @@ ast_enum_of_structs! {
     pub enum FnArg {
         /// The `self` argument of an associated method, whether taken by value
         /// or by reference.
+        ///
+        /// Note that `self` receivers with a specified type, such as `self:
+        /// Box<Self>`, are parsed as a `FnArg::Typed`.
         Receiver(Receiver),
 
         /// A function argument accepted by pattern and type.
@@ -1082,6 +1056,9 @@ ast_struct! {
     /// The `self` argument of an associated method, whether taken by value
     /// or by reference.
     ///
+    /// Note that `self` receivers with a specified type, such as `self:
+    /// Box<Self>`, are parsed as a `FnArg::Typed`.
+    ///
     /// *This type is available if Syn is built with the `"full"` feature.*
     pub struct Receiver {
         pub attrs: Vec<Attribute>,
diff --git a/src/lib.rs b/src/lib.rs
index 6cc430f8..42da2854 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -242,7 +242,7 @@
 //!   dynamic library libproc_macro from rustc toolchain.
 
 // Syn types in rustdoc of other crates get linked to here.
-#![doc(html_root_url = "https://docs.rs/syn/1.0.6")]
+#![doc(html_root_url = "https://docs.rs/syn/1.0.5")]
 #![deny(clippy::all, clippy::pedantic)]
 // Ignored clippy lints.
 #![allow(
diff --git a/src/macros.rs b/src/macros.rs
index 57091c09..9cac5c15 100644
--- a/src/macros.rs
+++ b/src/macros.rs
@@ -120,9 +120,15 @@ macro_rules! ast_enum_of_structs_impl {
         check_keyword_matches!(pub $pub);
         check_keyword_matches!(enum $enum);
 
-        $($(
-            ast_enum_from_struct!($name::$variant, $member);
-        )*)*
+        $(
+            $(
+                impl From<$member> for $name {
+                    fn from(e: $member) -> $name {
+                        $name::$variant(e)
+                    }
+                }
+            )*
+        )*
 
         #[cfg(feature = "printing")]
         generate_to_tokens! {
@@ -134,19 +140,6 @@ macro_rules! ast_enum_of_structs_impl {
     };
 }
 
-macro_rules! ast_enum_from_struct {
-    // No From<TokenStream> for verbatim variants.
-    ($name:ident::Verbatim, $member:ident) => {};
-
-    ($name:ident::$variant:ident, $member:ident) => {
-        impl From<$member> for $name {
-            fn from(e: $member) -> $name {
-                $name::$variant(e)
-            }
-        }
-    };
-}
-
 #[cfg(feature = "printing")]
 macro_rules! generate_to_tokens {
     (do_not_generate_to_tokens $($foo:tt)*) => ();
diff --git a/syn.json b/syn.json
index 49a7a85e..a732a3c2 100644
--- a/syn.json
+++ b/syn.json
@@ -1,5 +1,5 @@
 {
-  "version": "1.0.6",
+  "version": "1.0.5",
   "types": [
     {
       "ident": "Abi",


Test patch:
diff --git a/tests/test_receiver.rs b/tests/test_receiver.rs
new file mode 100644
index 00000000..168db8f8
--- /dev/null
+++ b/tests/test_receiver.rs
@@ -0,0 +1,109 @@
+mod features;
+
+use syn::{FnArg, Receiver, TraitItemMethod};
+
+#[test]
+fn test_by_value() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn by_value(self: Self););
+    match sig.receiver() {
+        Some(FnArg::Typed(_)) => (),
+        value => panic!("expected FnArg::Typed, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_by_mut_value() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn by_mut(mut self: Self););
+    match sig.receiver() {
+        Some(FnArg::Typed(_)) => (),
+        value => panic!("expected FnArg::Typed, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_by_ref() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn by_ref(self: &Self););
+    match sig.receiver() {
+        Some(FnArg::Typed(_)) => (),
+        value => panic!("expected FnArg::Typed, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_by_box() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn by_box(self: Box<Self>););
+    match sig.receiver() {
+        Some(FnArg::Typed(_)) => (),
+        value => panic!("expected FnArg::Typed, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_by_pin() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn by_pin(self: Pin<Self>););
+    match sig.receiver() {
+        Some(FnArg::Typed(_)) => (),
+        value => panic!("expected FnArg::Typed, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_explicit_type() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn explicit_type(self: Pin<MyType>););
+    match sig.receiver() {
+        Some(FnArg::Typed(_)) => (),
+        value => panic!("expected FnArg::Typed, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_value_shorthand() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn value_shorthand(self););
+    match sig.receiver() {
+        Some(FnArg::Receiver(Receiver {
+            reference: None,
+            mutability: None,
+            ..
+        })) => (),
+        value => panic!("expected FnArg::Receiver without ref/mut, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_mut_value_shorthand() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn mut_value_shorthand(mut self););
+    match sig.receiver() {
+        Some(FnArg::Receiver(Receiver {
+            reference: None,
+            mutability: Some(_),
+            ..
+        })) => (),
+        value => panic!("expected FnArg::Receiver with mut, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_ref_shorthand() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn ref_shorthand(&self););
+    match sig.receiver() {
+        Some(FnArg::Receiver(Receiver {
+            reference: Some(_),
+            mutability: None,
+            ..
+        })) => (),
+        value => panic!("expected FnArg::Receiver with ref, got {:?}", value),
+    }
+}
+
+#[test]
+fn test_ref_mut_shorthand() {
+    let TraitItemMethod { sig, .. } = syn::parse_quote!(fn ref_mut_shorthand(&mut self););
+    match sig.receiver() {
+        Some(FnArg::Receiver(Receiver {
+            reference: Some(_),
+            mutability: Some(_),
+            ..
+        })) => (),
+        value => panic!("expected FnArg::Receiver with ref+mut, got {:?}", value),
+    }
+}