blob: 84024204b529cb6ef2b0c0d34a34b7867e0686a3 [file] [log] [blame]
David Tolnay55535012018-01-05 16:39:23 -08001// Copyright 2018 Syn Developers
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
David Tolnayf733f7e2016-11-25 10:36:25 -08009use std::borrow::Cow;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070010use std::cmp::Ordering;
David Tolnay42f50292016-09-04 13:54:21 -070011use std::fmt::{self, Display};
Alex Crichtonccbb45d2017-05-23 10:58:24 -070012use std::hash::{Hash, Hasher};
David Tolnay42f50292016-09-04 13:54:21 -070013
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070014use proc_macro2::Term;
David Tolnay570695e2017-06-03 16:15:13 -070015use unicode_xid::UnicodeXID;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070016
David Tolnay98942562017-12-26 21:24:35 -050017use proc_macro2::Span;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070018
David Tolnay9c92c412018-01-23 00:28:30 -080019/// A word of Rust code, which may be a keyword or legal variable name.
David Tolnay570695e2017-06-03 16:15:13 -070020///
21/// An identifier consists of at least one Unicode code point, the first of
22/// which has the XID_Start property and the rest of which have the XID_Continue
23/// property. An underscore may be used as the first character as long as it is
24/// not the only character.
25///
26/// - The empty string is not an identifier. Use `Option<Ident>`.
27/// - An underscore by itself is not an identifier. Use
David Tolnayf8db7ba2017-11-11 22:52:16 -080028/// `Token![_]` instead.
David Tolnay570695e2017-06-03 16:15:13 -070029/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
30///
31/// An identifier constructed with `Ident::new` is permitted to be a Rust
David Tolnay1cf80912017-12-31 18:35:12 -050032/// keyword, though parsing one through its [`Synom`] implementation rejects
33/// Rust keywords.
Mikko Rantanena87ac032017-10-30 08:19:00 +020034///
David Tolnay1cf80912017-12-31 18:35:12 -050035/// [`Synom`]: synom/trait.Synom.html
Mikko Rantanena87ac032017-10-30 08:19:00 +020036///
37/// # Examples
38///
39/// A new ident can be created from a string using the `Ident::from` function.
David Tolnay9c92c412018-01-23 00:28:30 -080040/// Idents produced by `Ident::from` are set to resolve at the procedural macro
41/// *def site* by default. A different span can be provided explicitly by using
42/// `Ident::new`.
Mikko Rantanena87ac032017-10-30 08:19:00 +020043///
44/// ```rust
45/// extern crate syn;
David Tolnay9c92c412018-01-23 00:28:30 -080046/// extern crate proc_macro2;
47///
Mikko Rantanena87ac032017-10-30 08:19:00 +020048/// use syn::Ident;
David Tolnay9c92c412018-01-23 00:28:30 -080049/// use proc_macro2::Span;
Mikko Rantanena87ac032017-10-30 08:19:00 +020050///
David Tolnay9c92c412018-01-23 00:28:30 -080051/// fn main() {
52/// let def_ident = Ident::from("definitely");
53/// let call_ident = Ident::new("calligraphy", Span::call_site());
Mikko Rantanena87ac032017-10-30 08:19:00 +020054///
David Tolnay9c92c412018-01-23 00:28:30 -080055/// println!("{} {}", def_ident, call_ident);
56/// }
Mikko Rantanena87ac032017-10-30 08:19:00 +020057/// ```
58///
David Tolnay9c92c412018-01-23 00:28:30 -080059/// An ident can be interpolated into a token stream using the `quote!` macro.
Mikko Rantanena87ac032017-10-30 08:19:00 +020060///
61/// ```rust
David Tolnay9c92c412018-01-23 00:28:30 -080062/// #[macro_use]
63/// extern crate quote;
Mikko Rantanena87ac032017-10-30 08:19:00 +020064///
David Tolnay9c92c412018-01-23 00:28:30 -080065/// extern crate syn;
66/// use syn::Ident;
Mikko Rantanena87ac032017-10-30 08:19:00 +020067///
David Tolnay9c92c412018-01-23 00:28:30 -080068/// fn main() {
69/// let ident = Ident::from("demo");
70///
71/// // Create a variable binding whose name is this ident.
72/// let expanded = quote! { let #ident = 10; };
73///
74/// // Create a variable binding with a slightly different name.
75/// let temp_ident = Ident::from(format!("new_{}", ident));
76/// let expanded = quote! { let #temp_ident = 10; };
77/// }
Mikko Rantanena87ac032017-10-30 08:19:00 +020078/// ```
79///
David Tolnay9c92c412018-01-23 00:28:30 -080080/// A string representation of the ident is available through the `as_ref()` and
81/// `to_string()` methods.
David Tolnay51382052017-12-27 13:46:21 -050082///
Mikko Rantanena87ac032017-10-30 08:19:00 +020083/// ```rust
84/// # use syn::Ident;
85/// # let ident = Ident::from("another_identifier");
86/// #
87/// // Examine the ident as a &str.
88/// let ident_str = ident.as_ref();
89/// if ident_str.len() > 60 {
90/// println!("Very long identifier: {}", ident_str)
91/// }
92///
93/// // Create a String from the ident.
94/// let ident_string = ident.to_string();
95/// give_away(ident_string);
96///
97/// fn give_away(s: String) { /* ... */ }
98/// ```
David Tolnay570695e2017-06-03 16:15:13 -070099#[derive(Copy, Clone, Debug)]
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700100pub struct Ident {
David Tolnay73c98de2017-12-31 15:56:56 -0500101 term: Term,
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700102 pub span: Span,
103}
David Tolnay42f50292016-09-04 13:54:21 -0700104
105impl Ident {
David Tolnay9c92c412018-01-23 00:28:30 -0800106 /// Creates an ident with the given string representation.
Mikko Rantanena87ac032017-10-30 08:19:00 +0200107 ///
David Tolnay9c92c412018-01-23 00:28:30 -0800108 /// # Panics
109 ///
110 /// Panics if the input string is neither a keyword nor a legal variable
111 /// name.
David Tolnayeb771d72017-12-27 22:11:06 -0500112 pub fn new(s: &str, span: Span) -> Self {
David Tolnay570695e2017-06-03 16:15:13 -0700113 if s.is_empty() {
114 panic!("ident is not allowed to be empty; use Option<Ident>");
115 }
116
117 if s.starts_with('\'') {
118 panic!("ident is not allowed to be a lifetime; use syn::Lifetime");
119 }
120
121 if s == "_" {
David Tolnay32954ef2017-12-26 22:43:16 -0500122 panic!("`_` is not a valid ident; use syn::token::Underscore");
David Tolnay570695e2017-06-03 16:15:13 -0700123 }
124
David Tolnay14982012017-12-29 00:49:51 -0500125 if s.bytes().all(|digit| digit >= b'0' && digit <= b'9') {
126 panic!("ident cannot be a number, use syn::Index instead");
127 }
128
David Tolnay570695e2017-06-03 16:15:13 -0700129 fn xid_ok(s: &str) -> bool {
130 let mut chars = s.chars();
131 let first = chars.next().unwrap();
132 if !(UnicodeXID::is_xid_start(first) || first == '_') {
133 return false;
134 }
135 for ch in chars {
136 if !UnicodeXID::is_xid_continue(ch) {
137 return false;
138 }
139 }
140 true
141 }
142
David Tolnay14982012017-12-29 00:49:51 -0500143 if !xid_ok(s) {
David Tolnay570695e2017-06-03 16:15:13 -0700144 panic!("{:?} is not a valid ident", s);
145 }
146
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700147 Ident {
David Tolnay73c98de2017-12-31 15:56:56 -0500148 term: Term::intern(s),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700149 span: span,
150 }
David Tolnay42f50292016-09-04 13:54:21 -0700151 }
152}
153
154impl<'a> From<&'a str> for Ident {
155 fn from(s: &str) -> Self {
David Tolnay66bb8d52018-01-08 08:22:31 -0800156 Ident::new(s, Span::def_site())
David Tolnay42f50292016-09-04 13:54:21 -0700157 }
158}
159
David Tolnayf8db7ba2017-11-11 22:52:16 -0800160impl From<Token![self]> for Ident {
161 fn from(tok: Token![self]) -> Self {
David Tolnayeb771d72017-12-27 22:11:06 -0500162 Ident::new("self", tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700163 }
164}
165
David Tolnayf8db7ba2017-11-11 22:52:16 -0800166impl From<Token![Self]> for Ident {
167 fn from(tok: Token![Self]) -> Self {
David Tolnayeb771d72017-12-27 22:11:06 -0500168 Ident::new("Self", tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700169 }
170}
171
David Tolnayf8db7ba2017-11-11 22:52:16 -0800172impl From<Token![super]> for Ident {
173 fn from(tok: Token![super]) -> Self {
David Tolnayeb771d72017-12-27 22:11:06 -0500174 Ident::new("super", tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700175 }
176}
177
David Tolnaydfc6df72017-12-25 23:44:08 -0500178impl From<Token![crate]> for Ident {
179 fn from(tok: Token![crate]) -> Self {
David Tolnayeb771d72017-12-27 22:11:06 -0500180 Ident::new("crate", tok.0)
David Tolnaydfc6df72017-12-25 23:44:08 -0500181 }
182}
183
David Tolnayf733f7e2016-11-25 10:36:25 -0800184impl<'a> From<Cow<'a, str>> for Ident {
185 fn from(s: Cow<'a, str>) -> Self {
David Tolnay66bb8d52018-01-08 08:22:31 -0800186 Ident::new(&s, Span::def_site())
David Tolnayf733f7e2016-11-25 10:36:25 -0800187 }
188}
189
David Tolnay42f50292016-09-04 13:54:21 -0700190impl From<String> for Ident {
191 fn from(s: String) -> Self {
David Tolnay66bb8d52018-01-08 08:22:31 -0800192 Ident::new(&s, Span::def_site())
David Tolnay42f50292016-09-04 13:54:21 -0700193 }
194}
195
David Tolnay26469072016-09-04 13:59:48 -0700196impl AsRef<str> for Ident {
197 fn as_ref(&self) -> &str {
David Tolnay73c98de2017-12-31 15:56:56 -0500198 self.term.as_str()
David Tolnay42f50292016-09-04 13:54:21 -0700199 }
200}
201
202impl Display for Ident {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700203 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
David Tolnay73c98de2017-12-31 15:56:56 -0500204 self.term.as_str().fmt(formatter)
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700205 }
206}
207
David Tolnaydaaf7742016-10-03 11:11:43 -0700208impl<T: ?Sized> PartialEq<T> for Ident
David Tolnay51382052017-12-27 13:46:21 -0500209where
210 T: AsRef<str>,
David Tolnaydaaf7742016-10-03 11:11:43 -0700211{
David Tolnay55337722016-09-11 12:58:56 -0700212 fn eq(&self, other: &T) -> bool {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700213 self.as_ref() == other.as_ref()
214 }
215}
216
217impl Eq for Ident {}
218
219impl PartialOrd for Ident {
220 fn partial_cmp(&self, other: &Ident) -> Option<Ordering> {
221 Some(self.cmp(other))
222 }
223}
224
225impl Ord for Ident {
226 fn cmp(&self, other: &Ident) -> Ordering {
227 self.as_ref().cmp(other.as_ref())
228 }
229}
230
231impl Hash for Ident {
232 fn hash<H: Hasher>(&self, h: &mut H) {
David Tolnay85b69a42017-12-27 20:43:10 -0500233 self.as_ref().hash(h);
David Tolnay55337722016-09-11 12:58:56 -0700234 }
David Tolnayb79ee962016-09-04 09:39:20 -0700235}
236
David Tolnay86eca752016-09-04 11:26:41 -0700237#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700238pub mod parsing {
239 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500240 use synom::Synom;
David Tolnaydfc886b2018-01-06 08:03:09 -0800241 use buffer::Cursor;
David Tolnay203557a2017-12-27 23:59:33 -0500242 use parse_error;
243 use synom::PResult;
David Tolnay9d8f1972016-09-04 11:58:48 -0700244
Alex Crichton954046c2017-05-30 21:49:42 -0700245 impl Synom for Ident {
Michael Layzell92639a52017-06-01 00:07:44 -0400246 fn parse(input: Cursor) -> PResult<Self> {
David Tolnay65729482017-12-31 16:14:50 -0500247 let (span, term, rest) = match input.term() {
David Tolnay73c98de2017-12-31 15:56:56 -0500248 Some(term) => term,
Michael Layzell92639a52017-06-01 00:07:44 -0400249 _ => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700250 };
David Tolnay73c98de2017-12-31 15:56:56 -0500251 if term.as_str().starts_with('\'') {
Michael Layzell92639a52017-06-01 00:07:44 -0400252 return parse_error();
Alex Crichton954046c2017-05-30 21:49:42 -0700253 }
David Tolnay73c98de2017-12-31 15:56:56 -0500254 match term.as_str() {
Michael Layzell416724e2017-05-24 21:12:34 -0400255 // From https://doc.rust-lang.org/grammar.html#keywords
David Tolnay51382052017-12-27 13:46:21 -0500256 "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const"
257 | "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final"
258 | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match"
259 | "mod" | "move" | "mut" | "offsetof" | "override" | "priv" | "proc" | "pub"
260 | "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct"
261 | "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use"
262 | "virtual" | "where" | "while" | "yield" => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700263 _ => {}
Michael Layzell416724e2017-05-24 21:12:34 -0400264 }
Alex Crichton954046c2017-05-30 21:49:42 -0700265
David Tolnay51382052017-12-27 13:46:21 -0500266 Ok((
David Tolnay51382052017-12-27 13:46:21 -0500267 Ident {
268 span: span,
David Tolnay73c98de2017-12-31 15:56:56 -0500269 term: term,
David Tolnay51382052017-12-27 13:46:21 -0500270 },
David Tolnayf4aa6b42017-12-31 16:40:33 -0500271 rest,
David Tolnay51382052017-12-27 13:46:21 -0500272 ))
Alex Crichton954046c2017-05-30 21:49:42 -0700273 }
274
275 fn description() -> Option<&'static str> {
276 Some("identifier")
David Tolnay05f462f2016-10-24 22:19:42 -0700277 }
278 }
David Tolnayb79ee962016-09-04 09:39:20 -0700279}
David Tolnay26469072016-09-04 13:59:48 -0700280
281#[cfg(feature = "printing")]
282mod printing {
283 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500284 use quote::{ToTokens, Tokens};
285 use proc_macro2::{TokenNode, TokenTree};
David Tolnay26469072016-09-04 13:59:48 -0700286
287 impl ToTokens for Ident {
288 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700289 tokens.append(TokenTree {
David Tolnay98942562017-12-26 21:24:35 -0500290 span: self.span,
David Tolnay73c98de2017-12-31 15:56:56 -0500291 kind: TokenNode::Term(self.term),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700292 })
David Tolnay26469072016-09-04 13:59:48 -0700293 }
294 }
295}