Skip to content

Commit 14bdc3c

Browse files
aseembits93claude
andcommitted
fix: detect attribute-referenced methods as used in unused helper detection
detect_unused_helper_functions only walked ast.Call nodes, missing methods referenced via attribute assignment (e.g., self._parse1 = self._parse_literal). This caused optimized helper methods used as callbacks to be incorrectly reverted to their original code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e909c27 commit 14bdc3c

2 files changed

Lines changed: 708 additions & 3 deletions

File tree

codeflash/languages/python/context/unused_definition_remover.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,7 @@ def detect_unused_helper_functions(
773773
# First, analyze imports to build a mapping of imported names to their original qualified names
774774
imported_names_map = _analyze_imports_in_optimized_code(optimized_ast, code_context)
775775

776-
# Extract all function calls in the entrypoint function
776+
# Extract all function calls and attribute references in the entrypoint function
777777
called_function_names = {function_to_optimize.function_name}
778778
for node in ast.walk(entrypoint_function_ast):
779779
if isinstance(node, ast.Call):
@@ -794,7 +794,6 @@ def detect_unused_helper_functions(
794794
# self.method_name() -> add both method_name and ClassName.method_name
795795
called_function_names.add(attr_name)
796796
# For class methods, also add the qualified name
797-
# For class methods, also add the qualified name
798797
if hasattr(function_to_optimize, "parents") and function_to_optimize.parents:
799798
class_name = function_to_optimize.parents[0].name
800799
called_function_names.add(f"{class_name}.{attr_name}")
@@ -807,9 +806,25 @@ def detect_unused_helper_functions(
807806
if mapped_names:
808807
called_function_names.update(mapped_names)
809808
# Handle nested attribute access like obj.attr.method()
810-
# Handle nested attribute access like obj.attr.method()
811809
else:
812810
called_function_names.add(node.func.attr)
811+
elif isinstance(node, ast.Attribute) and isinstance(node.value, ast.Name):
812+
# Attribute reference without call: e.g. self._parse1 = self._parse_literal
813+
# This covers methods used as callbacks, stored in variables, passed as arguments, etc.
814+
attr_name = node.attr
815+
value_id = node.value.id
816+
if value_id == "self":
817+
called_function_names.add(attr_name)
818+
if hasattr(function_to_optimize, "parents") and function_to_optimize.parents:
819+
class_name = function_to_optimize.parents[0].name
820+
called_function_names.add(f"{class_name}.{attr_name}")
821+
else:
822+
called_function_names.add(attr_name)
823+
full_ref = f"{value_id}.{attr_name}"
824+
called_function_names.add(full_ref)
825+
mapped_names = imported_names_map.get(full_ref)
826+
if mapped_names:
827+
called_function_names.update(mapped_names)
813828

814829
logger.debug(f"Functions called in optimized entrypoint: {called_function_names}")
815830
logger.debug(f"Imported names mapping: {imported_names_map}")

0 commit comments

Comments
 (0)