blob: 91eec34948892b995f9eb46d74b6e064bfb5d1f2 [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
David Tolnayb8a68e42019-04-22 14:01:56 -07007use buffer::Cursor;
8use parse::{ParseStream, Peek, Result};
9use sealed::lookahead;
10use token::CustomToken;
David Tolnay94d304f2018-08-30 23:43:53 -070011
David Tolnayc15a0002019-04-22 12:33:39 -070012/// Additional methods for `Ident` not provided by proc-macro2 or libproc_macro.
David Tolnay94d304f2018-08-30 23:43:53 -070013///
David Tolnayc15a0002019-04-22 12:33:39 -070014/// This trait is sealed and cannot be implemented for types outside of Syn. It
15/// is implemented only for `proc_macro2::Ident`.
David Tolnay94d304f2018-08-30 23:43:53 -070016///
17/// *This trait is available if Syn is built with the `"parsing"` feature.*
18pub trait IdentExt: Sized + private::Sealed {
19 /// Parses any identifier including keywords.
20 ///
David Tolnayb8a68e42019-04-22 14:01:56 -070021 /// This is useful when parsing macro input which allows Rust keywords as
David Tolnay94d304f2018-08-30 23:43:53 -070022 /// identifiers.
23 ///
David Tolnayc15a0002019-04-22 12:33:39 -070024 /// # Example
25 ///
David Tolnay95989db2019-01-01 15:05:57 -050026 /// ```edition2018
David Tolnayfd5b1172018-12-31 17:54:36 -050027 /// use syn::{Error, Ident, Result, Token};
David Tolnayac51b362018-08-30 23:59:52 -070028 /// use syn::ext::IdentExt;
David Tolnay67fea042018-11-24 14:50:20 -080029 /// use syn::parse::ParseStream;
David Tolnay94d304f2018-08-30 23:43:53 -070030 ///
David Tolnayb8a68e42019-04-22 14:01:56 -070031 /// mod kw {
32 /// syn::custom_keyword!(name);
33 /// }
34 ///
David Tolnay94d304f2018-08-30 23:43:53 -070035 /// // Parses input that looks like `name = NAME` where `NAME` can be
36 /// // any identifier.
37 /// //
38 /// // Examples:
39 /// //
40 /// // name = anything
41 /// // name = impl
David Tolnayac51b362018-08-30 23:59:52 -070042 /// fn parse_dsl(input: ParseStream) -> Result<Ident> {
David Tolnayb8a68e42019-04-22 14:01:56 -070043 /// input.parse::<kw::name>()?;
David Tolnayac51b362018-08-30 23:59:52 -070044 /// input.parse::<Token![=]>()?;
45 /// let name = input.call(Ident::parse_any)?;
46 /// Ok(name)
47 /// }
David Tolnay94d304f2018-08-30 23:43:53 -070048 /// ```
49 fn parse_any(input: ParseStream) -> Result<Self>;
David Tolnayc15a0002019-04-22 12:33:39 -070050
David Tolnayb8a68e42019-04-22 14:01:56 -070051 /// 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
David Tolnayc15a0002019-04-22 12:33:39 -070059 /// 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 /// ```edition2018
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;
David Tolnay94d304f2018-08-30 23:43:53 -070087}
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 }
David Tolnayc15a0002019-04-22 12:33:39 -070096
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 }
David Tolnay94d304f2018-08-30 23:43:53 -0700105}
106
David Tolnayb8a68e42019-04-22 14:01:56 -0700107impl 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
David Tolnay94d304f2018-08-30 23:43:53 -0700123mod private {
124 use proc_macro2::Ident;
125
126 pub trait Sealed {}
127
128 impl Sealed for Ident {}
David Tolnayb8a68e42019-04-22 14:01:56 -0700129
130 #[derive(Copy, Clone)]
131 pub struct PeekFn;
132 pub struct IdentAny;
David Tolnay94d304f2018-08-30 23:43:53 -0700133}