blob: fa55f2f1b7803e667641af6432951fea74b6d8bf [file] [log] [blame]
David Tolnayf733f7e2016-11-25 10:36:25 -08001use std::borrow::Cow;
Alex Crichtonccbb45d2017-05-23 10:58:24 -07002use std::cmp::Ordering;
David Tolnay42f50292016-09-04 13:54:21 -07003use std::fmt::{self, Display};
Alex Crichtonccbb45d2017-05-23 10:58:24 -07004use std::hash::{Hash, Hasher};
David Tolnay42f50292016-09-04 13:54:21 -07005
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -07006use proc_macro2::Term;
David Tolnay570695e2017-06-03 16:15:13 -07007use unicode_xid::UnicodeXID;
Alex Crichtonccbb45d2017-05-23 10:58:24 -07008
9use Span;
Alex Crichton954046c2017-05-30 21:49:42 -070010use tokens;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070011
David Tolnay570695e2017-06-03 16:15:13 -070012/// A word of Rust code, such as a keyword or variable name.
13///
14/// An identifier consists of at least one Unicode code point, the first of
15/// which has the XID_Start property and the rest of which have the XID_Continue
16/// property. An underscore may be used as the first character as long as it is
17/// not the only character.
18///
19/// - The empty string is not an identifier. Use `Option<Ident>`.
20/// - An underscore by itself is not an identifier. Use
21/// `syn::tokens::Underscore` instead.
22/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
23///
24/// An identifier constructed with `Ident::new` is permitted to be a Rust
Mikko Rantanena87ac032017-10-30 08:19:00 +020025/// keyword, though parsing input with [`parse`], [`parse_str`] or
26/// [`parse_tokens`] rejects Rust keywords.
27///
28/// [`parse`]: fn.parse.html
29/// [`parse_str`]: fn.parse_str.html
30/// [`parse_tokens`]: fn.parse_tokens.html
31///
32/// # Examples
33///
34/// A new ident can be created from a string using the `Ident::from` function.
35///
36/// ```rust
37/// extern crate syn;
38/// use syn::Ident;
39/// #
40/// # fn main() {
41///
42/// let ident = Ident::from("another_identifier");
43///
44/// # }
45/// ```
46///
47/// When the ident is used in Macros 1.1 output, it needs to be turned into
48/// a token stream. This is easy to do using the `quote!` macro from the `quote`
49/// crate.
50///
51/// ```rust
52/// # #[macro_use]
53/// # extern crate quote;
54/// # extern crate syn;
55/// # use syn::Ident;
56/// # fn main() {
57/// # let ident = Ident::from("another_identifier");
58/// #
59/// // Create tokens using the ident.
60/// let expanded = quote! { let #ident = 10; };
61///
62/// // Derive a new ident from the existing one.
63/// let temp_ident = Ident::from(format!("new_{}", ident));
64/// let expanded = quote! { let $temp_ident = 10; };
65///
66/// # }
67/// ```
68///
69/// If `syn` is used to parse existing Rust source code, it is often useful to
70/// convert the `Ident` to a more generic string data type at some point. The
71/// methods `as_ref()` and `to_string()` achieve this.
72///
73/// ```rust
74/// # use syn::Ident;
75/// # let ident = Ident::from("another_identifier");
76/// #
77/// // Examine the ident as a &str.
78/// let ident_str = ident.as_ref();
79/// if ident_str.len() > 60 {
80/// println!("Very long identifier: {}", ident_str)
81/// }
82///
83/// // Create a String from the ident.
84/// let ident_string = ident.to_string();
85/// give_away(ident_string);
86///
87/// fn give_away(s: String) { /* ... */ }
88/// ```
David Tolnay570695e2017-06-03 16:15:13 -070089#[derive(Copy, Clone, Debug)]
Alex Crichtonccbb45d2017-05-23 10:58:24 -070090pub struct Ident {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070091 pub sym: Term,
Alex Crichtonccbb45d2017-05-23 10:58:24 -070092 pub span: Span,
93}
David Tolnay42f50292016-09-04 13:54:21 -070094
95impl Ident {
Mikko Rantanena87ac032017-10-30 08:19:00 +020096
97 /// Creates a new `Ident` from the structured items. This is mainly used
98 /// by the parser to create `Ident`s from existing Rust source code.
99 ///
100 /// Creating new `Ident`s programmatically is easier with `Ident::from`.
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700101 pub fn new(sym: Term, span: Span) -> Self {
David Tolnay570695e2017-06-03 16:15:13 -0700102 let s = sym.as_str();
103
104 if s.is_empty() {
105 panic!("ident is not allowed to be empty; use Option<Ident>");
106 }
107
108 if s.starts_with('\'') {
109 panic!("ident is not allowed to be a lifetime; use syn::Lifetime");
110 }
111
112 if s == "_" {
113 panic!("`_` is not a valid ident; use syn::tokens::Underscore");
114 }
115
116 fn xid_ok(s: &str) -> bool {
117 let mut chars = s.chars();
118 let first = chars.next().unwrap();
119 if !(UnicodeXID::is_xid_start(first) || first == '_') {
120 return false;
121 }
122 for ch in chars {
123 if !UnicodeXID::is_xid_continue(ch) {
124 return false;
125 }
126 }
127 true
128 }
129
130 fn integer_ok(s: &str) -> bool {
131 s.bytes().all(|digit| digit >= b'0' && digit <= b'9')
132 }
133
134 if !(xid_ok(s) || integer_ok(s)) {
135 panic!("{:?} is not a valid ident", s);
136 }
137
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700138 Ident {
139 sym: sym,
140 span: span,
141 }
David Tolnay42f50292016-09-04 13:54:21 -0700142 }
143}
144
145impl<'a> From<&'a str> for Ident {
146 fn from(s: &str) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700147 Ident::new(Term::intern(s), Span::default())
David Tolnay42f50292016-09-04 13:54:21 -0700148 }
149}
150
Alex Crichton954046c2017-05-30 21:49:42 -0700151impl From<tokens::Self_> for Ident {
152 fn from(tok: tokens::Self_) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700153 Ident::new(Term::intern("self"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700154 }
155}
156
157impl From<tokens::CapSelf> for Ident {
158 fn from(tok: tokens::CapSelf) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700159 Ident::new(Term::intern("Self"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700160 }
161}
162
163impl From<tokens::Super> for Ident {
164 fn from(tok: tokens::Super) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700165 Ident::new(Term::intern("super"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700166 }
167}
168
David Tolnayf733f7e2016-11-25 10:36:25 -0800169impl<'a> From<Cow<'a, str>> for Ident {
170 fn from(s: Cow<'a, str>) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700171 Ident::new(Term::intern(&s), Span::default())
David Tolnayf733f7e2016-11-25 10:36:25 -0800172 }
173}
174
David Tolnay42f50292016-09-04 13:54:21 -0700175impl From<String> for Ident {
176 fn from(s: String) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700177 Ident::new(Term::intern(&s), Span::default())
David Tolnay42f50292016-09-04 13:54:21 -0700178 }
179}
180
David Tolnay26469072016-09-04 13:59:48 -0700181impl AsRef<str> for Ident {
182 fn as_ref(&self) -> &str {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700183 self.sym.as_str()
David Tolnay42f50292016-09-04 13:54:21 -0700184 }
185}
186
187impl Display for Ident {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700188 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
189 self.sym.as_str().fmt(formatter)
190 }
191}
192
David Tolnaydaaf7742016-10-03 11:11:43 -0700193impl<T: ?Sized> PartialEq<T> for Ident
194 where T: AsRef<str>
195{
David Tolnay55337722016-09-11 12:58:56 -0700196 fn eq(&self, other: &T) -> bool {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700197 self.as_ref() == other.as_ref()
198 }
199}
200
201impl Eq for Ident {}
202
203impl PartialOrd for Ident {
204 fn partial_cmp(&self, other: &Ident) -> Option<Ordering> {
205 Some(self.cmp(other))
206 }
207}
208
209impl Ord for Ident {
210 fn cmp(&self, other: &Ident) -> Ordering {
211 self.as_ref().cmp(other.as_ref())
212 }
213}
214
215impl Hash for Ident {
216 fn hash<H: Hasher>(&self, h: &mut H) {
217 self.as_ref().hash(h)
David Tolnay55337722016-09-11 12:58:56 -0700218 }
David Tolnayb79ee962016-09-04 09:39:20 -0700219}
220
David Tolnay86eca752016-09-04 11:26:41 -0700221#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700222pub mod parsing {
223 use super::*;
Michael Layzell92639a52017-06-01 00:07:44 -0400224 use synom::{Synom, PResult, Cursor, parse_error};
David Tolnay9d8f1972016-09-04 11:58:48 -0700225
Alex Crichton954046c2017-05-30 21:49:42 -0700226 impl Synom for Ident {
Michael Layzell92639a52017-06-01 00:07:44 -0400227 fn parse(input: Cursor) -> PResult<Self> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400228 let (rest, span, sym) = match input.word() {
229 Some(word) => word,
Michael Layzell92639a52017-06-01 00:07:44 -0400230 _ => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700231 };
Michael Layzell589a8f42017-06-02 19:47:01 -0400232 if sym.as_str().starts_with('\'') {
Michael Layzell92639a52017-06-01 00:07:44 -0400233 return parse_error();
Alex Crichton954046c2017-05-30 21:49:42 -0700234 }
Michael Layzell589a8f42017-06-02 19:47:01 -0400235 match sym.as_str() {
Michael Layzell416724e2017-05-24 21:12:34 -0400236 // From https://doc.rust-lang.org/grammar.html#keywords
237 "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" |
238 "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" |
239 "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" |
240 "mut" | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" |
241 "return" | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" |
242 "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" |
Michael Layzell92639a52017-06-01 00:07:44 -0400243 "while" | "yield" => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700244 _ => {}
Michael Layzell416724e2017-05-24 21:12:34 -0400245 }
Alex Crichton954046c2017-05-30 21:49:42 -0700246
Michael Layzell589a8f42017-06-02 19:47:01 -0400247 Ok((rest, Ident {
248 span: Span(span),
249 sym: sym,
Michael Layzell92639a52017-06-01 00:07:44 -0400250 }))
Alex Crichton954046c2017-05-30 21:49:42 -0700251 }
252
253 fn description() -> Option<&'static str> {
254 Some("identifier")
David Tolnay05f462f2016-10-24 22:19:42 -0700255 }
256 }
David Tolnayb79ee962016-09-04 09:39:20 -0700257}
David Tolnay26469072016-09-04 13:59:48 -0700258
259#[cfg(feature = "printing")]
260mod printing {
261 use super::*;
262 use quote::{Tokens, ToTokens};
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700263 use proc_macro2::{TokenTree, TokenNode};
David Tolnay26469072016-09-04 13:59:48 -0700264
265 impl ToTokens for Ident {
266 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700267 tokens.append(TokenTree {
268 span: self.span.0,
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700269 kind: TokenNode::Term(self.sym),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700270 })
David Tolnay26469072016-09-04 13:59:48 -0700271 }
272 }
273}