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, StrToken};
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        // string array like str[1] or str[N]
53        // or string slice like str
54        if let Some(str_token) = parser.take::<StrToken>() {
55            let length = SquareBrackets::try_parse_all_inner(parser, |mut parser| {
56                parser.emit_error(ParseErrorKind::UnexpectedTokenAfterStrLength)
57            })?;
58            let t = match length {
59                Some(length) => Ty::StringArray { str_token, length },
60                None => Ty::StringSlice(str_token),
61            };
62            return Ok(t);
63        }
64
65        if let Some(underscore_token) = parser.take() {
66            return Ok(Ty::Infer { underscore_token });
67        }
68
69        if let Some(ptr_token) = parser.take::<PtrToken>() {
70            let ty = SquareBrackets::parse_all_inner(parser, |mut parser| {
71                parser.emit_error(ParseErrorKind::UnexpectedTokenAfterPtrType)
72            })?;
73            return Ok(Ty::Ptr { ptr_token, ty });
74        }
75
76        // slice like __slice[type]
77        // TODO: deprecate this syntax (see https://github.com/FuelLabs/sway/issues/5110)
78        if let Some(slice_token) = parser.take::<SliceToken>() {
79            let ty = SquareBrackets::<Box<Ty>>::parse_all_inner(parser, |mut parser| {
80                parser.emit_error(ParseErrorKind::UnexpectedTokenAfterSliceType)
81            })?;
82            return Ok(Ty::Slice {
83                slice_token: Some(slice_token),
84                ty,
85            });
86        }
87
88        if let Some(ampersand_token) = parser.take() {
89            let mut_token = parser.take();
90            let ty = Box::new(parser.parse()?);
91            return Ok(Ty::Ref {
92                ampersand_token,
93                mut_token,
94                ty,
95            });
96        }
97
98        if let Some(bang_token) = parser.take() {
99            return Ok(Ty::Never { bang_token });
100        }
101
102        if parser.peek::<OpenAngleBracketToken>().is_some()
103            || parser.peek::<DoubleColonToken>().is_some()
104            || parser.peek::<Ident>().is_some()
105        {
106            let path_type = parser.parse()?;
107            return Ok(Ty::Path(path_type));
108        }
109
110        if let Ok(literal) = parser.parse::<Literal>() {
111            return Ok(Ty::Expr(Box::new(Expr::Literal(literal))));
112        }
113
114        Err(parser.emit_error(ParseErrorKind::ExpectedType))
115    }
116}
117
118impl ParseToEnd for TyArrayDescriptor {
119    fn parse_to_end<'a, 'e>(
120        mut parser: Parser<'a, '_>,
121    ) -> ParseResult<(TyArrayDescriptor, ParserConsumed<'a>)> {
122        let ty = parser.parse()?;
123        let semicolon_token = parser.parse()?;
124        let length = parser.parse()?;
125        let consumed = match parser.check_empty() {
126            Some(consumed) => consumed,
127            None => {
128                return Err(parser.emit_error(ParseErrorKind::UnexpectedTokenAfterArrayTypeLength))
129            }
130        };
131        let descriptor = TyArrayDescriptor {
132            ty,
133            semicolon_token,
134            length,
135        };
136        Ok((descriptor, consumed))
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use crate::test_utils::parse;
144    use assert_matches::*;
145
146    #[test]
147    fn parse_ptr() {
148        let item = parse::<Ty>(
149            r#"
150            __ptr[T]
151            "#,
152        );
153        assert_matches!(item, Ty::Ptr { .. });
154    }
155
156    #[test]
157    fn parse_array() {
158        let item = parse::<Ty>("[T; 1]");
159        assert_matches!(item, Ty::Array { .. });
160    }
161
162    #[test]
163    fn parse_slice() {
164        // deprecated syntax
165        let item = parse::<Ty>("__slice[T]");
166        assert_matches!(item, Ty::Slice { .. });
167
168        // " new"  syntax
169        let item = parse::<Ty>("[T]");
170        assert_matches!(item, Ty::Slice { .. });
171
172        let item = parse::<Ty>("&[T]");
173        assert_matches!(item, Ty::Ref { ty, .. } if matches!(&*ty, Ty::Slice { .. }));
174    }
175
176    #[test]
177    fn parse_ref() {
178        let item = parse::<Ty>(
179            r#"
180            &T
181            "#,
182        );
183        assert_matches!(
184            item,
185            Ty::Ref {
186                mut_token: None,
187                ..
188            }
189        );
190    }
191
192    #[test]
193    fn parse_mut_ref() {
194        let item = parse::<Ty>(
195            r#"
196            &mut T
197            "#,
198        );
199        assert_matches!(
200            item,
201            Ty::Ref {
202                mut_token: Some(_),
203                ..
204            }
205        );
206    }
207}