syn/
ext.rs

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