use super::super::common::*;
#[test]
fn functions_can_have_return_annotations() {
with_parse(
r#"
function foo(): number return 55 end
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let function = statement
.as_function_declaration()
.expect("expected function declaration")
.function;
let Some(return_annotation) = function.return_annotation else {
panic!("expected explicit return type pack");
};
let TypePackKind::Explicit {
type_list:
TypeList {
types,
tail_type: None,
},
} = return_annotation.kind()
else {
panic!("expected explicit return type pack");
};
assert_eq!(types.len(), 1);
},
);
}
#[test]
fn function_ty() {
parse_ok(
r#"
local f: (number, string) -> nil
"#,
);
}
#[test]
fn functions_can_have_a_function_ty() {
with_parse(
r#"
function f(): (number) -> nil return nil end
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let function = statement
.as_function_declaration()
.expect("expected function declaration")
.function;
let Some(return_annotation) = function.return_annotation else {
panic!("expected explicit return type pack");
};
let TypePackKind::Explicit {
type_list:
TypeList {
types,
tail_type: None,
},
} = return_annotation.kind()
else {
panic!("expected explicit return type pack");
};
assert_eq!(types.len(), 1);
assert!(matches!(&(types[0]).kind(), TypeKind::Function { .. }));
},
);
}
#[test]
fn parsing_type_suffix_for_return_type_with_variadic() {
with_parse(
r#"
function foo(): (string, ...number) | boolean
end
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
assert_eq!(result.metadata.errors.len(), 0);
},
);
}
#[test]
fn function_return_type_should_disambiguate_from_function_type_and_multiple_returns() {
with_parse(
r#"
function f(): (number, string) return 1, "foo" end
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let function = statement
.as_function_declaration()
.expect("expected function declaration")
.function;
let Some(return_annotation) = function.return_annotation else {
panic!("expected explicit return type pack");
};
let TypePackKind::Explicit {
type_list:
TypeList {
types,
tail_type: None,
},
} = return_annotation.kind()
else {
panic!("expected explicit return type pack");
};
let names: Vec<_> = types
.iter()
.map(|annotation| match &annotation.kind() {
TypeKind::Reference { name, .. } => name.bytes(),
_ => panic!("expected type reference"),
})
.collect();
assert_eq!(names, [b"number".as_slice(), b"string".as_slice()]);
},
);
}
#[test]
fn function_return_type_should_parse_as_function_ty_with_no_args() {
with_parse(
r#"
function f(): () -> nil return nil end
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let function = statement
.as_function_declaration()
.expect("expected function declaration")
.function;
let Some(return_annotation) = function.return_annotation else {
panic!("expected explicit return type pack");
};
let TypePackKind::Explicit {
type_list:
TypeList {
types,
tail_type: None,
},
} = return_annotation.kind()
else {
panic!("expected explicit return type pack");
};
let [returned_type] = *types else {
panic!("expected single function return annotation");
};
let TypeKind::Function {
arg_types,
return_types,
..
} = returned_type.kind()
else {
panic!("expected single function return annotation");
};
let TypeList {
types: parameters,
tail_type: None,
} = arg_types
else {
panic!("expected explicit parameter type pack");
};
assert!(parameters.is_empty());
let TypePackKind::Explicit {
type_list:
TypeList {
types: return_types,
tail_type: None,
},
} = &return_types.kind()
else {
panic!("expected explicit return type pack");
};
assert_eq!(return_types.len(), 1);
assert!(
matches!(&return_types[0].kind(), TypeKind::Reference { name, .. } if name == "nil"
)
);
},
);
}
#[test]
fn functions_can_return_multiple_values() {
parse_ok(
r#"
local f: (number) -> (number, number)
"#,
);
}
#[test]
fn functions_can_have_0_arguments() {
parse_ok(
r#"
local f: () -> number
"#,
);
}
#[test]
fn functions_can_return_0_values() {
parse_ok(
r#"
local f: (number) -> ()
"#,
);
}
#[test]
fn intersection_of_two_function_types_if_no_returns() {
with_first_local_annotation(
r#"
local f: (string) -> () & (number) -> ()
"#,
|annotation| {
let TypeKind::Intersection { types, .. } = &annotation.kind() else {
panic!("expected intersection annotation");
};
assert_eq!(types.len(), 2);
assert!(matches!(&(types[0]).kind(), TypeKind::Function { .. }));
assert!(matches!(&(types[1]).kind(), TypeKind::Function { .. }));
},
);
}
#[test]
fn intersection_of_two_function_types_if_two_or_more_returns() {
with_first_local_annotation(
r#"
local f: (string) -> (string, number) & (number) -> (number, string)
"#,
|annotation| {
let TypeKind::Intersection { types, .. } = &annotation.kind() else {
panic!("expected intersection annotation");
};
assert_eq!(types.len(), 2);
assert!(matches!(&(types[0]).kind(), TypeKind::Function { .. }));
assert!(matches!(&(types[1]).kind(), TypeKind::Function { .. }));
},
);
}
#[test]
fn return_type_is_an_intersection_type_if_led_with_one_parenthesized_type() {
with_first_local_annotation(
r#"
local f: (string) -> (string) & (number) -> (number)
"#,
|annotation| {
let TypeKind::Function { return_types, .. } = &annotation.kind() else {
panic!("expected function annotation");
};
let TypePackKind::Explicit {
type_list:
TypeList {
types,
tail_type: None,
},
} = &(return_types).kind()
else {
panic!("expected explicit return type pack");
};
let [intersection] = *types else {
panic!("expected return type intersection");
};
let TypeKind::Intersection { types, .. } = &intersection.kind() else {
panic!("expected return type intersection");
};
assert_eq!(types.len(), 2);
assert!(matches!(&(types[0]).kind(), TypeKind::Group { .. }));
assert!(matches!(&(types[1]).kind(), TypeKind::Function { .. }));
},
);
}
#[test]
fn function_type_named_arguments() {
{
let source = "type MyFunc = (a: number, b: string, c: number) -> string";
with_parse(source, ParseOptions::default(), |result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected function type alias")
.ty;
let TypeKind::Function {
arg_types,
arg_names,
..
} = ty.kind()
else {
panic!("expected function type alias");
};
assert!(
matches!(arg_types, TypeList { types, .. } if types.len() == 3
)
);
assert_eq!(arg_names.len(), 3);
assert_eq!(
arg_names[2].map(|name| name.name.bytes()),
Some(b"c".as_slice())
);
});
}
{
let source = "type MyFunc = (a: number, string, c: number) -> string";
with_parse(source, ParseOptions::default(), |result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected function type alias")
.ty;
let TypeKind::Function {
arg_types,
arg_names,
..
} = ty.kind()
else {
panic!("expected function type alias");
};
assert!(
matches!(arg_types, TypeList { types, .. } if types.len() == 3
)
);
assert_eq!(arg_names.len(), 3);
assert_eq!(arg_names[1], None);
assert_eq!(
arg_names[2].map(|name| name.name.bytes()),
Some(b"c".as_slice())
);
});
}
{
let source = "type MyFunc = (a: number, string, number) -> string";
with_parse(source, ParseOptions::default(), |result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected function type alias")
.ty;
let TypeKind::Function {
arg_types,
arg_names,
..
} = ty.kind()
else {
panic!("expected function type alias");
};
assert!(
matches!(arg_types, TypeList { types, .. } if types.len() == 3
)
);
assert_eq!(arg_names.len(), 3);
assert_eq!(arg_names[1], None);
assert_eq!(arg_names[2], None);
});
}
{
let source = "type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number) -> string";
with_parse(source, ParseOptions::default(), |result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected function type alias")
.ty;
let TypeKind::Function {
arg_types,
arg_names,
return_types,
..
} = ty.kind()
else {
panic!("expected function type alias");
};
assert!(
matches!(arg_types, TypeList { types, .. } if types.len() == 3
)
);
assert_eq!(arg_names.len(), 3);
assert_eq!(
arg_names[2].map(|name| name.name.bytes()),
Some(b"c".as_slice())
);
let TypePackKind::Explicit {
type_list: TypeList { types, .. },
} = &return_types.kind()
else {
panic!("expected explicit return type pack");
};
let [returned_type] = *types else {
panic!("expected returned function type");
};
let TypeKind::Function {
arg_types: returned_parameters,
arg_names: returned_argument_names,
..
} = returned_type.kind()
else {
panic!("expected returned function type");
};
assert!(
matches!(returned_parameters, TypeList { types, .. } if types.len() == 3
)
);
assert_eq!(returned_argument_names.len(), 3);
assert_eq!(
returned_argument_names[2].map(|name| name.name.bytes()),
Some(b"f".as_slice())
);
});
}
for (source, expected) in [
(
"type MyFunc = (a: number, b: string, c: number) -> (d: number, e: string, f: number)",
"Expected '->' when parsing function type, got <eof>",
),
(
"type MyFunc = (number) -> (d: number) <a, b, c> -> number",
"Expected '->' when parsing function type, got '<'",
),
] {
parse_errors(source).assert_first_message(expected);
}
}
#[test]
fn function_type_matching_parenthesis() {
parse_errors("local a: <T>(number -> string")
.assert_first_message("Expected ')' (to close '(' at column 13), got '->'");
}
#[test]
fn grouped_function_type() {
with_parse(
r#"
type X<T> = T
local x: X<(() -> ())?>
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [_, statement] = statement_kinds(result.root.as_slice()).exact();
let local = statement
.as_local()
.expect("expected type alias and local declaration");
assert!(local.values.is_empty());
assert_eq!(local.bindings[0].name, "x");
let Some(annotation) = &local.bindings[0].annotation else {
panic!("expected generic type annotation");
};
let TypeKind::Reference { parameters, .. } = &annotation.kind() else {
panic!("expected generic type annotation");
};
let [TypeOrPack::Type(annotation)] = parameters else {
panic!("expected union generic parameter");
};
let TypeKind::Union { types, .. } = annotation.kind() else {
panic!("expected union generic parameter");
};
assert_eq!(types.len(), 2);
let TypeKind::Group { ty: grouped, .. } = types[0].kind() else {
panic!("expected grouped function type");
};
assert!(matches!(grouped.kind(), TypeKind::Function { .. }));
assert!(matches!(types[1].kind(), TypeKind::Optional));
},
);
}