syn/
ext.rs

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