]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_pos/symbol.rs
Rollup merge of #52548 - tko:cursor-doc, r=sfackler
[rust.git] / src / libsyntax_pos / symbol.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! An "interner" is a data structure that associates values with usize tags and
12 //! allows bidirectional lookup; i.e. given a value, one can easily find the
13 //! type, and vice versa.
14
15 use edition::Edition;
16 use hygiene::SyntaxContext;
17 use {Span, DUMMY_SP, GLOBALS};
18
19 use rustc_data_structures::fx::FxHashMap;
20 use arena::DroplessArena;
21 use serialize::{Decodable, Decoder, Encodable, Encoder};
22 use std::fmt;
23 use std::str;
24 use std::cmp::{PartialEq, Ordering, PartialOrd, Ord};
25 use std::hash::{Hash, Hasher};
26
27 #[derive(Copy, Clone, Eq)]
28 pub struct Ident {
29     pub name: Symbol,
30     pub span: Span,
31 }
32
33 impl Ident {
34     #[inline]
35     pub const fn new(name: Symbol, span: Span) -> Ident {
36         Ident { name, span }
37     }
38     #[inline]
39     pub const fn with_empty_ctxt(name: Symbol) -> Ident {
40         Ident::new(name, DUMMY_SP)
41     }
42
43     /// Maps an interned string to an identifier with an empty syntax context.
44     pub fn from_interned_str(string: InternedString) -> Ident {
45         Ident::with_empty_ctxt(string.as_symbol())
46     }
47
48     /// Maps a string to an identifier with an empty syntax context.
49     pub fn from_str(string: &str) -> Ident {
50         Ident::with_empty_ctxt(Symbol::intern(string))
51     }
52
53     /// Replace `lo` and `hi` with those from `span`, but keep hygiene context.
54     pub fn with_span_pos(self, span: Span) -> Ident {
55         Ident::new(self.name, span.with_ctxt(self.span.ctxt()))
56     }
57
58     pub fn without_first_quote(self) -> Ident {
59         Ident::new(Symbol::intern(self.as_str().trim_left_matches('\'')), self.span)
60     }
61
62     /// "Normalize" ident for use in comparisons using "item hygiene".
63     /// Identifiers with same string value become same if they came from the same "modern" macro
64     /// (e.g. `macro` item, but not `macro_rules` item) and stay different if they came from
65     /// different "modern" macros.
66     /// Technically, this operation strips all non-opaque marks from ident's syntactic context.
67     pub fn modern(self) -> Ident {
68         Ident::new(self.name, self.span.modern())
69     }
70
71     /// "Normalize" ident for use in comparisons using "local variable hygiene".
72     /// Identifiers with same string value become same if they came from the same non-transparent
73     /// macro (e.g. `macro` or `macro_rules!` items) and stay different if they came from different
74     /// non-transparent macros.
75     /// Technically, this operation strips all transparent marks from ident's syntactic context.
76     pub fn modern_and_legacy(self) -> Ident {
77         Ident::new(self.name, self.span.modern_and_legacy())
78     }
79
80     pub fn gensym(self) -> Ident {
81         Ident::new(self.name.gensymed(), self.span)
82     }
83
84     pub fn as_str(self) -> LocalInternedString {
85         self.name.as_str()
86     }
87
88     pub fn as_interned_str(self) -> InternedString {
89         self.name.as_interned_str()
90     }
91 }
92
93 impl PartialEq for Ident {
94     fn eq(&self, rhs: &Self) -> bool {
95         self.name == rhs.name && self.span.ctxt() == rhs.span.ctxt()
96     }
97 }
98
99 impl Hash for Ident {
100     fn hash<H: Hasher>(&self, state: &mut H) {
101         self.name.hash(state);
102         self.span.ctxt().hash(state);
103     }
104 }
105
106 impl fmt::Debug for Ident {
107     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108         write!(f, "{}{:?}", self.name, self.span.ctxt())
109     }
110 }
111
112 impl fmt::Display for Ident {
113     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114         fmt::Display::fmt(&self.name, f)
115     }
116 }
117
118 impl Encodable for Ident {
119     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
120         if self.span.ctxt().modern() == SyntaxContext::empty() {
121             s.emit_str(&self.as_str())
122         } else { // FIXME(jseyfried) intercrate hygiene
123             let mut string = "#".to_owned();
124             string.push_str(&self.as_str());
125             s.emit_str(&string)
126         }
127     }
128 }
129
130 impl Decodable for Ident {
131     fn decode<D: Decoder>(d: &mut D) -> Result<Ident, D::Error> {
132         let string = d.read_str()?;
133         Ok(if !string.starts_with('#') {
134             Ident::from_str(&string)
135         } else { // FIXME(jseyfried) intercrate hygiene
136             Ident::with_empty_ctxt(Symbol::gensym(&string[1..]))
137         })
138     }
139 }
140
141 /// A symbol is an interned or gensymed string.
142 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
143 pub struct Symbol(u32);
144
145 // The interner is pointed to by a thread local value which is only set on the main thread
146 // with parallelization is disabled. So we don't allow Symbol to transfer between threads
147 // to avoid panics and other errors, even though it would be memory safe to do so.
148 #[cfg(not(parallel_queries))]
149 impl !Send for Symbol { }
150 #[cfg(not(parallel_queries))]
151 impl !Sync for Symbol { }
152
153 impl Symbol {
154     /// Maps a string to its interned representation.
155     pub fn intern(string: &str) -> Self {
156         with_interner(|interner| interner.intern(string))
157     }
158
159     pub fn interned(self) -> Self {
160         with_interner(|interner| interner.interned(self))
161     }
162
163     /// gensym's a new usize, using the current interner.
164     pub fn gensym(string: &str) -> Self {
165         with_interner(|interner| interner.gensym(string))
166     }
167
168     pub fn gensymed(self) -> Self {
169         with_interner(|interner| interner.gensymed(self))
170     }
171
172     pub fn as_str(self) -> LocalInternedString {
173         with_interner(|interner| unsafe {
174             LocalInternedString {
175                 string: ::std::mem::transmute::<&str, &str>(interner.get(self))
176             }
177         })
178     }
179
180     pub fn as_interned_str(self) -> InternedString {
181         with_interner(|interner| InternedString {
182             symbol: interner.interned(self)
183         })
184     }
185
186     pub fn as_u32(self) -> u32 {
187         self.0
188     }
189 }
190
191 impl fmt::Debug for Symbol {
192     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
193         let is_gensymed = with_interner(|interner| interner.is_gensymed(*self));
194         if is_gensymed {
195             write!(f, "{}({})", self, self.0)
196         } else {
197             write!(f, "{}", self)
198         }
199     }
200 }
201
202 impl fmt::Display for Symbol {
203     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
204         fmt::Display::fmt(&self.as_str(), f)
205     }
206 }
207
208 impl Encodable for Symbol {
209     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
210         s.emit_str(&self.as_str())
211     }
212 }
213
214 impl Decodable for Symbol {
215     fn decode<D: Decoder>(d: &mut D) -> Result<Symbol, D::Error> {
216         Ok(Symbol::intern(&d.read_str()?))
217     }
218 }
219
220 impl<T: ::std::ops::Deref<Target=str>> PartialEq<T> for Symbol {
221     fn eq(&self, other: &T) -> bool {
222         self.as_str() == other.deref()
223     }
224 }
225
226 // The &'static strs in this type actually point into the arena
227 pub struct Interner {
228     arena: DroplessArena,
229     names: FxHashMap<&'static str, Symbol>,
230     strings: Vec<&'static str>,
231     gensyms: Vec<Symbol>,
232 }
233
234 impl Interner {
235     pub fn new() -> Self {
236         Interner {
237             arena: DroplessArena::new(),
238             names: Default::default(),
239             strings: Default::default(),
240             gensyms: Default::default(),
241         }
242     }
243
244     fn prefill(init: &[&str]) -> Self {
245         let mut this = Interner::new();
246         for &string in init {
247             if string == "" {
248                 // We can't allocate empty strings in the arena, so handle this here
249                 let name = Symbol(this.strings.len() as u32);
250                 this.names.insert("", name);
251                 this.strings.push("");
252             } else {
253                 this.intern(string);
254             }
255         }
256         this
257     }
258
259     pub fn intern(&mut self, string: &str) -> Symbol {
260         if let Some(&name) = self.names.get(string) {
261             return name;
262         }
263
264         let name = Symbol(self.strings.len() as u32);
265
266         // from_utf8_unchecked is safe since we just allocated a &str which is known to be utf8
267         let string: &str = unsafe {
268             str::from_utf8_unchecked(self.arena.alloc_slice(string.as_bytes()))
269         };
270         // It is safe to extend the arena allocation to 'static because we only access
271         // these while the arena is still alive
272         let string: &'static str =  unsafe {
273             &*(string as *const str)
274         };
275         self.strings.push(string);
276         self.names.insert(string, name);
277         name
278     }
279
280     pub fn interned(&self, symbol: Symbol) -> Symbol {
281         if (symbol.0 as usize) < self.strings.len() {
282             symbol
283         } else {
284             self.interned(self.gensyms[(!0 - symbol.0) as usize])
285         }
286     }
287
288     fn gensym(&mut self, string: &str) -> Symbol {
289         let symbol = self.intern(string);
290         self.gensymed(symbol)
291     }
292
293     fn gensymed(&mut self, symbol: Symbol) -> Symbol {
294         self.gensyms.push(symbol);
295         Symbol(!0 - self.gensyms.len() as u32 + 1)
296     }
297
298     fn is_gensymed(&mut self, symbol: Symbol) -> bool {
299         symbol.0 as usize >= self.strings.len()
300     }
301
302     pub fn get(&self, symbol: Symbol) -> &str {
303         match self.strings.get(symbol.0 as usize) {
304             Some(string) => string,
305             None => self.get(self.gensyms[(!0 - symbol.0) as usize]),
306         }
307     }
308 }
309
310 // In this macro, there is the requirement that the name (the number) must be monotonically
311 // increasing by one in the special identifiers, starting at 0; the same holds for the keywords,
312 // except starting from the next number instead of zero.
313 macro_rules! declare_keywords {(
314     $( ($index: expr, $konst: ident, $string: expr) )*
315 ) => {
316     pub mod keywords {
317         use super::{Symbol, Ident};
318         #[derive(Clone, Copy, PartialEq, Eq)]
319         pub struct Keyword {
320             ident: Ident,
321         }
322         impl Keyword {
323             #[inline] pub fn ident(self) -> Ident { self.ident }
324             #[inline] pub fn name(self) -> Symbol { self.ident.name }
325         }
326         $(
327             #[allow(non_upper_case_globals)]
328             pub const $konst: Keyword = Keyword {
329                 ident: Ident::with_empty_ctxt(super::Symbol($index))
330             };
331         )*
332
333         impl ::std::str::FromStr for Keyword {
334             type Err = ();
335
336             fn from_str(s: &str) -> Result<Self, ()> {
337                 match s {
338                     $($string => Ok($konst),)*
339                     _ => Err(()),
340                 }
341             }
342         }
343     }
344
345     impl Interner {
346         pub fn fresh() -> Self {
347             Interner::prefill(&[$($string,)*])
348         }
349     }
350 }}
351
352 // NB: leaving holes in the ident table is bad! a different ident will get
353 // interned with the id from the hole, but it will be between the min and max
354 // of the reserved words, and thus tagged as "reserved".
355 // After modifying this list adjust `is_special`, `is_used_keyword`/`is_unused_keyword`,
356 // this should be rarely necessary though if the keywords are kept in alphabetic order.
357 declare_keywords! {
358     // Special reserved identifiers used internally for elided lifetimes,
359     // unnamed method parameters, crate root module, error recovery etc.
360     (0,  Invalid,            "")
361     (1,  CrateRoot,          "{{root}}")
362     (2,  DollarCrate,        "$crate")
363     (3,  Underscore,         "_")
364
365     // Keywords used in the language.
366     (4,  As,                 "as")
367     (5,  Box,                "box")
368     (6,  Break,              "break")
369     (7,  Const,              "const")
370     (8,  Continue,           "continue")
371     (9,  Crate,              "crate")
372     (10, Else,               "else")
373     (11, Enum,               "enum")
374     (12, Extern,             "extern")
375     (13, False,              "false")
376     (14, Fn,                 "fn")
377     (15, For,                "for")
378     (16, If,                 "if")
379     (17, Impl,               "impl")
380     (18, In,                 "in")
381     (19, Let,                "let")
382     (20, Loop,               "loop")
383     (21, Match,              "match")
384     (22, Mod,                "mod")
385     (23, Move,               "move")
386     (24, Mut,                "mut")
387     (25, Pub,                "pub")
388     (26, Ref,                "ref")
389     (27, Return,             "return")
390     (28, SelfValue,          "self")
391     (29, SelfType,           "Self")
392     (30, Static,             "static")
393     (31, Struct,             "struct")
394     (32, Super,              "super")
395     (33, Trait,              "trait")
396     (34, True,               "true")
397     (35, Type,               "type")
398     (36, Unsafe,             "unsafe")
399     (37, Use,                "use")
400     (38, Where,              "where")
401     (39, While,              "while")
402
403     // Keywords reserved for future use.
404     (40, Abstract,           "abstract")
405     (41, Become,             "become")
406     (42, Do,                 "do")
407     (43, Final,              "final")
408     (44, Macro,              "macro")
409     (45, Override,           "override")
410     (46, Priv,               "priv")
411     (47, Typeof,             "typeof")
412     (48, Unsized,            "unsized")
413     (49, Virtual,            "virtual")
414     (50, Yield,              "yield")
415
416     // Edition-specific keywords reserved for future use.
417     (51, Async,              "async") // >= 2018 Edition Only
418
419     // Special lifetime names
420     (52, UnderscoreLifetime, "'_")
421     (53, StaticLifetime,     "'static")
422
423     // Weak keywords, have special meaning only in specific contexts.
424     (54, Auto,               "auto")
425     (55, Catch,              "catch")
426     (56, Default,            "default")
427     (57, Dyn,                "dyn")
428     (58, Union,              "union")
429     (59, Existential,        "existential")
430 }
431
432 impl Symbol {
433     fn is_unused_keyword_2018(self) -> bool {
434         self == keywords::Async.name()
435     }
436 }
437
438 impl Ident {
439     // Returns true for reserved identifiers used internally for elided lifetimes,
440     // unnamed method parameters, crate root module, error recovery etc.
441     pub fn is_special(self) -> bool {
442         self.name <= keywords::Underscore.name()
443     }
444
445     /// Returns `true` if the token is a keyword used in the language.
446     pub fn is_used_keyword(self) -> bool {
447         self.name >= keywords::As.name() && self.name <= keywords::While.name()
448     }
449
450     /// Returns `true` if the token is a keyword reserved for possible future use.
451     pub fn is_unused_keyword(self) -> bool {
452         // Note: `span.edition()` is relatively expensive, don't call it unless necessary.
453         self.name >= keywords::Abstract.name() && self.name <= keywords::Yield.name() ||
454         self.name.is_unused_keyword_2018() && self.span.edition() == Edition::Edition2018
455     }
456
457     /// Returns `true` if the token is either a special identifier or a keyword.
458     pub fn is_reserved(self) -> bool {
459         self.is_special() || self.is_used_keyword() || self.is_unused_keyword()
460     }
461
462     /// A keyword or reserved identifier that can be used as a path segment.
463     pub fn is_path_segment_keyword(self) -> bool {
464         self.name == keywords::Super.name() ||
465         self.name == keywords::SelfValue.name() ||
466         self.name == keywords::SelfType.name() ||
467         self.name == keywords::Extern.name() ||
468         self.name == keywords::Crate.name() ||
469         self.name == keywords::CrateRoot.name() ||
470         self.name == keywords::DollarCrate.name()
471     }
472
473     // We see this identifier in a normal identifier position, like variable name or a type.
474     // How was it written originally? Did it use the raw form? Let's try to guess.
475     pub fn is_raw_guess(self) -> bool {
476         self.name != keywords::Invalid.name() &&
477         self.is_reserved() && !self.is_path_segment_keyword()
478     }
479 }
480
481 // If an interner exists, return it. Otherwise, prepare a fresh one.
482 #[inline]
483 fn with_interner<T, F: FnOnce(&mut Interner) -> T>(f: F) -> T {
484     GLOBALS.with(|globals| f(&mut *globals.symbol_interner.lock()))
485 }
486
487 /// Represents a string stored in the interner. Because the interner outlives any thread
488 /// which uses this type, we can safely treat `string` which points to interner data,
489 /// as an immortal string, as long as this type never crosses between threads.
490 // FIXME: Ensure that the interner outlives any thread which uses LocalInternedString,
491 //        by creating a new thread right after constructing the interner
492 #[derive(Clone, Copy, Hash, PartialOrd, Eq, Ord)]
493 pub struct LocalInternedString {
494     string: &'static str,
495 }
496
497 impl LocalInternedString {
498     pub fn as_interned_str(self) -> InternedString {
499         InternedString {
500             symbol: Symbol::intern(self.string)
501         }
502     }
503 }
504
505 impl<U: ?Sized> ::std::convert::AsRef<U> for LocalInternedString
506 where
507     str: ::std::convert::AsRef<U>
508 {
509     fn as_ref(&self) -> &U {
510         self.string.as_ref()
511     }
512 }
513
514 impl<T: ::std::ops::Deref<Target = str>> ::std::cmp::PartialEq<T> for LocalInternedString {
515     fn eq(&self, other: &T) -> bool {
516         self.string == other.deref()
517     }
518 }
519
520 impl ::std::cmp::PartialEq<LocalInternedString> for str {
521     fn eq(&self, other: &LocalInternedString) -> bool {
522         self == other.string
523     }
524 }
525
526 impl<'a> ::std::cmp::PartialEq<LocalInternedString> for &'a str {
527     fn eq(&self, other: &LocalInternedString) -> bool {
528         *self == other.string
529     }
530 }
531
532 impl ::std::cmp::PartialEq<LocalInternedString> for String {
533     fn eq(&self, other: &LocalInternedString) -> bool {
534         self == other.string
535     }
536 }
537
538 impl<'a> ::std::cmp::PartialEq<LocalInternedString> for &'a String {
539     fn eq(&self, other: &LocalInternedString) -> bool {
540         *self == other.string
541     }
542 }
543
544 impl !Send for LocalInternedString {}
545 impl !Sync for LocalInternedString {}
546
547 impl ::std::ops::Deref for LocalInternedString {
548     type Target = str;
549     fn deref(&self) -> &str { self.string }
550 }
551
552 impl fmt::Debug for LocalInternedString {
553     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
554         fmt::Debug::fmt(self.string, f)
555     }
556 }
557
558 impl fmt::Display for LocalInternedString {
559     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
560         fmt::Display::fmt(self.string, f)
561     }
562 }
563
564 impl Decodable for LocalInternedString {
565     fn decode<D: Decoder>(d: &mut D) -> Result<LocalInternedString, D::Error> {
566         Ok(Symbol::intern(&d.read_str()?).as_str())
567     }
568 }
569
570 impl Encodable for LocalInternedString {
571     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
572         s.emit_str(self.string)
573     }
574 }
575
576 /// Represents a string stored in the string interner
577 #[derive(Clone, Copy, Eq)]
578 pub struct InternedString {
579     symbol: Symbol,
580 }
581
582 impl InternedString {
583     pub fn with<F: FnOnce(&str) -> R, R>(self, f: F) -> R {
584         let str = with_interner(|interner| {
585             interner.get(self.symbol) as *const str
586         });
587         // This is safe because the interner keeps string alive until it is dropped.
588         // We can access it because we know the interner is still alive since we use a
589         // scoped thread local to access it, and it was alive at the begining of this scope
590         unsafe { f(&*str) }
591     }
592
593     pub fn as_symbol(self) -> Symbol {
594         self.symbol
595     }
596
597     pub fn as_str(self) -> LocalInternedString {
598         self.symbol.as_str()
599     }
600 }
601
602 impl Hash for InternedString {
603     fn hash<H: Hasher>(&self, state: &mut H) {
604         self.with(|str| str.hash(state))
605     }
606 }
607
608 impl PartialOrd<InternedString> for InternedString {
609     fn partial_cmp(&self, other: &InternedString) -> Option<Ordering> {
610         if self.symbol == other.symbol {
611             return Some(Ordering::Equal);
612         }
613         self.with(|self_str| other.with(|other_str| self_str.partial_cmp(other_str)))
614     }
615 }
616
617 impl Ord for InternedString {
618     fn cmp(&self, other: &InternedString) -> Ordering {
619         if self.symbol == other.symbol {
620             return Ordering::Equal;
621         }
622         self.with(|self_str| other.with(|other_str| self_str.cmp(&other_str)))
623     }
624 }
625
626 impl<T: ::std::ops::Deref<Target = str>> PartialEq<T> for InternedString {
627     fn eq(&self, other: &T) -> bool {
628         self.with(|string| string == other.deref())
629     }
630 }
631
632 impl PartialEq<InternedString> for InternedString {
633     fn eq(&self, other: &InternedString) -> bool {
634         self.symbol == other.symbol
635     }
636 }
637
638 impl PartialEq<InternedString> for str {
639     fn eq(&self, other: &InternedString) -> bool {
640         other.with(|string| self == string)
641     }
642 }
643
644 impl<'a> PartialEq<InternedString> for &'a str {
645     fn eq(&self, other: &InternedString) -> bool {
646         other.with(|string| *self == string)
647     }
648 }
649
650 impl PartialEq<InternedString> for String {
651     fn eq(&self, other: &InternedString) -> bool {
652         other.with(|string| self == string)
653     }
654 }
655
656 impl<'a> PartialEq<InternedString> for &'a String {
657     fn eq(&self, other: &InternedString) -> bool {
658         other.with(|string| *self == string)
659     }
660 }
661
662 impl ::std::convert::From<InternedString> for String {
663     fn from(val: InternedString) -> String {
664         val.as_symbol().to_string()
665     }
666 }
667
668 impl fmt::Debug for InternedString {
669     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
670         self.with(|str| fmt::Debug::fmt(&str, f))
671     }
672 }
673
674 impl fmt::Display for InternedString {
675     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
676         self.with(|str| fmt::Display::fmt(&str, f))
677     }
678 }
679
680 impl Decodable for InternedString {
681     fn decode<D: Decoder>(d: &mut D) -> Result<InternedString, D::Error> {
682         Ok(Symbol::intern(&d.read_str()?).as_interned_str())
683     }
684 }
685
686 impl Encodable for InternedString {
687     fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
688         self.with(|string| s.emit_str(string))
689     }
690 }
691
692 #[cfg(test)]
693 mod tests {
694     use super::*;
695     use Globals;
696
697     #[test]
698     fn interner_tests() {
699         let mut i: Interner = Interner::new();
700         // first one is zero:
701         assert_eq!(i.intern("dog"), Symbol(0));
702         // re-use gets the same entry:
703         assert_eq!(i.intern("dog"), Symbol(0));
704         // different string gets a different #:
705         assert_eq!(i.intern("cat"), Symbol(1));
706         assert_eq!(i.intern("cat"), Symbol(1));
707         // dog is still at zero
708         assert_eq!(i.intern("dog"), Symbol(0));
709         assert_eq!(i.gensym("zebra"), Symbol(4294967295));
710         // gensym of same string gets new number :
711         assert_eq!(i.gensym("zebra"), Symbol(4294967294));
712         // gensym of *existing* string gets new number:
713         assert_eq!(i.gensym("dog"), Symbol(4294967293));
714     }
715
716     #[test]
717     fn without_first_quote_test() {
718         GLOBALS.set(&Globals::new(), || {
719             let i = Ident::from_str("'break");
720             assert_eq!(i.without_first_quote().name, keywords::Break.name());
721         });
722     }
723 }