use super::super::common::*;
#[test]
fn can_haz_annotations() {
parse_ok(r#"local foo: string = "Hello Types!""#);
}
#[test]
fn local_with_annotation() {
with_parse(
r#"
local foo: string = "Hello Types!"
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
let [statement] = statement_kinds(result.root.as_slice()).exact();
let local = statement.as_local().expect("expected local declaration");
assert_eq!(local.bindings.len(), 1);
assert!(local.bindings[0].annotation.is_some());
assert_eq!(local.values.len(), 1);
assert_eq!(local.bindings[0].name, "foo");
assert_eq!(local.bindings[0].location, loc!(pos!(1, 14), pos!(1, 17)));
},
);
}
#[test]
fn type_names_can_contain_dots() {
parse_ok(
r#"
local foo: SomeModule.CoolType
"#,
);
}
#[test]
fn nil_is_a_valid_type_name() {
parse_ok(
r#"
local n: nil
"#,
);
}
#[test]
fn annotations_can_be_tables() {
parse_ok(
r#"
local zero: number
local one: {x: number, y: string}
"#,
);
}
#[test]
fn tables_should_have_an_indexer_and_keys() {
parse_ok(
r#"
local t: {
[string]: number,
f: () -> nil
}
"#,
);
}
#[test]
fn tables_can_have_trailing_separator() {
parse_ok(
r#"
local zero: number
local one: {x: number, y: string, }
"#,
);
}
#[test]
fn tables_can_use_semicolons() {
parse_ok(
r#"
local zero: number
local one: {x: number; y: string; }
"#,
);
}
#[test]
fn other_places_where_type_annotations_are_allowed() {
parse_ok(
r#"
for i: number = 0, 50 do end
for i: number, s: string in expr() do end
"#,
);
}
#[test]
fn type_assertion_expression_accepts_luau_source() {
parse_ok(
r#"
local a = something() :: any
"#,
);
}
#[test]
fn last_line_does_not_have_to_be_blank() {
parse_ok("-- print('hello')");
}
#[test]
fn parse_error_type_name() {
let errors = parse_errors(
r#"
local a: Foo.=
"#,
);
assert_eq!(
errors[0].message,
"Expected identifier when parsing field name, got '='"
);
}
#[test]
fn read_write_table_properties() {
with_parse(
r#"
type A = {read x: number}
type B = {write x: number}
type C = {read x: number, write x: number}
type D = {read: () -> string}
type E = {write: (string) -> ()}
type F = {read read: () -> string}
type G = {read write: (string) -> ()}
type H = {read ["A"]: number}
type I = {write ["A"]: string}
type J = {read [number]: number}
type K = {write [number]: string}
"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
assert_eq!(result.metadata.errors.len(), 0);
},
);
}
#[test]
fn parsing_string_union_indexers() {
with_parse(
r#"type foo = { ["bar" | "baz"]: number }"#,
ParseOptions::default(),
|result| {
let result = result.unwrap();
assert_eq!(result.metadata.errors.len(), 0);
},
);
}
#[test]
fn parse_type_name() {
with_parse_type(
"<A>(A, string, boolean?) -> number",
ParseOptions::default(),
|result| {
let result = result.unwrap();
assert!(result.metadata.errors.is_empty());
let annotation = result.node;
let TypeKind::Function {
generics,
arg_types,
return_types,
..
} = &(annotation).kind()
else {
panic!("expected function type");
};
assert_eq!(generics.len(), 1);
let TypeList {
types,
tail_type: None,
} = arg_types
else {
panic!("expected explicit argument type pack");
};
assert_eq!(types.len(), 3);
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);
},
);
}
#[test]
fn extern_read_write_attributes() {
with_parse(
r#"
declare extern type Foo with
read ReadOnlyMember: string
write WriteOnlyMember: number
ReadWriteMember: vector
wRITE BadAttributeMember: buffer
end"#,
ParseOptions::default().with_declaration_syntax(true),
|result| {
let result = result.unwrap();
assert_eq!(result.metadata.errors.len(), 1);
assert_eq!(result.metadata.errors[0].location.begin.line, 5);
assert_eq!(
result.metadata.errors[0].message,
"Expected blank or 'read' or 'write' attribute, got 'wRITE'"
);
let [statement] = statement_kinds(result.root.as_slice()).exact();
let extern_type = statement
.as_declare_extern_type()
.expect("expected recovered extern type declaration");
assert_eq!(extern_type.props.len(), 4);
assert_eq!(extern_type.props[0].access, TableAccess::Read);
assert_eq!(extern_type.props[1].access, TableAccess::Write);
assert_eq!(extern_type.props[2].access, TableAccess::ReadWrite);
assert_eq!(extern_type.props[3].access, TableAccess::ReadWrite);
},
);
}