standalone_syn/
ident.rs

1// 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
9use 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/// A word of Rust code, which may be a keyword or legal variable name.
20///
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
28///   `Token![_]` instead.
29/// - A lifetime is not an identifier. Use `syn::Lifetime` instead.
30///
31/// An identifier constructed with `Ident::new` is permitted to be a Rust
32/// keyword, though parsing one through its [`Synom`] implementation rejects
33/// Rust keywords.
34///
35/// [`Synom`]: synom/trait.Synom.html
36///
37/// # Examples
38///
39/// A new ident can be created from a string using the `Ident::from` function.
40/// 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`.
43///
44/// ```rust
45/// extern crate syn;
46/// extern crate proc_macro2;
47///
48/// use syn::Ident;
49/// use proc_macro2::Span;
50///
51/// fn main() {
52///     let def_ident = Ident::from("definitely");
53///     let call_ident = Ident::new("calligraphy", Span::call_site());
54///
55///     println!("{} {}", def_ident, call_ident);
56/// }
57/// ```
58///
59/// An ident can be interpolated into a token stream using the `quote!` macro.
60///
61/// ```rust
62/// #[macro_use]
63/// extern crate quote;
64///
65/// extern crate syn;
66/// use syn::Ident;
67///
68/// 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/// }
78/// ```
79///
80/// A string representation of the ident is available through the `as_ref()` and
81/// `to_string()` methods.
82///
83/// ```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/// ```
99#[derive(Copy, Clone, Debug)]
100pub struct Ident {
101    term: Term,
102    pub span: Span,
103}
104
105impl Ident {
106    /// Creates an ident with the given string representation.
107    ///
108    /// # Panics
109    ///
110    /// Panics if the input string is neither a keyword nor a legal variable
111    /// name.
112    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                // From https://doc.rust-lang.org/grammar.html#keywords
256                "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}