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))
}
#[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));
},
);
}
#[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));
},
);
}
#[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));
},
);
}
#[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));
},
);
}
#[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);
},
);
}
#[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));
},
);
}
#[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));
},
);
}
#[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)
);
},
);
}
#[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));
},
);
}