#[cfg(test)]
mod test {
use crate::{DiagnosticCode, VirtualWorkspace};
const STACKED_PCALL_ALIAS_GUARDS: usize = 180;
#[test]
fn test_issue_263() {
let mut ws = VirtualWorkspace::new_with_init_std_lib();
ws.def(
r#"
---@alias aaa fun(a: string, b: integer): integer
---@type aaa
local a
d, b = pcall(a, "", 1)
"#,
);
let aaa_ty = ws.expr_ty("b");
let expected = ws.ty("integer|string");
assert_eq!(aaa_ty, expected);
}
#[test]
fn test_issue_280() {
let mut ws = VirtualWorkspace::new_with_init_std_lib();
assert!(ws.check_code_for(
DiagnosticCode::ParamTypeMismatch,
r#"
---@class D11.AAA
local AAA = {}
---@param a string
---@param b number
function AAA:name(a, b)
end
---@param a string
---@param b number
function AAA:t(a, b)
local ok, err = pcall(self.name, self, a, b)
end
"#
));
}
#[test]
fn test_nested_pcall_higher_order_return_shape() {
let mut ws = VirtualWorkspace::new_with_init_std_lib();
ws.def(
r#"
---@return integer
local function f()
return 1
end
ok, status, payload = pcall(pcall, f)
"#,
);
assert_eq!(ws.expr_ty("status"), ws.ty("true|false|string"));
assert_eq!(ws.expr_ty("payload"), ws.ty("string|integer|nil"));
}
#[test]
fn test_pcall_return_overload_narrow_after_error_guard() {
let mut ws = VirtualWorkspace::new_with_init_std_lib();
ws.def(
r#"
---@return integer
local function foo()
return 2
end
local ok, result = pcall(foo)
if not ok then
error(result)
end
a = result
"#,
);
assert_eq!(ws.expr_ty("a"), ws.ty("integer"));
}
#[test]
fn test_nested_pcall_like_without_return_overload() {
let mut ws = VirtualWorkspace::new();
ws.def(
r#"
---@generic T, R
---@param f fun(...: T...): R...
---@param ... T...
---@return boolean, R...
local function safe_call(f, ...)
return true, f(...)
end
---@return integer
local function produce()
return 1
end
ok, status, payload = safe_call(safe_call, produce)
"#,
);
assert_eq!(ws.expr_ty("status"), ws.ty("boolean"));
assert_eq!(ws.expr_ty("payload"), ws.ty("integer"));
}
#[test]
fn test_nested_pcall_like_without_return_overload2() {
let mut ws = VirtualWorkspace::new();
ws.def(
r#"
---@generic T, R, R1
---@param f sync fun(...: T...): R1, R...
---@param ... T...
---@return boolean, R1|string, R...
local function pcall_like(f, ...) end
---@return integer
local function produce()
return 1
end
ok, status, payload = pcall_like(pcall_like, produce)
"#,
);
assert_eq!(ws.expr_ty("ok"), ws.ty("boolean"));
assert_eq!(ws.expr_ty("status"), ws.ty("boolean|string"));
assert_eq!(ws.expr_ty("payload"), ws.ty("integer|string"));
}
#[test]
fn test_pcall_stacked_alias_guards_build_semantic_model() {
let mut ws = VirtualWorkspace::new_with_init_std_lib();
let repeated_guards =
"if failed then error(result) end\n".repeat(STACKED_PCALL_ALIAS_GUARDS);
let block = format!(
r#"
---@return integer
local function foo()
return 1
end
local ok, result = pcall(foo)
local failed = ok == false
{repeated_guards}
narrowed = result
"#,
);
let file_id = ws.def(&block);
assert!(
ws.analysis
.compilation
.get_semantic_model(file_id)
.is_some(),
"expected semantic model for stacked pcall alias guard repro"
);
assert_eq!(ws.expr_ty("narrowed"), ws.ty("integer"));
}
#[test]
fn test_pcall_any_callable_splits_success_unknown_and_failure_string() {
let mut ws = VirtualWorkspace::new_with_init_std_lib();
ws.def(
r#"
---@type any
local x
local ok, result = pcall(x)
outside = result
if ok then
success = result
else
failure = result
end
"#,
);
assert_eq!(ws.expr_ty("outside"), ws.ty("unknown|string"));
assert_eq!(ws.expr_ty("success"), ws.ty("unknown"));
assert_eq!(ws.expr_ty("failure"), ws.ty("string"));
}
}