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/// Symbol interner.
357///
358/// Initialized in `SessionGlobals` with the `symbols!` macro's initial symbols.
359pub(crate) struct Interner(lasso::ThreadedRodeo<Symbol, solar_data_structures::map::FxBuildHasher>);
360
361impl Interner {
362    pub(crate) fn fresh() -> Self {
363        Self::prefill(PREINTERNED)
364    }
365
366    pub(crate) fn prefill(init: &[&'static str]) -> Self {
367        let capacity = if init.is_empty() {
368            Default::default()
369        } else {
370            let actual_string = init.len();
371            let strings = actual_string.next_power_of_two();
372            let actual_bytes = PREINTERNED_SYMBOLS_BYTES as usize;
373            let bytes = actual_bytes.next_power_of_two().max(4096);
374            trace!(strings, bytes, "prefill capacity");
375            lasso::Capacity::new(strings, std::num::NonZeroUsize::new(bytes).unwrap())
376        };
377        let rodeo = lasso::ThreadedRodeo::with_capacity_and_hasher(capacity, Default::default());
378        for &s in init {
379            rodeo.get_or_intern_static(s);
380        }
381        Self(rodeo)
382    }
383
384    #[inline]
385    fn intern(&self, string: &str) -> Symbol {
386        self.0.get_or_intern(string)
387    }
388
389    #[inline]
390    fn get(&self, symbol: Symbol) -> &str {
391        self.0.resolve(&symbol)
392    }
393}
394
395unsafe impl lasso::Key for Symbol {
396    #[inline]
397    fn into_usize(self) -> usize {
398        self.0.index()
399    }
400
401    #[inline]
402    fn try_from_usize(value: usize) -> Option<Self> {
403        BaseIndex32::try_from_usize(value).map(Self)
404    }
405}
406
407// This module has a very short name because it's used a lot.
408/// This module contains all the defined keyword `Symbol`s.
409///
410/// Given that `kw` is imported, use them like `kw::keyword_name`.
411/// For example `kw::For` or `kw::Break`.
412pub mod kw {
413    use crate::Symbol;
414
415    #[doc(inline)]
416    pub use super::kw_generated::*;
417
418    /// Returns the boolean keyword for the given value.
419    #[inline]
420    pub const fn boolean(b: bool) -> Symbol {
421        if b {
422            True
423        } else {
424            False
425        }
426    }
427
428    /// Returns the `int` keyword for the given byte (**not bit**) size.
429    ///
430    /// If `n` is 0, returns [`kw::Uint`](Int).
431    ///
432    /// # Panics
433    ///
434    /// Panics if `n` is greater than 32.
435    #[inline]
436    #[track_caller]
437    pub const fn int(n: u8) -> Symbol {
438        assert!(n <= 32);
439        Symbol::new(Int.as_u32() + n as u32)
440    }
441
442    /// Returns the `uint` keyword for the given byte (**not bit**) size.
443    ///
444    /// If `n` is 0, returns [`kw::UInt`](UInt).
445    ///
446    /// # Panics
447    ///
448    /// Panics if `n` is greater than 32.
449    #[inline]
450    #[track_caller]
451    pub const fn uint(n: u8) -> Symbol {
452        assert!(n <= 32);
453        Symbol::new(UInt.as_u32() + n as u32)
454    }
455
456    /// Returns the `bytes` keyword for the given byte size.
457    ///
458    /// # Panics
459    ///
460    /// Panics if `n` is 0 or is greater than 32.
461    #[inline]
462    #[track_caller]
463    pub const fn fixed_bytes(n: u8) -> Symbol {
464        assert!(n > 0 && n <= 32);
465        Symbol::new(Bytes.as_u32() + n as u32)
466    }
467}
468
469// This module has a very short name because it's used a lot.
470/// This module contains all the defined non-keyword `Symbol`s.
471///
472/// Given that `sym` is imported, use them like `sym::symbol_name`.
473/// For example `sym::rustfmt` or `sym::u8`.
474pub mod sym {
475    use super::Symbol;
476
477    #[doc(inline)]
478    pub use super::sym_generated::*;
479
480    /// Get the symbol for an integer.
481    ///
482    /// The first few non-negative integers each have a static symbol and therefore are fast.
483    pub fn integer<N: TryInto<usize> + Copy + itoa::Integer>(n: N) -> Symbol {
484        if let Ok(idx @ 0..=9) = n.try_into() {
485            return Symbol::new(super::SYMBOL_DIGITS_BASE + idx as u32);
486        }
487        Symbol::intern(itoa::Buffer::new().format(n))
488    }
489}
490
491// The proc macro code for this is in `crates/macros/src/symbols/mod.rs`.
492symbols! {
493    // Solidity keywords.
494    // When modifying this, also update all the `is_keyword` functions in this file.
495    // Modified from the `TOKEN_LIST` macro in Solc: https://github.com/ethereum/solidity/blob/194b114664c7daebc2ff68af3c573272f5d28913/liblangutil/Token.h#L67
496    Keywords {
497        // Special symbols used internally.
498        Empty:       "",
499
500        Abstract:    "abstract",
501        Anonymous:   "anonymous",
502        As:          "as",
503        Assembly:    "assembly",
504        Break:       "break",
505        Calldata:    "calldata",
506        Catch:       "catch",
507        Constant:    "constant",
508        Constructor: "constructor",
509        Continue:    "continue",
510        Contract:    "contract",
511        Delete:      "delete",
512        Do:          "do",
513        Else:        "else",
514        Emit:        "emit",
515        Enum:        "enum",
516        Event:       "event",
517        External:    "external",
518        Fallback:    "fallback",
519        For:         "for",
520        Function:    "function",
521        Hex:         "hex",
522        If:          "if",
523        Immutable:   "immutable",
524        Import:      "import",
525        Indexed:     "indexed",
526        Interface:   "interface",
527        Internal:    "internal",
528        Is:          "is",
529        Library:     "library",
530        Mapping:     "mapping",
531        Memory:      "memory",
532        Modifier:    "modifier",
533        New:         "new",
534        Override:    "override",
535        Payable:     "payable",
536        Pragma:      "pragma",
537        Private:     "private",
538        Public:      "public",
539        Pure:        "pure",
540        Receive:     "receive",
541        Return:      "return",
542        Returns:     "returns",
543        Storage:     "storage",
544        Struct:      "struct",
545        Throw:       "throw",
546        Try:         "try",
547        Type:        "type",
548        Unchecked:   "unchecked",
549        Unicode:     "unicode",
550        Using:       "using",
551        View:        "view",
552        Virtual:     "virtual",
553        While:       "while",
554
555        // Types.
556        Int:         "int",
557        Int8:        "int8",
558        Int16:       "int16",
559        Int24:       "int24",
560        Int32:       "int32",
561        Int40:       "int40",
562        Int48:       "int48",
563        Int56:       "int56",
564        Int64:       "int64",
565        Int72:       "int72",
566        Int80:       "int80",
567        Int88:       "int88",
568        Int96:       "int96",
569        Int104:      "int104",
570        Int112:      "int112",
571        Int120:      "int120",
572        Int128:      "int128",
573        Int136:      "int136",
574        Int144:      "int144",
575        Int152:      "int152",
576        Int160:      "int160",
577        Int168:      "int168",
578        Int176:      "int176",
579        Int184:      "int184",
580        Int192:      "int192",
581        Int200:      "int200",
582        Int208:      "int208",
583        Int216:      "int216",
584        Int224:      "int224",
585        Int232:      "int232",
586        Int240:      "int240",
587        Int248:      "int248",
588        Int256:      "int256",
589        UInt:        "uint",
590        UInt8:       "uint8",
591        UInt16:      "uint16",
592        UInt24:      "uint24",
593        UInt32:      "uint32",
594        UInt40:      "uint40",
595        UInt48:      "uint48",
596        UInt56:      "uint56",
597        UInt64:      "uint64",
598        UInt72:      "uint72",
599        UInt80:      "uint80",
600        UInt88:      "uint88",
601        UInt96:      "uint96",
602        UInt104:     "uint104",
603        UInt112:     "uint112",
604        UInt120:     "uint120",
605        UInt128:     "uint128",
606        UInt136:     "uint136",
607        UInt144:     "uint144",
608        UInt152:     "uint152",
609        UInt160:     "uint160",
610        UInt168:     "uint168",
611        UInt176:     "uint176",
612        UInt184:     "uint184",
613        UInt192:     "uint192",
614        UInt200:     "uint200",
615        UInt208:     "uint208",
616        UInt216:     "uint216",
617        UInt224:     "uint224",
618        UInt232:     "uint232",
619        UInt240:     "uint240",
620        UInt248:     "uint248",
621        UInt256:     "uint256",
622        Bytes:       "bytes",
623        Bytes1:      "bytes1",
624        Bytes2:      "bytes2",
625        Bytes3:      "bytes3",
626        Bytes4:      "bytes4",
627        Bytes5:      "bytes5",
628        Bytes6:      "bytes6",
629        Bytes7:      "bytes7",
630        Bytes8:      "bytes8",
631        Bytes9:      "bytes9",
632        Bytes10:     "bytes10",
633        Bytes11:     "bytes11",
634        Bytes12:     "bytes12",
635        Bytes13:     "bytes13",
636        Bytes14:     "bytes14",
637        Bytes15:     "bytes15",
638        Bytes16:     "bytes16",
639        Bytes17:     "bytes17",
640        Bytes18:     "bytes18",
641        Bytes19:     "bytes19",
642        Bytes20:     "bytes20",
643        Bytes21:     "bytes21",
644        Bytes22:     "bytes22",
645        Bytes23:     "bytes23",
646        Bytes24:     "bytes24",
647        Bytes25:     "bytes25",
648        Bytes26:     "bytes26",
649        Bytes27:     "bytes27",
650        Bytes28:     "bytes28",
651        Bytes29:     "bytes29",
652        Bytes30:     "bytes30",
653        Bytes31:     "bytes31",
654        Bytes32:     "bytes32",
655        String:      "string",
656        Address:     "address",
657        Bool:        "bool",
658        Fixed:       "fixed",
659        UFixed:      "ufixed",
660
661        // Number subdenominations.
662        Wei:         "wei",
663        Gwei:        "gwei",
664        Ether:       "ether",
665        Seconds:     "seconds",
666        Minutes:     "minutes",
667        Hours:       "hours",
668        Days:        "days",
669        Weeks:       "weeks",
670        Years:       "years",
671
672        // Literals.
673        False:       "false",
674        True:        "true",
675
676        // Reserved for future use.
677        After:       "after",
678        Alias:       "alias",
679        Apply:       "apply",
680        Auto:        "auto",
681        Byte:        "byte",
682        Case:        "case",
683        CopyOf:      "copyof",
684        Default:     "default",
685        Define:      "define",
686        Final:       "final",
687        Implements:  "implements",
688        In:          "in",
689        Inline:      "inline",
690        Let:         "let",
691        Macro:       "macro",
692        Match:       "match",
693        Mutable:     "mutable",
694        NullLiteral: "null",
695        Of:          "of",
696        Partial:     "partial",
697        Promise:     "promise",
698        Reference:   "reference",
699        Relocatable: "relocatable",
700        Sealed:      "sealed",
701        Sizeof:      "sizeof",
702        Static:      "static",
703        Supports:    "supports",
704        Switch:      "switch",
705        Typedef:     "typedef",
706        TypeOf:      "typeof",
707        Var:         "var",
708
709        // All the following keywords are 'weak' keywords,
710        // which means they can be used as variable names.
711
712        // Yul specific keywords.
713        Leave:       "leave",
714        Revert:      "revert",
715
716        // Yul EVM builtins.
717        // Some builtins have already been previously declared, so they can't be redeclared here.
718        // See `is_yul_builtin`.
719        // https://docs.soliditylang.org/en/latest/yul.html#evm-dialect
720        // TODO: The remaining internal dialect builtins.
721        Add:            "add",
722        Addmod:         "addmod",
723        And:            "and",
724        Balance:        "balance",
725        Basefee:        "basefee",
726        Blobbasefee:    "blobbasefee",
727        Blobhash:       "blobhash",
728        Blockhash:      "blockhash",
729        Call:           "call",
730        Callcode:       "callcode",
731        Calldatacopy:   "calldatacopy",
732        Calldataload:   "calldataload",
733        Calldatasize:   "calldatasize",
734        Caller:         "caller",
735        Callvalue:      "callvalue",
736        Chainid:        "chainid",
737        Coinbase:       "coinbase",
738        Create:         "create",
739        Create2:        "create2",
740        Delegatecall:   "delegatecall",
741        Difficulty:     "difficulty",
742        Div:            "div",
743        Eq:             "eq",
744        Exp:            "exp",
745        Extcodecopy:    "extcodecopy",
746        Extcodehash:    "extcodehash",
747        Extcodesize:    "extcodesize",
748        Gas:            "gas",
749        Gaslimit:       "gaslimit",
750        Gasprice:       "gasprice",
751        Gt:             "gt",
752        Invalid:        "invalid",
753        Iszero:         "iszero",
754        Keccak256:      "keccak256",
755        Log0:           "log0",
756        Log1:           "log1",
757        Log2:           "log2",
758        Log3:           "log3",
759        Log4:           "log4",
760        Lt:             "lt",
761        Mcopy:          "mcopy",
762        Mload:          "mload",
763        Mod:            "mod",
764        Msize:          "msize",
765        Mstore:         "mstore",
766        Mstore8:        "mstore8",
767        Mul:            "mul",
768        Mulmod:         "mulmod",
769        Not:            "not",
770        Number:         "number",
771        Or:             "or",
772        Origin:         "origin",
773        Pop:            "pop",
774        Prevrandao:     "prevrandao",
775        Returndatacopy: "returndatacopy",
776        Returndatasize: "returndatasize",
777        Sar:            "sar",
778        Sdiv:           "sdiv",
779        Selfbalance:    "selfbalance",
780        Selfdestruct:   "selfdestruct",
781        Sgt:            "sgt",
782        Shl:            "shl",
783        Shr:            "shr",
784        Signextend:     "signextend",
785        Sload:          "sload",
786        Slt:            "slt",
787        Smod:           "smod",
788        Sstore:         "sstore",
789        Staticcall:     "staticcall",
790        Stop:           "stop",
791        Sub:            "sub",
792        Timestamp:      "timestamp",
793        Tload:          "tload",
794        Tstore:         "tstore",
795        Xor:            "xor",
796
797        // Experimental Solidity specific keywords.
798        Class:         "class",
799        Instantiation: "instantiation",
800        Integer:       "Integer",
801        Itself:        "itself",
802        StaticAssert:  "static_assert",
803        Builtin:       "__builtin",
804        ForAll:        "forall",
805    }
806
807    // Pre-interned symbols that can be referred to with `sym::*`.
808    //
809    // The symbol is the stringified identifier unless otherwise specified, in
810    // which case the name should mention the non-identifier punctuation.
811    // E.g. `sym::proc_dash_macro` represents "proc-macro", and it shouldn't be
812    // called `sym::proc_macro` because then it's easy to mistakenly think it
813    // represents "proc_macro".
814    //
815    // As well as the symbols listed, there are symbols for the strings
816    // "0", "1", ..., "9", which are accessible via `sym::integer`.
817    //
818    // The proc macro will abort if symbols are not in alphabetical order (as
819    // defined by `impl Ord for str`) or if any symbols are duplicated. Vim
820    // users can sort the list by selecting it and executing the command
821    // `:'<,'>!LC_ALL=C sort`.
822    //
823    // There is currently no checking that all symbols are used; that would be
824    // nice to have.
825    Symbols {
826        X,
827        __tmp_struct,
828        abi,
829        abicoder,
830        assert,
831        at,
832        block,
833        code,
834        codehash,
835        concat,
836        creationCode,
837        data,
838        decode,
839        ecrecover,
840        encode,
841        encodeCall,
842        encodePacked,
843        encodeWithSelector,
844        encodeWithSignature,
845        error,
846        experimental,
847        from,
848        gasleft,
849        global,
850        interfaceId,
851        layout,
852        length,
853        max,
854        min,
855        msg,
856        name,
857        object,
858        push,
859        require,
860        ripemd160,
861        runtimeCode,
862        selector,
863        send,
864        sender,
865        sha256,
866        sig,
867        solidity,
868        super_: "super",
869        this,
870        transfer,
871        transient,
872        tx,
873        underscore: "_",
874        unwrap,
875        value,
876        wrap,
877        x,
878    }
879}
880
881#[cfg(test)]
882mod tests {
883    use super::*;
884
885    #[test]
886    fn interner_tests() {
887        let i = Interner::prefill(&[]);
888        // first one is zero:
889        assert_eq!(i.intern("dog"), Symbol::new(0));
890        // re-use gets the same entry:
891        assert_eq!(i.intern("dog"), Symbol::new(0));
892        // different string gets a different #:
893        assert_eq!(i.intern("cat"), Symbol::new(1));
894        assert_eq!(i.intern("cat"), Symbol::new(1));
895        // dog is still at zero
896        assert_eq!(i.intern("dog"), Symbol::new(0));
897    }
898
899    #[test]
900    fn defaults() {
901        assert_eq!(Symbol::DUMMY, Symbol::new(0));
902        assert_eq!(Symbol::DUMMY, Symbol::default());
903        assert_eq!(Ident::DUMMY, Ident::new(Symbol::DUMMY, Span::DUMMY));
904        assert_eq!(Ident::DUMMY, Ident::default());
905
906        crate::enter(|| {
907            assert_eq!(Symbol::DUMMY.as_str(), "");
908            assert_eq!(Symbol::DUMMY.to_string(), "");
909            assert_eq!(Ident::DUMMY.as_str(), "");
910            assert_eq!(Ident::DUMMY.to_string(), "");
911        });
912    }
913}