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