Skip to content

Commit 6ddcef7

Browse files
committed
fix: preserve custom types in self-assignment replay
fix #1178
1 parent 17e4c3f commit 6ddcef7

3 files changed

Lines changed: 63 additions & 12 deletions

File tree

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3599,6 +3599,25 @@ _2 = a[1]
35993599
assert_eq!(ws.humanize_type(after_assign), "Counter");
36003600
}
36013601

3602+
#[test]
3603+
fn test_assignment_rhs_keeps_self_type_for_scalar_operator() {
3604+
let mut ws = VirtualWorkspace::new();
3605+
ws.def(
3606+
r#"
3607+
---@class Vector
3608+
---@operator div(number): Vector
3609+
3610+
local position ---@type Vector
3611+
3612+
position = position / 1
3613+
after_assign = position
3614+
"#,
3615+
);
3616+
3617+
let after_assign = ws.expr_ty("after_assign");
3618+
assert_eq!(ws.humanize_type(after_assign), "Vector");
3619+
}
3620+
36023621
#[test]
36033622
#[timeout(5000)]
36043623
fn test_issue_1114_repeated_self_dependent_assignments_build_semantic_model() {

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1602,4 +1602,37 @@ return t
16021602
"#
16031603
));
16041604
}
1605+
1606+
#[test]
1607+
fn test_self_assignment_with_scalar_operator_keeps_declared_class() {
1608+
let mut ws = VirtualWorkspace::new();
1609+
assert!(ws.has_no_diagnostic(
1610+
DiagnosticCode::AssignTypeMismatch,
1611+
r#"
1612+
---@class Vector
1613+
---@operator div(number): Vector
1614+
1615+
local v ---@type Vector
1616+
local position ---@type Vector
1617+
1618+
position = position / 1
1619+
v = position
1620+
"#,
1621+
));
1622+
}
1623+
1624+
#[test]
1625+
fn test_self_assignment_without_scalar_operator_reports_mismatch() {
1626+
let mut ws = VirtualWorkspace::new();
1627+
assert!(!ws.has_no_diagnostic(
1628+
DiagnosticCode::AssignTypeMismatch,
1629+
r#"
1630+
---@class Vector
1631+
1632+
local position ---@type Vector
1633+
1634+
position = position / 1
1635+
"#,
1636+
));
1637+
}
16051638
}

crates/emmylua_code_analysis/src/semantic/infer/narrow/get_type_at_flow.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -218,14 +218,6 @@ impl FlowReplayQuery {
218218
Ok(())
219219
}
220220

221-
fn resolve_dependencies(&mut self, var_ref_id: &VarRefId, typ: LuaType) {
222-
for query in &mut self.dependency_queries {
223-
if query.var_ref_id == *var_ref_id {
224-
query.resolved_type = Some(typ.clone());
225-
}
226-
}
227-
}
228-
229221
fn accept_result(&mut self, dependency_result: InferResult) -> Result<(), InferFailReason> {
230222
let dependency_query = self
231223
.dependency_queries
@@ -1114,11 +1106,18 @@ impl<'a> FlowTypeEngine<'a> {
11141106
expr.clone(),
11151107
true,
11161108
);
1117-
// A plain self-dependent RHS would replay this assignment while
1118-
// trying to type itself. Treat that self read as unknown; `and`/`or`
1119-
// assignments still need the antecedent value for their semantics.
1109+
// 普通自引用 RHS 优先使用明确的自定义声明类型, 避免回放时重新进入当前赋值节点.
1110+
// `and`/`or` 中的读取仍保留表达式内部流位置, 以应用短路分支的收窄结果.
11201111
if explicit_var_type.is_none() && !contains_short_circuit_binary_expr(&expr) {
1121-
replay_query.resolve_dependencies(&var_ref_id, LuaType::Unknown);
1112+
let self_dependency_type = get_var_ref_type(self.db, self.cache, &var_ref_id)
1113+
.ok()
1114+
.filter(|typ| typ.is_custom_type())
1115+
.unwrap_or(LuaType::Unknown);
1116+
for dependency_query in &mut replay_query.dependency_queries {
1117+
if dependency_query.var_ref_id == var_ref_id {
1118+
dependency_query.resolved_type = Some(self_dependency_type.clone());
1119+
}
1120+
}
11221121
}
11231122
return self.start_expr_replay(
11241123
walk,

0 commit comments

Comments
 (0)