sway_parse/ty/
mod.rs

1use crate::{Parse, ParseBracket, ParseResult, ParseToEnd, Parser, ParserConsumed};
2use sway_ast::brackets::{Parens, SquareBrackets};
3use sway_ast::keywords::{DoubleColonToken, OpenAngleBracketToken, PtrToken, SliceToken};
4use sway_ast::ty::{Ty, TyArrayDescriptor, TyTupleDescriptor};
5use sway_error::parser_error::ParseErrorKind;
6use sway_types::{ast::Delimiter, Ident};
7
8impl Parse for Ty {
9    fn parse(parser: &mut Parser) -> ParseResult<Ty> {
10        // parse parens carefully, such that only patterns of (ty) are parsed as ty,
11        // and patterns of (ty,) are parsed as one-arity tuples with one element ty
12        if let Some((mut parser, span)) = parser.enter_delimited(Delimiter::Parenthesis) {
13            if let Some(_consumed) = parser.check_empty() {
14                return Ok(Ty::Tuple(Parens::new(TyTupleDescriptor::Nil, span)));
15            }
16            let head = parser.parse()?;
17            if let Some(comma_token) = parser.take() {
18                let (tail, _consumed) = parser.parse_to_end()?;
19                let tuple = TyTupleDescriptor::Cons {
20                    head,
21                    comma_token,
22                    tail,
23                };
24                return Ok(Ty::Tuple(Parens::new(tuple, span)));
25            }
26            if parser.check_empty().is_some() {
27                return Ok(*head);
28            }
29            return Err(parser
30                .emit_error(ParseErrorKind::ExpectedCommaOrCloseParenInTupleOrParenExpression));
31        }
32
33        if let Some((mut inner_parser, span)) = parser.enter_delimited(Delimiter::Bracket) {
34            // array like [type; len]
35            if let Ok((array, _)) = inner_parser.try_parse_to_end::<TyArrayDescriptor>(false) {
36                return Ok(Ty::Array(SquareBrackets { inner: array, span }));
37            }
38
39            // slice like [type]
40            if let Ok(Some((ty, _))) = inner_parser.try_parse_and_check_empty::<Ty>(false) {
41                return Ok(Ty::Slice {
42                    slice_token: None,
43                    ty: SquareBrackets {
44                        inner: Box::new(ty),
45                        span,
46                    },
47                });
48            }
49        }
50
51        if let Some(str_token) = parser.take() {
52            let length = SquareBrackets::try_parse_all_inner(parser, |mut parser| {
53                parser.emit_error(ParseErrorKind::UnexpectedTokenAfterStrLength)
54            })?;
55            let t = match length {
56                Some(length) => Ty::StringArray { str_token, length },
57                None => Ty::StringSlice(str_token),
58            };
59            return Ok(t);
60        }
61
62        if let Some(underscore_token) = parser.take() {
63            return Ok(Ty::Infer { underscore_token });
64        }
65
66        if let Some(ptr_token) = parser.take::<PtrToken>() {
67            let ty = SquareBrackets::parse_all_inner(parser, |mut parser| {
68                parser.emit_error(ParseErrorKind::UnexpectedTokenAfterPtrType)
69            })?;
70            return Ok(Ty::Ptr { ptr_token, ty });
71        }
72
73        // slice like __slice[type]
74        // TODO: deprecate this syntax (see https://github.com/FuelLabs/sway/issues/5110)
75        if let Some(slice_token) = parser.take::<SliceToken>() {
76            let ty = SquareBrackets::<Box<Ty>>::parse_all_inner(parser, |mut parser| {
77                parser.emit_error(ParseErrorKind::UnexpectedTokenAfterSliceType)
78            })?;
79            return Ok(Ty::Slice {
80                slice_token: Some(slice_token),
81                ty,
82            });
83        }
84
85        if let Some(ampersand_token) = parser.take() {
86            let mut_token = parser.take();
87            let ty = Box::new(parser.parse()?);
88            return Ok(Ty::Ref {
89                ampersand_token,
90                mut_token,
91                ty,
92            });
93        }
94
95        if let Some(bang_token) = parser.take() {
96            return Ok(Ty::Never { bang_token });
97        }
98
99        if parser.peek::<OpenAngleBracketToken>().is_some()
100            || parser.peek::<DoubleColonToken>().is_some()
101            || parser.peek::<Ident>().is_some()
102        {
103            let path_type = parser.parse()?;
104            return Ok(Ty::Path(path_type));
105        }
106
107        Err(parser.emit_error(ParseErrorKind::ExpectedType))
108    }
109}
110
111impl ParseToEnd for TyArrayDescriptor {
112    fn parse_to_end<'a, 'e>(
113        mut parser: Parser<'a, '_>,
114    ) -> ParseResult<(TyArrayDescriptor, ParserConsumed<'a>)> {
115        let ty = parser.parse()?;
116        let semicolon_token = parser.parse()?;
117        let length = parser.parse()?;
118        let consumed = match parser.check_empty() {
119            Some(consumed) => consumed,
120            None => {
121                return Err(parser.emit_error(ParseErrorKind::UnexpectedTokenAfterArrayTypeLength))
122            }
123        };
124        let descriptor = TyArrayDescriptor {
125            ty,
126            semicolon_token,
127            length,
128        };
129        Ok((descriptor, consumed))
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use crate::test_utils::parse;
137    use assert_matches::*;
138
139    #[test]
140    fn parse_ptr() {
141        let item = parse::<Ty>(
142            r#"
143            __ptr[T]
144            "#,
145        );
146        assert_matches!(item, Ty::Ptr { .. });
147    }
148
149    #[test]
150    fn parse_array() {
151        let item = parse::<Ty>("[T; 1]");
152        assert_matches!(item, Ty::Array { .. });
153    }
154
155    #[test]
156    fn parse_slice() {
157        // deprecated syntax
158        let item = parse::<Ty>("__slice[T]");
159        assert_matches!(item, Ty::Slice { .. });
160
161        // " new"  syntax
162        let item = parse::<Ty>("[T]");
163        assert_matches!(item, Ty::Slice { .. });
164
165        let item = parse::<Ty>("&[T]");
166        assert_matches!(item, Ty::Ref { ty, .. } if matches!(&*ty, Ty::Slice { .. }));
167    }
168
169    #[test]
170    fn parse_ref() {
171        let item = parse::<Ty>(
172            r#"
173            &T
174            "#,
175        );
176        assert_matches!(
177            item,
178            Ty::Ref {
179                mut_token: None,
180                ..
181            }
182        );
183    }
184
185    #[test]
186    fn parse_mut_ref() {
187        let item = parse::<Ty>(
188            r#"
189            &mut T
190            "#,
191        );
192        assert_matches!(
193            item,
194            Ty::Ref {
195                mut_token: Some(_),
196                ..
197            }
198        );
199    }
200}