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