1 //! Extension traits to provide parsing methods on foreign types.
2 //!
3 //! *This module is available if Syn is built with the `"parsing"` feature.*
4 
5 use proc_macro2::Ident;
6 
7 use crate::parse::{ParseStream, Result};
8 
9 use crate::buffer::Cursor;
10 use crate::parse::Peek;
11 use crate::sealed::lookahead;
12 use crate::token::CustomToken;
13 
14 /// Additional methods for `Ident` not provided by proc-macro2 or libproc_macro.
15 ///
16 /// This trait is sealed and cannot be implemented for types outside of Syn. It
17 /// is implemented only for `proc_macro2::Ident`.
18 ///
19 /// *This trait is available if Syn is built with the `"parsing"` feature.*
20 pub trait IdentExt: Sized + private::Sealed {
21     /// Parses any identifier including keywords.
22     ///
23     /// This is useful when parsing macro input which allows Rust keywords as
24     /// identifiers.
25     ///
26     /// # Example
27     ///
28     /// ```
29     /// use syn::{Error, Ident, Result, Token};
30     /// use syn::ext::IdentExt;
31     /// use syn::parse::ParseStream;
32     ///
33     /// mod kw {
34     ///     syn::custom_keyword!(name);
35     /// }
36     ///
37     /// // Parses input that looks like `name = NAME` where `NAME` can be
38     /// // any identifier.
39     /// //
40     /// // Examples:
41     /// //
42     /// //     name = anything
43     /// //     name = impl
44     /// fn parse_dsl(input: ParseStream) -> Result<Ident> {
45     ///     input.parse::<kw::name>()?;
46     ///     input.parse::<Token![=]>()?;
47     ///     let name = input.call(Ident::parse_any)?;
48     ///     Ok(name)
49     /// }
50     /// ```
parse_any(input: ParseStream) -> Result<Self>51     fn parse_any(input: ParseStream) -> Result<Self>;
52 
53     /// Peeks any identifier including keywords. Usage:
54     /// `input.peek(Ident::peek_any)`
55     ///
56     /// This is different from `input.peek(Ident)` which only returns true in
57     /// the case of an ident which is not a Rust keyword.
58     #[allow(non_upper_case_globals)]
59     const peek_any: private::PeekFn = private::PeekFn;
60 
61     /// Strips the raw marker `r#`, if any, from the beginning of an ident.
62     ///
63     ///   - unraw(`x`) = `x`
64     ///   - unraw(`move`) = `move`
65     ///   - unraw(`r#move`) = `move`
66     ///
67     /// # Example
68     ///
69     /// In the case of interop with other languages like Python that have a
70     /// different set of keywords than Rust, we might come across macro input
71     /// that involves raw identifiers to refer to ordinary variables in the
72     /// other language with a name that happens to be a Rust keyword.
73     ///
74     /// The function below appends an identifier from the caller's input onto a
75     /// fixed prefix. Without using `unraw()`, this would tend to produce
76     /// invalid identifiers like `__pyo3_get_r#move`.
77     ///
78     /// ```
79     /// use proc_macro2::Span;
80     /// use syn::Ident;
81     /// use syn::ext::IdentExt;
82     ///
83     /// fn ident_for_getter(variable: &Ident) -> Ident {
84     ///     let getter = format!("__pyo3_get_{}", variable.unraw());
85     ///     Ident::new(&getter, Span::call_site())
86     /// }
87     /// ```
unraw(&self) -> Ident88     fn unraw(&self) -> Ident;
89 }
90 
91 impl IdentExt for Ident {
parse_any(input: ParseStream) -> Result<Self>92     fn parse_any(input: ParseStream) -> Result<Self> {
93         input.step(|cursor| match cursor.ident() {
94             Some((ident, rest)) => Ok((ident, rest)),
95             None => Err(cursor.error("expected ident")),
96         })
97     }
98 
unraw(&self) -> Ident99     fn unraw(&self) -> Ident {
100         let string = self.to_string();
101         if string.starts_with("r#") {
102             Ident::new(&string[2..], self.span())
103         } else {
104             self.clone()
105         }
106     }
107 }
108 
109 impl Peek for private::PeekFn {
110     type Token = private::IdentAny;
111 }
112 
113 impl CustomToken for private::IdentAny {
peek(cursor: Cursor) -> bool114     fn peek(cursor: Cursor) -> bool {
115         cursor.ident().is_some()
116     }
117 
display() -> &'static str118     fn display() -> &'static str {
119         "identifier"
120     }
121 }
122 
123 impl lookahead::Sealed for private::PeekFn {}
124 
125 mod private {
126     use proc_macro2::Ident;
127 
128     pub trait Sealed {}
129 
130     impl Sealed for Ident {}
131 
132     #[derive(Copy, Clone)]
133     pub struct PeekFn;
134     pub struct IdentAny;
135 }
136