• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 use crate::buffer::Cursor;
6 use crate::parse::Peek;
7 use crate::parse::{ParseStream, Result};
8 use crate::sealed::lookahead;
9 use crate::token::CustomToken;
10 use 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.*
18 pub 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     /// ```
parse_any(input: ParseStream) -> Result<Self>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     /// ```
unraw(&self) -> Ident86     fn unraw(&self) -> Ident;
87 }
88 
89 impl IdentExt for Ident {
parse_any(input: ParseStream) -> Result<Self>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 
unraw(&self) -> Ident97     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 
107 impl Peek for private::PeekFn {
108     type Token = private::IdentAny;
109 }
110 
111 impl CustomToken for private::IdentAny {
peek(cursor: Cursor) -> bool112     fn peek(cursor: Cursor) -> bool {
113         cursor.ident().is_some()
114     }
115 
display() -> &'static str116     fn display() -> &'static str {
117         "identifier"
118     }
119 }
120 
121 impl lookahead::Sealed for private::PeekFn {}
122 
123 mod 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 {
clone(&self) -> Self135         fn clone(&self) -> Self {
136             *self
137         }
138     }
139 }
140