blob: 1e717c94ce91739a58e8aac39a88528c7c2d4408 [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
David Tolnay98942562017-12-26 21:24:35 -05009use proc_macro2::Span;
Alex Crichtonccbb45d2017-05-23 10:58:24 -070010
David Tolnay570695e2017-06-03 16:15:13 -070011/// A word of Rust code, such as a keyword or variable name.
12///
13/// An identifier consists of at least one Unicode code point, the first of
14/// which has the XID_Start property and the rest of which have the XID_Continue
15/// property. An underscore may be used as the first character as long as it is
16/// not the only character.
17///
18/// - The empty string is not an identifier. Use `Option<Ident>`.
19/// - An underscore by itself is not an identifier. Use
David Tolnayf8db7ba2017-11-11 22:52:16 -080020/// `Token![_]` instead.
David Tolnay570695e2017-06-03 16:15:13 -070021/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
22///
23/// An identifier constructed with `Ident::new` is permitted to be a Rust
Mikko Rantanena87ac032017-10-30 08:19:00 +020024/// keyword, though parsing input with [`parse`], [`parse_str`] or
25/// [`parse_tokens`] rejects Rust keywords.
26///
27/// [`parse`]: fn.parse.html
28/// [`parse_str`]: fn.parse_str.html
29/// [`parse_tokens`]: fn.parse_tokens.html
30///
31/// # Examples
32///
33/// A new ident can be created from a string using the `Ident::from` function.
34///
35/// ```rust
36/// extern crate syn;
37/// use syn::Ident;
38/// #
39/// # fn main() {
40///
41/// let ident = Ident::from("another_identifier");
42///
43/// # }
44/// ```
45///
46/// When the ident is used in Macros 1.1 output, it needs to be turned into
47/// a token stream. This is easy to do using the `quote!` macro from the `quote`
48/// crate.
49///
50/// ```rust
51/// # #[macro_use]
52/// # extern crate quote;
53/// # extern crate syn;
54/// # use syn::Ident;
55/// # fn main() {
56/// # let ident = Ident::from("another_identifier");
57/// #
58/// // Create tokens using the ident.
59/// let expanded = quote! { let #ident = 10; };
60///
61/// // Derive a new ident from the existing one.
62/// let temp_ident = Ident::from(format!("new_{}", ident));
63/// let expanded = quote! { let $temp_ident = 10; };
64///
65/// # }
66/// ```
67///
68/// If `syn` is used to parse existing Rust source code, it is often useful to
69/// convert the `Ident` to a more generic string data type at some point. The
70/// methods `as_ref()` and `to_string()` achieve this.
David Tolnay51382052017-12-27 13:46:21 -050071///
Mikko Rantanena87ac032017-10-30 08:19:00 +020072/// ```rust
73/// # use syn::Ident;
74/// # let ident = Ident::from("another_identifier");
75/// #
76/// // Examine the ident as a &str.
77/// let ident_str = ident.as_ref();
78/// if ident_str.len() > 60 {
79/// println!("Very long identifier: {}", ident_str)
80/// }
81///
82/// // Create a String from the ident.
83/// let ident_string = ident.to_string();
84/// give_away(ident_string);
85///
86/// fn give_away(s: String) { /* ... */ }
87/// ```
David Tolnay570695e2017-06-03 16:15:13 -070088#[derive(Copy, Clone, Debug)]
Alex Crichtonccbb45d2017-05-23 10:58:24 -070089pub struct Ident {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070090 pub sym: Term,
Alex Crichtonccbb45d2017-05-23 10:58:24 -070091 pub span: Span,
92}
David Tolnay42f50292016-09-04 13:54:21 -070093
94impl Ident {
Mikko Rantanena87ac032017-10-30 08:19:00 +020095 /// Creates a new `Ident` from the structured items. This is mainly used
96 /// by the parser to create `Ident`s from existing Rust source code.
97 ///
98 /// Creating new `Ident`s programmatically is easier with `Ident::from`.
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070099 pub fn new(sym: Term, span: Span) -> Self {
David Tolnay570695e2017-06-03 16:15:13 -0700100 let s = sym.as_str();
101
102 if s.is_empty() {
103 panic!("ident is not allowed to be empty; use Option<Ident>");
104 }
105
106 if s.starts_with('\'') {
107 panic!("ident is not allowed to be a lifetime; use syn::Lifetime");
108 }
109
110 if s == "_" {
David Tolnay32954ef2017-12-26 22:43:16 -0500111 panic!("`_` is not a valid ident; use syn::token::Underscore");
David Tolnay570695e2017-06-03 16:15:13 -0700112 }
113
114 fn xid_ok(s: &str) -> bool {
115 let mut chars = s.chars();
116 let first = chars.next().unwrap();
117 if !(UnicodeXID::is_xid_start(first) || first == '_') {
118 return false;
119 }
120 for ch in chars {
121 if !UnicodeXID::is_xid_continue(ch) {
122 return false;
123 }
124 }
125 true
126 }
127
128 fn integer_ok(s: &str) -> bool {
129 s.bytes().all(|digit| digit >= b'0' && digit <= b'9')
130 }
131
132 if !(xid_ok(s) || integer_ok(s)) {
133 panic!("{:?} is not a valid ident", s);
134 }
135
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700136 Ident {
137 sym: sym,
138 span: span,
139 }
David Tolnay42f50292016-09-04 13:54:21 -0700140 }
141}
142
143impl<'a> From<&'a str> for Ident {
144 fn from(s: &str) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700145 Ident::new(Term::intern(s), Span::default())
David Tolnay42f50292016-09-04 13:54:21 -0700146 }
147}
148
David Tolnayf8db7ba2017-11-11 22:52:16 -0800149impl From<Token![self]> for Ident {
150 fn from(tok: Token![self]) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700151 Ident::new(Term::intern("self"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700152 }
153}
154
David Tolnayf8db7ba2017-11-11 22:52:16 -0800155impl From<Token![Self]> for Ident {
156 fn from(tok: Token![Self]) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700157 Ident::new(Term::intern("Self"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700158 }
159}
160
David Tolnayf8db7ba2017-11-11 22:52:16 -0800161impl From<Token![super]> for Ident {
162 fn from(tok: Token![super]) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700163 Ident::new(Term::intern("super"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -0700164 }
165}
166
David Tolnaydfc6df72017-12-25 23:44:08 -0500167impl From<Token![crate]> for Ident {
168 fn from(tok: Token![crate]) -> Self {
169 Ident::new(Term::intern("crate"), tok.0)
170 }
171}
172
David Tolnayf733f7e2016-11-25 10:36:25 -0800173impl<'a> From<Cow<'a, str>> for Ident {
174 fn from(s: Cow<'a, str>) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700175 Ident::new(Term::intern(&s), Span::default())
David Tolnayf733f7e2016-11-25 10:36:25 -0800176 }
177}
178
David Tolnay42f50292016-09-04 13:54:21 -0700179impl From<String> for Ident {
180 fn from(s: String) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700181 Ident::new(Term::intern(&s), Span::default())
David Tolnay42f50292016-09-04 13:54:21 -0700182 }
183}
184
David Tolnay26469072016-09-04 13:59:48 -0700185impl AsRef<str> for Ident {
186 fn as_ref(&self) -> &str {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700187 self.sym.as_str()
David Tolnay42f50292016-09-04 13:54:21 -0700188 }
189}
190
191impl Display for Ident {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700192 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
193 self.sym.as_str().fmt(formatter)
194 }
195}
196
David Tolnaydaaf7742016-10-03 11:11:43 -0700197impl<T: ?Sized> PartialEq<T> for Ident
David Tolnay51382052017-12-27 13:46:21 -0500198where
199 T: AsRef<str>,
David Tolnaydaaf7742016-10-03 11:11:43 -0700200{
David Tolnay55337722016-09-11 12:58:56 -0700201 fn eq(&self, other: &T) -> bool {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700202 self.as_ref() == other.as_ref()
203 }
204}
205
206impl Eq for Ident {}
207
208impl PartialOrd for Ident {
209 fn partial_cmp(&self, other: &Ident) -> Option<Ordering> {
210 Some(self.cmp(other))
211 }
212}
213
214impl Ord for Ident {
215 fn cmp(&self, other: &Ident) -> Ordering {
216 self.as_ref().cmp(other.as_ref())
217 }
218}
219
220impl Hash for Ident {
221 fn hash<H: Hasher>(&self, h: &mut H) {
222 self.as_ref().hash(h)
David Tolnay55337722016-09-11 12:58:56 -0700223 }
David Tolnayb79ee962016-09-04 09:39:20 -0700224}
225
David Tolnay86eca752016-09-04 11:26:41 -0700226#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700227pub mod parsing {
228 use super::*;
David Tolnayc5ab8c62017-12-26 16:43:39 -0500229 use synom::Synom;
230 use cursor::Cursor;
David Tolnay51382052017-12-27 13:46:21 -0500231 use {parse_error, PResult};
David Tolnay9d8f1972016-09-04 11:58:48 -0700232
Alex Crichton954046c2017-05-30 21:49:42 -0700233 impl Synom for Ident {
Michael Layzell92639a52017-06-01 00:07:44 -0400234 fn parse(input: Cursor) -> PResult<Self> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400235 let (rest, span, sym) = match input.word() {
236 Some(word) => word,
Michael Layzell92639a52017-06-01 00:07:44 -0400237 _ => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700238 };
Michael Layzell589a8f42017-06-02 19:47:01 -0400239 if sym.as_str().starts_with('\'') {
Michael Layzell92639a52017-06-01 00:07:44 -0400240 return parse_error();
Alex Crichton954046c2017-05-30 21:49:42 -0700241 }
Michael Layzell589a8f42017-06-02 19:47:01 -0400242 match sym.as_str() {
Michael Layzell416724e2017-05-24 21:12:34 -0400243 // From https://doc.rust-lang.org/grammar.html#keywords
David Tolnay51382052017-12-27 13:46:21 -0500244 "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const"
245 | "continue" | "crate" | "do" | "else" | "enum" | "extern" | "false" | "final"
246 | "fn" | "for" | "if" | "impl" | "in" | "let" | "loop" | "macro" | "match"
247 | "mod" | "move" | "mut" | "offsetof" | "override" | "priv" | "proc" | "pub"
248 | "pure" | "ref" | "return" | "Self" | "self" | "sizeof" | "static" | "struct"
249 | "super" | "trait" | "true" | "type" | "typeof" | "unsafe" | "unsized" | "use"
250 | "virtual" | "where" | "while" | "yield" => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700251 _ => {}
Michael Layzell416724e2017-05-24 21:12:34 -0400252 }
Alex Crichton954046c2017-05-30 21:49:42 -0700253
David Tolnay51382052017-12-27 13:46:21 -0500254 Ok((
255 rest,
256 Ident {
257 span: span,
258 sym: sym,
259 },
260 ))
Alex Crichton954046c2017-05-30 21:49:42 -0700261 }
262
263 fn description() -> Option<&'static str> {
264 Some("identifier")
David Tolnay05f462f2016-10-24 22:19:42 -0700265 }
266 }
David Tolnayb79ee962016-09-04 09:39:20 -0700267}
David Tolnay26469072016-09-04 13:59:48 -0700268
269#[cfg(feature = "printing")]
270mod printing {
271 use super::*;
David Tolnay51382052017-12-27 13:46:21 -0500272 use quote::{ToTokens, Tokens};
273 use proc_macro2::{TokenNode, TokenTree};
David Tolnay26469072016-09-04 13:59:48 -0700274
275 impl ToTokens for Ident {
276 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700277 tokens.append(TokenTree {
David Tolnay98942562017-12-26 21:24:35 -0500278 span: self.span,
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700279 kind: TokenNode::Term(self.sym),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700280 })
David Tolnay26469072016-09-04 13:59:48 -0700281 }
282 }
283}