solar_interface/
symbol.rs

1use crate::{Session, SessionGlobals, Span};
2use solar_data_structures::{index::BaseIndex32, trustme};
3use solar_macros::symbols;
4use std::{cmp, fmt, hash, str};
5
6/// An identifier.
7#[derive(Clone, Copy)]
8pub struct Ident {
9    /// The identifier's name.
10    pub name: Symbol,
11    /// The identifier's span.
12    pub span: Span,
13}
14
15impl Default for Ident {
16    #[inline]
17    fn default() -> Self {
18        Self::DUMMY
19    }
20}
21
22impl PartialEq for Ident {
23    #[inline]
24    fn eq(&self, rhs: &Self) -> bool {
25        self.name == rhs.name
26    }
27}
28
29impl Eq for Ident {}
30
31impl PartialOrd for Ident {
32    #[inline]
33    fn partial_cmp(&self, rhs: &Self) -> Option<cmp::Ordering> {
34        Some(self.cmp(rhs))
35    }
36}
37
38impl Ord for Ident {
39    #[inline]
40    fn cmp(&self, rhs: &Self) -> cmp::Ordering {
41        self.name.cmp(&rhs.name)
42    }
43}
44
45impl hash::Hash for Ident {
46    #[inline]
47    fn hash<H: hash::Hasher>(&self, state: &mut H) {
48        self.name.hash(state);
49    }
50}
51
52impl fmt::Debug for Ident {
53    #[inline]
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        fmt::Display::fmt(self, f)
56    }
57}
58
59impl fmt::Display for Ident {
60    #[inline]
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        self.name.fmt(f)
63    }
64}
65
66impl Ident {
67    /// A dummy identifier.
68    pub const DUMMY: Self = Self::new(Symbol::DUMMY, Span::DUMMY);
69
70    /// Constructs a new identifier from a symbol and a span.
71    #[inline]
72    pub const fn new(name: Symbol, span: Span) -> Self {
73        Self { name, span }
74    }
75
76    /// Constructs a new identifier with a dummy span.
77    #[inline]
78    pub const fn with_dummy_span(name: Symbol) -> Self {
79        Self::new(name, Span::DUMMY)
80    }
81
82    /// Maps a string to an identifier with a dummy span.
83    #[allow(clippy::should_implement_trait)]
84    pub fn from_str(string: &str) -> Self {
85        Self::with_dummy_span(Symbol::intern(string))
86    }
87
88    /// Maps a string and a span to an identifier.
89    pub fn from_str_and_span(string: &str, span: Span) -> Self {
90        Self::new(Symbol::intern(string), span)
91    }
92
93    /// "Specialization" of [`ToString`] using [`as_str`](Self::as_str).
94    #[inline]
95    #[allow(clippy::inherent_to_string_shadow_display)]
96    #[cfg_attr(debug_assertions, track_caller)]
97    pub fn to_string(&self) -> String {
98        self.as_str().to_string()
99    }
100
101    /// Access the underlying string.
102    ///
103    /// Note that the lifetime of the return value is a lie. See [`Symbol::as_str()`] for details.
104    #[cfg_attr(debug_assertions, track_caller)]
105    pub fn as_str(&self) -> &str {
106        self.name.as_str()
107    }
108
109    /// Access the underlying string in the given session.
110    ///
111    /// The identifier must have been interned in the given session.
112    #[inline]
113    #[cfg_attr(debug_assertions, track_caller)]
114    pub fn as_str_in(self, session: &Session) -> &str {
115        self.name.as_str_in(session)
116    }
117
118    /// Returns `true` if the identifier is a keyword used in the language.
119    #[inline]
120    pub fn is_used_keyword(self) -> bool {
121        self.name.is_used_keyword()
122    }
123
124    /// Returns `true` if the identifier is a keyword reserved for possible future use.
125    #[inline]
126    pub fn is_unused_keyword(self) -> bool {
127        self.name.is_unused_keyword()
128    }
129
130    /// Returns `true` if the identifier is a weak keyword and can be used in variable names.
131    #[inline]
132    pub fn is_weak_keyword(self) -> bool {
133        self.name.is_weak_keyword()
134    }
135
136    /// Returns `true` if the identifier is a keyword in a Yul context.
137    #[inline]
138    pub fn is_yul_keyword(self) -> bool {
139        self.name.is_yul_keyword()
140    }
141
142    /// Returns `true` if the identifier is a Yul EVM builtin keyword.
143    #[inline]
144    pub fn is_yul_evm_builtin(self) -> bool {
145        self.name.is_yul_builtin()
146    }
147
148    /// Returns `true` if the identifier is either a keyword, either currently in use or reserved
149    /// for possible future use.
150    #[inline]
151    pub fn is_reserved(self, yul: bool) -> bool {
152        self.name.is_reserved(yul)
153    }
154
155    /// Returns `true` if the identifier is not a reserved keyword.
156    /// See [`is_reserved`](Self::is_reserved).
157    #[inline]
158    pub fn is_non_reserved(self, yul: bool) -> bool {
159        self.name.is_non_reserved(yul)
160    }
161
162    /// Returns `true` if the identifier is an elementary type name.
163    ///
164    /// Note that this does not include `[u]fixedMxN` types.
165    #[inline]
166    pub fn is_elementary_type(self) -> bool {
167        self.name.is_elementary_type()
168    }
169
170    /// Returns `true` if the identifier is `true` or `false`.
171    #[inline]
172    pub fn is_bool_lit(self) -> bool {
173        self.name.is_bool_lit()
174    }
175
176    /// Returns `true` if the identifier is a location specifier.
177    #[inline]
178    pub fn is_location_specifier(self) -> bool {
179        self.name.is_location_specifier()
180    }
181
182    /// Returns `true` if the identifier is a mutability specifier.
183    #[inline]
184    pub fn is_mutability_specifier(self) -> bool {
185        self.name.is_mutability_specifier()
186    }
187
188    /// Returns `true` if the identifier is a visibility specifier.
189    #[inline]
190    pub fn is_visibility_specifier(self) -> bool {
191        self.name.is_visibility_specifier()
192    }
193}
194
195/// An interned string.
196///
197/// Internally, a `Symbol` is implemented as an index, and all operations
198/// (including hashing, equality, and ordering) operate on that index.
199#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
200pub struct Symbol(BaseIndex32);
201
202impl Default for Symbol {
203    #[inline]
204    fn default() -> Self {
205        Self::DUMMY
206    }
207}
208
209impl Symbol {
210    /// A dummy symbol.
211    pub const DUMMY: Self = kw::Empty;
212
213    const fn new(n: u32) -> Self {
214        Self(BaseIndex32::new(n))
215    }
216
217    /// Maps a string to its interned representation.
218    pub fn intern(string: &str) -> Self {
219        SessionGlobals::with(|g| g.symbol_interner.intern(string))
220    }
221
222    /// Maps a string to its interned representation in the given session.
223    #[inline]
224    pub fn intern_in(string: &str, session: &Session) -> Self {
225        session.intern(string)
226    }
227
228    /// "Specialization" of [`ToString`] using [`as_str`](Self::as_str).
229    #[inline]
230    #[allow(clippy::inherent_to_string_shadow_display)]
231    pub fn to_string(&self) -> String {
232        self.as_str().to_string()
233    }
234
235    /// Access the underlying string.
236    ///
237    /// Note that the lifetime of the return value is a lie. It's not the same
238    /// as `&self`, but actually tied to the lifetime of the underlying
239    /// interner. Interners are long-lived, and there are very few of them, and
240    /// this function is typically used for short-lived things, so in practice
241    /// it works out ok.
242    #[cfg_attr(debug_assertions, track_caller)]
243    pub fn as_str(&self) -> &str {
244        SessionGlobals::with(|g| unsafe { trustme::decouple_lt(g.symbol_interner.get(*self)) })
245    }
246
247    /// Access the underlying string in the given session.
248    ///
249    /// The symbol must have been interned in the given session.
250    #[inline]
251    #[cfg_attr(debug_assertions, track_caller)]
252    pub fn as_str_in(self, session: &Session) -> &str {
253        session.resolve_symbol(self)
254    }
255
256    /// Returns the internal representation of the symbol.
257    #[inline(always)]
258    pub const fn as_u32(self) -> u32 {
259        self.0.get()
260    }
261
262    /// Returns `true` if the symbol is a keyword used in the Solidity language.
263    ///
264    /// For Yul keywords, use [`is_yul_keyword`](Self::is_yul_keyword).
265    #[inline]
266    pub fn is_used_keyword(self) -> bool {
267        self < kw::After
268    }
269
270    /// Returns `true` if the symbol is a keyword reserved for possible future use.
271    #[inline]
272    pub fn is_unused_keyword(self) -> bool {
273        self >= kw::After && self <= kw::Var
274    }
275
276    /// Returns `true` if the symbol is a weak keyword and can be used in variable names.
277    #[inline]
278    pub fn is_weak_keyword(self) -> bool {
279        self >= kw::Leave && self <= kw::Builtin
280    }
281
282    /// Returns `true` if the symbol is a keyword in a Yul context. Excludes EVM builtins.
283    #[inline]
284    pub fn is_yul_keyword(self) -> bool {
285        // https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/liblangutil/Token.h#L329
286        matches!(
287            self,
288            kw::Function
289                | kw::Let
290                | kw::If
291                | kw::Switch
292                | kw::Case
293                | kw::Default
294                | kw::For
295                | kw::Break
296                | kw::Continue
297                | kw::Leave
298                | kw::True
299                | kw::False
300        )
301    }
302
303    /// Returns `true` if the symbol is a Yul EVM builtin keyword.
304    #[inline]
305    pub fn is_yul_builtin(self) -> bool {
306        (self >= kw::Add && self <= kw::Xor)
307            | matches!(self, kw::Address | kw::Byte | kw::Return | kw::Revert)
308    }
309
310    /// Returns `true` if the symbol is either a keyword, either currently in use or reserved for
311    /// possible future use.
312    #[inline]
313    pub fn is_reserved(self, yul: bool) -> bool {
314        if yul {
315            self.is_yul_keyword() | self.is_yul_builtin()
316        } else {
317            self.is_used_keyword() | self.is_unused_keyword()
318        }
319    }
320
321    /// Returns `true` if the symbol is not a reserved keyword.
322    /// See [`is_reserved`](Self::is_reserved).
323    #[inline]
324    pub fn is_non_reserved(self, yul: bool) -> bool {
325        !self.is_reserved(yul)
326    }
327
328    /// Returns `true` if the symbol is an elementary type name.
329    ///
330    /// Note that this does not include `[u]fixedMxN` types as they are not pre-interned.
331    #[inline]
332    pub fn is_elementary_type(self) -> bool {
333        self >= kw::Int && self <= kw::UFixed
334    }
335
336    /// Returns `true` if the symbol is `true` or `false`.
337    #[inline]
338    pub fn is_bool_lit(self) -> bool {
339        self == kw::False || self == kw::True
340    }
341
342    /// Returns `true` if the symbol is a location specifier.
343    #[inline]
344    pub fn is_location_specifier(self) -> bool {
345        matches!(self, kw::Calldata | kw::Memory | kw::Storage)
346    }
347
348    /// Returns `true` if the symbol is a mutability specifier.
349    #[inline]
350    pub fn is_mutability_specifier(self) -> bool {
351        matches!(self, kw::Immutable | kw::Constant)
352    }
353
354    /// Returns `true` if the symbol is a visibility specifier.
355    #[inline]
356    pub fn is_visibility_specifier(self) -> bool {
357        matches!(self, kw::Public | kw::Private | kw::Internal | kw::External)
358    }
359
360    /// Returns `true` if the symbol was interned in the compiler's `symbols!` macro.
361    #[inline]
362    pub const fn is_preinterned(self) -> bool {
363        self.as_u32() < PREINTERNED_SYMBOLS_COUNT
364    }
365}
366
367impl fmt::Debug for Symbol {
368    #[inline]
369    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370        fmt::Debug::fmt(self.as_str(), f)
371    }
372}
373
374impl fmt::Display for Symbol {
375    #[inline]
376    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
377        fmt::Display::fmt(self.as_str(), f)
378    }
379}
380
381/// Like [`Symbol`], but for byte strings.
382///
383/// [`ByteSymbol`] is used less widely, so it has fewer operations defined than [`Symbol`].
384#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
385pub struct ByteSymbol(BaseIndex32);
386
387impl ByteSymbol {
388    /// Maps a string to its interned representation.
389    pub fn intern(byte_str: &[u8]) -> Self {
390        SessionGlobals::with(|g| g.symbol_interner.intern_byte_str(byte_str))
391    }
392
393    /// Access the underlying byte string.
394    ///
395    /// See [`Symbol::as_str`] for more information.
396    #[cfg_attr(debug_assertions, track_caller)]
397    pub fn as_byte_str(&self) -> &[u8] {
398        SessionGlobals::with(|g| unsafe {
399            trustme::decouple_lt(g.symbol_interner.get_byte_str(*self))
400        })
401    }
402
403    /// Access the underlying byte string in the given session.
404    ///
405    /// The symbol must have been interned in the given session.
406    #[cfg_attr(debug_assertions, track_caller)]
407    pub fn as_byte_str_in(self, session: &Session) -> &[u8] {
408        session.resolve_byte_str(self)
409    }
410
411    /// Returns the internal representation of the symbol.
412    #[inline(always)]
413    pub fn as_u32(self) -> u32 {
414        self.0.get()
415    }
416}
417
418impl fmt::Debug for ByteSymbol {
419    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
420        fmt::Debug::fmt(self.as_byte_str(), f)
421    }
422}
423
424/// Symbol interner.
425///
426/// Initialized in `SessionGlobals` with the `symbols!` macro's initial symbols.
427pub(crate) struct Interner {
428    inner: inturn::BytesInterner<ByteSymbol, solar_data_structures::map::FxBuildHasher>,
429}
430
431impl Interner {
432    pub(crate) fn fresh() -> Self {
433        Self::prefill(PREINTERNED)
434    }
435
436    pub(crate) fn prefill(init: &[&'static str]) -> Self {
437        let mut inner =
438            inturn::BytesInterner::with_capacity_and_hasher(init.len() * 4, Default::default());
439        inner.intern_many_mut_static(init.iter().map(|s| s.as_bytes()));
440        Self { inner }
441    }
442
443    #[inline]
444    pub(crate) fn intern(&self, string: &str) -> Symbol {
445        Symbol(self.inner.intern(string.as_bytes()).0)
446    }
447
448    #[inline]
449    #[cfg_attr(debug_assertions, track_caller)]
450    pub(crate) fn get(&self, symbol: Symbol) -> &str {
451        let s = self.inner.resolve(ByteSymbol(symbol.0));
452        // SAFETY: `Symbol` can only be constructed from UTF-8 strings in `intern`.
453        unsafe { std::str::from_utf8_unchecked(s) }
454    }
455
456    #[inline]
457    pub(crate) fn intern_byte_str(&self, byte_str: &[u8]) -> ByteSymbol {
458        self.inner.intern(byte_str)
459    }
460
461    #[inline]
462    #[cfg_attr(debug_assertions, track_caller)]
463    pub(crate) fn get_byte_str(&self, symbol: ByteSymbol) -> &[u8] {
464        self.inner.resolve(symbol)
465    }
466
467    fn trace_stats(&mut self) {
468        if enabled!(tracing::Level::TRACE) {
469            self.trace_stats_impl();
470        }
471    }
472
473    #[inline(never)]
474    fn trace_stats_impl(&mut self) {
475        let mut lengths = self.inner.iter().map(|(_, s)| s.len()).collect::<Vec<_>>();
476        lengths.sort_unstable();
477        let len = lengths.len();
478        assert!(len > 0);
479        let bytes = lengths.iter().copied().sum::<usize>();
480        trace!(
481            preinterned=PREINTERNED_SYMBOLS_COUNT,
482            len,
483            bytes,
484            max=lengths.last().copied().unwrap_or(0),
485            mean=%format!("{:.2}", (bytes as f64 / len as f64)),
486            median=%format!("{:.2}", if len.is_multiple_of(2) {
487                (lengths[len / 2 - 1] + lengths[len / 2]) as f64 / 2.0
488            } else {
489                lengths[len / 2] as f64
490            }),
491            "Interner stats",
492        );
493    }
494}
495
496impl inturn::InternerSymbol for ByteSymbol {
497    #[inline]
498    fn try_from_usize(n: usize) -> Option<Self> {
499        BaseIndex32::try_from_usize(n).map(Self)
500    }
501
502    #[inline]
503    fn to_usize(self) -> usize {
504        self.0.index()
505    }
506}
507
508impl Drop for Interner {
509    fn drop(&mut self) {
510        self.trace_stats();
511    }
512}
513
514// This module has a very short name because it's used a lot.
515/// This module contains all the defined keyword `Symbol`s.
516///
517/// Given that `kw` is imported, use them like `kw::keyword_name`.
518/// For example `kw::For` or `kw::Break`.
519pub mod kw {
520    use crate::Symbol;
521
522    #[doc(inline)]
523    pub use super::kw_generated::*;
524
525    /// Returns the boolean keyword for the given value.
526    #[inline]
527    pub const fn boolean(b: bool) -> Symbol {
528        if b { True } else { False }
529    }
530
531    /// Returns the `int` keyword for the given byte (**not bit**) size.
532    ///
533    /// If `n` is 0, returns [`kw::Uint`](Int).
534    ///
535    /// # Panics
536    ///
537    /// Panics if `n` is greater than 32.
538    #[inline]
539    #[track_caller]
540    pub const fn int(n: u8) -> Symbol {
541        assert!(n <= 32);
542        Symbol::new(Int.as_u32() + n as u32)
543    }
544
545    /// Returns the `uint` keyword for the given byte (**not bit**) size.
546    ///
547    /// If `n` is 0, returns [`kw::UInt`](UInt).
548    ///
549    /// # Panics
550    ///
551    /// Panics if `n` is greater than 32.
552    #[inline]
553    #[track_caller]
554    pub const fn uint(n: u8) -> Symbol {
555        assert!(n <= 32);
556        Symbol::new(UInt.as_u32() + n as u32)
557    }
558
559    /// Returns the `bytes` keyword for the given byte size.
560    ///
561    /// # Panics
562    ///
563    /// Panics if `n` is 0 or is greater than 32.
564    #[inline]
565    #[track_caller]
566    pub const fn fixed_bytes(n: u8) -> Symbol {
567        assert!(n > 0 && n <= 32);
568        Symbol::new(Bytes.as_u32() + n as u32)
569    }
570}
571
572// This module has a very short name because it's used a lot.
573/// This module contains all the defined non-keyword `Symbol`s.
574///
575/// Given that `sym` is imported, use them like `sym::symbol_name`.
576/// For example `sym::rustfmt` or `sym::u8`.
577pub mod sym {
578    use super::Symbol;
579
580    #[doc(inline)]
581    pub use super::sym_generated::*;
582
583    /// Get the symbol for an integer.
584    ///
585    /// The first few non-negative integers each have a static symbol and therefore are fast.
586    pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
587        if let Ok(idx @ 0..=9) = n.try_into() {
588            return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
589        }
590        Symbol::intern(itoa::Buffer::new().format(n))
591    }
592}
593
594// The proc macro code for this is in `crates/macros/src/symbols/mod.rs`.
595symbols! {
596    // Solidity keywords.
597    // When modifying this, also update all the `is_keyword` functions in this file.
598    // Modified from the `TOKEN_LIST` macro in Solc: https://github.com/argotorg/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/liblangutil/Token.h#L67
599    Keywords {
600        // Special symbols used internally.
601        Empty:       "",
602
603        Abstract:    "abstract",
604        Anonymous:   "anonymous",
605        As:          "as",
606        Assembly:    "assembly",
607        Break:       "break",
608        Calldata:    "calldata",
609        Catch:       "catch",
610        Constant:    "constant",
611        Constructor: "constructor",
612        Continue:    "continue",
613        Contract:    "contract",
614        Delete:      "delete",
615        Do:          "do",
616        Else:        "else",
617        Emit:        "emit",
618        Enum:        "enum",
619        Event:       "event",
620        External:    "external",
621        Fallback:    "fallback",
622        For:         "for",
623        Function:    "function",
624        Hex:         "hex",
625        If:          "if",
626        Immutable:   "immutable",
627        Import:      "import",
628        Indexed:     "indexed",
629        Interface:   "interface",
630        Internal:    "internal",
631        Is:          "is",
632        Library:     "library",
633        Mapping:     "mapping",
634        Memory:      "memory",
635        Modifier:    "modifier",
636        New:         "new",
637        Override:    "override",
638        Payable:     "payable",
639        Pragma:      "pragma",
640        Private:     "private",
641        Public:      "public",
642        Pure:        "pure",
643        Receive:     "receive",
644        Return:      "return",
645        Returns:     "returns",
646        Storage:     "storage",
647        Struct:      "struct",
648        Throw:       "throw",
649        Try:         "try",
650        Type:        "type",
651        Unchecked:   "unchecked",
652        Unicode:     "unicode",
653        Using:       "using",
654        View:        "view",
655        Virtual:     "virtual",
656        While:       "while",
657
658        // Types.
659        Int:         "int",
660        Int8:        "int8",
661        Int16:       "int16",
662        Int24:       "int24",
663        Int32:       "int32",
664        Int40:       "int40",
665        Int48:       "int48",
666        Int56:       "int56",
667        Int64:       "int64",
668        Int72:       "int72",
669        Int80:       "int80",
670        Int88:       "int88",
671        Int96:       "int96",
672        Int104:      "int104",
673        Int112:      "int112",
674        Int120:      "int120",
675        Int128:      "int128",
676        Int136:      "int136",
677        Int144:      "int144",
678        Int152:      "int152",
679        Int160:      "int160",
680        Int168:      "int168",
681        Int176:      "int176",
682        Int184:      "int184",
683        Int192:      "int192",
684        Int200:      "int200",
685        Int208:      "int208",
686        Int216:      "int216",
687        Int224:      "int224",
688        Int232:      "int232",
689        Int240:      "int240",
690        Int248:      "int248",
691        Int256:      "int256",
692        UInt:        "uint",
693        UInt8:       "uint8",
694        UInt16:      "uint16",
695        UInt24:      "uint24",
696        UInt32:      "uint32",
697        UInt40:      "uint40",
698        UInt48:      "uint48",
699        UInt56:      "uint56",
700        UInt64:      "uint64",
701        UInt72:      "uint72",
702        UInt80:      "uint80",
703        UInt88:      "uint88",
704        UInt96:      "uint96",
705        UInt104:     "uint104",
706        UInt112:     "uint112",
707        UInt120:     "uint120",
708        UInt128:     "uint128",
709        UInt136:     "uint136",
710        UInt144:     "uint144",
711        UInt152:     "uint152",
712        UInt160:     "uint160",
713        UInt168:     "uint168",
714        UInt176:     "uint176",
715        UInt184:     "uint184",
716        UInt192:     "uint192",
717        UInt200:     "uint200",
718        UInt208:     "uint208",
719        UInt216:     "uint216",
720        UInt224:     "uint224",
721        UInt232:     "uint232",
722        UInt240:     "uint240",
723        UInt248:     "uint248",
724        UInt256:     "uint256",
725        Bytes:       "bytes",
726        Bytes1:      "bytes1",
727        Bytes2:      "bytes2",
728        Bytes3:      "bytes3",
729        Bytes4:      "bytes4",
730        Bytes5:      "bytes5",
731        Bytes6:      "bytes6",
732        Bytes7:      "bytes7",
733        Bytes8:      "bytes8",
734        Bytes9:      "bytes9",
735        Bytes10:     "bytes10",
736        Bytes11:     "bytes11",
737        Bytes12:     "bytes12",
738        Bytes13:     "bytes13",
739        Bytes14:     "bytes14",
740        Bytes15:     "bytes15",
741        Bytes16:     "bytes16",
742        Bytes17:     "bytes17",
743        Bytes18:     "bytes18",
744        Bytes19:     "bytes19",
745        Bytes20:     "bytes20",
746        Bytes21:     "bytes21",
747        Bytes22:     "bytes22",
748        Bytes23:     "bytes23",
749        Bytes24:     "bytes24",
750        Bytes25:     "bytes25",
751        Bytes26:     "bytes26",
752        Bytes27:     "bytes27",
753        Bytes28:     "bytes28",
754        Bytes29:     "bytes29",
755        Bytes30:     "bytes30",
756        Bytes31:     "bytes31",
757        Bytes32:     "bytes32",
758        String:      "string",
759        Address:     "address",
760        Bool:        "bool",
761        Fixed:       "fixed",
762        UFixed:      "ufixed",
763
764        // Number subdenominations.
765        Wei:         "wei",
766        Gwei:        "gwei",
767        Ether:       "ether",
768        Seconds:     "seconds",
769        Minutes:     "minutes",
770        Hours:       "hours",
771        Days:        "days",
772        Weeks:       "weeks",
773        Years:       "years",
774
775        // Literals.
776        False:       "false",
777        True:        "true",
778
779        // Reserved for future use.
780        After:       "after",
781        Alias:       "alias",
782        Apply:       "apply",
783        Auto:        "auto",
784        Byte:        "byte",
785        Case:        "case",
786        CopyOf:      "copyof",
787        Default:     "default",
788        Define:      "define",
789        Final:       "final",
790        Implements:  "implements",
791        In:          "in",
792        Inline:      "inline",
793        Let:         "let",
794        Macro:       "macro",
795        Match:       "match",
796        Mutable:     "mutable",
797        NullLiteral: "null",
798        Of:          "of",
799        Partial:     "partial",
800        Promise:     "promise",
801        Reference:   "reference",
802        Relocatable: "relocatable",
803        Sealed:      "sealed",
804        Sizeof:      "sizeof",
805        Static:      "static",
806        Supports:    "supports",
807        Switch:      "switch",
808        Typedef:     "typedef",
809        TypeOf:      "typeof",
810        Var:         "var",
811
812        // All the following keywords are 'weak' keywords,
813        // which means they can be used as variable names.
814
815        // Yul specific keywords.
816        Leave:       "leave",
817        Revert:      "revert",
818
819        // Yul EVM builtins.
820        // Some builtins have already been previously declared, so they can't be redeclared here.
821        // See `is_yul_builtin`.
822        // https://docs.soliditylang.org/en/latest/yul.html#evm-dialect
823        // TODO: The remaining internal dialect builtins.
824        Add:            "add",
825        Addmod:         "addmod",
826        And:            "and",
827        Balance:        "balance",
828        Basefee:        "basefee",
829        Blobbasefee:    "blobbasefee",
830        Blobhash:       "blobhash",
831        Blockhash:      "blockhash",
832        Call:           "call",
833        Callcode:       "callcode",
834        Calldatacopy:   "calldatacopy",
835        Calldataload:   "calldataload",
836        Calldatasize:   "calldatasize",
837        Caller:         "caller",
838        Callvalue:      "callvalue",
839        Chainid:        "chainid",
840        Coinbase:       "coinbase",
841        Create:         "create",
842        Create2:        "create2",
843        Delegatecall:   "delegatecall",
844        Difficulty:     "difficulty",
845        Div:            "div",
846        Eq:             "eq",
847        Exp:            "exp",
848        Extcodecopy:    "extcodecopy",
849        Extcodehash:    "extcodehash",
850        Extcodesize:    "extcodesize",
851        Gas:            "gas",
852        Gaslimit:       "gaslimit",
853        Gasprice:       "gasprice",
854        Gt:             "gt",
855        Invalid:        "invalid",
856        Iszero:         "iszero",
857        Keccak256:      "keccak256",
858        Log0:           "log0",
859        Log1:           "log1",
860        Log2:           "log2",
861        Log3:           "log3",
862        Log4:           "log4",
863        Lt:             "lt",
864        Mcopy:          "mcopy",
865        Mload:          "mload",
866        Mod:            "mod",
867        Msize:          "msize",
868        Mstore:         "mstore",
869        Mstore8:        "mstore8",
870        Mul:            "mul",
871        Mulmod:         "mulmod",
872        Not:            "not",
873        Number:         "number",
874        Or:             "or",
875        Origin:         "origin",
876        Pop:            "pop",
877        Prevrandao:     "prevrandao",
878        Returndatacopy: "returndatacopy",
879        Returndatasize: "returndatasize",
880        Sar:            "sar",
881        Sdiv:           "sdiv",
882        Selfbalance:    "selfbalance",
883        Selfdestruct:   "selfdestruct",
884        Sgt:            "sgt",
885        Shl:            "shl",
886        Shr:            "shr",
887        Signextend:     "signextend",
888        Sload:          "sload",
889        Slt:            "slt",
890        Smod:           "smod",
891        Sstore:         "sstore",
892        Staticcall:     "staticcall",
893        Stop:           "stop",
894        Sub:            "sub",
895        Timestamp:      "timestamp",
896        Tload:          "tload",
897        Tstore:         "tstore",
898        Xor:            "xor",
899
900        // Experimental Solidity specific keywords.
901        Class:         "class",
902        Instantiation: "instantiation",
903        Integer:       "Integer",
904        Itself:        "itself",
905        StaticAssert:  "static_assert",
906        Builtin:       "__builtin",
907        ForAll:        "forall",
908    }
909
910    // Pre-interned symbols that can be referred to with `sym::*`.
911    //
912    // The symbol is the stringified identifier unless otherwise specified, in
913    // which case the name should mention the non-identifier punctuation.
914    // E.g. `sym::proc_dash_macro` represents "proc-macro", and it shouldn't be
915    // called `sym::proc_macro` because then it's easy to mistakenly think it
916    // represents "proc_macro".
917    //
918    // As well as the symbols listed, there are symbols for the strings
919    // "0", "1", ..., "9", which are accessible via `sym::integer`.
920    //
921    // The proc macro will abort if symbols are not in alphabetical order (as
922    // defined by `impl Ord for str`) or if any symbols are duplicated. Vim
923    // users can sort the list by selecting it and executing the command
924    // `:'<,'>!LC_ALL=C sort`.
925    //
926    // There is currently no checking that all symbols are used; that would be
927    // nice to have.
928    Symbols {
929        X,
930        __tmp_struct,
931        abi,
932        abicoder,
933        assert,
934        at,
935        block,
936        code,
937        codehash,
938        concat,
939        creationCode,
940        data,
941        decode,
942        ecrecover,
943        encode,
944        encodeCall,
945        encodePacked,
946        encodeWithSelector,
947        encodeWithSignature,
948        error,
949        experimental,
950        from,
951        gasleft,
952        global,
953        interfaceId,
954        layout,
955        length,
956        max,
957        memory_dash_safe: "memory-safe",
958        min,
959        msg,
960        name,
961        object,
962        push,
963        require,
964        ripemd160,
965        runtimeCode,
966        selector,
967        send,
968        sender,
969        sha256,
970        sig,
971        solidity,
972        super_: "super",
973        this,
974        transfer,
975        transient,
976        tx,
977        underscore: "_",
978        unwrap,
979        value,
980        wrap,
981        x,
982    }
983}
984
985#[cfg(test)]
986mod tests {
987    use super::*;
988
989    #[test]
990    fn interner_tests() {
991        let i = Interner::prefill(&[]);
992        // first one is zero:
993        assert_eq!(i.intern("dog"), Symbol::new(0));
994        // re-use gets the same entry:
995        assert_eq!(i.intern("dog"), Symbol::new(0));
996        // different string gets a different #:
997        assert_eq!(i.intern("cat"), Symbol::new(1));
998        assert_eq!(i.intern("cat"), Symbol::new(1));
999        // dog is still at zero
1000        assert_eq!(i.intern("dog"), Symbol::new(0));
1001    }
1002
1003    #[test]
1004    fn defaults() {
1005        assert_eq!(Symbol::DUMMY, Symbol::new(0));
1006        assert_eq!(Symbol::DUMMY, Symbol::default());
1007        assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
1008        assert_eq!(Ident::DUMMY, Ident::default());
1009
1010        crate::enter(|| {
1011            assert_eq!(Symbol::DUMMY.as_str(), "");
1012            assert_eq!(Symbol::DUMMY.to_string(), "");
1013            assert_eq!(Ident::DUMMY.as_str(), "");
1014            assert_eq!(Ident::DUMMY.to_string(), "");
1015        });
1016    }
1017}