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}