luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::super::*;

impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
    'name: 'ast,
{
    pub(in crate::parser) fn parse_table_type(
        &mut self,
        in_declaration_context: bool,
    ) -> Result<Type<'ast>> {
        let brace_location = self.current_location();
        self.advance();

        let mut props = self.temp_table_type_props();
        let mut indexer = None;
        let mut cst_items = self.cst.enabled().then(|| self.temp_cst_table_type_items());
        let mut is_array = false;

        while self.current.kind() != TokenKind::RightBrace {
            let access = self.parse_table_access(false)?;
            if self.current.kind() == TokenKind::LeftBracket {
                let indexer_location = self.current_token_location();
                let indexer_open = self.current_position();
                if matches!(
                    self.peek(),
                    Token::QuotedString { .. } | Token::RawString { .. }
                ) && self.peek_nth(2).kind() == TokenKind::RightBracket
                {
                    self.advance();
                    let (name, string_info, string_position) = match self.current {
                        Token::QuotedString { value, quote_style } => {
                            let location = self.current_token_location();
                            let string_info = self.cst.enabled().then(|| {
                                (
                                    self.current_source_string(),
                                    CstStringQuoteStyle::from(quote_style),
                                    0,
                                )
                            });
                            let name = fixup_string_bytes(value);
                            self.advance();
                            (name, string_info, location.begin)
                        }
                        Token::RawString { value, block_depth } => {
                            let location = self.current_token_location();
                            let string_info = self.cst.enabled().then(|| {
                                (
                                    self.current_source_string(),
                                    CstStringQuoteStyle::QuotedRaw,
                                    block_depth as u32,
                                )
                            });
                            self.advance();
                            (
                                Some(multiline_string_bytes(value)),
                                string_info,
                                location.begin,
                            )
                        }
                        _ => unreachable!("checked string table key"),
                    };
                    let indexer_close = if self.current.kind() == TokenKind::RightBracket {
                        let position = self.current_position();
                        self.advance();
                        position
                    } else if self.expect_match_token(
                        Token::RightBracket,
                        "]",
                        "[",
                        indexer_location,
                        false,
                    ) {
                        self.previous_token_location().begin
                    } else {
                        Position::missing()
                    };
                    let colon = if self.current.kind() == TokenKind::Colon {
                        let position = self.current_position();
                        self.advance();
                        position
                    } else {
                        self.expect_and_consume(Token::Colon, "table field");
                        Position::missing()
                    };
                    let ty = self.parse_type_annotation()?;

                    if let Some(name) = name.filter(|name| !name.contains(&0)) {
                        props.push_back(TableTypeProp {
                            name: self.string_property_name(&name),
                            location: indexer_location,
                            ty,
                            access: access.access,
                            access_location: access.location,
                        });
                        let string_info =
                            string_info.map(|(source_string, quote_style, block_depth)| {
                                CstExprConstantString {
                                    source_string,
                                    quote_style,
                                    block_depth,
                                }
                            });
                        if let Some(cst_items) = &mut cst_items {
                            cst_items.push_back(CstTypeTableItem {
                                kind: CstTypeTableItemKind::StringProperty,
                                indexer_open,
                                indexer_close,
                                colon,
                                separator: None,
                                separator_position: None,
                                string_info,
                                string_position,
                            });
                        }
                    } else {
                        self.report_parse_error(ParseError::new(
                            indexer_location,
                            "String literal contains malformed escape sequence or \\0",
                        ));
                    }
                } else {
                    if indexer.is_some() {
                        let bad_indexer_start = self.current_token_location().begin;
                        self.advance();
                        let _ = self.parse_type_annotation();
                        self.expect_match_token(
                            Token::RightBracket,
                            "]",
                            "[",
                            indexer_location,
                            false,
                        );
                        self.expect_and_consume(Token::Colon, "table field");
                        let _ = self.parse_type_annotation();
                        self.report_parse_error(ParseError::new(
                            Location::new(bad_indexer_start, self.previous_token_end_position()),
                            "Cannot have more than one table indexer",
                        ));
                    } else {
                        self.advance();
                        let index_type = self.parse_type_annotation()?;
                        let indexer_close = if self.current.kind() == TokenKind::RightBracket {
                            let position = self.current_position();
                            self.advance();
                            position
                        } else if self.expect_match_token(
                            Token::RightBracket,
                            "]",
                            "[",
                            indexer_location,
                            false,
                        ) {
                            self.previous_token_location().begin
                        } else {
                            Position::missing()
                        };
                        let colon = if self.current.kind() == TokenKind::Colon {
                            let position = self.current_position();
                            self.advance();
                            position
                        } else {
                            self.expect_and_consume(Token::Colon, "table field");
                            Position::missing()
                        };
                        let result_type = self.parse_type_annotation()?;
                        indexer = Some(self.arena.alloc_node(TableTypeIndexer {
                            index_type,
                            result_type,
                            location: Location::new(indexer_open, result_type.location.end),
                            access: access.access,
                            access_location: access.location,
                        }));
                        if let Some(cst_items) = &mut cst_items {
                            cst_items.push_back(CstTypeTableItem {
                                kind: CstTypeTableItemKind::Indexer,
                                indexer_open,
                                indexer_close,
                                colon,
                                separator: None,
                                separator_position: None,
                                string_info: None,
                                string_position: Position::zero(),
                            });
                        }
                    }
                }
            } else if props.len() == 0
                && indexer.is_none()
                && !(matches!(self.current, Token::Ident(_))
                    && self.peek().kind() == TokenKind::Colon)
            {
                let result_type = self.parse_type_annotation()?;
                is_array = true;
                let number_name = self.name_number;
                let parameters = self.arena.alloc_slice_fill_iter(std::iter::empty());
                let index_location = Location::from_length(brace_location.begin, 0);
                let index_type = self.alloc_type(
                    index_location,
                    TypeKind::Reference {
                        prefix: None,
                        prefix_location: None,
                        prefix_local: None,
                        name: number_name,
                        location: index_location,
                        name_location: index_location,
                        has_parameter_list: false,
                        parameters,
                    },
                );
                indexer = Some(self.arena.alloc_node(TableTypeIndexer {
                    index_type,
                    result_type,
                    location: result_type.location,
                    access: access.access,
                    access_location: access.location,
                }));
                break;
            } else {
                let (name, name_location) = match self.current {
                    Token::Ident(name) => {
                        let location = self.current_token_location();
                        self.advance();
                        (name, location)
                    }
                    _ => {
                        self.report_parse_error(self.table_field_name_error());
                        break;
                    }
                };
                let colon = if self.current.kind() == TokenKind::Colon {
                    let position = self.current_position();
                    self.advance();
                    position
                } else {
                    self.expect_and_consume(Token::Colon, "table field");
                    Position::missing()
                };
                let ty = self.parse_type_annotation_with_context(in_declaration_context)?;
                props.push_back(TableTypeProp {
                    name,
                    location: name_location,
                    ty,
                    access: access.access,
                    access_location: access.location,
                });
                if let Some(cst_items) = &mut cst_items {
                    cst_items.push_back(CstTypeTableItem {
                        kind: CstTypeTableItemKind::Property,
                        indexer_open: Position::missing(),
                        indexer_close: Position::missing(),
                        colon,
                        separator: None,
                        separator_position: None,
                        string_info: None,
                        string_position: Position::zero(),
                    });
                }
            }

            if let Some(item) = cst_items
                .as_mut()
                .and_then(|items| items.as_mut_slice().last_mut())
            {
                item.separator = match self.current {
                    Token::Comma => Some(TableSeparator::Comma),
                    Token::Semicolon => Some(TableSeparator::Semicolon),
                    _ => None,
                };
                item.separator_position = item.separator.as_ref().map(|_| self.current_position());
            }

            if matches!(self.current, Token::Comma | Token::Semicolon) {
                self.advance();
                if self.current.kind() == TokenKind::RightBrace {
                    break;
                }
            } else {
                break;
            }
        }

        let end_location = self.current_token_location();
        let mut end = end_location.end;
        if !self.expect_match_token(Token::RightBrace, "}", "{", brace_location, true) {
            end = self.previous_token_end_position();
        }
        let location = Location::new(brace_location.begin, end);
        Ok(self.alloc_type_with_cst(
            location,
            TypeKind::Table {
                props: self.arena.alloc_slice_copy(props.as_slice()),
                indexer,
            },
            cst_items.map(|items| {
                CstNode::TypeTable(CstTypeTable {
                    items: items.as_slice().to_vec(),
                    is_array,
                })
            }),
        ))
    }
}