Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 32 additions & 31 deletions codeflash/languages/javascript/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -1301,38 +1301,39 @@ def _find_and_extract_body(self, source: str, function_name: str, analyzer: Tree

def find_function_node(node: Any, target_name: str) -> Any:
"""Recursively find a function/method with the given name."""
# Check method definitions
if node.type == "method_definition":
name_node = node.child_by_field_name("name")
if name_node:
name = source_bytes[name_node.start_byte : name_node.end_byte].decode("utf8")
if name == target_name:
return node

# Check function declarations
if node.type in ("function_declaration", "function"):
name_node = node.child_by_field_name("name")
if name_node:
name = source_bytes[name_node.start_byte : name_node.end_byte].decode("utf8")
if name == target_name:
return node

# Check arrow functions assigned to variables
if node.type == "lexical_declaration":
for child in node.children:
if child.type == "variable_declarator":
name_node = child.child_by_field_name("name")
value_node = child.child_by_field_name("value")
if name_node and value_node and value_node.type == "arrow_function":
name = source_bytes[name_node.start_byte : name_node.end_byte].decode("utf8")
if name == target_name:
return value_node
target_bytes = target_name.encode("utf8")
stack = [node]
while stack:
n = stack.pop()

# Check method definitions
if n.type == "method_definition":
name_node = n.child_by_field_name("name")
if name_node:
if source_bytes[name_node.start_byte : name_node.end_byte] == target_bytes:
return n

# Check function declarations
if n.type in ("function_declaration", "function"):
name_node = n.child_by_field_name("name")
if name_node:
if source_bytes[name_node.start_byte : name_node.end_byte] == target_bytes:
return n

# Check arrow functions assigned to variables
if n.type == "lexical_declaration":
for child in n.children:
if child.type == "variable_declarator":
name_node = child.child_by_field_name("name")
value_node = child.child_by_field_name("value")
if name_node and value_node and value_node.type == "arrow_function":
if source_bytes[name_node.start_byte : name_node.end_byte] == target_bytes:
return value_node

# Recurse into children (stack push)
for child in n.children:
stack.append(child)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The conversion from recursion to iterative stack.pop() + stack.extend(n.children) changes traversal order from left-to-right DFS to right-to-left DFS. The original recursive version would find the first (leftmost) matching function in source order, while this iterative version will find a different one if there are duplicate function names.

In practice this is unlikely to cause issues since function names are typically unique within a file, but worth being aware of. If strict left-to-right order is needed, use stack.pop(0) (BFS) or reverse children before pushing: stack.extend(reversed(n.children)).

Comment on lines +1334 to +1335
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ruff PERF402: This for child in n.children: stack.append(child) loop should use stack.extend(n.children) instead.

Suggested change
for child in n.children:
stack.append(child)
stack.extend(n.children)


# Recurse into children
for child in node.children:
result = find_function_node(child, target_name)
if result:
return result

return None

Expand Down
Loading