luau-syntax 0.732.0

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

// Parser.test.cpp: can_parse_leading_bar_unions_successfully
#[test]
fn can_parse_leading_bar_unions_successfully() {
    with_parse_ok(r#"type A = | "Hello" | "World""#, |result| {
        assert_eq!(result.metadata.errors.len(), 0);
    });
}
// Parser.test.cpp: can_parse_leading_ampersand_intersections_successfully
#[test]
fn can_parse_leading_ampersand_intersections_successfully() {
    with_parse_ok(r#"type A = & { string } & { number }"#, |result| {
        assert_eq!(result.metadata.errors.len(), 0);
    });
}
// Parser.test.cpp: leading_union_intersection_with_single_type_preserves_the_union_intersection_ast_node
#[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));
        },
    );
}
// Parser.test.cpp: parse_simple_ast_type_group
#[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 { .. }));
    });
}
// Parser.test.cpp: parse_nested_ast_type_group
#[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 { .. }));
    });
}
// Parser.test.cpp: parse_return_type_ast_type_group
#[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 { .. }));
        },
    );
}
// Parser.test.cpp: complex_union_in_generic_ty
#[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()
                ]
            );
        },
    );
}
// Parser.test.cpp: can_parse_complex_unions_successfully
#[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",
    );
}