Skip to content

Commit bc264f6

Browse files
committed
Fix #594
1 parent 9a574e7 commit bc264f6

File tree

3 files changed

+90
-32
lines changed

3 files changed

+90
-32
lines changed

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#[cfg(test)]
22
mod test {
3-
use crate::{DiagnosticCode, VirtualWorkspace};
3+
use crate::{DiagnosticCode, EmmyrcLuaVersion, VirtualWorkspace};
44

55
#[test]
66
fn test_unpack() {
@@ -42,4 +42,22 @@ mod test {
4242
"#,
4343
));
4444
}
45+
46+
#[test]
47+
fn test_issue_594() {
48+
let mut ws = VirtualWorkspace::new_with_init_std_lib();
49+
let mut emmyrc = ws.get_emmyrc();
50+
emmyrc.runtime.version = EmmyrcLuaVersion::Lua51;
51+
ws.analysis.update_config(emmyrc.into());
52+
assert!(ws.check_code_for(
53+
DiagnosticCode::AssignTypeMismatch,
54+
r#"
55+
--- @type string[]
56+
local s = {}
57+
58+
--- @type string[]
59+
local s2 = { 'a', unpack(s) }
60+
"#,
61+
));
62+
}
4563
}

crates/emmylua_code_analysis/src/diagnostic/checker/assign_type_mismatch.rs

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::ops::Deref;
2+
13
use emmylua_parser::{
24
LuaAssignStat, LuaAst, LuaAstNode, LuaAstToken, LuaExpr, LuaIndexExpr, LuaLocalStat,
35
LuaNameExpr, LuaTableExpr, LuaVarExpr,
@@ -6,7 +8,7 @@ use rowan::TextRange;
68

79
use crate::{
810
infer_index_expr, DiagnosticCode, LuaDeclExtra, LuaDeclId, LuaMemberKey, LuaSemanticDeclId,
9-
LuaType, SemanticDeclLevel, SemanticModel, TypeCheckFailReason, TypeCheckResult,
11+
LuaType, SemanticDeclLevel, SemanticModel, TypeCheckFailReason, TypeCheckResult, VariadicType,
1012
};
1113

1214
use super::{humanize_lint_type, Checker, DiagnosticContext};
@@ -252,15 +254,14 @@ fn check_table_expr_content(
252254
// 位于的最后的 TableFieldValue 允许接受函数调用返回的多值, 而且返回的值必然会从下标 1 开始覆盖掉所有索引字段.
253255
if field.is_value_field() && idx == fields.len() - 1 {
254256
match &expr_type {
255-
LuaType::Variadic(_) => {
256-
// 解开可变参数
257-
let expr_types =
258-
semantic_model.infer_expr_list_types(&vec![value_expr.clone()], None);
257+
LuaType::Variadic(variadic) => {
259258
if let Some(result) = check_table_last_variadic_type(
260259
context,
261260
semantic_model,
262261
table_type,
263-
&expr_types,
262+
idx,
263+
&variadic,
264+
field.get_range(),
264265
) {
265266
has_diagnostic = has_diagnostic || result;
266267
}
@@ -319,38 +320,40 @@ fn check_table_last_variadic_type(
319320
context: &mut DiagnosticContext,
320321
semantic_model: &SemanticModel,
321322
table_type: &LuaType,
322-
expr_types: &[(LuaType, TextRange)],
323+
idx: usize,
324+
value_variadic: &VariadicType,
325+
range: TextRange,
323326
) -> Option<bool> {
324-
let mut has_diagnostic = false;
325-
326-
for (idx, (expr_type, range)) in expr_types.iter().enumerate() {
327-
// 此时的值必然是从下标 1 开始递增的
328-
let member_key = LuaMemberKey::Integer(idx as i64 + 1);
327+
// test max 10
328+
for offset in idx..(idx + 10) {
329+
let member_key = LuaMemberKey::Integer((idx + offset) as i64 + 1);
329330
let source_type = semantic_model
330331
.infer_member_type(&table_type, &member_key)
331332
.ok()?;
333+
match source_type {
334+
LuaType::Variadic(source_variadic) => {
335+
return Some(source_variadic.deref() != value_variadic)
336+
}
337+
_ => {
338+
let expr_type = value_variadic.get_type(offset)?;
332339

333-
let expr_type = if let LuaType::Variadic(variadic) = expr_type {
334-
let Some(typ) = variadic.get_type(idx) else {
335-
continue;
336-
};
337-
typ.clone()
338-
} else {
339-
expr_type.clone()
340-
};
341-
342-
if let Some(result) = check_assign_type_mismatch(
343-
context,
344-
semantic_model,
345-
*range,
346-
Some(&source_type),
347-
&expr_type,
348-
false,
349-
) {
350-
has_diagnostic = has_diagnostic || result;
340+
if let Some(result) = check_assign_type_mismatch(
341+
context,
342+
semantic_model,
343+
range,
344+
Some(&source_type),
345+
&expr_type,
346+
false,
347+
) {
348+
if result {
349+
return Some(true);
350+
}
351+
}
352+
}
351353
}
352354
}
353-
Some(has_diagnostic)
355+
356+
Some(false)
354357
}
355358

356359
fn check_assign_type_mismatch(

crates/emmylua_code_analysis/src/semantic/infer/infer_table.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use emmylua_parser::{
66
};
77

88
use crate::{
9+
check_type_compact,
910
db_index::{DbIndex, LuaType},
1011
infer_call_expr_func, infer_expr, InferGuard, LuaArrayType, LuaDeclId, LuaInferCache,
1112
LuaMemberId, LuaTupleStatus, LuaTupleType, LuaUnionType, TypeOps, VariadicType,
@@ -75,6 +76,42 @@ fn infer_table_tuple_or_array(
7576
}
7677
}
7778

79+
if let Some(last_field) = fields.last() {
80+
let last_value_expr = last_field.get_value_expr().ok_or(InferFailReason::None)?;
81+
let last_expr_type = infer_expr(db, cache, last_value_expr)?;
82+
match last_expr_type {
83+
LuaType::Variadic(multi) => match &multi.deref() {
84+
VariadicType::Base(base) => {
85+
let non_nil_base = TypeOps::Remove.apply(db, base, &LuaType::Nil);
86+
if fields.len() <= 1 {
87+
return Ok(LuaType::Array(
88+
LuaArrayType::from_base_type(non_nil_base).into(),
89+
));
90+
}
91+
let len = fields.len() - 1;
92+
let mut all_can_accept_base = true;
93+
for i in 0..len {
94+
let field = fields.get(i).ok_or(InferFailReason::None)?;
95+
let value_expr = field.get_value_expr().ok_or(InferFailReason::None)?;
96+
let typ = infer_expr(db, cache, value_expr)?;
97+
if check_type_compact(db, &non_nil_base, &typ).is_err() {
98+
all_can_accept_base = false;
99+
break;
100+
}
101+
}
102+
103+
if all_can_accept_base {
104+
return Ok(LuaType::Array(
105+
LuaArrayType::from_base_type(non_nil_base).into(),
106+
));
107+
}
108+
}
109+
_ => {}
110+
},
111+
_ => {}
112+
};
113+
}
114+
78115
let mut types = Vec::new();
79116
for field in fields {
80117
let value_expr = field.get_value_expr().ok_or(InferFailReason::None)?;

0 commit comments

Comments
 (0)