syn/
ext.rs

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