use super::*;
use crate::ascii::ascii_display;
impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
'name: 'ast,
{
pub(in crate::parser) fn parse_type_alias_after_keyword(
&mut self,
location: Location,
exported: bool,
type_keyword: Position,
) -> Result<ParsedStatement<'ast>> {
if self.current.kind() == TokenKind::Reserved(R::Function) {
return self.parse_type_function(location, exported, type_keyword);
}
let (name, name_location) = match self.current {
Token::Ident(name) => {
let location = self.current_token_location();
self.advance();
(name, location)
}
_ => {
let location = self.current_token_location();
self.report_parse_error(self.type_name_error());
(self.name_error, location)
}
};
let generics = self.parse_optional_generic_parameters(true)?;
let equals = if self.expect_and_consume(Token::Equal, "type alias") {
self.previous_token_location().begin
} else {
Position::missing()
};
let ty = self.parse_type_annotation()?;
let full_location = Location::new(location.begin, ty.location.end);
let statement = self.arena.alloc_statement_node(StatementTypeAlias::new(
full_location,
false,
name,
name_location,
self.arena.alloc_slice_fill_iter(generics.types),
self.arena.alloc_slice_fill_iter(generics.type_packs),
exported,
ty,
));
Ok(self.parsed_statement_from_node(
statement,
self.cst_node(|| {
CstNode::StatTypeAlias(CstStatTypeAlias {
type_keyword,
generics_open: generics.open,
generics_commas: generics.commas,
generics_close: generics.close,
equals,
})
}),
))
}
pub(in crate::parser) fn parse_type_function(
&mut self,
location: Location,
exported: bool,
type_keyword: Position,
) -> Result<ParsedStatement<'ast>> {
let function_location = self.current_token_location();
let function_keyword = self.current_position();
self.advance();
let errors_at_start = self.diagnostics.errors.len();
let (name, name_location) = match self.current {
Token::Ident(name) => {
let location = self.current_token_location();
self.advance();
(name, location)
}
_ => {
let location = self.current_token_location();
self.report_parse_error(self.unexpected("type function name"));
(self.name_error, location)
}
};
let previous_type_function_local_depth = self.locals.type_function_local_depth;
self.locals.type_function_local_depth = Some(self.contexts.functions.len() + 1);
let function = self.parse_function_after_name(FunctionParseContext {
attributes: ParsedAttributes::default(),
location: function_location,
match_location: function_location,
function_keyword,
self_parameter: None,
debug_name: Some(name),
});
self.locals.type_function_local_depth = previous_type_function_local_depth;
let function = function?.function;
let has_errors = self.diagnostics.errors.len() > errors_at_start;
let location = Location::new(location.begin, function.location.end);
let statement = self.arena.alloc_statement_node(StatementTypeFunction::new(
location,
false,
name,
name_location,
exported,
function,
has_errors,
));
Ok(self.parsed_statement_from_node(
statement,
self.cst_node(|| {
CstNode::StatTypeFunction(CstStatTypeFunction {
type_keyword,
function_keyword,
})
}),
))
}
pub(in crate::parser) fn parse_declaration_after_keyword(
&mut self,
location: Location,
attributes: Vec<&'ast Attribute<'ast>>,
) -> Result<Statement<'ast>> {
if self.current.kind() == TokenKind::Reserved(R::Function) {
return self.parse_function_declaration_signature(location, attributes);
}
if !attributes.is_empty() {
let got = &self.current;
let message_index = self.report_parse_error(self.located(format!(
"Expected a function type declaration after attribute, but got {got} instead"
)));
let location = self.current_location();
return Ok(self.error_statement(location, message_index));
}
if flags::LuauDisallowExternClassInTypeDefinitions.get() {
if self.current.is_keyword("extern") {
return self.parse_extern_type_declaration(true);
}
} else if self.current.is_keyword("class")
&& (!flags::LuauAllowGlobalDeclarationToBeCalledClass.get()
|| self.peek().kind() != TokenKind::Colon)
{
return self.parse_extern_type_declaration(false);
} else if self.current.is_keyword("extern") {
return self.parse_extern_type_declaration(true);
}
let Token::Ident(name) = self.current else {
let message_index = self.report_parse_error(self.error_at(
location,
"declare must be followed by an identifier, 'function', or 'extern type'",
));
return Ok(self.error_statement(location, message_index));
};
let name_location = self.current_token_location();
self.advance();
self.expect_declaration_colon("global variable declaration");
let ty = self.parse_type_annotation_in_declaration()?;
Ok(self.arena.alloc_statement_node(StatementDeclareGlobal::new(
Location::new(location.begin, ty.location.end),
false,
name,
name_location,
ty,
)))
}
pub(in crate::parser) fn parse_function_declaration_signature(
&mut self,
location: Location,
attributes: Vec<&'ast Attribute<'ast>>,
) -> Result<Statement<'ast>> {
self.advance();
let name = self.parse_name("global function name");
let name_location = name.location;
let name = name.name;
let generics = self.parse_optional_generic_parameters(false)?;
let paren_location = self.current_location();
self.expect_and_consume(Token::LeftParen, "global function declaration");
let mut bindings = self.temp_bindings();
let mut commas = self.temp_positions();
let parsed_parameters = if self.current.kind() != TokenKind::RightParen {
self.parse_binding_list_into(&mut bindings, true, &mut commas, false)?
} else {
ParsedBindingList {
variadic: false,
vararg_location: Location::zero(),
vararg_annotation: None,
vararg_annotation_colon: Position::missing(),
}
};
self.expect_match_token(Token::RightParen, ")", "(", paren_location, false);
let return_types = if let Some(return_types) = self.parse_optional_return_annotation()? {
return_types
} else {
let type_list = TypeList {
types: self.empty_type_slice(),
tail_type: None,
};
self.alloc_type_pack(
self.current_token_location(),
TypePackKind::Explicit { type_list },
)
};
let declaration_end = self.current_token_location();
if bindings.iter().any(|binding| binding.annotation.is_none())
|| (parsed_parameters.variadic && parsed_parameters.vararg_annotation.is_none())
{
let message_index = self.report_parse_error(self.error_at(
Location::new(location.begin, declaration_end.end),
"All declaration parameters must be annotated",
));
return Ok(self.error_statement(
Location::new(location.begin, declaration_end.end),
message_index,
));
}
let full_location = Location::new(location.begin, declaration_end.end);
let mut params_vec = self.temp_types();
let mut param_names = self.temp_argument_names();
for binding in bindings.iter() {
if let Some(annotation) = binding.annotation {
params_vec.push_back(annotation);
}
param_names.push_back(ArgumentName {
name: binding.name,
location: binding.name_location,
});
}
let params = TypeList {
types: self.arena.alloc_slice_copy(params_vec.as_slice()),
tail_type: parsed_parameters.vararg_annotation,
};
Ok(self
.arena
.alloc_statement_node(StatementDeclareFunction::new(
full_location,
false,
name,
name_location,
params,
self.arena.alloc_slice_copy(param_names.as_slice()),
parsed_parameters.variadic,
parsed_parameters.vararg_location,
self.arena.alloc_slice_fill_iter(generics.types),
self.arena.alloc_slice_fill_iter(generics.type_packs),
return_types,
self.arena.alloc_slice_fill_iter(attributes),
)))
}
pub(in crate::parser) fn expect_declaration_colon(&mut self, context: &'static str) -> bool {
if self.current.kind() == TokenKind::Colon {
self.advance();
true
} else {
self.report_parse_error(self.located(format!(
"Expected ':' when parsing {context}, got {}",
self.current
)));
false
}
}
pub(in crate::parser) fn parse_extern_type_declaration(
&mut self,
extern_type: bool,
) -> Result<Statement<'ast>> {
if extern_type {
self.advance();
if !self.current.is_keyword("type") {
let message_index = self.report_parse_error(self.located(format!(
"Expected `type` keyword after `extern`, but got {} instead",
self.current.name()
)));
return Ok(self.error_statement(self.current_location(), message_index));
}
}
self.advance();
let declaration_location = self.current_location();
let (name, _name_location) = match self.current {
Token::Ident(name) => {
let location = self.current_token_location();
self.advance();
(name, location)
}
_ => {
let location = self.current_token_location();
self.report_parse_error(self.type_name_error());
(self.name_error, location)
}
};
let super_name = if self.current.is_keyword("extends") {
self.advance();
let super_name = match self.current {
Token::Ident(super_name) => {
self.advance();
super_name
}
_ => {
self.report_parse_error(self.unexpected("supertype name"));
self.name_error
}
};
Some(super_name)
} else {
None
};
if extern_type {
if self.current.is_keyword("with") {
self.advance();
} else if let Token::Ident(found) = self.current {
let error = self.located(format!(
"Expected `with` keyword before listing properties of the external type, but got {} instead",
ascii_display(found.bytes())
)
);
self.report_parse_error(error);
} else {
let error = self.located(format!(
"Expected `with` keyword before listing properties of the external type, but got {} instead",
self.current.name()
)
);
self.report_parse_error(error);
}
}
let mut props = self.temp_declared_extern_type_props();
let mut indexer = None;
while self.current.kind() != TokenKind::Reserved(R::End) {
let attributes = self.parse_attributes()?;
if !attributes.attributes.is_empty()
&& self.current.kind() != TokenKind::Reserved(R::Function)
{
let got = &self.current;
let message_index = self.report_parse_error(self.located(format!(
"Expected a method type declaration after attribute, but got {got} instead"
)));
return Ok(self.error_statement(self.current_location(), message_index));
}
if self.current.kind() == TokenKind::Reserved(R::Function) {
props.push_back(self.parse_declared_extern_type_method(attributes.attributes)?);
continue;
}
if self.current.kind() == TokenKind::LeftBracket {
if matches!(
self.peek(),
Token::QuotedString { .. } | Token::RawString { .. }
) && self.peek_nth(2).kind() == TokenKind::RightBracket
{
if let Some(prop) = self.parse_declared_extern_type_string_property()? {
props.push_back(prop);
}
} else {
let parsed_indexer =
self.parse_declared_extern_type_indexer(TableAccess::ReadWrite, None)?;
if indexer.is_some() {
let error = self.error_at(
parsed_indexer.location,
"Cannot have more than one indexer on an extern type",
);
self.report_parse_error(error);
} else {
indexer = Some(self.arena.alloc_node(parsed_indexer));
}
}
} else {
let access = if extern_type {
self.parse_table_access(true)?
} else {
ParsedTableAccess {
access: TableAccess::ReadWrite,
location: None,
}
};
let Some(prop) = self.parse_declared_extern_type_property(access.access)? else {
break;
};
props.push_back(prop);
}
}
let end_location = self.current_token_location();
if self.current.kind() == TokenKind::Reserved(R::End) {
self.advance();
}
Ok(self
.arena
.alloc_statement_node(StatementDeclareExternType::new(
Location::new(declaration_location.begin, end_location.end),
false,
name,
super_name,
self.arena.alloc_slice_copy(props.as_slice()),
indexer,
)))
}
pub(in crate::parser) fn parse_declared_extern_type_string_property(
&mut self,
) -> Result<Option<DeclaredExternTypeProperty<'ast>>> {
let bracket_location = self.current_token_location();
self.advance();
let name_location = self.current_token_location();
let chars = match self.current {
Token::QuotedString { value, .. } => {
self.advance();
fixup_string_bytes(value)
}
Token::RawString { value, .. } => {
self.advance();
Some(multiline_string_bytes(value))
}
_ => unreachable!("checked string property"),
};
self.expect_match_token(Token::RightBracket, "]", "[", bracket_location, false);
self.expect_and_consume(Token::Colon, "property type annotation");
let ty = self.parse_type_annotation()?;
let contains_null = chars.as_ref().is_some_and(|chars| chars.contains(&0));
if let Some(chars) = chars.filter(|_| !contains_null) {
let name = self.string_property_name(&chars);
Ok(Some(DeclaredExternTypeProperty {
name,
name_location,
ty,
is_method: false,
location: Location::new(bracket_location.begin, self.previous_token_end_position()),
access: TableAccess::ReadWrite,
}))
} else {
self.report_parse_error(ParseError::new(
bracket_location,
"String literal contains malformed escape sequence or \\0",
));
Ok(None)
}
}
pub(in crate::parser) fn parse_declared_extern_type_indexer(
&mut self,
access: TableAccess,
access_location: Option<Location>,
) -> Result<TableTypeIndexer<'ast>> {
let bracket_location = self.current_token_location();
let start = self.current_position();
self.advance();
let index_type = self.parse_type_annotation()?;
self.expect_match_token(Token::RightBracket, "]", "[", bracket_location, false);
self.expect_and_consume(Token::Colon, "property type annotation");
let result_type = self.parse_type_annotation()?;
Ok(TableTypeIndexer {
index_type,
result_type,
location: Location::new(start, result_type.location.end),
access,
access_location,
})
}
pub(in crate::parser) fn parse_declared_extern_type_property(
&mut self,
access: TableAccess,
) -> Result<Option<DeclaredExternTypeProperty<'ast>>> {
let location_start = self.current_position();
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.property_name_error());
return Ok(None);
}
};
self.expect_and_consume(Token::Colon, "property type annotation");
let ty = self.parse_type_annotation()?;
let location = Location::new(location_start, self.previous_token_end_position());
Ok(Some(DeclaredExternTypeProperty {
name,
name_location,
ty,
is_method: false,
location,
access,
}))
}
pub(in crate::parser) fn parse_declared_extern_type_method(
&mut self,
attributes: Vec<&'ast Attribute<'ast>>,
) -> Result<DeclaredExternTypeProperty<'ast>> {
let location_start = self.current_position();
let start_location = self.current_location();
self.advance();
let name = self.parse_name("function name");
let name_location = name.location;
let name = name.name;
let paren_location = self.current_location();
self.expect_and_consume(Token::LeftParen, "function parameter list start");
let mut bindings = self.temp_bindings();
let mut commas = self.temp_positions();
let parsed_parameters = if self.current.kind() != TokenKind::RightParen {
self.parse_binding_list_into(&mut bindings, true, &mut commas, false)?
} else {
ParsedBindingList {
variadic: false,
vararg_location: Location::zero(),
vararg_annotation: None,
vararg_annotation_colon: Position::missing(),
}
};
self.expect_match_token(Token::RightParen, ")", "(", paren_location, false);
let return_types =
if let Some(return_annotation) = self.parse_optional_return_annotation()? {
return_annotation
} else {
let pack = self.explicit_type_pack(Vec::new(), None);
self.alloc_type_pack(self.current_token_location(), pack)
};
let location = Location::new(location_start, self.previous_token_end_position());
let mut params = self.temp_types();
let mut arg_names = self.temp_optional_argument_names();
let invalid_self = bindings
.as_slice()
.first()
.is_none_or(|binding| binding.name != "self" || binding.annotation.is_some());
if invalid_self {
let message_index = self.report_parse_error(self.error_at(
location,
"'self' must be present as the unannotated first parameter",
));
let ty = self.error_type(message_index);
let ty = self.alloc_type(location, ty);
return Ok(DeclaredExternTypeProperty {
name,
name_location,
ty,
is_method: true,
location,
access: TableAccess::ReadWrite,
});
}
for binding in bindings.iter().skip(1) {
arg_names.push_back(Some(ArgumentName {
name: binding.name,
location: binding.name_location,
}));
if let Some(annotation) = binding.annotation {
params.push_back(annotation);
} else {
let message_index = self.report_parse_error(self.error_at(
location,
"All declaration parameters aside from 'self' must be annotated",
));
let ty = self.error_type(message_index);
params.push_back(self.alloc_type(location, ty));
}
}
if parsed_parameters.variadic && parsed_parameters.vararg_annotation.is_none() {
self.report_parse_error(self.error_at(
start_location,
"All declaration parameters aside from 'self' must be annotated",
));
}
let arg_types = TypeList {
types: self.arena.alloc_slice_copy(params.as_slice()),
tail_type: parsed_parameters.vararg_annotation,
};
let attributes = self.arena.alloc_slice_fill_iter(attributes);
let generics = self.arena.alloc_slice_fill_iter(std::iter::empty());
let generic_packs = self.arena.alloc_slice_fill_iter(std::iter::empty());
let arg_names = self.arena.alloc_slice_copy(arg_names.as_slice());
let ty = self.alloc_type(
location,
TypeKind::Function {
attributes,
generics,
generic_packs,
arg_types,
arg_names,
return_types,
},
);
Ok(DeclaredExternTypeProperty {
name,
name_location,
ty,
is_method: true,
location,
access: TableAccess::ReadWrite,
})
}
pub(in crate::parser) fn parse_table_access(
&mut self,
reject_unknown: bool,
) -> Result<ParsedTableAccess> {
let Token::Ident(name) = self.current else {
return Ok(ParsedTableAccess {
access: TableAccess::ReadWrite,
location: None,
});
};
if self.peek().kind() == TokenKind::Colon {
return Ok(ParsedTableAccess {
access: TableAccess::ReadWrite,
location: None,
});
}
if name == "read" {
let location = self.current_token_location();
self.advance();
return Ok(ParsedTableAccess {
access: TableAccess::Read,
location: Some(location),
});
}
if name == "write" {
let location = self.current_token_location();
self.advance();
return Ok(ParsedTableAccess {
access: TableAccess::Write,
location: Some(location),
});
}
if reject_unknown {
let error = self.located(format!(
"Expected blank or 'read' or 'write' attribute, got '{}'",
ascii_display(name.bytes())
));
self.report_parse_error(error);
self.advance();
Ok(ParsedTableAccess {
access: TableAccess::ReadWrite,
location: None,
})
} else {
Ok(ParsedTableAccess {
access: TableAccess::ReadWrite,
location: None,
})
}
}
}