@@ -756,14 +756,17 @@ def __raw_call_graph_using_symbol_table_target_method(self, target_class_name: s
756756 callee_signature = call_site .callee_signature
757757
758758 if call_site .receiver_type != "" :
759- # call to any class
759+ # call to any class - check if the target method exists in receiver type hierarchy
760760 if self .get_class (qualified_class_name = call_site .receiver_type ):
761- if callee_signature == target_method_signature and call_site .receiver_type == target_class_name :
761+ # Use hierarchy search to find the method (including inherited methods)
762+ found_method , found_class = self .__find_method_in_hierarchy (call_site .receiver_type , callee_signature )
763+ if found_method is not None and callee_signature == target_method_signature and found_class == target_class_name :
762764 source_method_details = self .get_method (method_signature = method , qualified_class_name = class_name )
763765 source_class = class_name
764766 else :
765- # check if any method exists with the signature in the class even if the receiver type is blank
766- if callee_signature == target_method_signature and class_name == target_class_name :
767+ # check if any method exists with the signature in the class (including inherited) even if the receiver type is blank
768+ found_method , found_class = self .__find_method_in_hierarchy (class_name , callee_signature )
769+ if found_method is not None and callee_signature == target_method_signature and found_class == target_class_name :
767770 source_method_details = self .get_method (method_signature = method , qualified_class_name = class_name )
768771 source_class = class_name
769772
@@ -782,6 +785,46 @@ def __raw_call_graph_using_symbol_table_target_method(self, target_class_name: s
782785 cg .append (call_edge )
783786 return cg
784787
788+ def __find_method_in_hierarchy (self , qualified_class_name : str , method_signature : str ) -> Tuple [JCallable | None , str ]:
789+ """Finds a method in the class hierarchy (including inherited methods).
790+
791+ Ignores interface methods and only returns concrete implementations.
792+
793+ Args:
794+ qualified_class_name (str): The qualified class name to start searching from.
795+ method_signature (str): The method signature to find.
796+
797+ Returns:
798+ Tuple[JCallable | None, str]: A tuple of (method_details, declaring_class).
799+ Returns (None, "") if the method is not found.
800+ """
801+ # First, check if the method exists in the current class
802+ klass = self .get_class (qualified_class_name = qualified_class_name )
803+ method_details = self .get_method (method_signature = method_signature , qualified_class_name = qualified_class_name )
804+
805+ # If found and it's not an interface, return it (concrete implementation)
806+ if method_details is not None and klass is not None and not klass .is_interface :
807+ return method_details , qualified_class_name
808+
809+ # If not found or is an interface, check parent classes (extends) first
810+ # This ensures we find concrete implementations before interface methods
811+ if klass is not None :
812+ # Check extended classes (these are more likely to have concrete implementations)
813+ for parent_class in klass .extends_list :
814+ parent_method , found_class = self .__find_method_in_hierarchy (parent_class , method_signature )
815+ if parent_method is not None :
816+ return parent_method , found_class
817+
818+ # Only check implemented interfaces if no concrete implementation was found
819+ # This is a fallback for cases where only the interface method exists
820+ # for interface in klass.implements_list:
821+ # interface_method, found_class = self.__find_method_in_hierarchy(interface, method_signature)
822+ # if interface_method is not None:
823+ # return interface_method, found_class
824+
825+ # Do not return interface methods - only concrete implementations are included in call graph
826+ return None , ""
827+
785828 def __raw_call_graph_using_symbol_table (self , qualified_class_name : str , method_signature : str , cg = None ) -> list [JGraphEdgesST ]:
786829 """Generates a call graph using symbol table information.
787830
@@ -826,16 +869,17 @@ def __raw_call_graph_using_symbol_table(self, qualified_class_name: str, method_
826869 if call_site .receiver_type != "" :
827870 # call to any class
828871 if self .get_class (qualified_class_name = call_site .receiver_type ):
829- tmd = self .get_method (method_signature = callee_signature , qualified_class_name = call_site .receiver_type )
872+ # Check for method in the receiver type and its hierarchy
873+ tmd , found_class = self .__find_method_in_hierarchy (call_site .receiver_type , callee_signature )
830874 if tmd is not None :
831875 target_method_details = tmd
832- target_class = call_site . receiver_type
876+ target_class = found_class
833877 else :
834- # check if any method exists with the signature in the class even if the receiver type is blank
835- tmd = self .get_method ( method_signature = callee_signature , qualified_class_name = qualified_class_name )
878+ # check if any method exists with the signature in the class (including inherited) even if the receiver type is blank
879+ tmd , found_class = self .__find_method_in_hierarchy ( qualified_class_name , callee_signature )
836880 if tmd is not None :
837881 target_method_details = tmd
838- target_class = qualified_class_name
882+ target_class = found_class
839883
840884 if target_class != "" and target_method_details is not None :
841885 source : JMethodDetail
0 commit comments