fe_parser/grammar/
types.rs

1use crate::ast::{
2    self, Enum, Field, GenericArg, Impl, Path, Trait, TypeAlias, TypeDesc, Variant, VariantKind,
3};
4use crate::grammar::expressions::parse_expr;
5use crate::grammar::functions::{parse_fn_def, parse_fn_sig};
6use crate::node::{Node, Span};
7use crate::Token;
8use crate::{ParseFailed, ParseResult, Parser, TokenKind};
9use fe_common::diagnostics::Label;
10use if_chain::if_chain;
11use smol_str::SmolStr;
12use vec1::Vec1;
13
14/// Parse a [`ModuleStmt::Struct`].
15/// # Panics
16/// Panics if the next token isn't `struct`.
17pub fn parse_struct_def(
18    par: &mut Parser,
19    pub_qual: Option<Span>,
20) -> ParseResult<Node<ast::Struct>> {
21    let struct_tok = par.assert(TokenKind::Struct);
22    let name = par.expect_with_notes(TokenKind::Name, "failed to parse struct definition", |_| {
23        vec!["Note: a struct name must start with a letter or underscore, and contain letters, numbers, or underscores".into()]
24    })?;
25
26    let mut span = struct_tok.span + name.span;
27    let mut fields = vec![];
28    let mut functions = vec![];
29    par.enter_block(span, "struct body must start with `{`")?;
30
31    loop {
32        par.eat_newlines();
33
34        let attributes = if let Some(attr) = par.optional(TokenKind::Hash) {
35            let attr_name = par.expect_with_notes(TokenKind::Name, "failed to parse attribute definition", |_|
36                vec!["Note: an attribute name must start with a letter or underscore, and contain letters, numbers, or underscores".into()])?;
37            // This hints to a future where we would support multiple attributes per field. For now we don't need it.
38            vec![Node::new(attr_name.text.into(), attr.span + attr_name.span)]
39        } else {
40            vec![]
41        };
42
43        par.eat_newlines();
44
45        let pub_qual = par.optional(TokenKind::Pub).map(|tok| tok.span);
46        match par.peek_or_err()? {
47            TokenKind::Name => {
48                let field = parse_field(par, attributes, pub_qual, None)?;
49                if !functions.is_empty() {
50                    par.error(
51                        field.span,
52                        "struct field definitions must come before any function definitions",
53                    );
54                }
55                fields.push(field);
56            }
57            TokenKind::Fn | TokenKind::Unsafe => {
58                functions.push(parse_fn_def(par, pub_qual)?);
59            }
60            TokenKind::BraceClose if pub_qual.is_none() => {
61                span += par.next()?.span;
62                break;
63            }
64            _ => {
65                let tok = par.next()?;
66                par.unexpected_token_error(&tok, "failed to parse struct definition", vec![]);
67                return Err(ParseFailed);
68            }
69        }
70    }
71    Ok(Node::new(
72        ast::Struct {
73            name: name.into(),
74            fields,
75            functions,
76            pub_qual,
77        },
78        span,
79    ))
80}
81
82#[allow(clippy::unnecessary_literal_unwrap)]
83/// Parse a [`ModuleStmt::Enum`].
84/// # Panics
85/// Panics if the next token isn't [`TokenKind::Enum`].
86pub fn parse_enum_def(par: &mut Parser, pub_qual: Option<Span>) -> ParseResult<Node<Enum>> {
87    let enum_tok = par.assert(TokenKind::Enum);
88    let name = par.expect_with_notes(
89        TokenKind::Name,
90        "failed to parse enum definition",
91        |_| vec!["Note: `enum` must be followed by a name, which must start with a letter and contain only letters, numbers, or underscores".into()],
92    )?;
93
94    let mut span = enum_tok.span + name.span;
95    let mut variants = vec![];
96    let mut functions = vec![];
97
98    par.enter_block(span, "enum definition")?;
99    loop {
100        par.eat_newlines();
101        match par.peek_or_err()? {
102            TokenKind::Name => {
103                let variant = parse_variant(par)?;
104                if !functions.is_empty() {
105                    par.error(
106                        variant.span,
107                        "enum variant definitions must come before any function definitions",
108                    );
109                }
110                variants.push(variant);
111            }
112
113            TokenKind::Fn | TokenKind::Unsafe => {
114                functions.push(parse_fn_def(par, None)?);
115            }
116
117            TokenKind::Pub => {
118                let pub_qual = Some(par.next().unwrap().span);
119                match par.peek() {
120                    Some(TokenKind::Fn | TokenKind::Unsafe) => {
121                        functions.push(parse_fn_def(par, pub_qual)?);
122                    }
123
124                    _ => {
125                        par.error(
126                            pub_qual.unwrap(),
127                            "expected `fn` or `unsafe fn` after `pub`",
128                        );
129                    }
130                }
131            }
132
133            TokenKind::BraceClose => {
134                span += par.next()?.span;
135                break;
136            }
137
138            _ => {
139                let tok = par.next()?;
140                par.unexpected_token_error(&tok, "failed to parse enum definition body", vec![]);
141                return Err(ParseFailed);
142            }
143        };
144    }
145
146    Ok(Node::new(
147        ast::Enum {
148            name: name.into(),
149            variants,
150            functions,
151            pub_qual,
152        },
153        span,
154    ))
155}
156
157/// Parse a trait definition.
158/// # Panics
159/// Panics if the next token isn't `trait`.
160pub fn parse_trait_def(par: &mut Parser, pub_qual: Option<Span>) -> ParseResult<Node<Trait>> {
161    let trait_tok = par.assert(TokenKind::Trait);
162
163    // trait Event {}
164    let trait_name = par.expect_with_notes(
165        TokenKind::Name,
166        "failed to parse trait definition",
167        |_| vec!["Note: `trait` must be followed by a name, which must start with a letter and contain only letters, numbers, or underscores".into()],
168    )?;
169
170    let header_span = trait_tok.span + trait_name.span;
171    let mut functions = vec![];
172    par.enter_block(header_span, "trait definition")?;
173
174    loop {
175        match par.peek_or_err()? {
176            TokenKind::Fn => {
177                // TODO: Traits should also be allowed to have functions that do contain a body.
178                functions.push(parse_fn_sig(par, None)?);
179                par.expect_with_notes(
180                    TokenKind::Semi,
181                    "failed to parse trait definition",
182                    |_| vec!["Note: trait functions must appear without body and followed by a semicolon.".into()],
183                )?;
184                par.eat_newlines();
185            }
186            TokenKind::BraceClose => {
187                par.next()?;
188                break;
189            }
190            _ => {
191                let tok = par.next()?;
192                par.unexpected_token_error(&tok, "failed to parse trait definition body", vec![]);
193                return Err(ParseFailed);
194            }
195        };
196    }
197
198    let span = header_span + pub_qual;
199    Ok(Node::new(
200        Trait {
201            name: Node::new(trait_name.text.into(), trait_name.span),
202            functions,
203            pub_qual,
204        },
205        span,
206    ))
207}
208
209/// Parse an impl block.
210/// # Panics
211/// Panics if the next token isn't `impl`.
212pub fn parse_impl_def(par: &mut Parser) -> ParseResult<Node<Impl>> {
213    let impl_tok = par.assert(TokenKind::Impl);
214
215    // impl SomeTrait for SomeType {}
216    let trait_name =
217        par.expect_with_notes(TokenKind::Name, "failed to parse `impl` definition", |_| {
218            vec!["Note: `impl` must be followed by the name of a trait".into()]
219        })?;
220
221    let for_tok =
222        par.expect_with_notes(TokenKind::For, "failed to parse `impl` definition", |_| {
223            vec![format!(
224                "Note: `impl {}` must be followed by the keyword `for`",
225                trait_name.text
226            )]
227        })?;
228
229    let receiver = parse_type_desc(par)?;
230    let mut functions = vec![];
231
232    let header_span = impl_tok.span + trait_name.span + for_tok.span + receiver.span;
233
234    par.enter_block(header_span, "impl definition")?;
235
236    loop {
237        par.eat_newlines();
238        match par.peek_or_err()? {
239            TokenKind::Fn => {
240                functions.push(parse_fn_def(par, None)?);
241            }
242            TokenKind::BraceClose => {
243                par.next()?;
244                break;
245            }
246            _ => {
247                let tok = par.next()?;
248                par.unexpected_token_error(&tok, "failed to parse `impl` definition body", vec![]);
249                return Err(ParseFailed);
250            }
251        };
252    }
253
254    Ok(Node::new(
255        Impl {
256            impl_trait: Node::new(trait_name.text.into(), trait_name.span),
257            receiver,
258            functions,
259        },
260        header_span,
261    ))
262}
263
264/// Parse a type alias definition, e.g. `type MyMap = Map<u8, address>`.
265/// # Panics
266/// Panics if the next token isn't `type`.
267pub fn parse_type_alias(par: &mut Parser, pub_qual: Option<Span>) -> ParseResult<Node<TypeAlias>> {
268    let type_tok = par.assert(TokenKind::Type);
269    let name = par.expect(TokenKind::Name, "failed to parse type declaration")?;
270    par.expect_with_notes(TokenKind::Eq, "failed to parse type declaration", |_| {
271        vec![
272            "Note: a type alias name must be followed by an equals sign and a type description"
273                .into(),
274            format!("Example: `type {} = Map<address, u64>`", name.text),
275        ]
276    })?;
277    let typ = parse_type_desc(par)?;
278    let span = type_tok.span + pub_qual + typ.span;
279    Ok(Node::new(
280        TypeAlias {
281            name: name.into(),
282            typ,
283            pub_qual,
284        },
285        span,
286    ))
287}
288
289/// Parse a field for a struct or contract. The leading optional `pub` and
290/// `const` qualifiers must be parsed by the caller, and passed in.
291pub fn parse_field(
292    par: &mut Parser,
293    attributes: Vec<Node<SmolStr>>,
294    pub_qual: Option<Span>,
295    const_qual: Option<Span>,
296) -> ParseResult<Node<Field>> {
297    let name = par.expect(TokenKind::Name, "failed to parse field definition")?;
298    par.expect_with_notes(
299        TokenKind::Colon,
300        "failed to parse field definition",
301        |next| {
302            let mut notes = vec![];
303            if name.text == "def" && next.kind == TokenKind::Name {
304                notes.push("Hint: use `fn` to define a function".into());
305                notes.push(format!(
306                    "Example: `{}fn {}( ...`",
307                    if pub_qual.is_some() { "pub " } else { "" },
308                    next.text
309                ));
310            }
311            notes
312                .push("Note: field name must be followed by a colon and a type description".into());
313            notes.push(format!(
314                "Example: {}{}{}: address",
315                if pub_qual.is_some() { "pub " } else { "" },
316                if const_qual.is_some() { "const " } else { "" },
317                name.text
318            ));
319            notes
320        },
321    )?;
322
323    let typ = parse_type_desc(par)?;
324    let value = if par.peek() == Some(TokenKind::Eq) {
325        par.next()?;
326        Some(parse_expr(par)?)
327    } else {
328        None
329    };
330    par.expect_stmt_end("field definition")?;
331    let span = name.span + pub_qual + const_qual + &typ;
332    Ok(Node::new(
333        Field {
334            is_pub: pub_qual.is_some(),
335            is_const: const_qual.is_some(),
336            attributes,
337            name: name.into(),
338            typ,
339            value,
340        },
341        span,
342    ))
343}
344
345/// Parse a variant for a enum definition.
346/// # Panics
347/// Panics if the next token isn't [`TokenKind::Name`].
348pub fn parse_variant(par: &mut Parser) -> ParseResult<Node<Variant>> {
349    let name = par.expect(TokenKind::Name, "failed to parse enum variant")?;
350    let mut span = name.span;
351
352    let kind = match par.peek_or_err()? {
353        TokenKind::ParenOpen => {
354            span += par.next().unwrap().span;
355            let mut tys = vec![];
356            loop {
357                match par.peek_or_err()? {
358                    TokenKind::ParenClose => {
359                        span += par.next().unwrap().span;
360                        break;
361                    }
362
363                    _ => {
364                        let ty = parse_type_desc(par)?;
365                        span += ty.span;
366                        tys.push(ty);
367                        if par.peek_or_err()? == TokenKind::Comma {
368                            par.next()?;
369                        } else {
370                            span += par
371                                .expect(
372                                    TokenKind::ParenClose,
373                                    "unexpected token while parsing enum variant",
374                                )?
375                                .span;
376                            break;
377                        }
378                    }
379                }
380            }
381
382            VariantKind::Tuple(tys)
383        }
384
385        _ => VariantKind::Unit,
386    };
387
388    par.expect_stmt_end("enum variant")?;
389    Ok(Node::new(
390        Variant {
391            name: name.into(),
392            kind,
393        },
394        span,
395    ))
396}
397
398/// Parse an optional qualifier (`pub`, `const`, or `idx`).
399pub fn parse_opt_qualifier(par: &mut Parser, tk: TokenKind) -> Option<Span> {
400    if par.peek() == Some(tk) {
401        let tok = par.next().unwrap();
402        Some(tok.span)
403    } else {
404        None
405    }
406}
407
408/// Parse an angle-bracket-wrapped list of generic arguments (eg. the tail end
409/// of `Map<address, u256>`).
410/// # Panics
411/// Panics if the first token isn't `<`.
412pub fn parse_generic_args(par: &mut Parser) -> ParseResult<Node<Vec<GenericArg>>> {
413    use TokenKind::*;
414    let mut span = par.assert(Lt).span;
415
416    let mut args = vec![];
417
418    let expect_end = |par: &mut Parser| {
419        // If there's no comma, the next token must be `>`
420        match par.peek_or_err()? {
421            Gt => Ok(par.next()?.span),
422            GtGt => Ok(par.split_next()?.span),
423            _ => {
424                let tok = par.next()?;
425                par.unexpected_token_error(
426                    &tok,
427                    "Unexpected token while parsing generic arg list",
428                    vec![],
429                );
430                Err(ParseFailed)
431            }
432        }
433    };
434
435    loop {
436        match par.peek_or_err()? {
437            Gt => {
438                span += par.next()?.span;
439                break;
440            }
441            GtGt => {
442                span += par.split_next()?.span;
443                break;
444            }
445            // Parse non-constant generic argument.
446            Name | ParenOpen => {
447                let typ = parse_type_desc(par)?;
448                args.push(GenericArg::TypeDesc(Node::new(typ.kind, typ.span)));
449                if par.peek() == Some(Comma) {
450                    par.next()?;
451                } else {
452                    span += expect_end(par)?;
453                    break;
454                }
455            }
456            // Parse literal-type constant generic argument.
457            Int => {
458                let tok = par.next()?;
459                if let Ok(num) = tok.text.parse() {
460                    args.push(GenericArg::Int(Node::new(num, tok.span)));
461                    if par.peek() == Some(Comma) {
462                        par.next()?;
463                    } else {
464                        span += expect_end(par)?;
465                        break;
466                    }
467                } else {
468                    par.error(tok.span, "failed to parse integer literal");
469                    return Err(ParseFailed);
470                }
471            }
472            // Parse expr-type constant generic argument.
473            BraceOpen => {
474                let brace_open = par.next()?;
475                let expr = parse_expr(par)?;
476                if !matches!(par.next()?.kind, BraceClose) {
477                    par.error(brace_open.span, "missing closing delimiter `}`");
478                    return Err(ParseFailed);
479                }
480
481                args.push(GenericArg::ConstExpr(expr));
482
483                if par.peek() == Some(Comma) {
484                    par.next()?;
485                } else {
486                    span += expect_end(par)?;
487                    break;
488                }
489            }
490
491            // Invalid generic argument.
492            _ => {
493                let tok = par.next()?;
494                par.unexpected_token_error(
495                    &tok,
496                    "failed to parse generic type argument list",
497                    vec![],
498                );
499                return Err(ParseFailed);
500            }
501        }
502    }
503    Ok(Node::new(args, span))
504}
505
506/// Returns path and trailing `::` token, if present.
507pub fn parse_path_tail<'a>(
508    par: &mut Parser<'a>,
509    head: Node<SmolStr>,
510) -> (Path, Span, Option<Token<'a>>) {
511    let mut span = head.span;
512    let mut segments = vec![head];
513    while let Some(delim) = par.optional(TokenKind::ColonColon) {
514        if let Some(name) = par.optional(TokenKind::Name) {
515            span += name.span;
516            segments.push(name.into());
517        } else {
518            return (Path { segments }, span, Some(delim));
519        }
520    }
521    (Path { segments }, span, None)
522}
523
524/// Parse a type description, e.g. `u8` or `Map<address, u256>`.
525pub fn parse_type_desc(par: &mut Parser) -> ParseResult<Node<TypeDesc>> {
526    use TokenKind::*;
527    let mut typ = match par.peek_or_err()? {
528        SelfType => {
529            let _self = par.next()?;
530            Node::new(TypeDesc::SelfType, _self.span)
531        }
532        Name => {
533            let name = par.next()?;
534            match par.peek() {
535                Some(ColonColon) => {
536                    let (path, span, trailing_delim) = parse_path_tail(par, name.into());
537                    if let Some(colons) = trailing_delim {
538                        let next = par.next()?;
539                        par.fancy_error(
540                            "failed to parse type description",
541                            vec![
542                                Label::secondary(colons.span, "path delimiter"),
543                                Label::primary(next.span, "expected a name"),
544                            ],
545                            vec![],
546                        );
547                        return Err(ParseFailed);
548                    }
549                    Node::new(TypeDesc::Path(path), span)
550                }
551                Some(Lt) => {
552                    let args = parse_generic_args(par)?;
553                    let span = name.span + args.span;
554                    Node::new(
555                        TypeDesc::Generic {
556                            base: name.into(),
557                            args,
558                        },
559                        span,
560                    )
561                }
562                _ => Node::new(
563                    TypeDesc::Base {
564                        base: name.text.into(),
565                    },
566                    name.span,
567                ),
568            }
569        }
570        ParenOpen => {
571            let mut span = par.next()?.span;
572            let mut items = vec![];
573            loop {
574                match par.peek_or_err()? {
575                    ParenClose => {
576                        span += par.next()?.span;
577                        break;
578                    }
579
580                    Name | ParenOpen => {
581                        let item = parse_type_desc(par)?;
582                        span += item.span;
583                        items.push(item);
584                        if par.peek_or_err()? == Comma {
585                            par.next()?;
586                        } else {
587                            span += par
588                                .expect(
589                                    ParenClose,
590                                    "Unexpected token while parsing tuple type description",
591                                )?
592                                .span;
593                            break;
594                        }
595                    }
596
597                    _ => {
598                        let tok = par.next()?;
599                        par.unexpected_token_error(
600                            &tok,
601                            "failed to parse type description",
602                            vec![],
603                        );
604                        return Err(ParseFailed);
605                    }
606                }
607            }
608            if items.is_empty() {
609                Node::new(TypeDesc::Unit, span)
610            } else {
611                Node::new(
612                    TypeDesc::Tuple {
613                        items: Vec1::try_from_vec(items).expect("couldn't convert vec to vec1"),
614                    },
615                    span,
616                )
617            }
618        }
619        _ => {
620            let tok = par.next()?;
621            par.unexpected_token_error(&tok, "failed to parse type description", vec![]);
622            return Err(ParseFailed);
623        }
624    };
625
626    while par.peek() == Some(BracketOpen) {
627        let l_brack = par.next()?.span;
628
629        if_chain! {
630            if let Some(size_token) = par.optional(TokenKind::Int);
631            if let Ok(dimension) = size_token.text.parse::<usize>();
632            if let Some(r_brack) = par.optional(TokenKind::BracketClose);
633            then {
634                let span = typ.span + l_brack + r_brack.span;
635                par.fancy_error(
636                    "Outdated array syntax",
637                    vec![
638                        Label::primary(
639                            span,
640                            ""
641                        )
642                    ],
643                    vec![
644                        format!("Hint: Use `Array<{}, {}>`", typ.kind, dimension)
645                    ]
646                );
647                typ = Node::new(
648                    TypeDesc::Generic {
649                        base: Node::new("Array".into(), typ.span),
650                        args: Node::new(vec![
651                            GenericArg::TypeDesc(
652                                Node::new(typ.kind, typ.span)
653                            ),
654                            GenericArg::Int(
655                                Node::new(dimension, size_token.span)
656                            )
657                        ], span)
658                    },
659                    span,
660                );
661            } else {
662                par.fancy_error(
663                    "Unexpected token while parsing type description",
664                    vec![
665                        Label::primary(
666                            l_brack,
667                            "Unexpected token"
668                        )
669                    ],
670                    vec![
671                        format!("Hint: To define an array type use `Array<{}, 10>`", typ.kind)
672                    ]
673                );
674                return Err(ParseFailed);
675            }
676        }
677    }
678
679    Ok(typ)
680}