blob: 3f3437a247b0d2a99a0610fd4a4ecb09586e86a7 [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
25/// keyword, though parsing an identifier with `syn!(Ident)` rejects Rust
26/// keywords.
27#[derive(Copy, Clone, Debug)]
Alex Crichtonccbb45d2017-05-23 10:58:24 -070028pub struct Ident {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070029 pub sym: Term,
Alex Crichtonccbb45d2017-05-23 10:58:24 -070030 pub span: Span,
31}
David Tolnay42f50292016-09-04 13:54:21 -070032
33impl Ident {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070034 pub fn new(sym: Term, span: Span) -> Self {
David Tolnay570695e2017-06-03 16:15:13 -070035 let s = sym.as_str();
36
37 if s.is_empty() {
38 panic!("ident is not allowed to be empty; use Option<Ident>");
39 }
40
41 if s.starts_with('\'') {
42 panic!("ident is not allowed to be a lifetime; use syn::Lifetime");
43 }
44
45 if s == "_" {
46 panic!("`_` is not a valid ident; use syn::tokens::Underscore");
47 }
48
49 fn xid_ok(s: &str) -> bool {
50 let mut chars = s.chars();
51 let first = chars.next().unwrap();
52 if !(UnicodeXID::is_xid_start(first) || first == '_') {
53 return false;
54 }
55 for ch in chars {
56 if !UnicodeXID::is_xid_continue(ch) {
57 return false;
58 }
59 }
60 true
61 }
62
63 fn integer_ok(s: &str) -> bool {
64 s.bytes().all(|digit| digit >= b'0' && digit <= b'9')
65 }
66
67 if !(xid_ok(s) || integer_ok(s)) {
68 panic!("{:?} is not a valid ident", s);
69 }
70
Alex Crichtonccbb45d2017-05-23 10:58:24 -070071 Ident {
72 sym: sym,
73 span: span,
74 }
David Tolnay42f50292016-09-04 13:54:21 -070075 }
76}
77
78impl<'a> From<&'a str> for Ident {
79 fn from(s: &str) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070080 Ident::new(Term::intern(s), Span::default())
David Tolnay42f50292016-09-04 13:54:21 -070081 }
82}
83
Alex Crichton954046c2017-05-30 21:49:42 -070084impl From<tokens::Self_> for Ident {
85 fn from(tok: tokens::Self_) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070086 Ident::new(Term::intern("self"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -070087 }
88}
89
90impl From<tokens::CapSelf> for Ident {
91 fn from(tok: tokens::CapSelf) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070092 Ident::new(Term::intern("Self"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -070093 }
94}
95
96impl From<tokens::Super> for Ident {
97 fn from(tok: tokens::Super) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -070098 Ident::new(Term::intern("super"), tok.0)
Alex Crichton954046c2017-05-30 21:49:42 -070099 }
100}
101
David Tolnayf733f7e2016-11-25 10:36:25 -0800102impl<'a> From<Cow<'a, str>> for Ident {
103 fn from(s: Cow<'a, str>) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700104 Ident::new(Term::intern(&s), Span::default())
David Tolnayf733f7e2016-11-25 10:36:25 -0800105 }
106}
107
David Tolnay42f50292016-09-04 13:54:21 -0700108impl From<String> for Ident {
109 fn from(s: String) -> Self {
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700110 Ident::new(Term::intern(&s), Span::default())
David Tolnay42f50292016-09-04 13:54:21 -0700111 }
112}
113
David Tolnay26469072016-09-04 13:59:48 -0700114impl AsRef<str> for Ident {
115 fn as_ref(&self) -> &str {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700116 self.sym.as_str()
David Tolnay42f50292016-09-04 13:54:21 -0700117 }
118}
119
120impl Display for Ident {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700121 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
122 self.sym.as_str().fmt(formatter)
123 }
124}
125
David Tolnaydaaf7742016-10-03 11:11:43 -0700126impl<T: ?Sized> PartialEq<T> for Ident
127 where T: AsRef<str>
128{
David Tolnay55337722016-09-11 12:58:56 -0700129 fn eq(&self, other: &T) -> bool {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700130 self.as_ref() == other.as_ref()
131 }
132}
133
134impl Eq for Ident {}
135
136impl PartialOrd for Ident {
137 fn partial_cmp(&self, other: &Ident) -> Option<Ordering> {
138 Some(self.cmp(other))
139 }
140}
141
142impl Ord for Ident {
143 fn cmp(&self, other: &Ident) -> Ordering {
144 self.as_ref().cmp(other.as_ref())
145 }
146}
147
148impl Hash for Ident {
149 fn hash<H: Hasher>(&self, h: &mut H) {
150 self.as_ref().hash(h)
David Tolnay55337722016-09-11 12:58:56 -0700151 }
David Tolnayb79ee962016-09-04 09:39:20 -0700152}
153
David Tolnay86eca752016-09-04 11:26:41 -0700154#[cfg(feature = "parsing")]
David Tolnay9d8f1972016-09-04 11:58:48 -0700155pub mod parsing {
156 use super::*;
Michael Layzell92639a52017-06-01 00:07:44 -0400157 use synom::{Synom, PResult, Cursor, parse_error};
David Tolnay9d8f1972016-09-04 11:58:48 -0700158
Alex Crichton954046c2017-05-30 21:49:42 -0700159 impl Synom for Ident {
Michael Layzell92639a52017-06-01 00:07:44 -0400160 fn parse(input: Cursor) -> PResult<Self> {
Michael Layzell589a8f42017-06-02 19:47:01 -0400161 let (rest, span, sym) = match input.word() {
162 Some(word) => word,
Michael Layzell92639a52017-06-01 00:07:44 -0400163 _ => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700164 };
Michael Layzell589a8f42017-06-02 19:47:01 -0400165 if sym.as_str().starts_with('\'') {
Michael Layzell92639a52017-06-01 00:07:44 -0400166 return parse_error();
Alex Crichton954046c2017-05-30 21:49:42 -0700167 }
Michael Layzell589a8f42017-06-02 19:47:01 -0400168 match sym.as_str() {
Michael Layzell416724e2017-05-24 21:12:34 -0400169 // From https://doc.rust-lang.org/grammar.html#keywords
170 "abstract" | "alignof" | "as" | "become" | "box" | "break" | "const" | "continue" |
171 "crate" | "do" | "else" | "enum" | "extern" | "false" | "final" | "fn" | "for" |
172 "if" | "impl" | "in" | "let" | "loop" | "macro" | "match" | "mod" | "move" |
173 "mut" | "offsetof" | "override" | "priv" | "proc" | "pub" | "pure" | "ref" |
174 "return" | "Self" | "self" | "sizeof" | "static" | "struct" | "super" | "trait" |
175 "true" | "type" | "typeof" | "unsafe" | "unsized" | "use" | "virtual" | "where" |
Michael Layzell92639a52017-06-01 00:07:44 -0400176 "while" | "yield" => return parse_error(),
Alex Crichton954046c2017-05-30 21:49:42 -0700177 _ => {}
Michael Layzell416724e2017-05-24 21:12:34 -0400178 }
Alex Crichton954046c2017-05-30 21:49:42 -0700179
Michael Layzell589a8f42017-06-02 19:47:01 -0400180 Ok((rest, Ident {
181 span: Span(span),
182 sym: sym,
Michael Layzell92639a52017-06-01 00:07:44 -0400183 }))
Alex Crichton954046c2017-05-30 21:49:42 -0700184 }
185
186 fn description() -> Option<&'static str> {
187 Some("identifier")
David Tolnay05f462f2016-10-24 22:19:42 -0700188 }
189 }
David Tolnayb79ee962016-09-04 09:39:20 -0700190}
David Tolnay26469072016-09-04 13:59:48 -0700191
192#[cfg(feature = "printing")]
193mod printing {
194 use super::*;
195 use quote::{Tokens, ToTokens};
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700196 use proc_macro2::{TokenTree, TokenNode};
David Tolnay26469072016-09-04 13:59:48 -0700197
198 impl ToTokens for Ident {
199 fn to_tokens(&self, tokens: &mut Tokens) {
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700200 tokens.append(TokenTree {
201 span: self.span.0,
Alex Crichtonf9e8f1a2017-07-05 18:20:44 -0700202 kind: TokenNode::Term(self.sym),
Alex Crichtonccbb45d2017-05-23 10:58:24 -0700203 })
David Tolnay26469072016-09-04 13:59:48 -0700204 }
205 }
206}