Skip to main content

syn/
ext.rs

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