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