Skip to content

Commit 279d040

Browse files
committed
Fix #868
1 parent eb64335 commit 279d040

File tree

2 files changed

+98
-3
lines changed
  • crates/emmylua_code_analysis/src

2 files changed

+98
-3
lines changed

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1459,4 +1459,21 @@ _2 = a[1]
14591459
let e_expected = ws.ty("string");
14601460
assert_eq!(e, e_expected);
14611461
}
1462+
1463+
#[test]
1464+
fn test_issue_868() {
1465+
let mut ws = VirtualWorkspace::new();
1466+
1467+
ws.check_code_for(
1468+
DiagnosticCode::AssignTypeMismatch,
1469+
r#"
1470+
local a --- @type string|{foo:boolean, bar:string}
1471+
1472+
if a.foo then
1473+
--- @type string
1474+
local _ = a.bar
1475+
end
1476+
"#,
1477+
);
1478+
}
14621479
}

crates/emmylua_code_analysis/src/semantic/infer/narrow/condition_flow/index_flow.rs

Lines changed: 81 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use emmylua_parser::{LuaChunk, LuaExpr, LuaIndexExpr};
1+
use emmylua_parser::{LuaChunk, LuaExpr, LuaIndexExpr, LuaIndexMemberExpr};
22

33
use crate::{
4-
DbIndex, FlowNode, FlowTree, InferFailReason, LuaInferCache,
4+
DbIndex, FlowNode, FlowTree, InferFailReason, InferGuard, LuaInferCache, LuaType, TypeOps,
55
semantic::infer::{
66
VarRefId,
7+
infer_index::infer_member_by_member_key,
78
narrow::{
89
ResultTypeOrContinue, condition_flow::InferConditionFlow, get_single_antecedent,
910
get_type_at_flow::get_type_at_flow, narrow_false_or_nil, remove_false_or_nil,
@@ -30,7 +31,16 @@ pub fn get_type_at_index_expr(
3031
};
3132

3233
if name_var_ref_id != *var_ref_id {
33-
return Ok(ResultTypeOrContinue::Continue);
34+
return maybe_field_exist_narrow(
35+
db,
36+
tree,
37+
cache,
38+
root,
39+
var_ref_id,
40+
flow_node,
41+
index_expr,
42+
condition_flow,
43+
);
3444
}
3545

3646
let antecedent_flow_id = get_single_antecedent(tree, flow_node)?;
@@ -43,3 +53,71 @@ pub fn get_type_at_index_expr(
4353

4454
Ok(ResultTypeOrContinue::Result(result_type))
4555
}
56+
57+
#[allow(clippy::too_many_arguments)]
58+
fn maybe_field_exist_narrow(
59+
db: &DbIndex,
60+
tree: &FlowTree,
61+
cache: &mut LuaInferCache,
62+
root: &LuaChunk,
63+
var_ref_id: &VarRefId,
64+
flow_node: &FlowNode,
65+
index_expr: LuaIndexExpr,
66+
condition_flow: InferConditionFlow,
67+
) -> Result<ResultTypeOrContinue, InferFailReason> {
68+
let Some(prefix_expr) = index_expr.get_prefix_expr() else {
69+
return Ok(ResultTypeOrContinue::Continue);
70+
};
71+
72+
let Some(maybe_var_ref_id) = get_var_expr_var_ref_id(db, cache, prefix_expr.clone()) else {
73+
// If we cannot find a reference declaration ID, we cannot narrow it
74+
return Ok(ResultTypeOrContinue::Continue);
75+
};
76+
77+
if maybe_var_ref_id != *var_ref_id {
78+
return Ok(ResultTypeOrContinue::Continue);
79+
}
80+
81+
let antecedent_flow_id = get_single_antecedent(tree, flow_node)?;
82+
let left_type = get_type_at_flow(db, tree, cache, root, var_ref_id, antecedent_flow_id)?;
83+
let LuaType::Union(union_type) = &left_type else {
84+
return Ok(ResultTypeOrContinue::Continue);
85+
};
86+
87+
let index = LuaIndexMemberExpr::IndexExpr(index_expr);
88+
let mut result = vec![];
89+
let union_types = union_type.into_vec();
90+
for sub_type in &union_types {
91+
let member_type = match infer_member_by_member_key(
92+
db,
93+
cache,
94+
sub_type,
95+
index.clone(),
96+
&InferGuard::new(),
97+
) {
98+
Ok(member_type) => member_type,
99+
Err(_) => continue, // If we cannot infer the member type, skip this type
100+
};
101+
// donot use always true
102+
if !member_type.is_always_falsy() {
103+
result.push(sub_type.clone());
104+
}
105+
}
106+
107+
match condition_flow {
108+
InferConditionFlow::TrueCondition => {
109+
if !result.is_empty() {
110+
return Ok(ResultTypeOrContinue::Result(LuaType::from_vec(result)));
111+
}
112+
}
113+
InferConditionFlow::FalseCondition => {
114+
if !result.is_empty() {
115+
let target = LuaType::from_vec(result);
116+
let t = TypeOps::Remove.apply(db, &left_type, &target);
117+
return Ok(ResultTypeOrContinue::Result(t));
118+
}
119+
}
120+
}
121+
122+
Ok(ResultTypeOrContinue::Continue)
123+
}

0 commit comments

Comments
 (0)