1use std::borrow::Cow;
10use std::cmp::Ordering;
11use std::fmt::{self, Display};
12use std::hash::{Hash, Hasher};
13
14use proc_macro2::Term;
15use unicode_xid::UnicodeXID;
16
17use proc_macro2::Span;
18
19#[derive(Copy, Clone, Debug)]
100pub struct Ident {
101 term: Term,
102 pub span: Span,
103}
104
105impl Ident {
106 pub fn new(s: &str, span: Span) -> Self {
113 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 == "_" {
122 panic!("`_` is not a valid ident; use syn::token::Underscore");
123 }
124
125 if s.bytes().all(|digit| digit >= b'0' && digit <= b'9') {
126 panic!("ident cannot be a number, use syn::Index instead");
127 }
128
129 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
143 if !xid_ok(s) {
144 panic!("{:?} is not a valid ident", s);
145 }
146
147 Ident {
148 term: Term::intern(s),
149 span: span,
150 }
151 }
152}
153
154impl<'a> From<&'a str> for Ident {
155 fn from(s: &str) -> Self {
156 Ident::new(s, Span::def_site())
157 }
158}
159
160impl From<Token![self]> for Ident {
161 fn from(tok: Token![self]) -> Self {
162 Ident::new("self", tok.0)
163 }
164}
165
166impl From<Token![Self]> for Ident {
167 fn from(tok: Token![Self]) -> Self {
168 Ident::new("Self", tok.0)
169 }
170}
171
172impl From<Token![super]> for Ident {
173 fn from(tok: Token![super]) -> Self {
174 Ident::new("super", tok.0)
175 }
176}
177
178impl From<Token![crate]> for Ident {
179 fn from(tok: Token![crate]) -> Self {
180 Ident::new("crate", tok.0)
181 }
182}
183
184impl<'a> From<Cow<'a, str>> for Ident {
185 fn from(s: Cow<'a, str>) -> Self {
186 Ident::new(&s, Span::def_site())
187 }
188}
189
190impl From<String> for Ident {
191 fn from(s: String) -> Self {
192 Ident::new(&s, Span::def_site())
193 }
194}
195
196impl AsRef<str> for Ident {
197 fn as_ref(&self) -> &str {
198 self.term.as_str()
199 }
200}
201
202impl Display for Ident {
203 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
204 self.term.as_str().fmt(formatter)
205 }
206}
207
208impl<T: ?Sized> PartialEq<T> for Ident
209where
210 T: AsRef<str>,
211{
212 fn eq(&self, other: &T) -> bool {
213 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) {
233 self.as_ref().hash(h);
234 }
235}
236
237#[cfg(feature = "parsing")]
238pub mod parsing {
239 use super::*;
240 use synom::Synom;
241 use buffer::Cursor;
242 use parse_error;
243 use synom::PResult;
244
245 impl Synom for Ident {
246 fn parse(input: Cursor) -> PResult<Self> {
247 let (span, term, rest) = match input.term() {
248 Some(term) => term,
249 _ => return parse_error(),
250 };
251 if term.as_str().starts_with('\'') {
252 return parse_error();
253 }
254 match term.as_str() {
255 "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(),
263 _ => {}
264 }
265
266 Ok((
267 Ident {
268 span: span,
269 term: term,
270 },
271 rest,
272 ))
273 }
274
275 fn description() -> Option<&'static str> {
276 Some("identifier")
277 }
278 }
279}
280
281#[cfg(feature = "printing")]
282mod printing {
283 use super::*;
284 use quote::{ToTokens, Tokens};
285 use proc_macro2::{TokenNode, TokenTree};
286
287 impl ToTokens for Ident {
288 fn to_tokens(&self, tokens: &mut Tokens) {
289 tokens.append(TokenTree {
290 span: self.span,
291 kind: TokenNode::Term(self.term),
292 })
293 }
294 }
295}