blob: 8d679e7f12781b40b8a92351bc5c13fe9e54811c [file] [log] [blame]
David Tolnayac51b362018-08-30 23:59:52 -07001//! Extension traits to provide parsing methods on foreign types.
David Tolnay94d304f2018-08-30 23:43:53 -07002//!
3//! *This module is available if Syn is built with the `"parsing"` feature.*
4
5use proc_macro2::Ident;
6
7use parse::{ParseStream, Result};
8
David Tolnayc15a0002019-04-22 12:33:39 -07009/// Additional methods for `Ident` not provided by proc-macro2 or libproc_macro.
David Tolnay94d304f2018-08-30 23:43:53 -070010///
David Tolnayc15a0002019-04-22 12:33:39 -070011/// This trait is sealed and cannot be implemented for types outside of Syn. It
12/// is implemented only for `proc_macro2::Ident`.
David Tolnay94d304f2018-08-30 23:43:53 -070013///
14/// *This trait is available if Syn is built with the `"parsing"` feature.*
15pub trait IdentExt: Sized + private::Sealed {
16 /// Parses any identifier including keywords.
17 ///
18 /// This is useful when parsing a DSL which allows Rust keywords as
19 /// identifiers.
20 ///
David Tolnayc15a0002019-04-22 12:33:39 -070021 /// # Example
22 ///
David Tolnay95989db2019-01-01 15:05:57 -050023 /// ```edition2018
David Tolnayfd5b1172018-12-31 17:54:36 -050024 /// use syn::{Error, Ident, Result, Token};
David Tolnayac51b362018-08-30 23:59:52 -070025 /// use syn::ext::IdentExt;
David Tolnay67fea042018-11-24 14:50:20 -080026 /// use syn::parse::ParseStream;
David Tolnay94d304f2018-08-30 23:43:53 -070027 ///
28 /// // Parses input that looks like `name = NAME` where `NAME` can be
29 /// // any identifier.
30 /// //
31 /// // Examples:
32 /// //
33 /// // name = anything
34 /// // name = impl
David Tolnayac51b362018-08-30 23:59:52 -070035 /// fn parse_dsl(input: ParseStream) -> Result<Ident> {
36 /// let name_token: Ident = input.parse()?;
37 /// if name_token != "name" {
38 /// return Err(Error::new(name_token.span(), "expected `name`"));
39 /// }
40 /// input.parse::<Token![=]>()?;
41 /// let name = input.call(Ident::parse_any)?;
42 /// Ok(name)
43 /// }
David Tolnay94d304f2018-08-30 23:43:53 -070044 /// ```
45 fn parse_any(input: ParseStream) -> Result<Self>;
David Tolnayc15a0002019-04-22 12:33:39 -070046
47 /// Strips the raw marker `r#`, if any, from the beginning of an ident.
48 ///
49 /// - unraw(`x`) = `x`
50 /// - unraw(`move`) = `move`
51 /// - unraw(`r#move`) = `move`
52 ///
53 /// # Example
54 ///
55 /// In the case of interop with other languages like Python that have a
56 /// different set of keywords than Rust, we might come across macro input
57 /// that involves raw identifiers to refer to ordinary variables in the
58 /// other language with a name that happens to be a Rust keyword.
59 ///
60 /// The function below appends an identifier from the caller's input onto a
61 /// fixed prefix. Without using `unraw()`, this would tend to produce
62 /// invalid identifiers like `__pyo3_get_r#move`.
63 ///
64 /// ```edition2018
65 /// use proc_macro2::Span;
66 /// use syn::Ident;
67 /// use syn::ext::IdentExt;
68 ///
69 /// fn ident_for_getter(variable: &Ident) -> Ident {
70 /// let getter = format!("__pyo3_get_{}", variable.unraw());
71 /// Ident::new(&getter, Span::call_site())
72 /// }
73 /// ```
74 fn unraw(&self) -> Ident;
David Tolnay94d304f2018-08-30 23:43:53 -070075}
76
77impl IdentExt for Ident {
78 fn parse_any(input: ParseStream) -> Result<Self> {
79 input.step(|cursor| match cursor.ident() {
80 Some((ident, rest)) => Ok((ident, rest)),
81 None => Err(cursor.error("expected ident")),
82 })
83 }
David Tolnayc15a0002019-04-22 12:33:39 -070084
85 fn unraw(&self) -> Ident {
86 let string = self.to_string();
87 if string.starts_with("r#") {
88 Ident::new(&string[2..], self.span())
89 } else {
90 self.clone()
91 }
92 }
David Tolnay94d304f2018-08-30 23:43:53 -070093}
94
95mod private {
96 use proc_macro2::Ident;
97
98 pub trait Sealed {}
99
100 impl Sealed for Ident {}
101}