@@ -451,36 +451,56 @@ def _resolve_callee(
451451 Returns None for ambiguous matches (multiple candidates)
452452 to avoid creating phantom pipeline edges.
453453 """
454+ # Direct match
454455 if callee in funcs :
455456 return callee
456457
457- # Strip self. prefix for method→method calls
458- bare = callee
459- is_self_call = False
458+ bare , is_self_call = self ._strip_self_prefix (callee )
459+
460+ # Try same-class resolution first
461+ if result := self ._try_same_class_resolution (bare , caller , funcs ):
462+ return result
463+
464+ # Suffix match
465+ candidates = self ._get_suffix_candidates (bare , funcs )
466+ if len (candidates ) == 1 :
467+ return candidates [0 ]
468+
469+ # Prefer same-class candidates for method calls
470+ return self ._select_same_class_candidate (candidates , caller , is_self_call )
471+
472+ def _strip_self_prefix (self , callee : str ) -> Tuple [str , bool ]:
473+ """Strip self. prefix and return bare name + flag."""
460474 if callee .startswith ("self." ):
461- bare = callee [5 :] # strip "self."
462- is_self_call = True
475+ return callee [5 :], True
476+ return callee , False
463477
464- # Try same-class resolution first (for self.X or unqualified method calls)
478+ def _try_same_class_resolution (
479+ self , bare : str , caller : Optional [FunctionInfo ], funcs : Dict [str , FunctionInfo ]
480+ ) -> Optional [str ]:
481+ """Try to resolve method in the same class as caller."""
465482 if caller and caller .class_name :
466483 class_prefix = f"{ caller .module } .{ caller .class_name } ."
467484 class_candidate = class_prefix + bare
468485 if class_candidate in funcs :
469486 return class_candidate
487+ return None
470488
471- # Suffix match
472- candidates = [qn for qn in funcs if qn .endswith (f".{ bare } " )]
473- if len (candidates ) == 1 :
474- return candidates [0 ]
489+ def _get_suffix_candidates (self , bare : str , funcs : Dict [str , FunctionInfo ]) -> List [str ]:
490+ """Find candidates matching by suffix."""
491+ return [qn for qn in funcs if qn .endswith (f".{ bare } " )]
475492
476- # For self.X calls, prefer candidates in the same class
477- if (is_self_call or caller and caller .class_name ) and len (candidates ) > 1 :
478- same_class = [
479- qn for qn in candidates
480- if caller and caller .class_name and f".{ caller .class_name } ." in qn
481- ]
482- if len (same_class ) == 1 :
483- return same_class [0 ]
493+ def _select_same_class_candidate (
494+ self , candidates : List [str ], caller : Optional [FunctionInfo ], is_self_call : bool
495+ ) -> Optional [str ]:
496+ """Select candidate from same class if applicable."""
497+ if not candidates or not (is_self_call or (caller and caller .class_name )):
498+ return None
484499
485- # Ambiguous or not found — skip to avoid wrong edges
500+ same_class = [
501+ qn for qn in candidates
502+ if caller and caller .class_name and f".{ caller .class_name } ." in qn
503+ ]
504+ if len (same_class ) == 1 :
505+ return same_class [0 ]
486506 return None
0 commit comments