Skip to main content

solar_interface/
symbol.rs

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