syn/
ext.rs

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
5use proc_macro2::Ident;
6
7use crate::parse::{ParseStream, Result};
8
9use crate::buffer::Cursor;
10use crate::parse::Peek;
11use crate::sealed::lookahead;
12use 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.*
20pub 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    /// ```
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    /// ```
88    fn unraw(&self) -> Ident;
89}
90
91impl IdentExt for Ident {
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
99    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
109impl Peek for private::PeekFn {
110    type Token = private::IdentAny;
111}
112
113impl CustomToken for private::IdentAny {
114    fn peek(cursor: Cursor) -> bool {
115        cursor.ident().is_some()
116    }
117
118    fn display() -> &'static str {
119        "identifier"
120    }
121}
122
123impl lookahead::Sealed for private::PeekFn {}
124
125mod 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}