use super::*;
mod generic;
mod pack;
mod table;
use self::pack::{ParsedFunctionParameterTypes, ParsedParenthesizedFunctionParameterTypes};
impl<'source, 'ast, 'name, 'names> Parser<'source, 'ast, 'name, 'names>
where
'name: 'ast,
{
pub(in crate::parser) fn parse_type_node(
self,
) -> std::result::Result<ParseNodeResult<'ast, Type<'ast>>, ParseErrors> {
self.parse_node(|parser| parser.parse_type_annotation())
}
pub(in crate::parser) fn parse_optional_type_annotation(
&mut self,
) -> Result<Option<Type<'ast>>> {
self.parse_optional_type_annotation_with_colon()
.map(|(annotation, _)| annotation)
}
pub(in crate::parser) fn parse_optional_type_annotation_with_colon(
&mut self,
) -> Result<(Option<Type<'ast>>, Option<Position>)> {
if self.current.kind() != TokenKind::Colon {
return Ok((None, None));
}
let colon = self.current_position();
self.advance();
self.parse_type_annotation()
.map(|annotation| (Some(annotation), Some(colon)))
}
pub(in crate::parser) fn parse_optional_return_annotation(
&mut self,
) -> Result<Option<TypePack<'ast>>> {
self.parse_optional_return_annotation_with_colon()
.map(|(annotation, _)| annotation)
}
pub(in crate::parser) fn parse_optional_return_annotation_with_colon(
&mut self,
) -> Result<(Option<TypePack<'ast>>, Option<Position>)> {
if self.current.kind() != TokenKind::Colon && self.current.kind() != TokenKind::SkinnyArrow
{
return Ok((None, None));
}
let colon = self.current_position();
if self.current.kind() == TokenKind::SkinnyArrow {
self.report_parse_error(self.function_return_arrow_error());
self.advance();
} else {
self.advance();
}
let old_recursion_counter = self.contexts.recursion_counter;
let annotation = self.parse_return_type_pack()?;
self.contexts.recursion_counter = old_recursion_counter;
if self.current.kind() == TokenKind::Comma {
self.report_parse_error(self.located("Expected a statement, got ','; did you forget to wrap the list of return types in parentheses?"
));
self.advance();
}
Ok((Some(annotation), Some(colon)))
}
pub(in crate::parser) fn parse_type_annotation(&mut self) -> Result<Type<'ast>> {
self.parse_type_annotation_with_context(false)
}
pub(in crate::parser) fn parse_type_annotation_in_declaration(&mut self) -> Result<Type<'ast>> {
self.parse_type_annotation_with_context(true)
}
pub(in crate::parser) fn parse_type_annotation_with_context(
&mut self,
in_declaration_context: bool,
) -> Result<Type<'ast>> {
match self.parse_type(in_declaration_context) {
Ok(annotation) => Ok(annotation),
Err(error) => {
let location = error.location;
let message_index = self.report_parse_error(error);
let annotation = self.error_type(message_index);
Ok(self.alloc_type(location, annotation))
}
}
}
pub(in crate::parser) fn parse_type(
&mut self,
in_declaration_context: bool,
) -> Result<Type<'ast>> {
let old_recursion_counter = self.contexts.recursion_counter;
let begin = self.current_location();
let first = if matches!(self.current, Token::Pipe | Token::Ampersand) {
None
} else {
let first = self.parse_simple_type(in_declaration_context)?;
self.contexts.recursion_counter = old_recursion_counter;
Some(first)
};
let result = self.parse_type_suffix(first, begin);
self.contexts.recursion_counter = old_recursion_counter;
result
}
pub(in crate::parser) fn parse_simple_type_or_pack(&mut self) -> Result<TypeOrPack<'ast>> {
let old_recursion_counter = self.contexts.recursion_counter;
let begin = self.current_location();
match self.parse_simple_type_or_pack_inner(true, false)? {
TypeOrPack::Pack(pack) => Ok(TypeOrPack::Pack(pack)),
TypeOrPack::Type(annotation) => {
self.contexts.recursion_counter = old_recursion_counter;
let annotation = self.parse_type_suffix(Some(annotation), begin)?;
self.contexts.recursion_counter = old_recursion_counter;
Ok(TypeOrPack::Type(annotation))
}
}
}
pub(in crate::parser) fn parse_simple_type(
&mut self,
in_declaration_context: bool,
) -> Result<Type<'ast>> {
let location = self.current_location();
match self.parse_simple_type_or_pack_inner(false, in_declaration_context)? {
TypeOrPack::Type(annotation) => Ok(annotation),
TypeOrPack::Pack(_) => {
let message_index = self
.report_parse_error(self.error_at(location, "Expected type, got type pack"));
let ty = self.error_type(message_index);
Ok(self.alloc_type(location, ty))
}
}
}
pub(in crate::parser) fn parse_simple_type_or_pack_inner(
&mut self,
allow_pack: bool,
in_declaration_context: bool,
) -> Result<TypeOrPack<'ast>> {
let old_recursion_counter = self.enter_recursion("type annotation")?;
let annotation = match self.current {
Token::LeftParen | Token::Less => self.parse_function_type(allow_pack, Vec::new())?,
Token::Attribute(_) | Token::AttributeOpen => {
if !in_declaration_context {
let location = self.current_location();
let message_index = self.report_parse_error(self.error_at(
location,
"attributes are not allowed in declaration context",
));
let ty = self.error_type(message_index);
TypeOrPack::Type(self.alloc_type(location, ty))
} else {
let attributes = self.parse_attributes()?;
self.parse_function_type(allow_pack, attributes.attributes)?
}
}
Token::LeftBrace => TypeOrPack::Type(self.parse_table_type(in_declaration_context)?),
Token::Reserved(R::Nil) => {
let location = self.current_token_location();
let nil = self.name_nil;
self.advance();
TypeOrPack::Type(self.alloc_type(
location,
TypeKind::Reference {
prefix: None,
prefix_location: None,
prefix_local: None,
name: nil,
location,
name_location: location,
has_parameter_list: false,
parameters: self.arena.alloc_slice_fill_iter(std::iter::empty()),
},
))
}
Token::Reserved(R::True) => {
let location = self.current_token_location();
self.advance();
TypeOrPack::Type(self.alloc_type(location, TypeKind::SingletonBool { value: true }))
}
Token::Reserved(R::False) => {
let location = self.current_token_location();
self.advance();
TypeOrPack::Type(
self.alloc_type(location, TypeKind::SingletonBool { value: false }),
)
}
Token::Reserved(R::Function) => {
let location = self.current_token_location();
self.advance();
let error = self.error_at(
location,
"Using 'function' as a type annotation is not supported, consider replacing with a function type annotation e.g. '(...any) -> ...any'"
,
);
let message_index = self.report_parse_error(error);
let ty = self.error_type(message_index);
TypeOrPack::Type(self.alloc_type(location, ty))
}
Token::QuotedString { value, quote_style } => {
let location = self.current_token_location();
let cst = self.cst_node(|| {
CstNode::TypeSingletonString(CstTypeSingletonString {
source_string: self.current_source_string(),
quote_style: CstStringQuoteStyle::from(quote_style),
block_depth: 0,
})
});
let Some(bytes) = fixup_string_bytes(value) else {
self.advance();
let message_index = self.report_parse_error(self.error_at(
location,
"String literal contains malformed escape sequence",
));
let ty = self.error_type(message_index);
let annotation = self.alloc_type(location, ty);
self.contexts.recursion_counter = old_recursion_counter;
return Ok(TypeOrPack::Type(annotation));
};
self.advance();
TypeOrPack::Type(self.alloc_type_with_cst(
location,
TypeKind::SingletonString {
value: self.ast_string(&bytes),
},
cst,
))
}
Token::RawString { value, block_depth } => {
let location = self.current_token_location();
let cst = self.cst_node(|| {
CstNode::TypeSingletonString(CstTypeSingletonString {
source_string: self.current_source_string(),
quote_style: CstStringQuoteStyle::QuotedRaw,
block_depth: block_depth as u32,
})
});
let bytes = multiline_string_bytes(value);
self.advance();
TypeOrPack::Type(self.alloc_type_with_cst(
location,
TypeKind::SingletonString {
value: self.ast_string(&bytes),
},
cst,
))
}
Token::BrokenString => {
let location = self.current_token_location();
let error = self.malformed_string_error();
self.advance();
let message_index = self.report_parse_error(error);
let ty = self.error_type(message_index);
TypeOrPack::Type(self.alloc_type(location, ty))
}
Token::InterpStringSimple(value) => {
let location = self.current_location();
if let Err(error) = self.parse_interpolated_literal(value) {
self.report_parse_error(ParseError::new(location, error.message));
}
self.advance();
let message_index =
self.report_parse_error(self.interpolated_string_as_type_error(location));
let ty = self.error_type(message_index);
TypeOrPack::Type(self.alloc_type(location, ty))
}
Token::InterpStringBegin(value) => {
let location = self.current_location();
let _ = self.parse_interpolated_string(value)?;
let message_index =
self.report_parse_error(self.interpolated_string_as_type_error(location));
let ty = self.error_type(message_index);
TypeOrPack::Type(self.alloc_type(location, ty))
}
Token::Ident(name) => {
let mut prefix = None;
let mut prefix_dot = None;
let mut prefix_location = None;
let mut prefix_local = None;
let mut location = self.current_token_location();
let mut name_location = location;
let mut name = name;
self.advance();
if self.current.kind() == TokenKind::Dot {
let op_position = self.current_position();
prefix_dot = Some(op_position);
self.advance();
let field = self.parse_index_name(Some("field name"), op_position)?;
prefix = Some(name);
prefix_location = Some(name_location);
if flags::LuauTrackPrefixLocal.get() {
prefix_local = self.visible_local(name);
}
name_location = field.location;
location = Location::new(location.begin, name_location.end);
name = field.name;
} else if self.current.kind() == TokenKind::Ellipsis {
self.report_parse_error(self.located("Unexpected '...' after type name; type pack is not allowed in this context"
));
self.advance();
} else if name == "typeof" {
let open_location = self.current_location();
let open = if self.current.kind() == TokenKind::LeftParen {
open_location.begin
} else {
Position::missing()
};
self.expect_and_consume(Token::LeftParen, "typeof type");
let expression = self.parse_expression()?;
let end = self.current_token_location();
let close = if self.expect_match_token(
Token::RightParen,
")",
"(",
open_location,
false,
) {
self.previous_token_location().begin
} else {
Position::missing()
};
let location = Location::new(location.begin, end.end);
let annotation = self.alloc_type_with_cst(
location,
TypeKind::Typeof { expr: expression },
self.cst_node(|| CstNode::TypeTypeof(CstTypeTypeof { open, close })),
);
self.contexts.recursion_counter = old_recursion_counter;
return Ok(TypeOrPack::Type(annotation));
}
let type_parameters = if self.current.kind() == TokenKind::Less {
Some(self.parse_type_parameters_with_cst()?)
} else {
None
};
let (
has_parameter_list,
open_parameters,
parameter_commas,
close_parameters,
parameter_arguments,
) = if let Some(type_parameters) = type_parameters {
(
true,
type_parameters.open,
type_parameters.commas,
type_parameters.close,
type_parameters.arguments,
)
} else {
(
false,
Position::missing(),
Vec::new(),
Position::missing(),
Vec::new(),
)
};
location = Location::new(location.begin, self.previous_token_end_position());
TypeOrPack::Type(self.alloc_type_with_cst(
location,
TypeKind::Reference {
prefix,
prefix_location,
prefix_local,
name,
location,
name_location,
has_parameter_list,
parameters: self.arena.alloc_slice_fill_iter(parameter_arguments),
},
self.cst_node(|| {
CstNode::TypeReference(CstTypeReference {
prefix_dot,
open_parameters,
parameter_commas,
close_parameters,
})
}),
))
}
_ => {
let ast_location = Location::new(
self.previous_token_end_position(),
self.current_token_location().begin,
);
TypeOrPack::Type(
self.report_missing_type_error(self.missing_type_parse_error(), ast_location),
)
}
};
self.contexts.recursion_counter = old_recursion_counter;
Ok(annotation)
}
pub(in crate::parser) fn parse_function_type(
&mut self,
allow_pack: bool,
attributes: Vec<&'ast Attribute<'ast>>,
) -> Result<TypeOrPack<'ast>> {
let old_recursion_counter = self.enter_recursion("type annotation")?;
let mut force_function_type = self.current.kind() == TokenKind::Less;
let begin = self.current_location();
let generics = if self.current.kind() == TokenKind::Less {
self.parse_generic_parameters_with_cst(false)?
} else {
ParsedGenericParameters::default()
};
let arg_types = self.parse_function_type_parameter_pack()?;
if arg_types.parameters.has_named_parameters() {
force_function_type = true;
}
let return_type_introducer = self.current.kind() == TokenKind::SkinnyArrow
|| self.current.kind() == TokenKind::Colon;
if arg_types.parameters.result.len() == 1
&& arg_types.parameters.vararg_annotation.is_none()
&& !force_function_type
&& !return_type_introducer
{
let result = if allow_pack {
TypeOrPack::Pack(self.alloc_explicit_type_pack_from_parameters(
begin,
arg_types.parameters,
Some(CstTypePackParentheses {
open: arg_types.open,
close: arg_types.close,
}),
))
} else {
let cst = self.cst_node(|| {
CstNode::TypeGroup(CstTypeGroup {
close_position: arg_types.close,
})
});
TypeOrPack::Type(self.alloc_type_with_cst(
Location::new(arg_types.open, arg_types.close_location.end),
TypeKind::Group {
ty: arg_types.parameters.result[0],
},
cst,
))
};
self.contexts.recursion_counter = old_recursion_counter;
return Ok(result);
}
if !force_function_type && !return_type_introducer && allow_pack {
let result = TypeOrPack::Pack(self.alloc_explicit_type_pack_from_parameters(
begin,
arg_types.parameters,
Some(CstTypePackParentheses {
open: arg_types.open,
close: arg_types.close,
}),
));
self.contexts.recursion_counter = old_recursion_counter;
return Ok(result);
}
let function = self.parse_function_type_tail(begin, attributes, generics, arg_types)?;
self.contexts.recursion_counter = old_recursion_counter;
Ok(TypeOrPack::Type(function))
}
pub(in crate::parser) fn parse_type_suffix(
&mut self,
first: Option<Type<'ast>>,
begin: Location,
) -> Result<Type<'ast>> {
let old_recursion_counter = self.enter_recursion("type annotation")?;
let result = self.parse_type_suffix_inner(first, begin);
self.contexts.recursion_counter = old_recursion_counter;
result
}
pub(in crate::parser) fn parse_type_suffix_inner(
&mut self,
first: Option<Type<'ast>>,
begin: Location,
) -> Result<Type<'ast>> {
let mut types = self.temp_types();
let mut separators = self.temp_positions();
let mut leading = None;
if let Some(first) = first {
types.push_back(first);
}
let mut composition = None;
let mut is_mixed_composition = false;
let mut optional_count = 0;
loop {
if let Some(next_composition) = TypeComposition::from_token(&self.current) {
let separator = self.current_position();
if let Some(composition) = composition {
if next_composition != composition {
is_mixed_composition = true;
}
separators.push_back(separator);
} else {
composition = Some(next_composition);
if first.is_none() {
leading = Some(separator);
} else {
separators.push_back(separator);
}
}
self.advance();
let old_recursion_counter = self.contexts.recursion_counter;
let annotation = self.parse_simple_type(false)?;
self.contexts.recursion_counter = old_recursion_counter;
types.push_back(annotation);
} else if self.current.kind() == TokenKind::Question {
if let Some(TypeComposition::Intersection) = composition {
is_mixed_composition = true;
}
composition = Some(TypeComposition::Union);
let location = self.current_token_location();
self.advance();
types.push_back(self.alloc_type(location, TypeKind::Optional));
optional_count += 1;
} else if self.current.kind() == TokenKind::Ellipsis {
self.report_parse_error(self.located("Unexpected '...' after type annotation"));
self.advance();
continue;
} else {
break;
}
if types.len() > flags::LuauTypeLengthLimit.get() as usize + optional_count {
let location = types
.as_slice()
.last()
.map(|ty| ty.location)
.unwrap_or(begin);
return Err(self.error_at(
location,
"Exceeded allowed type length; simplify your type annotation to make the code compile"
,
));
}
}
if types.len() == 1 && composition.is_none() {
return Ok(types[0]);
}
let Some(composition) = composition else {
if let Some(ty) = types.as_slice().first().copied() {
return Ok(ty);
}
return Ok(self.report_missing_type_error(self.missing_type_parse_error(), begin));
};
let location = Location::new(begin.begin, types[types.len() - 1].location.end);
if is_mixed_composition {
let message_index = self.report_parse_error(ParseError::new(
location,
"Mixing union and intersection types is not allowed; consider wrapping in parentheses.",
));
let types = self.alloc_type_slice(types.as_slice().to_vec());
return Ok(self.alloc_type(
location,
TypeKind::Error {
types,
missing: false,
message_index,
},
));
}
Ok(self.composed_type_annotation(
composition,
leading,
separators.as_slice().to_vec(),
types.as_slice().to_vec(),
location,
))
}
pub(in crate::parser) fn composed_type_annotation(
&mut self,
composition: TypeComposition,
leading: Option<Position>,
separators: Vec<Position>,
types: Vec<Type<'ast>>,
location: Location,
) -> Type<'ast> {
match composition {
TypeComposition::Union => {
let types = self.alloc_type_slice(types);
self.alloc_type_with_cst(
location,
TypeKind::Union { types },
self.cst_node(|| {
CstNode::TypeUnion(CstTypeUnion {
leading,
separators,
})
}),
)
}
TypeComposition::Intersection => {
let types = self.alloc_type_slice(types);
self.alloc_type_with_cst(
location,
TypeKind::Intersection { types },
self.cst_node(|| {
CstNode::TypeIntersection(CstTypeIntersection {
leading,
separators,
})
}),
)
}
}
}
pub(in crate::parser) fn nil_type_at(&mut self, location: Location) -> Type<'ast> {
let nil = self.name_nil;
self.alloc_type(
location,
TypeKind::Reference {
prefix: None,
prefix_location: None,
prefix_local: None,
name: nil,
location,
name_location: location,
has_parameter_list: false,
parameters: self.arena.alloc_slice_fill_iter(std::iter::empty()),
},
)
}
pub(in crate::parser) fn parse_function_type_tail(
&mut self,
begin: Location,
attributes: Vec<&'ast Attribute<'ast>>,
generics: ParsedGenericParameters<'ast>,
arg_types: ParsedParenthesizedFunctionParameterTypes<'ast>,
) -> Result<Type<'ast>> {
let old_recursion_counter = self.enter_recursion("type annotation")?;
let return_arrow = self.current_position();
if self.current.kind() == TokenKind::Colon {
self.report_parse_error(self.function_type_colon_return_error());
self.advance();
} else if self.current.kind() == TokenKind::SkinnyArrow {
self.advance();
} else if generics.types.is_empty()
&& generics.type_packs.is_empty()
&& arg_types.parameters.result.is_empty()
&& arg_types.parameters.vararg_annotation.is_none()
{
self.report_parse_error(self.empty_function_type_arrow_error(Location::new(
begin.begin,
arg_types.close_location.end,
)));
let result = Ok(self.nil_type_at(begin));
self.contexts.recursion_counter = old_recursion_counter;
return result;
} else {
self.report_parse_error(self.function_type_arrow_error());
}
let return_types = self.parse_return_type_pack()?;
let ParsedFunctionParameterTypes {
result,
result_names,
comma_positions,
name_colon_positions,
vararg_annotation,
} = arg_types.parameters;
let cst = self.cst_node(|| {
CstNode::TypeFunction(CstTypeFunction {
open_generics: generics.open,
generics_commas: generics.commas,
close_generics: generics.close,
open_arguments: arg_types.open,
argument_name_colons: name_colon_positions,
argument_commas: comma_positions,
close_arguments: arg_types.close,
return_arrow,
})
});
let arg_types = TypeList {
types: self.alloc_type_slice(result),
tail_type: vararg_annotation,
};
let arg_names = self.arena.alloc_slice_fill_iter(result_names);
let result = Ok(self.alloc_type_with_cst(
Location::new(begin.begin, return_types.location.end),
TypeKind::Function {
attributes: self.arena.alloc_slice_fill_iter(attributes),
generics: self.arena.alloc_slice_fill_iter(generics.types),
generic_packs: self.arena.alloc_slice_fill_iter(generics.type_packs),
arg_types,
arg_names,
return_types,
},
cst,
));
self.contexts.recursion_counter = old_recursion_counter;
result
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(in crate::parser) enum TypeComposition {
Union,
Intersection,
}
impl TypeComposition {
pub(in crate::parser) fn from_token(token: &Token<'_, '_>) -> Option<Self> {
match token {
Token::Pipe => Some(Self::Union),
Token::Ampersand => Some(Self::Intersection),
_ => None,
}
}
}
pub(super) fn is_type_follow(token: &Token<'_, '_>) -> bool {
matches!(token, Token::Pipe | Token::Question | Token::Ampersand)
}