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