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