use super::super::common::*;
use luau_common::flags;
#[test]
fn can_parse_leading_bar_unions_successfully() {
with_parse_ok(r#"type A = | "Hello" | "World""#, |result| {
assert_eq!(result.metadata.errors.len(), 0);
});
}
#[test]
fn can_parse_leading_ampersand_intersections_successfully() {
with_parse_ok(r#"type A = & { string } & { number }"#, |result| {
assert_eq!(result.metadata.errors.len(), 0);
});
}
#[test]
fn leading_union_intersection_with_single_type_preserves_the_union_intersection_ast_node() {
with_parse(
r#"
type Foo = | string
type Bar = & number
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [foo, bar] = statement_kinds(result.root.as_slice()).exact();
let foo = foo.as_type_alias().expect("expected type alias").ty;
let bar = bar.as_type_alias().expect("expected type alias").ty;
assert!(matches!(foo.kind(), TypeKind::Union { types, .. } if types.len() == 1));
assert!(matches!(bar.kind(), TypeKind::Intersection { types, .. } if types.len() == 1));
},
);
}
#[test]
fn parse_simple_ast_type_group() {
with_parse("type Foo = (string)", ParseOptions::default(), |result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected one type alias")
.ty;
let TypeKind::Group { ty: inner, .. } = ty.kind() else {
panic!("expected type group");
};
assert!(matches!(inner.kind(), TypeKind::Reference { .. }));
});
}
#[test]
fn parse_nested_ast_type_group() {
with_parse("type Foo = ((string))", ParseOptions::default(), |result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected one type alias")
.ty;
let TypeKind::Group { ty: outer, .. } = ty.kind() else {
panic!("expected outer type group");
};
let TypeKind::Group { ty: inner, .. } = outer.kind() else {
panic!("expected inner type group");
};
assert!(matches!(inner.kind(), TypeKind::Reference { .. }));
});
}
#[test]
fn parse_return_type_ast_type_group() {
with_parse(
"type Foo = () -> (string)",
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let ty = statement
.as_type_alias()
.expect("expected one type alias")
.ty;
let TypeKind::Function { return_types, .. } = &ty.kind() else {
panic!("expected function type");
};
let TypePackKind::Explicit {
type_list:
TypeList {
types,
tail_type: None,
},
} = &(return_types).kind()
else {
panic!("expected explicit return type pack");
};
assert_eq!(types.len(), 1);
assert!(matches!(&(types[0]).kind(), TypeKind::Group { .. }));
},
);
}
#[test]
fn complex_union_in_generic_ty() {
with_parse(
r#"
type X<T> = T
local x: X<
| number
| boolean
| string
>
"#,
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");
};
let names: Vec<_> = types
.iter()
.map(|annotation| match &annotation.kind() {
TypeKind::Reference { name, .. } => name.bytes(),
_ => panic!("expected reference type"),
})
.collect();
assert_eq!(
names,
[
b"number".as_slice(),
b"boolean".as_slice(),
b"string".as_slice()
]
);
},
);
}
#[test]
fn can_parse_complex_unions_successfully() {
parse_ok(
r#"
local f:
() -> ()
|
() -> ()
|
{a: number}
|
{b: number}
|
((number))
|
((number))
|
(a & (b & nil))
|
(a & (b & nil))
"#,
);
parse_ok("local f: a? | b? | c? | d? | e? | f? | g? | h?");
let _recursion_limit = flags::LuauRecursionLimit.scoped(10);
let _type_length_limit = flags::LuauTypeLengthLimit.scoped(10);
parse_errors_with_options(
"local t: a & b & c & d & e & f & g & h & i & j & nil",
ParseOptions::default(),
)
.assert_first_message(
"Exceeded allowed type length; simplify your type annotation to make the code compile",
);
}