solar_interface/
symbol.rs

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