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