1- use emmylua_parser:: { LuaChunk , LuaExpr , LuaIndexExpr } ;
1+ use emmylua_parser:: { LuaChunk , LuaExpr , LuaIndexExpr , LuaIndexMemberExpr } ;
22
33use 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