luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::super::common::*;

// Parser.test.cpp: can_haz_annotations
#[test]
fn can_haz_annotations() {
    parse_ok(r#"local foo: string = "Hello Types!""#);
}
// Parser.test.cpp: local_with_annotation
#[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)));
        },
    );
}
// Parser.test.cpp: type_names_can_contain_dots
#[test]
fn type_names_can_contain_dots() {
    parse_ok(
        r#"
        local foo: SomeModule.CoolType
    "#,
    );
}
// Parser.test.cpp: nil_is_a_valid_type_name
#[test]
fn nil_is_a_valid_type_name() {
    parse_ok(
        r#"
        local n: nil
    "#,
    );
}
// Parser.test.cpp: annotations_can_be_tables
#[test]
fn annotations_can_be_tables() {
    parse_ok(
        r#"
        local zero: number
        local one: {x: number, y: string}
    "#,
    );
}
// Parser.test.cpp: tables_should_have_an_indexer_and_keys
#[test]
fn tables_should_have_an_indexer_and_keys() {
    parse_ok(
        r#"
        local t: {
            [string]: number,
            f: () -> nil
        }
    "#,
    );
}
// Parser.test.cpp: tables_can_have_trailing_separator
#[test]
fn tables_can_have_trailing_separator() {
    parse_ok(
        r#"
        local zero: number
        local one: {x: number, y: string, }
    "#,
    );
}
// Parser.test.cpp: tables_can_use_semicolons
#[test]
fn tables_can_use_semicolons() {
    parse_ok(
        r#"
        local zero: number
        local one: {x: number; y: string; }
    "#,
    );
}
// Parser.test.cpp: other_places_where_type_annotations_are_allowed
#[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
    "#,
    );
}
// Parser.test.cpp: type_assertion_expression
#[test]
fn type_assertion_expression_accepts_luau_source() {
    parse_ok(
        r#"
        local a = something() :: any
    "#,
    );
}
// Parser.test.cpp: last_line_does_not_have_to_be_blank
#[test]
fn last_line_does_not_have_to_be_blank() {
    parse_ok("-- print('hello')");
}
// Parser.test.cpp: parse_error_type_name
#[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 '='"
    );
}
// Parser.test.cpp: read_write_table_properties
#[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);
        },
    );
}
// Parser.test.cpp: parsing_string_union_indexers
#[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);
        },
    );
}
// Parser.test.cpp: parse_type_name
#[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);
        },
    );
}
// Parser.test.cpp: extern_read_write_attributes
#[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);
        },
    );
}