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