@@ -634,43 +634,40 @@ def _analyze_imports_in_optimized_code(
634634 func_name = helper .only_function_name
635635 module_name = helper .file_path .stem
636636 # Cache function lookup for this (module, func)
637- file_entry = helpers_by_file_and_func [module_name ]
638- if func_name in file_entry :
639- file_entry [func_name ].append (helper )
640- else :
641- file_entry [func_name ] = [helper ]
637+ helpers_by_file_and_func [module_name ].setdefault (func_name , []).append (helper )
642638 helpers_by_file [module_name ].append (helper )
643639
644- # Optimize attribute lookups and method binding outside the loop
645- helpers_by_file_and_func_get = helpers_by_file_and_func .get
646- helpers_by_file_get = helpers_by_file .get
647-
648640 for node in ast .walk (optimized_ast ):
649641 if isinstance (node , ast .ImportFrom ):
650642 # Handle "from module import function" statements
651643 module_name = node .module
652644 if module_name :
653- file_entry = helpers_by_file_and_func_get (module_name , None )
645+ file_entry = helpers_by_file_and_func . get (module_name )
654646 if file_entry :
655647 for alias in node .names :
656648 imported_name = alias .asname if alias .asname else alias .name
657649 original_name = alias .name
658- helpers = file_entry .get (original_name , None )
650+ helpers = file_entry .get (original_name )
659651 if helpers :
652+ imported_set = imported_names_map [imported_name ]
660653 for helper in helpers :
661- imported_names_map [ imported_name ] .add (helper .qualified_name )
662- imported_names_map [ imported_name ] .add (helper .fully_qualified_name )
654+ imported_set .add (helper .qualified_name )
655+ imported_set .add (helper .fully_qualified_name )
663656
664657 elif isinstance (node , ast .Import ):
665658 # Handle "import module" statements
666659 for alias in node .names :
667660 imported_name = alias .asname if alias .asname else alias .name
668661 module_name = alias .name
669- for helper in helpers_by_file_get (module_name , []):
670- # For "import module" statements, functions would be called as module.function
671- full_call = f"{ imported_name } .{ helper .only_function_name } "
672- imported_names_map [full_call ].add (helper .qualified_name )
673- imported_names_map [full_call ].add (helper .fully_qualified_name )
662+ helpers = helpers_by_file .get (module_name )
663+ if helpers :
664+ imported_set = imported_names_map [f"{ imported_name } .{{func}}" ]
665+ for helper in helpers :
666+ # For "import module" statements, functions would be called as module.function
667+ full_call = f"{ imported_name } .{ helper .only_function_name } "
668+ full_call_set = imported_names_map [full_call ]
669+ full_call_set .add (helper .qualified_name )
670+ full_call_set .add (helper .fully_qualified_name )
674671
675672 return dict (imported_names_map )
676673
@@ -750,27 +747,31 @@ def detect_unused_helper_functions(
750747 called_name = node .func .id
751748 called_function_names .add (called_name )
752749 # Also add the qualified name if this is an imported function
753- if called_name in imported_names_map :
754- called_function_names .update (imported_names_map [called_name ])
750+ mapped_names = imported_names_map .get (called_name )
751+ if mapped_names :
752+ called_function_names .update (mapped_names )
755753 elif isinstance (node .func , ast .Attribute ):
756754 # Method call: obj.method() or self.method() or module.function()
757755 if isinstance (node .func .value , ast .Name ):
758- if node .func .value .id == "self" :
756+ attr_name = node .func .attr
757+ value_id = node .func .value .id
758+ if value_id == "self" :
759759 # self.method_name() -> add both method_name and ClassName.method_name
760- called_function_names .add (node .func .attr )
760+ called_function_names .add (attr_name )
761+ # For class methods, also add the qualified name
761762 # For class methods, also add the qualified name
762763 if hasattr (function_to_optimize , "parents" ) and function_to_optimize .parents :
763764 class_name = function_to_optimize .parents [0 ].name
764- called_function_names .add (f"{ class_name } .{ node . func . attr } " )
765+ called_function_names .add (f"{ class_name } .{ attr_name } " )
765766 else :
766- # obj.method() or module.function()
767- attr_name = node .func .attr
768767 called_function_names .add (attr_name )
769- called_function_names .add (f"{ node .func .value .id } .{ attr_name } " )
768+ full_call = f"{ value_id } .{ attr_name } "
769+ called_function_names .add (full_call )
770770 # Check if this is a module.function call that maps to a helper
771- full_call = f"{ node .func .value .id } .{ attr_name } "
772- if full_call in imported_names_map :
773- called_function_names .update (imported_names_map [full_call ])
771+ mapped_names = imported_names_map .get (full_call )
772+ if mapped_names :
773+ called_function_names .update (mapped_names )
774+ # Handle nested attribute access like obj.attr.method()
774775 # Handle nested attribute access like obj.attr.method()
775776 else :
776777 called_function_names .add (node .func .attr )
@@ -780,32 +781,35 @@ def detect_unused_helper_functions(
780781
781782 # Find helper functions that are no longer called
782783 unused_helpers = []
784+ entrypoint_file_path = function_to_optimize .file_path
783785 for helper_function in code_context .helper_functions :
784786 if helper_function .jedi_definition .type != "class" :
785787 # Check if the helper function is called using multiple name variants
786788 helper_qualified_name = helper_function .qualified_name
787789 helper_simple_name = helper_function .only_function_name
788790 helper_fully_qualified_name = helper_function .fully_qualified_name
789791
790- # Create a set of all possible names this helper might be called by
791- possible_call_names = {helper_qualified_name , helper_simple_name , helper_fully_qualified_name }
792-
792+ # Check membership efficiently - exit early on first match
793+ if (
794+ helper_qualified_name in called_function_names
795+ or helper_simple_name in called_function_names
796+ or helper_fully_qualified_name in called_function_names
797+ ):
798+ is_called = True
793799 # For cross-file helpers, also consider module-based calls
794- if helper_function .file_path != function_to_optimize . file_path :
800+ elif helper_function .file_path != entrypoint_file_path :
795801 # Add potential module.function combinations
796802 module_name = helper_function .file_path .stem
797- possible_call_names . add ( f"{ module_name } .{ helper_simple_name } " )
798-
799- # Check if any of the possible names are in the called functions
800- is_called = bool ( possible_call_names . intersection ( called_function_names ))
803+ module_call = f"{ module_name } .{ helper_simple_name } "
804+ is_called = module_call in called_function_names
805+ else :
806+ is_called = False
801807
802808 if not is_called :
803809 unused_helpers .append (helper_function )
804810 logger .debug (f"Helper function { helper_qualified_name } is not called in optimized code" )
805- logger .debug (f" Checked names: { possible_call_names } " )
806811 else :
807812 logger .debug (f"Helper function { helper_qualified_name } is still called in optimized code" )
808- logger .debug (f" Called via: { possible_call_names .intersection (called_function_names )} " )
809813
810814 except Exception as e :
811815 logger .debug (f"Error detecting unused helper functions: { e } " )
0 commit comments