Skip to content

Commit 021c518

Browse files
authored
Merge pull request #621 from xuhuanzy/merge
fix
2 parents 2dfb1e8 + 80795a6 commit 021c518

File tree

9 files changed

+350
-13
lines changed

9 files changed

+350
-13
lines changed
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#[cfg(test)]
2+
mod test {
3+
use std::{ops::Deref, sync::Arc};
4+
5+
use crate::{DiagnosticCode, VirtualWorkspace};
6+
7+
#[test]
8+
fn test_array_index() {
9+
let mut ws = VirtualWorkspace::new();
10+
let mut emmyrc = ws.analysis.get_emmyrc().deref().clone();
11+
emmyrc.strict.array_index = false;
12+
ws.analysis.update_config(Arc::new(emmyrc));
13+
ws.def(
14+
r#"
15+
---@class Test.Add
16+
---@field a string
17+
18+
---@type int
19+
index = 1
20+
---@type Test.Add[]
21+
items = {}
22+
"#,
23+
);
24+
25+
assert!(ws.check_code_for(
26+
DiagnosticCode::NeedCheckNil,
27+
r#"
28+
local a = items[index]
29+
local b = a.a
30+
"#,
31+
));
32+
}
33+
}

crates/emmylua_code_analysis/src/compilation/test/flow.rs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,4 +1104,58 @@ end
11041104
"#,
11051105
));
11061106
}
1107+
1108+
#[test]
1109+
fn test_issue_622() {
1110+
let mut ws = VirtualWorkspace::new();
1111+
ws.def(
1112+
r#"
1113+
---@class Test.A
1114+
---@field base number
1115+
---@field add number
1116+
T = {}
1117+
1118+
---@enum Test.op
1119+
Op = {
1120+
base = "base",
1121+
add = "add",
1122+
};
1123+
"#,
1124+
);
1125+
ws.def(
1126+
r#"
1127+
---@param op Test.op
1128+
---@param value number
1129+
---@return boolean
1130+
function T:SetValue(op, value)
1131+
local oldValue = self[op]
1132+
if oldValue == value then
1133+
return false
1134+
end
1135+
A = oldValue
1136+
return true
1137+
end
1138+
"#,
1139+
);
1140+
let a = ws.expr_ty("A");
1141+
assert_eq!(ws.humanize_type(a), "number");
1142+
}
1143+
1144+
#[test]
1145+
fn test_nil_1() {
1146+
let mut ws = VirtualWorkspace::new();
1147+
ws.def(
1148+
r#"
1149+
---@type number?
1150+
local angle
1151+
1152+
if angle ~= nil and angle >= 0 then
1153+
A = angle
1154+
end
1155+
1156+
"#,
1157+
);
1158+
let a = ws.expr_ty("A");
1159+
assert_eq!(ws.humanize_type(a), "number");
1160+
}
11071161
}

crates/emmylua_code_analysis/src/compilation/test/metatable_test.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,36 @@ mod test {
7070
"#,
7171
);
7272

73-
// let ty = ws.expr_ty("a");
74-
// disable test temp
75-
// assert_eq!(ws.humanize_type(ty), "switch");
73+
let ty = ws.expr_ty("a");
74+
assert_eq!(ws.humanize_type(ty), "switch");
75+
}
76+
77+
#[test]
78+
fn test_issue_599() {
79+
let mut ws = VirtualWorkspace::new();
80+
81+
ws.def(
82+
r#"
83+
---@class Class.Config
84+
---@field abc string
85+
local ClassConfigMeta = {}
86+
87+
---@type table<string, Class.Config>
88+
local _classConfigMap = {}
89+
90+
91+
---@param name string
92+
---@return Class.Config
93+
local function getConfig(name)
94+
local config = _classConfigMap[name]
95+
if not config then
96+
A = setmetatable({ name = name }, { __index = ClassConfigMeta })
97+
end
98+
end
99+
"#,
100+
);
101+
102+
let ty = ws.expr_ty("A");
103+
assert_eq!(ws.humanize_type(ty), "Class.Config");
76104
}
77105
}

crates/emmylua_code_analysis/src/compilation/test/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
mod and_or_test;
22
mod annotation_test;
3+
mod array_test;
34
mod closure_generic;
45
mod closure_param_infer_test;
56
mod closure_return_test;

crates/emmylua_code_analysis/src/diagnostic/checker/generic/generic_constraint_mismatch.rs

Lines changed: 143 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
use emmylua_parser::{LuaAst, LuaAstNode, LuaAstToken, LuaCallExpr, LuaDocTagType};
1+
use std::ops::Deref;
2+
3+
use emmylua_parser::{LuaAst, LuaAstNode, LuaAstToken, LuaCallExpr, LuaDocTagType, LuaExpr};
24
use rowan::TextRange;
35

46
use crate::diagnostic::checker::generic::infer_doc_type::infer_doc_type;
57
use crate::diagnostic::checker::param_type_check::get_call_source_type;
68
use crate::{
7-
humanize_type, DiagnosticCode, GenericTplId, LuaMemberOwner, LuaSemanticDeclId, LuaSignature,
8-
LuaStringTplType, LuaType, RenderLevel, SemanticDeclLevel, SemanticModel, TypeCheckFailReason,
9-
TypeCheckResult,
9+
humanize_type, DiagnosticCode, GenericTplId, LuaDeclExtra, LuaMemberOwner, LuaSemanticDeclId,
10+
LuaSignature, LuaStringTplType, LuaType, RenderLevel, SemanticDeclLevel, SemanticModel,
11+
TypeCheckFailReason, TypeCheckResult, TypeOps, VariadicType,
1012
};
1113

1214
use crate::diagnostic::checker::Checker;
@@ -83,9 +85,8 @@ fn check_call_expr(
8385
.get_signature_index()
8486
.get(&signature_id)?;
8587
let mut params = signature.get_type_params();
88+
let mut arg_infos = get_arg_infos(semantic_model, &call_expr)?;
8689

87-
let arg_exprs = call_expr.get_args_list()?.get_args().collect::<Vec<_>>();
88-
let mut arg_infos = semantic_model.infer_expr_list_types(&arg_exprs, None);
8990
match (call_expr.is_colon_call(), signature.is_colon_define) {
9091
(true, true) | (false, false) => {}
9192
(false, true) => {
@@ -102,7 +103,6 @@ fn check_call_expr(
102103
);
103104
}
104105
}
105-
106106
for (i, (_, param_type)) in params.iter().enumerate() {
107107
let param_type = if let Some(param_type) = param_type {
108108
param_type
@@ -344,3 +344,139 @@ fn add_type_check_diagnostic(
344344
}
345345
}
346346
}
347+
348+
fn get_arg_infos(
349+
semantic_model: &SemanticModel,
350+
call_expr: &LuaCallExpr,
351+
) -> Option<Vec<(LuaType, TextRange)>> {
352+
let arg_exprs = call_expr.get_args_list()?.get_args().collect::<Vec<_>>();
353+
let mut arg_infos = infer_expr_list_types(semantic_model, &arg_exprs);
354+
for (arg_type, arg_expr) in arg_infos.iter_mut() {
355+
let extend_type = try_instantiate_arg_type(semantic_model, arg_type, arg_expr, 0);
356+
if let Some(extend_type) = extend_type {
357+
*arg_type = extend_type;
358+
}
359+
}
360+
361+
let arg_infos = arg_infos
362+
.into_iter()
363+
.map(|(arg_type, arg_expr)| (arg_type, arg_expr.get_range()))
364+
.collect();
365+
366+
Some(arg_infos)
367+
}
368+
369+
fn try_instantiate_arg_type(
370+
semantic_model: &SemanticModel,
371+
arg_type: &LuaType,
372+
arg_expr: &LuaExpr,
373+
depth: usize,
374+
) -> Option<LuaType> {
375+
match arg_type {
376+
LuaType::TplRef(tpl_ref) => {
377+
let node_or_token = arg_expr.syntax().clone().into();
378+
let semantic_decl =
379+
semantic_model.find_decl(node_or_token, SemanticDeclLevel::default())?;
380+
match tpl_ref.get_tpl_id() {
381+
GenericTplId::Func(tpl_id) => {
382+
if let LuaSemanticDeclId::LuaDecl(decl_id) = semantic_decl {
383+
let decl = semantic_model
384+
.get_db()
385+
.get_decl_index()
386+
.get_decl(&decl_id)?;
387+
match decl.extra {
388+
LuaDeclExtra::Param { signature_id, .. } => {
389+
let signature = semantic_model
390+
.get_db()
391+
.get_signature_index()
392+
.get(&signature_id)?;
393+
if let Some((_, param_type)) =
394+
signature.generic_params.get(tpl_id as usize)
395+
{
396+
return param_type.clone();
397+
}
398+
}
399+
_ => return None,
400+
}
401+
}
402+
None
403+
}
404+
GenericTplId::Type(tpl_id) => {
405+
if let LuaSemanticDeclId::LuaDecl(decl_id) = semantic_decl {
406+
let decl = semantic_model
407+
.get_db()
408+
.get_decl_index()
409+
.get_decl(&decl_id)?;
410+
match decl.extra {
411+
LuaDeclExtra::Param {
412+
owner_member_id, ..
413+
} => {
414+
let owner_member_id = owner_member_id?;
415+
let parent_owner = semantic_model
416+
.get_db()
417+
.get_member_index()
418+
.get_current_owner(&owner_member_id)?;
419+
match parent_owner {
420+
LuaMemberOwner::Type(type_id) => {
421+
let generic_params = semantic_model
422+
.get_db()
423+
.get_type_index()
424+
.get_generic_params(&type_id)?;
425+
return generic_params.get(tpl_id as usize)?.1.clone();
426+
}
427+
_ => return None,
428+
}
429+
}
430+
_ => return None,
431+
}
432+
}
433+
None
434+
}
435+
}
436+
}
437+
LuaType::Union(union_type) => {
438+
if depth > 1 {
439+
return None;
440+
}
441+
let mut result = LuaType::Unknown;
442+
for union_member_type in union_type.into_vec().iter() {
443+
let extend_type = try_instantiate_arg_type(
444+
semantic_model,
445+
union_member_type,
446+
arg_expr,
447+
depth + 1,
448+
)
449+
.unwrap_or(union_member_type.clone());
450+
result = TypeOps::Union.apply(semantic_model.get_db(), &result, &extend_type);
451+
}
452+
Some(result)
453+
}
454+
_ => None,
455+
}
456+
}
457+
458+
fn infer_expr_list_types(
459+
semantic_model: &SemanticModel,
460+
exprs: &[LuaExpr],
461+
) -> Vec<(LuaType, LuaExpr)> {
462+
let mut value_types = Vec::new();
463+
for expr in exprs.iter() {
464+
let expr_type = semantic_model
465+
.infer_expr(expr.clone())
466+
.unwrap_or(LuaType::Unknown);
467+
match expr_type {
468+
LuaType::Variadic(variadic) => match variadic.deref() {
469+
VariadicType::Base(base) => {
470+
value_types.push((base.clone(), expr.clone()));
471+
}
472+
VariadicType::Multi(vecs) => {
473+
for typ in vecs {
474+
value_types.push((typ.clone(), expr.clone()));
475+
}
476+
}
477+
},
478+
_ => value_types.push((expr_type.clone(), expr.clone())),
479+
}
480+
}
481+
value_types
482+
}

crates/emmylua_code_analysis/src/diagnostic/test/generic_constraint_mismatch_test.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,4 +239,64 @@ mod test {
239239
"#
240240
));
241241
}
242+
243+
#[test]
244+
fn test_union_2() {
245+
let mut ws = VirtualWorkspace::new();
246+
ws.def(
247+
r#"
248+
---@generic T: table
249+
---@param obj T
250+
function add(obj)
251+
end
252+
253+
---@class GCNode
254+
"#,
255+
);
256+
assert!(ws.check_code_for(
257+
DiagnosticCode::GenericConstraintMismatch,
258+
r#"
259+
---@generic T: table
260+
---@param obj T | string
261+
---@return T?
262+
function bindGC(obj)
263+
if type(obj) == "string" then
264+
---@type GCNode
265+
obj = {}
266+
end
267+
268+
return add(obj)
269+
end
270+
"#
271+
));
272+
}
273+
274+
#[test]
275+
fn test_union_3() {
276+
let mut ws = VirtualWorkspace::new();
277+
ws.def(
278+
r#"
279+
---@generic T: table
280+
---@param obj T
281+
function add(obj)
282+
end
283+
284+
285+
"#,
286+
);
287+
assert!(ws.check_code_for(
288+
DiagnosticCode::GenericConstraintMismatch,
289+
r#"
290+
291+
---@class GCNode<T: table>
292+
GCNode = {}
293+
294+
---@param obj T
295+
---@return T?
296+
function GCNode:bindGC(obj)
297+
return add(obj)
298+
end
299+
"#
300+
));
301+
}
242302
}

crates/emmylua_code_analysis/src/lib.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,14 @@ impl EmmyLuaAnalysis {
241241
// 获取所有当前在VFS中的文件
242242
let vfs = self.compilation.get_db().get_vfs();
243243
for file_id in vfs.get_all_file_ids() {
244+
if self
245+
.compilation
246+
.get_db()
247+
.get_module_index()
248+
.is_std(&file_id)
249+
{
250+
continue;
251+
}
244252
if let Some(path) = vfs.get_file_path(&file_id) {
245253
if !path.exists() {
246254
if let Some(uri) = file_path_to_uri(path) {

0 commit comments

Comments
 (0)