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}