@@ -62,6 +62,28 @@ def __init__(self, node: TSNode, file_node_id: NodeId, G: CodebaseGraph, parent:
62
62
args = [Argument (x , i , self ) for i , x in enumerate (arg_list_node .named_children ) if x .type != "comment" ]
63
63
self ._arg_list = Collection (arg_list_node , self .file_node_id , self .G , self , children = args )
64
64
65
+ def __repr__ (self ) -> str :
66
+ """Custom string representation showing the function call chain structure.
67
+
68
+ Format: FunctionCall(name=current, pred=pred_name, succ=succ_name, base=base_name)
69
+
70
+ It will only print out predecessor, successor, and base that are of type FunctionCall. If it's a property, it will not be logged
71
+ """
72
+ # Helper to safely get name
73
+
74
+ # Get names for each part
75
+ parts = [f"name='{ self .name } '" ]
76
+
77
+ if self .predecessor and isinstance (self .predecessor , FunctionCall ):
78
+ parts .append (f"predecessor=FunctionCall(name='{ self .predecessor .name } ')" )
79
+
80
+ if self .successor and isinstance (self .successor , FunctionCall ):
81
+ parts .append (f"successor=FunctionCall(name='{ self .successor .name } ')" )
82
+
83
+ parts .append (f"filepath='{ self .file .filepath } '" )
84
+
85
+ return f"FunctionCall({ ', ' .join (parts )} )"
86
+
65
87
@classmethod
66
88
def from_usage (cls , node : Editable [Parent ], parent : Parent | None = None ) -> Self | None :
67
89
"""Creates a FunctionCall object from an Editable instance that represents a function call.
@@ -210,9 +232,36 @@ def predecessor(self) -> FunctionCall[Parent] | None:
210
232
or if the predecessor is not a function call.
211
233
"""
212
234
# Recursively travel down the tree to find the previous function call (child nodes are previous calls)
213
- return self .call_chain [- 2 ] if len (self .call_chain ) > 1 else None
235
+ name = self .get_name ()
236
+ while name :
237
+ if isinstance (name , FunctionCall ):
238
+ return name
239
+ elif isinstance (name , ChainedAttribute ):
240
+ name = name .object
241
+ else :
242
+ break
243
+ return None
244
+
245
+ @property
246
+ @reader
247
+ def successor (self ) -> FunctionCall [Parent ] | Name | None :
248
+ """Returns the next function call in a function call chain.
249
+
250
+ Returns the next function call in a function call chain. This method is useful for traversing function call chains
251
+ to analyze or modify sequences of chained function calls.
214
252
215
- # TODO: also define a successor?
253
+ Args:
254
+ None
255
+
256
+ Returns:
257
+ FunctionCall[Parent] | Name | None: The next function call in the chain, or None if there is no successor
258
+ or if the successor is not a function call.
259
+ """
260
+ # this will avoid parent function calls in tree-sitter that are NOT part of the chained calls
261
+ if not isinstance (self .parent , ChainedAttribute ):
262
+ return None
263
+
264
+ return self .parent_of_type (FunctionCall )
216
265
217
266
@property
218
267
@noapidoc
@@ -601,24 +650,35 @@ def register_api_call(self, url: str):
601
650
@property
602
651
@reader
603
652
def call_chain (self ) -> list [FunctionCall ]:
604
- """Returns a list of all function calls in this function call chain, including this call. Does not include calls made after this one."""
653
+ """Returns a list of all function calls in this function call chain, including this call. Does not include calls made after this one."""
605
654
ret = []
606
- name = self .get_name ()
607
- while name :
608
- if isinstance (name , FunctionCall ):
609
- ret .extend (name .call_chain )
610
- break
611
- elif isinstance (name , ChainedAttribute ):
612
- name = name .object
613
- else :
614
- break
655
+
656
+ # backward traversal
657
+ curr = self
658
+ pred = curr .predecessor
659
+ while pred is not None and isinstance (pred , FunctionCall ):
660
+ ret .insert (0 , pred )
661
+ pred = pred .predecessor
662
+
615
663
ret .append (self )
664
+
665
+ # forward traversal
666
+ curr = self
667
+ succ = curr .successor
668
+ while succ is not None and isinstance (succ , FunctionCall ):
669
+ ret .append (succ )
670
+ succ = succ .successor
671
+
616
672
return ret
617
673
618
674
@property
619
675
@reader
620
676
def base (self ) -> Editable | None :
621
- """Returns the base object of this function call chain."""
677
+ """Returns the base object of this function call chain.
678
+
679
+ Args:
680
+ Editable | None: The base object of this function call chain.
681
+ """
622
682
name = self .get_name ()
623
683
while isinstance (name , ChainedAttribute ):
624
684
if isinstance (name .object , FunctionCall ):
0 commit comments