fe_analyzer/namespace/
types.rs

1use crate::context::AnalyzerContext;
2use crate::display::DisplayWithDb;
3use crate::display::Displayable;
4use crate::errors::TypeError;
5use crate::namespace::items::{
6    ContractId, EnumId, FunctionId, FunctionSigId, ImplId, Item, StructId, TraitId,
7};
8use crate::AnalyzerDb;
9
10use fe_common::impl_intern_key;
11use fe_common::Span;
12use num_bigint::BigInt;
13use num_traits::ToPrimitive;
14use smol_str::SmolStr;
15use std::fmt;
16use std::rc::Rc;
17use std::str::FromStr;
18use strum::{AsRefStr, EnumIter, EnumString};
19
20pub fn u256_min() -> BigInt {
21    BigInt::from(0)
22}
23
24pub fn u256_max() -> BigInt {
25    BigInt::from(2).pow(256) - 1
26}
27
28pub fn i256_max() -> BigInt {
29    BigInt::from(2).pow(255) - 1
30}
31
32pub fn i256_min() -> BigInt {
33    BigInt::from(-2).pow(255)
34}
35
36pub fn address_max() -> BigInt {
37    BigInt::from(2).pow(160) - 1
38}
39
40/// Names that can be used to build identifiers without collision.
41pub trait SafeNames {
42    /// Name in the lower snake format (e.g. lower_snake_case).
43    fn lower_snake(&self) -> String;
44}
45
46#[derive(Clone, Debug, PartialEq, Eq, Hash)]
47pub enum Type {
48    Base(Base),
49    Array(Array),
50    Map(Map),
51    Tuple(Tuple),
52    String(FeString),
53    /// An "external" contract. Effectively just a `newtype`d address.
54    Contract(ContractId),
55    /// The type of a contract while it's being executed. Ie. the type
56    /// of `self` within a contract function.
57    SelfContract(ContractId),
58    // The type when `Self` is used within a trait or struct
59    SelfType(TraitOrType),
60    Struct(StructId),
61    Enum(EnumId),
62    Generic(Generic),
63    SPtr(TypeId),
64    Mut(TypeId),
65}
66
67#[derive(Clone, Debug, PartialEq, Eq, Hash)]
68pub enum TraitOrType {
69    TraitId(TraitId),
70    TypeId(TypeId),
71}
72
73type TraitFunctionLookup = (Vec<(FunctionId, ImplId)>, Vec<(FunctionId, ImplId)>);
74
75#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
76pub struct TypeId(pub(crate) u32);
77impl_intern_key!(TypeId);
78impl TypeId {
79    pub fn unit(db: &dyn AnalyzerDb) -> Self {
80        db.intern_type(Type::unit())
81    }
82    pub fn bool(db: &dyn AnalyzerDb) -> Self {
83        db.intern_type(Type::bool())
84    }
85    pub fn int(db: &dyn AnalyzerDb, int: Integer) -> Self {
86        db.intern_type(Type::int(int))
87    }
88    pub fn address(db: &dyn AnalyzerDb) -> Self {
89        db.intern_type(Type::Base(Base::Address))
90    }
91    pub fn base(db: &dyn AnalyzerDb, t: Base) -> Self {
92        db.intern_type(Type::Base(t))
93    }
94    pub fn tuple(db: &dyn AnalyzerDb, items: &[TypeId]) -> Self {
95        db.intern_type(Type::Tuple(Tuple {
96            items: items.into(),
97        }))
98    }
99
100    pub fn typ(&self, db: &dyn AnalyzerDb) -> Type {
101        db.lookup_intern_type(*self)
102    }
103    pub fn deref_typ(&self, db: &dyn AnalyzerDb) -> Type {
104        self.deref(db).typ(db)
105    }
106    pub fn deref(self, db: &dyn AnalyzerDb) -> TypeId {
107        match self.typ(db) {
108            Type::SPtr(inner) => inner,
109            Type::Mut(inner) => inner.deref(db),
110            Type::SelfType(TraitOrType::TypeId(inner)) => inner,
111            _ => self,
112        }
113    }
114    pub fn make_sptr(self, db: &dyn AnalyzerDb) -> TypeId {
115        Type::SPtr(self).id(db)
116    }
117
118    pub fn has_fixed_size(&self, db: &dyn AnalyzerDb) -> bool {
119        self.typ(db).has_fixed_size(db)
120    }
121
122    /// `true` if Type::Base or Type::Contract (which is just an Address)
123    pub fn is_primitive(&self, db: &dyn AnalyzerDb) -> bool {
124        matches!(self.typ(db), Type::Base(_) | Type::Contract(_))
125    }
126    pub fn is_bool(&self, db: &dyn AnalyzerDb) -> bool {
127        matches!(self.typ(db), Type::Base(Base::Bool))
128    }
129    pub fn is_contract(&self, db: &dyn AnalyzerDb) -> bool {
130        matches!(self.typ(db), Type::Contract(_) | Type::SelfContract(_))
131    }
132    pub fn is_integer(&self, db: &dyn AnalyzerDb) -> bool {
133        matches!(self.typ(db), Type::Base(Base::Numeric(_)))
134    }
135    pub fn is_map(&self, db: &dyn AnalyzerDb) -> bool {
136        matches!(self.typ(db), Type::Map(_))
137    }
138    pub fn is_string(&self, db: &dyn AnalyzerDb) -> bool {
139        matches!(self.typ(db), Type::String(_))
140    }
141    pub fn is_self_ty(&self, db: &dyn AnalyzerDb) -> bool {
142        matches!(self.typ(db), Type::SelfType(_))
143    }
144    pub fn as_struct(&self, db: &dyn AnalyzerDb) -> Option<StructId> {
145        if let Type::Struct(id) = self.typ(db) {
146            Some(id)
147        } else {
148            None
149        }
150    }
151    pub fn as_trait_or_type(&self) -> TraitOrType {
152        TraitOrType::TypeId(*self)
153    }
154    pub fn is_struct(&self, db: &dyn AnalyzerDb) -> bool {
155        matches!(self.typ(db), Type::Struct(_))
156    }
157    pub fn is_sptr(&self, db: &dyn AnalyzerDb) -> bool {
158        match self.typ(db) {
159            Type::SPtr(_) => true,
160            Type::Mut(inner) => inner.is_sptr(db),
161            _ => false,
162        }
163    }
164    pub fn is_generic(&self, db: &dyn AnalyzerDb) -> bool {
165        matches!(self.deref(db).typ(db), Type::Generic(_))
166    }
167
168    pub fn is_mut(&self, db: &dyn AnalyzerDb) -> bool {
169        matches!(self.typ(db), Type::Mut(_))
170    }
171
172    pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
173        self.typ(db).name(db)
174    }
175
176    pub fn kind_display_name(&self, db: &dyn AnalyzerDb) -> &str {
177        match self.typ(db) {
178            Type::Contract(_) | Type::SelfContract(_) => "contract",
179            Type::Struct(_) => "struct",
180            Type::Array(_) => "array",
181            Type::Tuple(_) => "tuple",
182            _ => "type",
183        }
184    }
185
186    /// Return the `impl` for the given trait. There can only ever be a single
187    /// implementation per concrete type and trait.
188    pub fn get_impl_for(&self, db: &dyn AnalyzerDb, trait_: TraitId) -> Option<ImplId> {
189        db.impl_for(*self, trait_)
190    }
191
192    /// Looks up all possible candidates of the given function name that are implemented via traits.
193    /// Groups results in two lists, the first contains all theoretical possible candidates and
194    /// the second contains only those that are actually callable because the trait is in scope.
195    pub fn trait_function_candidates(
196        &self,
197        context: &mut dyn AnalyzerContext,
198        fn_name: &str,
199    ) -> TraitFunctionLookup {
200        let candidates = context
201            .db()
202            .all_impls(*self)
203            .iter()
204            .cloned()
205            .filter_map(|_impl| {
206                _impl
207                    .function(context.db(), fn_name)
208                    .map(|fun| (fun, _impl))
209            })
210            .collect::<Vec<_>>();
211
212        let in_scope_candidates = candidates
213            .iter()
214            .cloned()
215            .filter(|(_, _impl)| {
216                context
217                    .module()
218                    .is_in_scope(context.db(), Item::Trait(_impl.trait_id(context.db())))
219            })
220            .collect::<Vec<_>>();
221
222        (candidates, in_scope_candidates)
223    }
224
225    /// Signature for the function with the given name defined directly on the type.
226    /// Does not consider trait impls.
227    pub fn function_sig(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionSigId> {
228        match self.typ(db) {
229            Type::SPtr(inner) => inner.function_sig(db, name),
230            Type::Contract(id) => id.function(db, name).map(|fun| fun.sig(db)),
231            Type::SelfContract(id) => id.function(db, name).map(|fun| fun.sig(db)),
232            Type::Struct(id) => id.function(db, name).map(|fun| fun.sig(db)),
233            Type::Enum(id) => id.function(db, name).map(|fun| fun.sig(db)),
234            // TODO: This won't hold when we support multiple bounds
235            Type::Generic(inner) => inner
236                .bounds
237                .first()
238                .and_then(|bound| bound.function(db, name)),
239            _ => None,
240        }
241    }
242
243    /// Like `function_sig` but returns a `Vec<FunctionSigId>` which not only
244    /// considers functions natively implemented on the type but also those
245    /// that are provided by implemented traits on the type.
246    pub fn function_sigs(&self, db: &dyn AnalyzerDb, name: &str) -> Rc<[FunctionSigId]> {
247        db.function_sigs(*self, name.into())
248    }
249
250    pub fn self_function(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionSigId> {
251        let fun = self.function_sig(db, name)?;
252        fun.takes_self(db).then_some(fun)
253    }
254
255    /// Returns `true` if the type qualifies to implement the `Emittable` trait
256    /// TODO: This function should be removed when we add `Encode / Decode`
257    /// trait
258    pub fn is_emittable(self, db: &dyn AnalyzerDb) -> bool {
259        matches!(self.typ(db), Type::Struct(_)) && self.is_encodable(db).unwrap_or(false)
260    }
261
262    /// Returns `true` if the type is encodable in Solidity ABI.
263    /// TODO: This function must be removed when we add `Encode`/`Decode` trait.
264    pub fn is_encodable(self, db: &dyn AnalyzerDb) -> Result<bool, TypeError> {
265        match self.typ(db) {
266            Type::Base(_) | Type::String(_) | Type::Contract(_) => Ok(true),
267            Type::Array(arr) => arr.inner.is_encodable(db),
268            Type::Struct(sid) => {
269                // Returns `false` if diagnostics is not empty.
270                // The diagnostics is properly emitted in struct definition site, so there is no
271                // need to emit the diagnostics here.
272                if !db.struct_dependency_graph(sid).diagnostics.is_empty() {
273                    return Ok(false);
274                };
275                let mut res = true;
276                // We have to continue the iteration even if an item is NOT encodable so that we
277                // could propagate an error which returned from `item.is_encodable()` for
278                // keeping consistency.
279                for (_, &fid) in sid.fields(db).iter() {
280                    res &= fid.typ(db)?.is_encodable(db)?;
281                }
282                Ok(res)
283            }
284            Type::Tuple(tup) => {
285                let mut res = true;
286                // We have to continue the iteration even if an item is NOT encodable so that we
287                // could propagate an error which returned from `item.is_encodable()` for
288                // keeping consistency.
289                for item in tup.items.iter() {
290                    res &= item.is_encodable(db)?;
291                }
292                Ok(res)
293            }
294            Type::Mut(inner) => inner.is_encodable(db),
295            Type::SelfType(id) => match id {
296                TraitOrType::TraitId(_) => Ok(false),
297                TraitOrType::TypeId(id) => id.is_encodable(db),
298            },
299            Type::Map(_)
300            | Type::SelfContract(_)
301            | Type::Generic(_)
302            | Type::Enum(_)
303            | Type::SPtr(_) => Ok(false),
304        }
305    }
306}
307
308#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
309pub enum Base {
310    Numeric(Integer),
311    Bool,
312    Address,
313    Unit,
314}
315
316impl Base {
317    pub fn name(&self) -> SmolStr {
318        match self {
319            Base::Numeric(num) => num.as_ref().into(),
320            Base::Bool => "bool".into(),
321            Base::Address => "address".into(),
322            Base::Unit => "()".into(),
323        }
324    }
325    pub fn u256() -> Base {
326        Base::Numeric(Integer::U256)
327    }
328}
329
330#[derive(
331    Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, AsRefStr, EnumString, EnumIter,
332)]
333#[strum(serialize_all = "snake_case")]
334pub enum Integer {
335    U256,
336    U128,
337    U64,
338    U32,
339    U16,
340    U8,
341    I256,
342    I128,
343    I64,
344    I32,
345    I16,
346    I8,
347}
348
349pub const U256: Base = Base::Numeric(Integer::U256);
350
351#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
352pub struct Array {
353    pub size: usize,
354    pub inner: TypeId,
355}
356
357#[derive(Clone, Debug, PartialEq, Eq, Hash)]
358pub struct Map {
359    pub key: TypeId,
360    pub value: TypeId,
361}
362
363#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
364pub struct Generic {
365    pub name: SmolStr,
366    pub bounds: Rc<[TraitId]>,
367}
368
369#[derive(Clone, Debug, PartialEq, Eq, Hash)]
370pub struct Tuple {
371    pub items: Rc<[TypeId]>,
372}
373
374#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Ord, Eq, Hash)]
375pub struct FeString {
376    pub max_size: usize,
377}
378
379#[derive(Clone, Debug, PartialEq, Eq, Hash)]
380pub struct FunctionSignature {
381    pub self_decl: Option<SelfDecl>,
382    pub ctx_decl: Option<CtxDecl>,
383    pub params: Vec<FunctionParam>,
384    pub return_type: Result<TypeId, TypeError>,
385}
386
387#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
388pub struct SelfDecl {
389    pub span: Span,
390    pub mut_: Option<Span>,
391}
392
393impl SelfDecl {
394    pub fn is_mut(&self) -> bool {
395        self.mut_.is_some()
396    }
397}
398
399#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
400pub struct CtxDecl {
401    pub span: Span,
402    pub mut_: Option<Span>,
403}
404
405#[derive(Clone, Debug, PartialEq, Eq, Hash)]
406pub struct FunctionParam {
407    label: Option<SmolStr>,
408    pub name: SmolStr,
409    pub typ: Result<TypeId, TypeError>,
410}
411impl FunctionParam {
412    pub fn new(label: Option<&str>, name: &str, typ: Result<TypeId, TypeError>) -> Self {
413        Self {
414            label: label.map(SmolStr::new),
415            name: name.into(),
416            typ,
417        }
418    }
419    pub fn label(&self) -> Option<&str> {
420        match &self.label {
421            Some(label) if label == "_" => None,
422            Some(label) => Some(label),
423            None => Some(&self.name),
424        }
425    }
426}
427
428#[derive(
429    Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, EnumString, AsRefStr, EnumIter,
430)]
431pub enum GenericType {
432    Array,
433    String,
434    Map,
435}
436
437impl GenericType {
438    pub fn name(&self) -> SmolStr {
439        self.as_ref().into()
440    }
441    pub fn params(&self) -> Vec<GenericParam> {
442        match self {
443            GenericType::String => vec![GenericParam {
444                name: "max size".into(),
445                kind: GenericParamKind::Int,
446            }],
447            GenericType::Map => vec![
448                GenericParam {
449                    name: "key".into(),
450                    kind: GenericParamKind::PrimitiveType,
451                },
452                GenericParam {
453                    name: "value".into(),
454                    kind: GenericParamKind::AnyType,
455                },
456            ],
457            GenericType::Array => vec![
458                GenericParam {
459                    name: "element type".into(),
460                    kind: GenericParamKind::AnyType,
461                },
462                GenericParam {
463                    name: "size".into(),
464                    kind: GenericParamKind::Int,
465                },
466            ],
467        }
468    }
469
470    // see traversal::types::apply_generic_type_args for error checking
471    pub fn apply(&self, db: &dyn AnalyzerDb, args: &[GenericArg]) -> Option<TypeId> {
472        let typ = match self {
473            GenericType::String => match args {
474                [GenericArg::Int(max_size)] => Some(Type::String(FeString {
475                    max_size: *max_size,
476                })),
477                _ => None,
478            },
479            GenericType::Map => match args {
480                [GenericArg::Type(key), GenericArg::Type(value)] => Some(Type::Map(Map {
481                    key: *key,
482                    value: *value,
483                })),
484                _ => None,
485            },
486            GenericType::Array => match args {
487                [GenericArg::Type(element), GenericArg::Int(size)] => Some(Type::Array(Array {
488                    size: *size,
489                    inner: *element,
490                })),
491                _ => None,
492            },
493        }?;
494        Some(db.intern_type(typ))
495    }
496}
497
498pub struct GenericParam {
499    pub name: SmolStr,
500    pub kind: GenericParamKind,
501}
502
503#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
504pub enum GenericParamKind {
505    Int,
506
507    // Ideally these would be represented as trait constraints.
508    PrimitiveType,
509    // FixedSizeType, // not needed yet
510    AnyType,
511}
512
513#[derive(Clone, Debug, PartialEq, Eq, Hash)]
514pub enum GenericArg {
515    Int(usize),
516    Type(TypeId),
517}
518
519impl Integer {
520    /// Returns `true` if the integer is signed, otherwise `false`
521    pub fn is_signed(&self) -> bool {
522        matches!(
523            self,
524            Integer::I256
525                | Integer::I128
526                | Integer::I64
527                | Integer::I32
528                | Integer::I16
529                | Integer::I8
530        )
531    }
532
533    pub fn size(&self) -> usize {
534        match self {
535            Integer::U256 | Integer::I256 => 32,
536            Integer::U128 | Integer::I128 => 16,
537            Integer::U64 | Integer::I64 => 8,
538            Integer::U32 | Integer::I32 => 4,
539            Integer::U16 | Integer::I16 => 2,
540            Integer::U8 | Integer::I8 => 1,
541        }
542    }
543
544    /// Returns size of integer type in bits.
545    pub fn bits(&self) -> usize {
546        self.size() * 8
547    }
548
549    /// Returns `true` if the integer is at least the same size (or larger) than
550    /// `other`
551    pub fn can_hold(&self, other: Integer) -> bool {
552        self.size() >= other.size()
553    }
554
555    /// Returns `true` if `num` represents a number that fits the type
556    pub fn fits(&self, num: BigInt) -> bool {
557        match self {
558            Integer::U8 => num.to_u8().is_some(),
559            Integer::U16 => num.to_u16().is_some(),
560            Integer::U32 => num.to_u32().is_some(),
561            Integer::U64 => num.to_u64().is_some(),
562            Integer::U128 => num.to_u128().is_some(),
563            Integer::I8 => num.to_i8().is_some(),
564            Integer::I16 => num.to_i16().is_some(),
565            Integer::I32 => num.to_i32().is_some(),
566            Integer::I64 => num.to_i64().is_some(),
567            Integer::I128 => num.to_i128().is_some(),
568            Integer::U256 => num >= u256_min() && num <= u256_max(),
569            Integer::I256 => num >= i256_min() && num <= i256_max(),
570        }
571    }
572
573    /// Returns max value of the integer type.
574    pub fn max_value(&self) -> BigInt {
575        match self {
576            Integer::U256 => u256_max(),
577            Integer::U128 => u128::MAX.into(),
578            Integer::U64 => u64::MAX.into(),
579            Integer::U32 => u32::MAX.into(),
580            Integer::U16 => u16::MAX.into(),
581            Integer::U8 => u8::MAX.into(),
582            Integer::I256 => i256_max(),
583            Integer::I128 => i128::MAX.into(),
584            Integer::I64 => i64::MAX.into(),
585            Integer::I32 => i32::MAX.into(),
586            Integer::I16 => i16::MAX.into(),
587            Integer::I8 => i8::MAX.into(),
588        }
589    }
590
591    /// Returns min value of the integer type.
592    pub fn min_value(&self) -> BigInt {
593        match self {
594            Integer::U256 => u256_min(),
595            Integer::U128 => u128::MIN.into(),
596            Integer::U64 => u64::MIN.into(),
597            Integer::U32 => u32::MIN.into(),
598            Integer::U16 => u16::MIN.into(),
599            Integer::U8 => u8::MIN.into(),
600            Integer::I256 => i256_min(),
601            Integer::I128 => i128::MIN.into(),
602            Integer::I64 => i64::MIN.into(),
603            Integer::I32 => i32::MIN.into(),
604            Integer::I16 => i16::MIN.into(),
605            Integer::I8 => i8::MIN.into(),
606        }
607    }
608}
609
610impl Type {
611    pub fn id(&self, db: &dyn AnalyzerDb) -> TypeId {
612        db.intern_type(self.clone())
613    }
614
615    pub fn name(&self, db: &dyn AnalyzerDb) -> SmolStr {
616        match self {
617            Type::Base(inner) => inner.name(),
618            _ => self.display(db).to_string().into(),
619        }
620    }
621
622    pub fn def_span(&self, context: &dyn AnalyzerContext) -> Option<Span> {
623        match self {
624            Self::Struct(id) => Some(id.span(context.db())),
625            Self::Contract(id) | Self::SelfContract(id) => Some(id.span(context.db())),
626            _ => None,
627        }
628    }
629
630    /// Creates an instance of bool.
631    pub fn bool() -> Self {
632        Type::Base(Base::Bool)
633    }
634
635    /// Creates an instance of address.
636    pub fn address() -> Self {
637        Type::Base(Base::Address)
638    }
639
640    /// Creates an instance of u256.
641    pub fn u256() -> Self {
642        Type::Base(Base::Numeric(Integer::U256))
643    }
644
645    /// Creates an instance of u8.
646    pub fn u8() -> Self {
647        Type::Base(Base::Numeric(Integer::U8))
648    }
649
650    /// Creates an instance of `()`.
651    pub fn unit() -> Self {
652        Type::Base(Base::Unit)
653    }
654
655    pub fn is_unit(&self) -> bool {
656        *self == Type::Base(Base::Unit)
657    }
658
659    pub fn int(int_type: Integer) -> Self {
660        Type::Base(Base::Numeric(int_type))
661    }
662
663    pub fn has_fixed_size(&self, db: &dyn AnalyzerDb) -> bool {
664        match self {
665            Type::Base(_)
666            | Type::Array(_)
667            | Type::Tuple(_)
668            | Type::String(_)
669            | Type::Struct(_)
670            | Type::Enum(_)
671            | Type::Generic(_)
672            | Type::Contract(_) => true,
673            Type::Map(_) | Type::SelfContract(_) => false,
674            Type::SelfType(inner) => match inner {
675                TraitOrType::TraitId(_) => true,
676                TraitOrType::TypeId(id) => id.has_fixed_size(db),
677            },
678            Type::SPtr(inner) | Type::Mut(inner) => inner.has_fixed_size(db),
679        }
680    }
681}
682
683pub trait TypeDowncast {
684    fn as_array(&self, db: &dyn AnalyzerDb) -> Option<Array>;
685    fn as_tuple(&self, db: &dyn AnalyzerDb) -> Option<Tuple>;
686    fn as_string(&self, db: &dyn AnalyzerDb) -> Option<FeString>;
687    fn as_map(&self, db: &dyn AnalyzerDb) -> Option<Map>;
688    fn as_int(&self, db: &dyn AnalyzerDb) -> Option<Integer>;
689}
690
691impl TypeDowncast for TypeId {
692    fn as_array(&self, db: &dyn AnalyzerDb) -> Option<Array> {
693        match self.typ(db) {
694            Type::Array(inner) => Some(inner),
695            _ => None,
696        }
697    }
698    fn as_tuple(&self, db: &dyn AnalyzerDb) -> Option<Tuple> {
699        match self.typ(db) {
700            Type::Tuple(inner) => Some(inner),
701            _ => None,
702        }
703    }
704    fn as_string(&self, db: &dyn AnalyzerDb) -> Option<FeString> {
705        match self.typ(db) {
706            Type::String(inner) => Some(inner),
707            _ => None,
708        }
709    }
710    fn as_map(&self, db: &dyn AnalyzerDb) -> Option<Map> {
711        match self.typ(db) {
712            Type::Map(inner) => Some(inner),
713            _ => None,
714        }
715    }
716    fn as_int(&self, db: &dyn AnalyzerDb) -> Option<Integer> {
717        match self.typ(db) {
718            Type::Base(Base::Numeric(int)) => Some(int),
719            _ => None,
720        }
721    }
722}
723
724impl From<Base> for Type {
725    fn from(value: Base) -> Self {
726        Type::Base(value)
727    }
728}
729
730impl DisplayWithDb for Type {
731    fn format(&self, db: &dyn AnalyzerDb, f: &mut fmt::Formatter<'_>) -> fmt::Result {
732        use std::fmt::Display;
733        match self {
734            Type::Base(inner) => inner.fmt(f),
735            Type::String(inner) => inner.fmt(f),
736            Type::Array(arr) => {
737                write!(f, "Array<{}, {}>", arr.inner.display(db), arr.size)
738            }
739            Type::Map(map) => {
740                let Map { key, value } = map;
741                write!(f, "Map<{}, {}>", key.display(db), value.display(db),)
742            }
743            Type::Tuple(id) => {
744                write!(f, "(")?;
745                let mut delim = "";
746                for item in id.items.iter() {
747                    write!(f, "{}{}", delim, item.display(db))?;
748                    delim = ", ";
749                }
750                write!(f, ")")
751            }
752            Type::Contract(id) | Type::SelfContract(id) => write!(f, "{}", id.name(db)),
753            Type::Struct(id) => write!(f, "{}", id.name(db)),
754            Type::Enum(id) => write!(f, "{}", id.name(db)),
755            Type::Generic(inner) => inner.fmt(f),
756            Type::SPtr(inner) => write!(f, "SPtr<{}>", inner.display(db)),
757            Type::Mut(inner) => write!(f, "mut {}", inner.display(db)),
758            Type::SelfType(_) => write!(f, "Self"),
759        }
760    }
761}
762impl DisplayWithDb for TypeId {
763    fn format(&self, db: &dyn AnalyzerDb, f: &mut fmt::Formatter<'_>) -> fmt::Result {
764        self.typ(db).format(db, f)
765    }
766}
767
768impl fmt::Display for Base {
769    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
770        let name = match self {
771            Base::Numeric(int) => return int.fmt(f),
772            Base::Bool => "bool",
773            Base::Address => "address",
774            Base::Unit => "()",
775        };
776        write!(f, "{name}")
777    }
778}
779
780impl fmt::Display for Integer {
781    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
782        let name = match self {
783            Integer::U256 => "u256",
784            Integer::U128 => "u128",
785            Integer::U64 => "u64",
786            Integer::U32 => "u32",
787            Integer::U16 => "u16",
788            Integer::U8 => "u8",
789            Integer::I256 => "i256",
790            Integer::I128 => "i128",
791            Integer::I64 => "i64",
792            Integer::I32 => "i32",
793            Integer::I16 => "i16",
794            Integer::I8 => "i8",
795        };
796        write!(f, "{name}")
797    }
798}
799
800impl fmt::Display for FeString {
801    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
802        write!(f, "String<{}>", self.max_size)
803    }
804}
805
806impl DisplayWithDb for FunctionSignature {
807    fn format(&self, db: &dyn AnalyzerDb, f: &mut fmt::Formatter<'_>) -> fmt::Result {
808        let FunctionSignature {
809            self_decl,
810            ctx_decl: _,
811            params,
812            return_type,
813        } = self;
814
815        write!(f, "params: [")?;
816        let mut delim = "";
817        if let Some(s) = self_decl {
818            write!(f, "{}self", if s.mut_.is_some() { "mut " } else { "" },)?;
819            delim = ", ";
820        }
821
822        for p in params {
823            write!(
824                f,
825                "{}{{ label: {:?}, name: {}, typ: {} }}",
826                delim,
827                p.label,
828                p.name,
829                p.typ.as_ref().unwrap().display(db),
830            )?;
831            delim = ", ";
832        }
833        write!(f, "] -> {}", return_type.as_ref().unwrap().display(db))
834    }
835}
836
837impl fmt::Display for Generic {
838    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
839        write!(f, "{}", self.name)
840    }
841}
842
843impl FromStr for Base {
844    type Err = strum::ParseError;
845
846    fn from_str(s: &str) -> Result<Self, Self::Err> {
847        match s {
848            "bool" => Ok(Base::Bool),
849            "address" => Ok(Base::Address),
850            "()" => Ok(Base::Unit),
851            _ => Ok(Base::Numeric(Integer::from_str(s)?)),
852        }
853    }
854}