1515from codeflash .models .models import CodeString , CodeStringsMarkdown
1616
1717if TYPE_CHECKING :
18+ from collections .abc import Callable
19+
1820 from codeflash .discovery .functions_to_optimize import FunctionToOptimize
1921 from codeflash .models .models import CodeOptimizationContext , FunctionSource
2022
@@ -49,6 +51,73 @@ def extract_names_from_targets(target: cst.CSTNode) -> list[str]:
4951 return names
5052
5153
54+ def is_assignment_used (
55+ node : cst .CSTNode ,
56+ definitions : dict [str , UsageInfo ],
57+ name_prefix : str = "" ,
58+ ) -> bool :
59+ if isinstance (node , cst .Assign ):
60+ for target in node .targets :
61+ names = extract_names_from_targets (target .target )
62+ for name in names :
63+ lookup = f"{ name_prefix } { name } " if name_prefix else name
64+ if lookup in definitions and definitions [lookup ].used_by_qualified_function :
65+ return True
66+ return False
67+ if isinstance (node , (cst .AnnAssign , cst .AugAssign )):
68+ names = extract_names_from_targets (node .target )
69+ for name in names :
70+ lookup = f"{ name_prefix } { name } " if name_prefix else name
71+ if lookup in definitions and definitions [lookup ].used_by_qualified_function :
72+ return True
73+ return False
74+ return False
75+
76+
77+ def recurse_sections (
78+ node : cst .CSTNode ,
79+ section_names : list [str ],
80+ prune_fn : Callable [[cst .CSTNode ], tuple [cst .CSTNode | None , bool ]],
81+ keep_non_target_children : bool = False ,
82+ ) -> tuple [cst .CSTNode | None , bool ]:
83+ updates : dict [str , list [cst .CSTNode ] | cst .CSTNode ] = {}
84+ found_any_target = False
85+ for section in section_names :
86+ original_content = getattr (node , section , None )
87+ if isinstance (original_content , (list , tuple )):
88+ new_children = []
89+ section_found_target = False
90+ for child in original_content :
91+ filtered , found_target = prune_fn (child )
92+ if filtered :
93+ new_children .append (filtered )
94+ section_found_target |= found_target
95+ if keep_non_target_children :
96+ if section_found_target or new_children :
97+ found_any_target |= section_found_target
98+ updates [section ] = new_children
99+ elif section_found_target :
100+ found_any_target = True
101+ updates [section ] = new_children
102+ elif original_content is not None :
103+ filtered , found_target = prune_fn (original_content )
104+ if keep_non_target_children :
105+ found_any_target |= found_target
106+ if filtered :
107+ updates [section ] = filtered
108+ elif found_target :
109+ found_any_target = True
110+ if filtered :
111+ updates [section ] = filtered
112+ if keep_non_target_children :
113+ if updates :
114+ return node .with_changes (** updates ), found_any_target
115+ return None , False
116+ if not found_any_target :
117+ return None , False
118+ return (node .with_changes (** updates ) if updates else node ), True
119+
120+
52121def collect_top_level_definitions (
53122 node : cst .CSTNode , definitions : Optional [dict [str , UsageInfo ]] = None
54123) -> dict [str , UsageInfo ]:
@@ -423,27 +492,9 @@ def remove_unused_definitions_recursively(
423492 elif isinstance (statement , (cst .Assign , cst .AnnAssign , cst .AugAssign )):
424493 var_used = False
425494
426- # Check if any variable in this assignment is used
427- if isinstance (statement , cst .Assign ):
428- for target in statement .targets :
429- names = extract_names_from_targets (target .target )
430- for name in names :
431- class_var_name = f"{ class_name } .{ name } "
432- if (
433- class_var_name in definitions
434- and definitions [class_var_name ].used_by_qualified_function
435- ):
436- var_used = True
437- method_or_var_used = True
438- break
439- elif isinstance (statement , (cst .AnnAssign , cst .AugAssign )):
440- names = extract_names_from_targets (statement .target )
441- for name in names :
442- class_var_name = f"{ class_name } .{ name } "
443- if class_var_name in definitions and definitions [class_var_name ].used_by_qualified_function :
444- var_used = True
445- method_or_var_used = True
446- break
495+ if is_assignment_used (statement , definitions , name_prefix = f"{ class_name } ." ):
496+ var_used = True
497+ method_or_var_used = True
447498
448499 if var_used or class_has_dependencies :
449500 new_statements .append (statement )
@@ -459,56 +510,21 @@ def remove_unused_definitions_recursively(
459510
460511 return node , method_or_var_used or class_has_dependencies
461512
462- # Handle assignments (Assign and AnnAssign)
463- if isinstance (node , cst .Assign ):
464- for target in node .targets :
465- names = extract_names_from_targets (target .target )
466- for name in names :
467- if name in definitions and definitions [name ].used_by_qualified_function :
468- return node , True
469- return None , False
470-
471- if isinstance (node , (cst .AnnAssign , cst .AugAssign )):
472- names = extract_names_from_targets (node .target )
473- for name in names :
474- if name in definitions and definitions [name ].used_by_qualified_function :
475- return node , True
513+ # Handle assignments (Assign, AnnAssign, AugAssign)
514+ if isinstance (node , (cst .Assign , cst .AnnAssign , cst .AugAssign )):
515+ if is_assignment_used (node , definitions ):
516+ return node , True
476517 return None , False
477518
478519 # For other nodes, recursively process children
479520 section_names = get_section_names (node )
480521 if not section_names :
481522 return node , False
482-
483- updates = {}
484- found_used = False
485-
486- for section in section_names :
487- original_content = getattr (node , section , None )
488- if isinstance (original_content , (list , tuple )):
489- new_children = []
490- section_found_used = False
491-
492- for child in original_content :
493- filtered , used = remove_unused_definitions_recursively (child , definitions )
494- if filtered :
495- new_children .append (filtered )
496- section_found_used |= used
497-
498- if new_children or section_found_used :
499- found_used |= section_found_used
500- updates [section ] = new_children
501- elif original_content is not None :
502- filtered , used = remove_unused_definitions_recursively (original_content , definitions )
503- found_used |= used
504- if filtered :
505- updates [section ] = filtered
506- if not found_used :
507- return None , False
508- if updates :
509- return node .with_changes (** updates ), found_used
510-
511- return node , False
523+ return recurse_sections (
524+ node ,
525+ section_names ,
526+ lambda child : remove_unused_definitions_recursively (child , definitions ),
527+ )
512528
513529
514530def collect_top_level_defs_with_usages (
0 commit comments