11use emmylua_parser:: {
2- LuaAssignStat , LuaAst , LuaAstNode , LuaBreakStat , LuaCallExprStat , LuaDoStat , LuaForRangeStat ,
3- LuaFuncStat , LuaGotoStat , LuaIfStat , LuaLabelStat , LuaLocalStat , LuaRepeatStat , LuaReturnStat ,
4- LuaVarExpr , LuaWhileStat , PathTrait ,
2+ LuaAssignStat , LuaAst , LuaAstNode , LuaBreakStat , LuaCallExprStat , LuaDoStat , LuaExpr , LuaForRangeStat , LuaForStat , LuaFuncStat , LuaGotoStat , LuaIfStat , LuaLabelStat , LuaLocalStat , LuaRepeatStat , LuaReturnStat , LuaUnaryExpr , LuaVarExpr , LuaWhileStat , PathTrait , UnaryOperator
53} ;
64
75use crate :: {
86 compilation:: analyzer:: flow:: {
97 bind_analyze:: { bind_block, bind_each_child, exprs:: bind_expr} ,
108 binder:: FlowBinder ,
11- flow_node:: { FlowAssignment , FlowId , FlowNodeKind } ,
9+ flow_node:: { FlowAssertion , FlowAssignment , FlowId , FlowNodeKind } ,
1210 } ,
1311 LuaClosureId , LuaDeclId , LuaVarRefId ,
1412} ;
@@ -331,16 +329,75 @@ pub fn bind_for_range_stat(
331329 for_range_stat : LuaForRangeStat ,
332330 current : FlowId ,
333331) -> FlowId {
334- bind_each_child ( binder, LuaAst :: LuaForRangeStat ( for_range_stat) , current) ;
332+ let loop_label = binder. create_loop_label ( ) ;
333+ binder. add_antecedent ( loop_label, current) ;
334+ let old_loop_label = binder. loop_label ;
335+ binder. loop_label = loop_label;
336+ bind_each_child ( binder, LuaAst :: LuaForRangeStat ( for_range_stat) , loop_label) ;
337+ binder. loop_label = old_loop_label;
335338 current
336339}
337340
338- pub fn bind_for_stat (
339- binder : & mut FlowBinder ,
340- _for_stat : emmylua_parser:: LuaForStat ,
341- current : FlowId ,
342- ) -> FlowId {
343- // For loops are not yet implemented
344- // For now, we just return the current flow
341+ pub fn bind_for_stat ( binder : & mut FlowBinder , for_stat : LuaForStat , current : FlowId ) -> FlowId {
342+ let loop_label = binder. create_loop_label ( ) ;
343+ binder. add_antecedent ( loop_label, current) ;
344+ let old_loop_label = binder. loop_label ;
345+ binder. loop_label = loop_label;
346+
347+ let Some ( var_name_token) = for_stat. get_var_name ( ) else {
348+ binder. loop_label = old_loop_label;
349+ return current;
350+ } ;
351+
352+ let var_name = var_name_token. get_name_text ( ) ;
353+ let var_list = for_stat. get_iter_expr ( ) . collect :: < Vec < _ > > ( ) ;
354+ for expr in & var_list {
355+ bind_expr ( binder, expr. clone ( ) , loop_label) ;
356+ }
357+
358+ if var_list. len ( ) < 2 {
359+ binder. loop_label = old_loop_label;
360+ return current;
361+ }
362+
363+ let mut parent_flow_id = loop_label;
364+ let second_expr = var_list[ 1 ] . clone ( ) ;
365+ if let LuaExpr :: UnaryExpr ( unary_expr) = second_expr {
366+ if let Some ( length_var_name) = get_for_length_name ( unary_expr) {
367+ let var_ref_id = LuaVarRefId :: Name ( format ! ( "{}.[{}]" , length_var_name, var_name) . into ( ) ) ;
368+ let flow_id = binder. create_node ( FlowNodeKind :: Assertion (
369+ FlowAssertion :: Truthy ( var_ref_id) . into ( ) ,
370+ ) ) ;
371+ binder. add_antecedent ( flow_id, loop_label) ;
372+ parent_flow_id = flow_id;
373+ }
374+ }
375+
376+ if let Some ( block) = for_stat. get_block ( ) {
377+ bind_block ( binder, block, parent_flow_id) ;
378+ }
379+
380+ binder. loop_label = old_loop_label;
345381 current
346382}
383+
384+ fn get_for_length_name ( unary_expr : LuaUnaryExpr ) -> Option < String > {
385+ let op_token = unary_expr. get_op_token ( ) ?;
386+ if op_token. get_op ( ) == UnaryOperator :: OpLen {
387+ if let Some ( inner) = unary_expr. get_expr ( ) {
388+ match inner {
389+ LuaExpr :: IndexExpr ( index_expr) => {
390+ if let Some ( access_path) = index_expr. get_access_path ( ) {
391+ return Some ( access_path. into ( ) ) ;
392+ }
393+ }
394+ LuaExpr :: NameExpr ( name_expr) => {
395+ return Some ( name_expr. get_name_text ( ) ?) ;
396+ }
397+ _ => { }
398+ }
399+ }
400+ }
401+
402+ None
403+ }
0 commit comments