@@ -150,11 +150,13 @@ def __init__(self, function_names_to_find: set[str]) -> None:
150150 self .imported_modules : set [str ] = set ()
151151 self .has_dynamic_imports : bool = False
152152 self .wildcard_modules : set [str ] = set ()
153+ # Track aliases: alias_name -> original_name
154+ self .alias_mapping : dict [str , str ] = {}
153155
154156 # Precompute function_names for prefix search
155157 # For prefix match, store mapping from prefix-root to candidates for O(1) matching
156158 self ._exact_names = function_names_to_find
157- self ._prefix_roots = {}
159+ self ._prefix_roots : dict [ str , list [ str ]] = {}
158160 for name in function_names_to_find :
159161 if "." in name :
160162 root = name .split ("." , 1 )[0 ]
@@ -223,6 +225,12 @@ def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
223225 return
224226
225227 # Fast prefix match: only for relevant roots
228+ for target_func in fnames :
229+ if target_func .startswith (f"{ aname } ." ):
230+ self .found_any_target_function = True
231+ self .found_qualified_name = target_func
232+ return
233+
226234 prefix = qname + "."
227235 # Only bother if one of the targets startswith the prefix-root
228236 candidates = proots .get (qname , ())
@@ -247,6 +255,15 @@ def visit_Attribute(self, node: ast.Attribute) -> None:
247255 self .found_qualified_name = node .attr
248256 return
249257
258+ if isinstance (node .value , ast .Name ) and node .value .id in self .imported_modules :
259+ for target_func in self .function_names_to_find :
260+ if "." in target_func :
261+ class_name , method_name = target_func .rsplit ("." , 1 )
262+ if node .attr == method_name :
263+ self .found_any_target_function = True
264+ self .found_qualified_name = target_func
265+ return
266+
250267 # Check if this is accessing a target function through a dynamically imported module
251268 # Only if we've detected dynamic imports are being used
252269 if self .has_dynamic_imports and node .attr in self .function_names_to_find :
0 commit comments