#[cfg(test)]
mod test {
use crate::{DiagnosticCode, LuaIndex, VirtualWorkspace};
use lsp_types::NumberOrString;
use tokio_util::sync::CancellationToken;
#[test]
fn test_call_non_callable() {
let mut ws = VirtualWorkspace::new();
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local i = 1
i()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local s = "hi"
s()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local b = true
b()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@type thread
local t
t()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local function f() end
f()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@type function
local f
f()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@type function|integer
local f
f()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local f ---@type { field: string } & fun()
f()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@type function|nil
local f
f()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
(1)()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
("hi")()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
(true)()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
(function() end)()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local i --- @type integer|fun():string
_ = i()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@class Callable
---@operator call: string
---@type Callable
local c
c()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local c = {}
c()
"#
));
assert!(!ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@class NonCallable
---@type NonCallable
local c = {}
c()
"#
));
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@type any
local c
c()
"#
));
}
#[test]
fn test_call_non_callable_fallback_from_initializer() {
let mut ws = VirtualWorkspace::new();
ws.enable_check(DiagnosticCode::CallNonCallable);
let file_id = ws.def("local i = 1\n i()\n");
let db = ws.get_db_mut();
db.get_type_index_mut().clear();
db.get_flow_index_mut().clear();
let diagnostics = ws
.analysis
.diagnose_file(file_id, CancellationToken::new())
.unwrap();
let code = Some(NumberOrString::String(
DiagnosticCode::CallNonCallable.get_name().to_string(),
));
assert!(diagnostics.iter().any(|diag| diag.code == code));
}
#[test]
fn test_no_call_non_callable_on_undefined_type() {
let mut ws = VirtualWorkspace::new();
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
local a --- @type NotDefined
a()
"#
));
}
#[test]
fn test_no_call_non_callable_on_alias() {
let mut ws = VirtualWorkspace::new();
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
---@alias FunAlias function
local a --- @type FunAlias
a()
"#
));
}
#[test]
fn test_no_call_non_callable_for_generic_function_param_in_a_lua() {
let mut ws = VirtualWorkspace::new();
assert!(ws.check_code_for(
DiagnosticCode::CallNonCallable,
r#"
--- @generic F: function
--- @param fn F
--- @return F
function once(fn)
return function(...)
return fn(...)
end
end
"#,
));
}
}