solar_parse/parser/
ty.rs

1use super::item::FunctionFlags;
2use crate::{PResult, Parser};
3use solar_ast::{token::*, *};
4use solar_interface::kw;
5use std::{fmt, ops::RangeInclusive};
6
7impl<'sess, 'ast> Parser<'sess, 'ast> {
8    /// Parses a type.
9    #[instrument(level = "debug", skip_all)]
10    pub fn parse_type(&mut self) -> PResult<'sess, Type<'ast>> {
11        let mut ty = self
12            .parse_spanned(Self::parse_basic_ty_kind)
13            .map(|(span, kind)| Type { span, kind })?;
14
15        // Parse suffixes.
16        while self.eat(TokenKind::OpenDelim(Delimiter::Bracket)) {
17            let size = if self.check_noexpect(TokenKind::CloseDelim(Delimiter::Bracket)) {
18                None
19            } else {
20                Some(self.parse_expr()?)
21            };
22            self.expect(TokenKind::CloseDelim(Delimiter::Bracket))?;
23            ty = Type {
24                span: ty.span.to(self.prev_token.span),
25                kind: TypeKind::Array(self.alloc(TypeArray { element: ty, size })),
26            };
27        }
28
29        Ok(ty)
30    }
31
32    /// Parses a type kind. Does not parse suffixes.
33    fn parse_basic_ty_kind(&mut self) -> PResult<'sess, TypeKind<'ast>> {
34        if self.check_elementary_type() {
35            self.parse_elementary_type().map(TypeKind::Elementary)
36        } else if self.eat_keyword(kw::Function) {
37            self.parse_function_header(FunctionFlags::FUNCTION_TY).map(|f| {
38                let FunctionHeader {
39                    name: _,
40                    parameters,
41                    visibility,
42                    state_mutability,
43                    modifiers: _,
44                    virtual_: _,
45                    override_: _,
46                    returns,
47                } = f;
48                TypeKind::Function(self.alloc(TypeFunction {
49                    parameters,
50                    visibility,
51                    state_mutability,
52                    returns,
53                }))
54            })
55        } else if self.eat_keyword(kw::Mapping) {
56            self.parse_mapping_type().map(|x| TypeKind::Mapping(self.alloc(x)))
57        } else if self.check_path() {
58            self.parse_path().map(TypeKind::Custom)
59        } else {
60            self.unexpected()
61        }
62    }
63
64    /// Parses an elementary type.
65    ///
66    /// Must be used after checking that the next token is an elementary type.
67    pub(super) fn parse_elementary_type(&mut self) -> PResult<'sess, ElementaryType> {
68        let id = self.parse_ident_any()?;
69        debug_assert!(id.is_elementary_type());
70        let mut ty = match id.name {
71            kw::Address => ElementaryType::Address(false),
72            kw::Bool => ElementaryType::Bool,
73            kw::String => ElementaryType::String,
74            kw::Bytes => ElementaryType::Bytes,
75            kw::Fixed => ElementaryType::Fixed(TypeSize::ZERO, TypeFixedSize::ZERO),
76            kw::UFixed => ElementaryType::UFixed(TypeSize::ZERO, TypeFixedSize::ZERO),
77            kw::Int => ElementaryType::Int(TypeSize::ZERO),
78            kw::UInt => ElementaryType::UInt(TypeSize::ZERO),
79            s if s >= kw::UInt8 && s <= kw::UInt256 => {
80                let bytes = s.as_u32() - kw::UInt8.as_u32() + 1;
81                ElementaryType::UInt(TypeSize::new(bytes as u8).unwrap())
82            }
83            s if s >= kw::Int8 && s <= kw::Int256 => {
84                let bytes = s.as_u32() - kw::Int8.as_u32() + 1;
85                ElementaryType::Int(TypeSize::new(bytes as u8).unwrap())
86            }
87            s if s >= kw::Bytes1 && s <= kw::Bytes32 => {
88                let bytes = s.as_u32() - kw::Bytes1.as_u32() + 1;
89                ElementaryType::FixedBytes(TypeSize::new(bytes as u8).unwrap())
90            }
91            s => unreachable!("unexpected elementary type: {s}"),
92        };
93
94        let sm = self.parse_state_mutability();
95        match (&mut ty, sm) {
96            (ElementaryType::Address(p), Some(StateMutability::Payable)) => *p = true,
97            (_, None) => {}
98            (_, Some(_)) => {
99                let msg = if matches!(ty, ElementaryType::Address(_)) {
100                    "address types can only be payable or non-payable"
101                } else {
102                    "only address types can have state mutability"
103                };
104                self.dcx().err(msg).span(id.span.to(self.prev_token.span)).emit();
105            }
106        }
107
108        // TODO: Move to type checking.
109        // if matches!(ty, ElementaryType::Fixed(..) | ElementaryType::UFixed(..)) {
110        //     self.dcx().err("`fixed` types are not yet supported").span(id.span).emit();
111        // }
112
113        Ok(ty)
114    }
115
116    /// Parses a mapping type.
117    fn parse_mapping_type(&mut self) -> PResult<'sess, TypeMapping<'ast>> {
118        self.expect(TokenKind::OpenDelim(Delimiter::Parenthesis))?;
119
120        let key = self.parse_type()?;
121        // TODO: Move to type checking.
122        if !key.is_elementary() && !key.is_custom() {
123            let msg =
124                "only elementary types or used-defined types can be used as key types in mappings";
125            self.dcx().err(msg).span(key.span).emit();
126        }
127        let key_name = self.parse_ident_opt()?;
128
129        self.expect(TokenKind::FatArrow)?;
130
131        let value = self.parse_type()?;
132        let value_name = self.parse_ident_opt()?;
133
134        self.expect(TokenKind::CloseDelim(Delimiter::Parenthesis))?;
135
136        Ok(TypeMapping { key, key_name, value, value_name })
137    }
138}
139
140#[derive(Debug, PartialEq)]
141enum ParseTySizeError {
142    Parse(std::num::ParseIntError),
143    TryFrom(std::num::TryFromIntError),
144    NotMultipleOf8,
145    OutOfRange(RangeInclusive<u16>),
146    FixedX,
147}
148
149impl fmt::Display for ParseTySizeError {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        match self {
152            Self::Parse(e) => e.fmt(f),
153            Self::TryFrom(e) => e.fmt(f),
154            Self::NotMultipleOf8 => f.write_str("number must be a multiple of 8"),
155            Self::OutOfRange(range) => {
156                write!(f, "size is out of range of {}:{} (inclusive)", range.start(), range.end())
157            }
158            Self::FixedX => f.write_str("`fixed` sizes must be separated by exactly one 'x'"),
159        }
160    }
161}
162
163/// Parses `fixedMxN` or `ufixedMxN`.
164#[allow(dead_code)]
165fn parse_fixed_type(original: &str) -> Result<Option<ElementaryType>, ParseTySizeError> {
166    let s = original;
167    let tmp = s.strip_prefix('u');
168    let unsigned = tmp.is_some();
169    let s = tmp.unwrap_or(s);
170
171    if let Some(s) = s.strip_prefix("fixed") {
172        debug_assert!(!s.is_empty());
173        let (m, n) = parse_fixed_size(s)?;
174        return Ok(Some(if unsigned {
175            ElementaryType::UFixed(m, n)
176        } else {
177            ElementaryType::Fixed(m, n)
178        }));
179    }
180
181    Ok(None)
182}
183
184#[allow(dead_code)]
185fn parse_fb_size(s: &str) -> Result<TypeSize, ParseTySizeError> {
186    parse_ty_size_u8(s, 1..=32, false).map(|x| TypeSize::new(x).unwrap())
187}
188
189#[allow(dead_code)]
190fn parse_int_size(s: &str) -> Result<TypeSize, ParseTySizeError> {
191    parse_ty_size_u8(s, 1..=32, true).map(|x| TypeSize::new(x).unwrap())
192}
193
194#[allow(dead_code)]
195fn parse_fixed_size(s: &str) -> Result<(TypeSize, TypeFixedSize), ParseTySizeError> {
196    let (m, n) = s.split_once('x').ok_or(ParseTySizeError::FixedX)?;
197    let m = parse_int_size(m)?;
198    let n = parse_ty_size_u8(n, 0..=80, false)?;
199    let n = TypeFixedSize::new(n).unwrap();
200    Ok((m, n))
201}
202
203/// Parses a type size.
204///
205/// If `to_bytes` is true, the size is checked to be a multiple of 8 and then converted from
206/// bits to bytes.
207///
208/// The final **converted** size must be in the range `range`. This means that if `to_bytes` is
209/// true, the range must be in bytes and not bits.
210fn parse_ty_size_u8(
211    s: &str,
212    real_range: RangeInclusive<u8>,
213    to_bytes: bool,
214) -> Result<u8, ParseTySizeError> {
215    let mut n = s.parse::<u16>().map_err(ParseTySizeError::Parse)?;
216
217    if to_bytes {
218        if n % 8 != 0 {
219            return Err(ParseTySizeError::NotMultipleOf8);
220        }
221        n /= 8;
222    }
223
224    let n = u8::try_from(n).map_err(ParseTySizeError::TryFrom)?;
225
226    if !real_range.contains(&n) {
227        let display_range = if to_bytes {
228            *real_range.start() as u16 * 8..=*real_range.end() as u16 * 8
229        } else {
230            *real_range.start() as u16..=*real_range.end() as u16
231        };
232        return Err(ParseTySizeError::OutOfRange(display_range));
233    }
234
235    Ok(n)
236}
237
238#[cfg(test)]
239mod tests {
240    use super::*;
241
242    #[test]
243    fn parse_size() {
244        use ParseTySizeError::*;
245
246        assert_eq!(parse_ty_size_u8("0", 0..=1, false), Ok(0));
247        assert_eq!(parse_ty_size_u8("1", 0..=1, false), Ok(1));
248        assert_eq!(parse_ty_size_u8("0", 0..=1, true), Ok(0));
249        assert_eq!(parse_ty_size_u8("1", 0..=1, true), Err(NotMultipleOf8));
250        assert_eq!(parse_ty_size_u8("8", 0..=1, true), Ok(1));
251
252        assert_eq!(parse_ty_size_u8("0", 1..=32, false), Err(OutOfRange(1..=32)));
253        assert_eq!(parse_ty_size_u8("0", 1..=32, true), Err(OutOfRange(8..=256)));
254        for n in 1..=32 {
255            assert_eq!(parse_ty_size_u8(&n.to_string(), 1..=32, false), Ok(n as u8));
256            for m in 1..=7u16 {
257                assert_eq!(
258                    parse_ty_size_u8(&((n - 1) * 8 + m).to_string(), 1..=32, true),
259                    Err(NotMultipleOf8)
260                );
261            }
262            assert_eq!(parse_ty_size_u8(&(n * 8).to_string(), 1..=32, true), Ok(n as u8));
263        }
264    }
265}