Repository: dtolnay/syn
Pull request #1375 (2023-02-07 05:57:40)

Parse typed self as FnArg::Receiver


Patch:
diff --git a/src/gen/clone.rs b/src/gen/clone.rs
index 4f0c8418..3e182d7b 100644
--- a/src/gen/clone.rs
+++ b/src/gen/clone.rs
@@ -1692,6 +1692,8 @@ impl Clone for Receiver {
             reference: self.reference.clone(),
             mutability: self.mutability.clone(),
             self_token: self.self_token.clone(),
+            colon_token: self.colon_token.clone(),
+            ty: self.ty.clone(),
         }
     }
 }
diff --git a/src/gen/debug.rs b/src/gen/debug.rs
index 408a1168..fa9123ba 100644
--- a/src/gen/debug.rs
+++ b/src/gen/debug.rs
@@ -2387,6 +2387,8 @@ impl Debug for Receiver {
         formatter.field("reference", &self.reference);
         formatter.field("mutability", &self.mutability);
         formatter.field("self_token", &self.self_token);
+        formatter.field("colon_token", &self.colon_token);
+        formatter.field("ty", &self.ty);
         formatter.finish()
     }
 }
diff --git a/src/gen/eq.rs b/src/gen/eq.rs
index 9149747d..dae62fa5 100644
--- a/src/gen/eq.rs
+++ b/src/gen/eq.rs
@@ -1623,6 +1623,7 @@ impl PartialEq for Receiver {
     fn eq(&self, other: &Self) -> bool {
         self.attrs == other.attrs && self.reference == other.reference
             && self.mutability == other.mutability
+            && self.colon_token == other.colon_token && self.ty == other.ty
     }
 }
 #[cfg(any(feature = "derive", feature = "full"))]
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index 0b9958d8..ce291580 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -2703,6 +2703,8 @@ where
             )),
         mutability: (node.mutability).map(|it| Token![mut](tokens_helper(f, &it.span))),
         self_token: Token![self](tokens_helper(f, &node.self_token.span)),
+        colon_token: (node.colon_token).map(|it| Token![:](tokens_helper(f, &it.spans))),
+        ty: Box::new(f.fold_type(*node.ty)),
     }
 }
 #[cfg(any(feature = "derive", feature = "full"))]
diff --git a/src/gen/hash.rs b/src/gen/hash.rs
index 1f545615..6d79eb13 100644
--- a/src/gen/hash.rs
+++ b/src/gen/hash.rs
@@ -2151,6 +2151,8 @@ impl Hash for Receiver {
         self.attrs.hash(state);
         self.reference.hash(state);
         self.mutability.hash(state);
+        self.colon_token.hash(state);
+        self.ty.hash(state);
     }
 }
 #[cfg(any(feature = "derive", feature = "full"))]
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index 5a0d0290..177d4b91 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -3067,6 +3067,10 @@ where
         tokens_helper(v, &it.span);
     }
     tokens_helper(v, &node.self_token.span);
+    if let Some(it) = &node.colon_token {
+        tokens_helper(v, &it.spans);
+    }
+    v.visit_type(&*node.ty);
 }
 #[cfg(any(feature = "derive", feature = "full"))]
 pub fn visit_return_type<'ast, V>(v: &mut V, node: &'ast ReturnType)
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index e5cb6159..0f2aa0eb 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -3070,6 +3070,10 @@ where
         tokens_helper(v, &mut it.span);
     }
     tokens_helper(v, &mut node.self_token.span);
+    if let Some(it) = &mut node.colon_token {
+        tokens_helper(v, &mut it.spans);
+    }
+    v.visit_type_mut(&mut *node.ty);
 }
 #[cfg(any(feature = "derive", feature = "full"))]
 pub fn visit_return_type_mut<V>(v: &mut V, node: &mut ReturnType)
diff --git a/src/item.rs b/src/item.rs
index b9f64031..f5385655 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -793,18 +793,11 @@ ast_struct! {
 
 impl Signature {
     /// A method's `self` receiver, such as `&self` or `self: Box<Self>`.
-    pub fn receiver(&self) -> Option<&FnArg> {
+    pub fn receiver(&self) -> Option<&Receiver> {
         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
-            }
+            FnArg::Receiver(receiver) => Some(receiver),
+            FnArg::Typed(_) => None,
         }
     }
 }
@@ -813,11 +806,7 @@ ast_enum_of_structs! {
     /// An argument in a function signature: the `n: usize` in `fn f(n: usize)`.
     #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
     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`.
+        /// The `self` argument of an associated method.
         Receiver(Receiver),
 
         /// A function argument accepted by pattern and type.
@@ -826,17 +815,21 @@ ast_enum_of_structs! {
 }
 
 ast_struct! {
-    /// The `self` argument of an associated method, whether taken by value
-    /// or by reference.
+    /// The `self` argument of an associated method.
     ///
-    /// Note that `self` receivers with a specified type, such as `self:
-    /// Box<Self>`, are parsed as a `FnArg::Typed`.
+    /// If `colon_token` is present, the receiver is written with an explicit
+    /// type such as `self: Box<Self>`. If `colon_token` is absent, the receiver
+    /// is written in shorthand such as `self` or `&self` or `&mut self`. In the
+    /// shorthand case, the type in `ty` is reconstructed as one of `Self`,
+    /// `&Self`, or `&mut Self`.
     #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))]
     pub struct Receiver {
         pub attrs: Vec<Attribute>,
         pub reference: Option<(Token![&], Option<Lifetime>)>,
         pub mutability: Option<Token![mut]>,
         pub self_token: Token![self],
+        pub colon_token: Option<Token![:]>,
+        pub ty: Box<Type>,
     }
 }
 
@@ -1491,11 +1484,9 @@ pub(crate) mod parsing {
     ) -> Result<FnArgOrVariadic> {
         let ahead = input.fork();
         if let Ok(mut receiver) = ahead.parse::<Receiver>() {
-            if !ahead.peek(Token![:]) {
-                input.advance_to(&ahead);
-                receiver.attrs = attrs;
-                return Ok(FnArgOrVariadic::FnArg(FnArg::Receiver(receiver)));
-            }
+            input.advance_to(&ahead);
+            receiver.attrs = attrs;
+            return Ok(FnArgOrVariadic::FnArg(FnArg::Receiver(receiver)));
         }
 
         // Hack to parse pre-2018 syntax in
@@ -1539,17 +1530,44 @@ pub(crate) mod parsing {
     #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
     impl Parse for Receiver {
         fn parse(input: ParseStream) -> Result<Self> {
+            let reference = if input.peek(Token![&]) {
+                let ampersand: Token![&] = input.parse()?;
+                let lifetime: Option<Lifetime> = input.parse()?;
+                Some((ampersand, lifetime))
+            } else {
+                None
+            };
+            let mutability: Option<Token![mut]> = input.parse()?;
+            let self_token: Token![self] = input.parse()?;
+            let colon_token: Option<Token![:]> = if reference.is_some() {
+                None
+            } else {
+                input.parse()?
+            };
+            let ty: Type = if colon_token.is_some() {
+                input.parse()?
+            } else {
+                let mut ty = Type::Path(TypePath {
+                    qself: None,
+                    path: Path::from(Ident::new("Self", self_token.span)),
+                });
+                if let Some((ampersand, lifetime)) = reference.as_ref() {
+                    ty = Type::Reference(TypeReference {
+                        and_token: Token![&](ampersand.span),
+                        lifetime: lifetime.clone(),
+                        mutability: mutability.as_ref().map(|m| Token![mut](m.span)),
+                        elem: Box::new(ty),
+                    });
+                }
+                ty
+            };
             Ok(Receiver {
                 attrs: Vec::new(),
-                reference: {
-                    if input.peek(Token![&]) {
-                        Some((input.parse()?, input.parse()?))
-                    } else {
-                        None
-                    }
-                },
-                mutability: input.parse()?,
-                self_token: input.parse()?,
+                reference,
+                mutability,
+                self_token,
+                colon_token,
+                ty: Box::new(ty),
             })
         }
     }
@@ -3225,6 +3243,26 @@ mod printing {
             }
             self.mutability.to_tokens(tokens);
             self.self_token.to_tokens(tokens);
+            if let Some(colon_token) = &self.colon_token {
+                colon_token.to_tokens(tokens);
+                self.ty.to_tokens(tokens);
+            } else {
+                let consistent = match (&self.reference, &self.mutability, &*self.ty) {
+                    (Some(_), mutability, Type::Reference(ty)) => {
+                        mutability.is_some() == ty.mutability.is_some()
+                            && match &*ty.elem {
+                                Type::Path(ty) => ty.qself.is_none() && ty.path.is_ident("Self"),
+                                _ => false,
+                            }
+                    }
+                    (None, _, Type::Path(ty)) => ty.qself.is_none() && ty.path.is_ident("Self"),
+                    _ => false,
+                };
+                if !consistent {
+                    <Token![:]>::default().to_tokens(tokens);
+                    self.ty.to_tokens(tokens);
+                }
+            }
         }
     }
 
diff --git a/syn.json b/syn.json
index ef37ba65..787a8b20 100644
--- a/syn.json
+++ b/syn.json
@@ -4272,6 +4272,16 @@
         },
         "self_token": {
           "token": "SelfValue"
+        },
+        "colon_token": {
+          "option": {
+            "token": "Colon"
+          }
+        },
+        "ty": {
+          "box": {
+            "syn": "Type"
+          }
         }
       }
     },


Test patch:
diff --git a/tests/debug/gen.rs b/tests/debug/gen.rs
index eee1e600..4d4b4f50 100644
--- a/tests/debug/gen.rs
+++ b/tests/debug/gen.rs
@@ -3632,6 +3632,10 @@ impl Debug for Lite<syn::Receiver> {
         if self.value.mutability.is_some() {
             formatter.field("mutability", &Present);
         }
+        if self.value.colon_token.is_some() {
+            formatter.field("colon_token", &Present);
+        }
+        formatter.field("ty", Lite(&self.value.ty));
         formatter.finish()
     }
 }
diff --git a/tests/test_receiver.rs b/tests/test_receiver.rs
index 0110e084..8decb555 100644
--- a/tests/test_receiver.rs
+++ b/tests/test_receiver.rs
@@ -11,10 +11,8 @@ fn test_by_value() {
         fn by_value(self: Self);
     };
     snapshot!(&sig.inputs[0], @r###"
-    FnArg::Typed(PatType {
-        pat: Pat::Ident {
-            ident: "self",
-        },
+    FnArg::Receiver(Receiver {
+        colon_token: Some,
         ty: Type::Path {
             path: Path {
                 segments: [
@@ -34,11 +32,9 @@ fn test_by_mut_value() {
         fn by_mut(mut self: Self);
     };
     snapshot!(&sig.inputs[0], @r###"
-    FnArg::Typed(PatType {
-        pat: Pat::Ident {
-            mutability: Some,
-            ident: "self",
-        },
+    FnArg::Receiver(Receiver {
+        mutability: Some,
+        colon_token: Some,
         ty: Type::Path {
             path: Path {
                 segments: [
@@ -58,10 +54,8 @@ fn test_by_ref() {
         fn by_ref(self: &Self);
     };
     snapshot!(&sig.inputs[0], @r###"
-    FnArg::Typed(PatType {
-        pat: Pat::Ident {
-            ident: "self",
-        },
+    FnArg::Receiver(Receiver {
+        colon_token: Some,
         ty: Type::Reference {
             elem: Type::Path {
                 path: Path {
@@ -83,10 +77,8 @@ fn test_by_box() {
         fn by_box(self: Box<Self>);
     };
     snapshot!(&sig.inputs[0], @r###"
-    FnArg::Typed(PatType {
-        pat: Pat::Ident {
-            ident: "self",
-        },
+    FnArg::Receiver(Receiver {
+        colon_token: Some,
         ty: Type::Path {
             path: Path {
                 segments: [
@@ -119,10 +111,8 @@ fn test_by_pin() {
         fn by_pin(self: Pin<Self>);
     };
     snapshot!(&sig.inputs[0], @r###"
-    FnArg::Typed(PatType {
-        pat: Pat::Ident {
-            ident: "self",
-        },
+    FnArg::Receiver(Receiver {
+        colon_token: Some,
         ty: Type::Path {
             path: Path {
                 segments: [
@@ -155,10 +145,8 @@ fn test_explicit_type() {
         fn explicit_type(self: Pin<MyType>);
     };
     snapshot!(&sig.inputs[0], @r###"
-    FnArg::Typed(PatType {
-        pat: Pat::Ident {
-            ident: "self",
-        },
+    FnArg::Receiver(Receiver {
+        colon_token: Some,
         ty: Type::Path {
             path: Path {
                 segments: [
@@ -190,7 +178,19 @@ fn test_value_shorthand() {
     let TraitItemFn { sig, .. } = parse_quote! {
         fn value_shorthand(self);
     };
-    snapshot!(&sig.inputs[0], @"FnArg::Receiver(Receiver)");
+    snapshot!(&sig.inputs[0], @r###"
+    FnArg::Receiver(Receiver {
+        ty: Type::Path {
+            path: Path {
+                segments: [
+                    PathSegment {
+                        ident: "Self",
+                    },
+                ],
+            },
+        },
+    })
+    "###);
 }
 
 #[test]
@@ -201,6 +201,15 @@ fn test_mut_value_shorthand() {
     snapshot!(&sig.inputs[0], @r###"
     FnArg::Receiver(Receiver {
         mutability: Some,
+        ty: Type::Path {
+            path: Path {
+                segments: [
+                    PathSegment {
+                        ident: "Self",
+                    },
+                ],
+            },
+        },
     })
     "###);
 }
@@ -213,6 +222,17 @@ fn test_ref_shorthand() {
     snapshot!(&sig.inputs[0], @r###"
     FnArg::Receiver(Receiver {
         reference: Some(None),
+        ty: Type::Reference {
+            elem: Type::Path {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Self",
+                        },
+                    ],
+                },
+            },
+        },
     })
     "###);
 }
@@ -227,6 +247,20 @@ fn test_ref_shorthand_with_lifetime() {
         reference: Some(Some(Lifetime {
             ident: "a",
         })),
+        ty: Type::Reference {
+            lifetime: Some(Lifetime {
+                ident: "a",
+            }),
+            elem: Type::Path {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Self",
+                        },
+                    ],
+                },
+            },
+        },
     })
     "###);
 }
@@ -240,6 +274,18 @@ fn test_ref_mut_shorthand() {
     FnArg::Receiver(Receiver {
         reference: Some(None),
         mutability: Some,
+        ty: Type::Reference {
+            mutability: Some,
+            elem: Type::Path {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Self",
+                        },
+                    ],
+                },
+            },
+        },
     })
     "###);
 }
@@ -255,6 +301,21 @@ fn test_ref_mut_shorthand_with_lifetime() {
             ident: "a",
         })),
         mutability: Some,
+        ty: Type::Reference {
+            lifetime: Some(Lifetime {
+                ident: "a",
+            }),
+            mutability: Some,
+            elem: Type::Path {
+                path: Path {
+                    segments: [
+                        PathSegment {
+                            ident: "Self",
+                        },
+                    ],
+                },
+            },
+        },
     })
     "###);
 }