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