luau-syntax 0.732.0

Luau lexer, parser, AST, CST, and source utilities
Documentation
use super::super::common::*;
fn expected_location(
    begin_line: u32,
    begin_column: u32,
    end_line: u32,
    end_column: u32,
) -> Location {
    loc!(pos!(begin_line, begin_column), pos!(end_line, end_column))
}

// Parser.test.cpp: inner_and_outer_scope_of_functions_have_correct_end_position
#[test]
fn inner_and_outer_scope_of_functions_have_correct_end_position() {
    with_parse(
        r#"
        local function foo()
            local x = 1
        end
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            let [statement] = statement_kinds(result.root.as_slice()).exact();
            let local_function = statement
                .as_local_function()
                .expect("expected local function");
            assert_eq!(
                local_function.function.body.location(),
                expected_location(1, 28, 3, 8)
            );
            assert_eq!(local_function.location(), expected_location(1, 8, 3, 11));
        },
    );
}

// Parser.test.cpp: end_extent_of_functions_unions_and_intersections
#[test]
fn end_extent_of_functions_unions_and_intersections() {
    with_parse(
        r#"
        type F = (string) -> string
        type G = string | number | boolean
        type H = string & number & boolean
        print('hello')
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            assert_eq!(result.root.len(), 4);
            assert_eq!(result.root[0].tag, StatementTag::TypeAlias);
            assert_eq!(result.root[1].tag, StatementTag::TypeAlias);
            assert_eq!(result.root[2].tag, StatementTag::TypeAlias);
            assert_eq!(result.root[3].tag, StatementTag::Expression);
            assert_eq!(result.root[0].location().end, pos!(1, 35));
            assert_eq!(result.root[1].location().end, pos!(2, 42));
            assert_eq!(result.root[2].location().end, pos!(3, 42));
        },
    );
}

// Parser.test.cpp: end_extent_doesnt_consume_comments
#[test]
fn end_extent_doesnt_consume_comments() {
    with_parse(
        r#"
        type F = number
        --comment
        print('hello')
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            assert_eq!(result.root.len(), 2);
            assert_eq!(result.root[0].location().end, pos!(1, 23));
        },
    );
}

// Parser.test.cpp: end_extent_doesnt_consume_comments_even_with_capture
#[test]
fn end_extent_doesnt_consume_comments_even_with_capture() {
    with_parse(
        r#"
        type F = number
        --comment
        print('hello')
    "#,
        ParseOptions::default().with_comment_capture(true),
        |result| {
            let result = result.unwrap();

            assert_eq!(result.root.len(), 2);
            assert_eq!(result.root[0].location().end, pos!(1, 23));
        },
    );
}

// Parser.test.cpp: stat_end_includes_semicolon_position
#[test]
fn stat_end_includes_semicolon_position() {
    with_parse(
        r#"
        local x = 1
        local y = 2;
        local z = 3  ;
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            let [first, second, third] = statement_kinds(result.root.as_slice()).exact();

            assert!(!first.has_semicolon());
            assert_eq!(Position::new(1, 19), first.location().end);

            assert!(second.has_semicolon());
            assert_eq!(Position::new(2, 20), second.location().end);

            assert!(third.has_semicolon());
            assert_eq!(Position::new(3, 22), third.location().end);
        },
    );
}

// Parser.test.cpp: do_end_block_with_cst
#[test]
fn do_end_block_with_cst() {
    with_parse(
        r#"
        do
            local hello = "world"
        end
    "#,
        ParseOptions::default().with_cst_data(true),
        |result| {
            let result = result.unwrap();

            let [body] = result.root.as_slice() else {
                panic!("expected do block");
            };
            assert_eq!(body.tag, StatementTag::Block);
            assert!(result.metadata.cst_nodes.get_block(result.root).is_none());
            let do_cst = result
                .metadata
                .cst_nodes
                .get_statement(*body)
                .and_then(|node| match node {
                    CstNode::StatDo(do_cst) => Some(do_cst),
                    _ => None,
                })
                .expect("expected do block CST node");
            assert_eq!(do_cst.stats_start, Position::new(2, 12));
            assert_eq!(do_cst.end, Position::new(3, 8));
        },
    );
}

// Parser.test.cpp: do_block_end_location_is_after_end_token
#[test]
fn do_block_end_location_is_after_end_token() {
    with_parse(
        r#"
        do
            local x = 1
        end
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            let [statement] = statement_kinds(result.root.as_slice()).exact();
            let block = statement.as_block().expect("expected do block");
            assert_eq!(block.location(), expected_location(1, 8, 3, 11));
        },
    );
}

// Parser.test.cpp: function_start_locations_are_before_attributes
#[test]
fn function_start_locations_are_before_attributes() {
    with_parse(
        r#"
        @native
        function globalFunction()
        end

        @native
        local function localFunction()
        end

        local _ = @native function()
        end
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            let [global, local, statement] = statement_kinds(result.root.as_slice()).exact();
            let global = global
                .as_function_declaration()
                .expect("expected function declaration");
            let local = local
                .as_local_function()
                .expect("expected local function declaration");
            let values = statement
                .as_local()
                .expect("expected local statement")
                .values;

            assert_eq!(global.location(), expected_location(1, 8, 3, 11));
            assert_eq!(local.location(), expected_location(5, 8, 7, 11));
            let [function_expr] = values else {
                panic!("expected anonymous function literal");
            };
            let ExpressionKind::FunctionLiteral(anonymous_function) = function_expr.kind() else {
                panic!("expected anonymous function literal");
            };
            assert_eq!(
                anonymous_function.location,
                expected_location(9, 18, 10, 11)
            );
        },
    );
}

// Parser.test.cpp: function_name_has_correct_start_location
#[test]
fn function_name_has_correct_start_location() {
    with_parse(
        r#"
        function simple()
        end

        function T:complex()
        end
    "#,
        ParseOptions::default(),
        |result| {
            let result = result.unwrap();

            let [simple, complex] = statement_kinds(result.root.as_slice()).exact();
            let simple = simple
                .as_function_declaration()
                .expect("expected function declaration")
                .name;
            let complex = complex
                .as_function_declaration()
                .expect("expected function declaration")
                .name;

            assert_eq!(simple.location().begin, Position::new(1, 17));
            assert_eq!(complex.location().begin, Position::new(4, 17));
        },
    );
}