use super::*;
impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
'name: 'ast,
{
pub(in crate::parser) fn parse_local(
&mut self,
location: Location,
attributes: ParsedAttributes<'ast>,
) -> Result<ParsedStatement<'ast>> {
let local_keyword = self.current_token_location();
self.advance();
self.parse_local_after_keyword(location, attributes, false, false, local_keyword)
}
pub(in crate::parser) fn parse_const(
&mut self,
location: Location,
attributes: ParsedAttributes<'ast>,
) -> Result<ParsedStatement<'ast>> {
let local_keyword = self.current_token_location();
self.advance();
self.parse_local_after_keyword(location, attributes, true, false, local_keyword)
}
pub(in crate::parser) fn parse_export_value_after_keyword(
&mut self,
location: Location,
keyword: Location,
attributes: ParsedAttributes<'ast>,
) -> Result<ParsedStatement<'ast>> {
if !self.contexts.functions.is_empty() || self.contexts.recursion_counter != 1 {
self.report_parse_error(self.error_at(
location,
"'export' may only be applied to top-level statements",
));
}
if self.has_module_return {
self.report_parse_error(self.error_at(
location,
"Exporting values is not compatible with top-level return (export/return conflict)",
));
}
if !attributes.attributes.is_empty()
&& self.current.kind() != TokenKind::Reserved(R::Function)
{
self.report_parse_error(self.error_at(
self.current_location(),
format!(
"Expected 'function' after export declaration with attribute, but got {} instead",
self.current
),
));
}
match self.current {
Token::Reserved(R::Local) => {
if self.peek().kind() == TokenKind::Reserved(R::Function) {
let message_index = self.report_parse_error(self.error_at(
location,
"'export' must be followed by an identifier or 'function'; try removing 'local'",
));
let statement = self.error_statement(location, message_index);
return Ok(self.parsed_statement_from_node(statement, None));
}
let local_keyword = self.current_token_location();
self.advance();
self.parse_local_after_keyword(
location,
ParsedAttributes::default(),
false,
true,
local_keyword,
)
}
Token::Reserved(R::Function) => {
self.parse_local_after_keyword(location, attributes, true, true, keyword)
}
Token::Ident(name) if name == "const" => {
let const_keyword = self.current_token_location();
self.advance();
if self.current.kind() == TokenKind::Reserved(R::Function) {
let message_index = self.report_parse_error(self.error_at(
location,
"'export' must be followed by an identifier or 'function'",
));
let statement = self.error_statement(location, message_index);
return Ok(self.parsed_statement_from_node(statement, None));
}
self.parse_local_after_keyword(
location,
ParsedAttributes::default(),
true,
true,
const_keyword,
)
}
Token::Ident(name) if flags::DebugLuauUserDefinedClasses.get() && name == "class" => {
self.advance();
let class = self.parse_class_statement_after_keyword(location, true)?;
let local = class.local;
if self.declared_export_bindings.contains(&local.name) {
let message_index = self.report_parse_error(self.error_at(
local.location,
self.name_message(b"Duplicate exported class '", local.name, b"'"),
));
let error = self.arena.alloc_statement_node(StatementError::new(
local.location,
false,
self.empty_expression_slice(),
self.arena.alloc_slice_fill_iter([class.statement]),
message_index,
));
return Ok(self.parsed_statement_from_node(error, None));
}
self.declared_export_bindings
.insert(local.name, local.location);
Ok(self.parsed_statement_from_node(class.statement, None))
}
_ => {
let message_index = self.report_parse_error(self.error_at(
location,
"'export' must be followed by an identifier or 'function'",
));
let statement = self.error_statement(location, message_index);
Ok(self.parsed_statement_from_node(statement, None))
}
}
}
pub(in crate::parser) fn parse_local_after_keyword(
&mut self,
location: Location,
attributes: ParsedAttributes<'ast>,
is_const: bool,
is_exported: bool,
local_keyword: Location,
) -> Result<ParsedStatement<'ast>> {
if self.current.kind() == TokenKind::Reserved(R::Function) {
let mut match_location = self.current_token_location();
let function_keyword = match_location.begin;
self.advance();
if match_location.begin.line == location.begin.line {
match_location.begin.column = location.begin.column;
}
let name = self.parse_name("variable name");
let name_location = name.location;
let name = name.name;
let attr_lists = attributes.attr_lists.clone();
let parsed = self.parse_local_function_after_name(
FunctionParseContext {
attributes,
location,
match_location,
function_keyword,
self_parameter: None,
debug_name: Some(name),
},
FunctionLocalBinding {
name,
location: name_location,
is_const,
is_exported,
},
)?;
let statement_location = Location::new(location.begin, parsed.function.location.end);
let local = parsed.local;
if is_exported {
if self.declared_export_bindings.contains(&local.name) {
let message_index = self.report_parse_error(self.error_at(
local.location,
self.name_message(b"Duplicate exported identifier '", local.name, b"'"),
));
let statement = self.arena.alloc_statement_node(StatementLocalFunction::new(
statement_location,
false,
local,
parsed.function,
is_const,
if is_const && flags::LuauStoreConstKeywordBegin.get() {
local_keyword.begin
} else {
Position::missing()
},
));
let error = self.arena.alloc_statement_node(StatementError::new(
local.location,
false,
self.empty_expression_slice(),
self.arena.alloc_slice_fill_iter([statement]),
message_index,
));
return Ok(self.parsed_statement_from_node(error, None));
}
self.declared_export_bindings
.insert(local.name, local.location);
}
let statement = self.arena.alloc_statement_node(StatementLocalFunction::new(
statement_location,
false,
local,
parsed.function,
is_const,
if is_const && flags::LuauStoreConstKeywordBegin.get() {
local_keyword.begin
} else {
Position::missing()
},
));
return Ok(self.parsed_statement_from_node(
statement,
if self.cst.enabled() {
Some(CstNode::StatLocalFunction(CstStatLocalFunction {
attr_lists,
local_keyword: local_keyword.begin,
function_keyword,
}))
} else {
None
},
));
}
if !attributes.attributes.is_empty() {
let error_location = self.current_location();
let error = self.located(format!(
"Expected 'function' after local declaration with attribute, but got {} instead",
self.current
));
return Ok(self.report_statement_error(error_location, error));
}
let mut bindings = self.temp_bindings();
let mut variable_commas = self.temp_positions();
self.with_match_recovery_stop(MatchRecoveryStop::Equal, |parser| {
parser
.parse_binding_list_into(&mut bindings, false, &mut variable_commas, is_const)
.map(|_| ())
})?;
let equals_sign_location = if self.current.kind() == TokenKind::Equal {
Some(self.current_token_location())
} else {
None
};
let (values, value_commas) = if self.current.kind() == TokenKind::Equal {
self.advance();
let mut values = self.temp_expressions();
let mut value_commas = self.temp_positions();
self.parse_expression_list_into(&mut values, &mut value_commas)?;
(Some(values), value_commas)
} else {
(None, self.temp_positions())
};
let values_slice = values
.as_ref()
.map(|values| values.as_slice())
.unwrap_or(&[]);
let const_underfilled = is_const && !values_slice.has_enough_values(bindings.len());
let mut locals = self.temp_locals();
for binding in bindings.iter() {
locals.push_back(self.declare_local(
binding.name,
binding.name_location,
binding.annotation,
binding.is_const,
is_exported,
));
}
let location_end = values_slice
.last()
.map(|value| value.location.end)
.unwrap_or_else(|| self.previous_token_end_position());
let location = Location::new(location.begin, location_end);
let local_bindings = self.arena.alloc_slice_copy(locals.as_slice());
let statement = self.arena.alloc_statement_node(StatementLocal::new(
location,
false,
local_bindings,
self.arena.alloc_slice_copy(values_slice),
is_exported.then_some(local_keyword),
equals_sign_location,
is_const,
is_exported,
));
let statement = self.parsed_statement_from_node(statement, {
if self.cst.enabled() {
let mut variable_annotation_colons = variable_commas.nested();
for binding in bindings.iter() {
variable_annotation_colons.push_back(binding.colon_position);
}
Some(CstNode::StatLocal(CstStatLocal {
declaration_keyword_position: local_keyword.begin,
variable_annotation_colons: self
.arena
.alloc_slice_copy(variable_annotation_colons.as_slice()),
variable_commas: self.arena.alloc_slice_copy(variable_commas.as_slice()),
value_commas: self.arena.alloc_slice_copy(value_commas.as_slice()),
}))
} else {
None
}
});
if const_underfilled {
self.report_parse_error(
self.error_at(location, "Missing initializer in const declaration"),
);
}
if is_exported {
for binding in local_bindings.iter() {
if self.declared_export_bindings.contains(&binding.name) {
let message_index = self.report_parse_error(self.error_at(
binding.location,
self.name_message(b"Duplicate exported identifier '", binding.name, b"'"),
));
let error = self.arena.alloc_statement_node(StatementError::new(
binding.location,
false,
self.empty_expression_slice(),
self.arena.alloc_slice_copy(&[statement.statement]),
message_index,
));
return Ok(self.parsed_statement_from_node(error, None));
}
self.declared_export_bindings
.insert(binding.name, binding.location);
}
}
Ok(statement)
}
}