Skip to content

Conversation

@codeflash-ai
Copy link
Contributor

@codeflash-ai codeflash-ai bot commented Oct 20, 2025

📄 7% (0.07x) speedup for FunctionCallFinder._is_target_function_call in codeflash/code_utils/code_extractor.py

⏱️ Runtime : 3.55 milliseconds 3.32 milliseconds (best of 86 runs)

📝 Explanation and details

Optimizations applied:

  • Used dict .get() lookups instead of retrieving and checking with in for imports, reducing key lookups.
  • Minimized string operations by only constructing the full path as needed and avoiding unnecessary calls.
  • In _get_call_name, uses list reversing via slicing for the attribute chain, which is faster than reversed and more py3-native.
  • No structural or behavioral changes—logic and returned results remain identical. Comment retention and annotations preserved.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 6591 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import ast

# imports
import pytest
from codeflash.code_utils.code_extractor import FunctionCallFinder


# Helper to create an ast.Call node from code
def get_call_node(code: str):
    """Extracts the first ast.Call node from a string of code."""
    tree = ast.parse(code)
    for node in ast.walk(tree):
        if isinstance(node, ast.Call):
            return node
    raise ValueError("No call node found in code.")

# Unit tests
class TestIsTargetFunctionCall:
    # --- Basic Test Cases ---

    def test_simple_direct_call(self):
        # Direct call to 'foo'
        finder = FunctionCallFinder("foo", "file.py", [])
        node = get_call_node("foo()")
        codeflash_output = finder._is_target_function_call(node) # 542ns -> 500ns (8.40% faster)

    def test_simple_direct_call_with_args(self):
        # Direct call with arguments
        finder = FunctionCallFinder("bar", "file.py", [])
        node = get_call_node("bar(1, 'x')")
        codeflash_output = finder._is_target_function_call(node) # 583ns -> 542ns (7.56% faster)

    def test_qualified_call(self):
        # Qualified call: module.func()
        finder = FunctionCallFinder("mymod.func", "file.py", [])
        node = get_call_node("mymod.func()")
        codeflash_output = finder._is_target_function_call(node) # 1.83μs -> 1.58μs (15.9% faster)

    def test_imported_base_name(self):
        # Imported base name with import mapping
        finder = FunctionCallFinder("othermod.baz", "file.py", [])
        finder.imports["baz"] = "othermod.baz"
        node = get_call_node("baz()")
        codeflash_output = finder._is_target_function_call(node) # 792ns -> 791ns (0.126% faster)

    def test_imported_qualified_name(self):
        # Imported qualified name with import mapping
        finder = FunctionCallFinder("pkg.subpkg.func", "file.py", [])
        finder.imports["pkg"] = "pkg"
        node = get_call_node("pkg.subpkg.func()")
        codeflash_output = finder._is_target_function_call(node) # 2.00μs -> 1.71μs (17.1% faster)

    def test_non_target_call(self):
        # Call to a different function
        finder = FunctionCallFinder("foo", "file.py", [])
        node = get_call_node("bar()")
        codeflash_output = finder._is_target_function_call(node) # 875ns -> 875ns (0.000% faster)

    def test_non_target_qualified_call(self):
        # Call to a different qualified function
        finder = FunctionCallFinder("foo.bar", "file.py", [])
        node = get_call_node("baz.qux()")
        codeflash_output = finder._is_target_function_call(node) # 2.17μs -> 1.96μs (10.7% faster)

    # --- Edge Test Cases ---

    def test_attribute_chain_call(self):
        # Long attribute chain: a.b.c.d()
        finder = FunctionCallFinder("a.b.c.d", "file.py", [])
        node = get_call_node("a.b.c.d()")
        codeflash_output = finder._is_target_function_call(node) # 2.17μs -> 1.79μs (20.9% faster)

    def test_imported_attribute_chain(self):
        # Imported attribute chain: x.y.z(), with import mapping
        finder = FunctionCallFinder("mod.y.z", "file.py", [])
        finder.imports["mod"] = "mod"
        node = get_call_node("mod.y.z()")
        codeflash_output = finder._is_target_function_call(node) # 2.00μs -> 1.67μs (20.0% faster)

    def test_partial_import_match(self):
        # Partial import match: import mapping only for base
        finder = FunctionCallFinder("foo.bar", "file.py", [])
        finder.imports["foo"] = "foo"
        node = get_call_node("foo.bar()")
        codeflash_output = finder._is_target_function_call(node) # 1.71μs -> 1.46μs (17.1% faster)

    def test_base_name_collision(self):
        # Base name collision: 'func' imported from two places
        finder = FunctionCallFinder("mod1.func", "file.py", [])
        finder.imports["func"] = "mod1.func"
        node = get_call_node("func()")
        codeflash_output = finder._is_target_function_call(node) # 792ns -> 709ns (11.7% faster)
        # Should not match if imported from a different module
        finder.imports["func"] = "mod2.func"
        codeflash_output = finder._is_target_function_call(node) # 667ns -> 625ns (6.72% faster)

    def test_call_with_no_name(self):
        # Call node with no name (lambda, etc.)
        finder = FunctionCallFinder("foo", "file.py", [])
        # e.g., (lambda x: x)(5)
        node = get_call_node("(lambda x: x)(5)")
        codeflash_output = finder._is_target_function_call(node) # 708ns -> 625ns (13.3% faster)

    def test_call_with_builtins(self):
        # Built-in function call
        finder = FunctionCallFinder("print", "file.py", [])
        node = get_call_node("print('hello')")
        codeflash_output = finder._is_target_function_call(node) # 583ns -> 541ns (7.76% faster)

    def test_call_with_imported_alias(self):
        # Imported alias: import foo as bar; bar()
        finder = FunctionCallFinder("foo", "file.py", [])
        finder.imports["bar"] = "foo"
        node = get_call_node("bar()")
        codeflash_output = finder._is_target_function_call(node) # 1.12μs -> 1.08μs (3.78% faster)

    def test_call_with_imported_alias_qualified(self):
        # Imported alias for qualified name: import pkg.mod as pm; pm.func()
        finder = FunctionCallFinder("pkg.mod.func", "file.py", [])
        finder.imports["pm"] = "pkg.mod"
        node = get_call_node("pm.func()")
        codeflash_output = finder._is_target_function_call(node) # 2.83μs -> 2.38μs (19.3% faster)

    def test_call_with_dotted_import_and_trailing_match(self):
        # Target is 'mod.func', import mapping is 'mod'->'pkg.mod'
        finder = FunctionCallFinder("mod.func", "file.py", [])
        finder.imports["mod"] = "pkg.mod"
        node = get_call_node("mod.func()")
        codeflash_output = finder._is_target_function_call(node) # 1.75μs -> 1.46μs (19.9% faster)

    def test_call_with_non_matching_import(self):
        # Import mapping does not match target
        finder = FunctionCallFinder("foo.bar", "file.py", [])
        finder.imports["foo"] = "baz.qux"
        node = get_call_node("foo.bar()")
        codeflash_output = finder._is_target_function_call(node) # 1.67μs -> 1.42μs (17.6% faster)

    # --- Large Scale Test Cases ---

    def test_many_calls_performance(self):
        # Test performance/scalability with many calls
        finder = FunctionCallFinder("foo", "file.py", [])
        code = "\n".join(["foo()" for _ in range(1000)])
        tree = ast.parse(code)
        call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
        # All should match
        for node in call_nodes:
            codeflash_output = finder._is_target_function_call(node) # 246μs -> 239μs (2.53% faster)

    def test_many_non_matching_calls(self):
        # Many calls that do NOT match
        finder = FunctionCallFinder("target", "file.py", [])
        code = "\n".join([f"func{i}()" for i in range(1000)])
        tree = ast.parse(code)
        call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
        # None should match
        for node in call_nodes:
            codeflash_output = finder._is_target_function_call(node) # 328μs -> 337μs (2.86% slower)

    def test_many_imported_calls(self):
        # Many calls with import mapping
        finder = FunctionCallFinder("mod.func", "file.py", [])
        finder.imports["mod"] = "mod"
        code = "\n".join([f"mod.func()" for _ in range(1000)])
        tree = ast.parse(code)
        call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
        for node in call_nodes:
            codeflash_output = finder._is_target_function_call(node) # 698μs -> 611μs (14.2% faster)

    def test_mixed_calls_large_scale(self):
        # Mixed matching and non-matching calls
        finder = FunctionCallFinder("foo", "file.py", [])
        code = "\n".join(["foo()" if i % 2 == 0 else "bar()" for i in range(1000)])
        tree = ast.parse(code)
        call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
        for i, node in enumerate(call_nodes):
            expected = (i % 2 == 0)
            codeflash_output = finder._is_target_function_call(node) # 309μs -> 314μs (1.53% slower)

    def test_large_attribute_chain_calls(self):
        # 1000 calls to a long attribute chain
        finder = FunctionCallFinder("a.b.c.d.e", "file.py", [])
        code = "\n".join(["a.b.c.d.e()" for _ in range(1000)])
        tree = ast.parse(code)
        call_nodes = [node for node in ast.walk(tree) if isinstance(node, ast.Call)]
        for node in call_nodes:
            codeflash_output = finder._is_target_function_call(node) # 1.01ms -> 915μs (10.7% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import ast

# imports
import pytest
from codeflash.code_utils.code_extractor import FunctionCallFinder


# Helper to build ast.Call nodes for test cases
def make_call_node(expr: str):
    """Parse a single function call expression and return the ast.Call node."""
    node = ast.parse(expr, mode="exec")
    # Should be ast.Module(body=[ast.Expr(value=ast.Call(...))])
    call_node = node.body[0].value
    return call_node

# -------------------
# Basic Test Cases
# -------------------

def test_direct_function_call_by_name():
    # Test direct call to the target function by its base name
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 666ns -> 625ns (6.56% faster)

def test_non_target_function_call():
    # Test call to a different function
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("bar()")
    codeflash_output = finder._is_target_function_call(call_node) # 916ns -> 917ns (0.109% slower)

def test_direct_qualified_function_call():
    # Test call to the target function using a qualified name
    finder = FunctionCallFinder(target_function_name="module.foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("module.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 1.79μs -> 1.50μs (19.5% faster)

def test_qualified_call_non_target():
    # Test call to a different qualified function
    finder = FunctionCallFinder(target_function_name="module.foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("module.bar()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.17μs -> 1.92μs (13.0% faster)

def test_base_name_with_imports_match():
    # Test call to base name, where base name is imported as target
    finder = FunctionCallFinder(target_function_name="pkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"foo": "pkg.mod.foo"}
    call_node = make_call_node("foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 834ns -> 833ns (0.120% faster)

def test_base_name_with_imports_no_match():
    # Test call to base name, where base name is imported as something else
    finder = FunctionCallFinder(target_function_name="pkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"foo": "pkg.mod.bar"}
    call_node = make_call_node("foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 1.08μs -> 1.04μs (4.13% faster)

def test_qualified_import_alias_match():
    # Test call to alias.qualified_name() where alias is imported as target module
    finder = FunctionCallFinder(target_function_name="pkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"alias": "pkg.mod"}
    call_node = make_call_node("alias.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.79μs -> 2.25μs (24.1% faster)

def test_qualified_import_alias_no_match():
    # Test call to alias.bar() where alias is imported as target module, but function is not the target
    finder = FunctionCallFinder(target_function_name="pkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"alias": "pkg.mod"}
    call_node = make_call_node("alias.bar()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.88μs -> 2.50μs (15.0% faster)

# -------------------
# Edge Test Cases
# -------------------

def test_call_with_no_name():
    # Test call where function is a lambda or a call result, not a Name or Attribute
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    # (lambda x: x)()
    node = ast.parse("(lambda x: x)()", mode="exec").body[0].value
    codeflash_output = finder._is_target_function_call(node) # 708ns -> 708ns (0.000% faster)

def test_call_with_chained_attributes():
    # Test call to a deeply chained attribute, e.g., pkg.mod.submod.foo()
    finder = FunctionCallFinder(target_function_name="pkg.mod.submod.foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("pkg.mod.submod.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.08μs -> 1.75μs (19.1% faster)

def test_imported_chained_attribute():
    # Test call to alias.submod.foo() where alias is imported as pkg.mod
    finder = FunctionCallFinder(target_function_name="pkg.mod.submod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"alias": "pkg.mod"}
    call_node = make_call_node("alias.submod.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 3.04μs -> 2.50μs (21.7% faster)

def test_partial_qualified_name_no_match():
    # Test call to pkg.foo() when target is pkg.mod.foo
    finder = FunctionCallFinder(target_function_name="pkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("pkg.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.08μs -> 1.83μs (13.6% faster)

def test_imported_base_name_suffix_match():
    # Test imported base name where import path ends with target function name
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"foo": "pkg.mod.foo"}
    call_node = make_call_node("foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 583ns -> 584ns (0.171% slower)

def test_imported_base_name_suffix_no_match():
    # Test imported base name where import path does not end with target function name
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"foo": "pkg.mod.bar"}
    call_node = make_call_node("foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 583ns -> 583ns (0.000% faster)

def test_call_to_method_on_object():
    # Test call to method on object (not imported), should not match
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("obj.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.12μs -> 1.79μs (18.6% faster)

def test_imported_object_method_with_matching_name():
    # Test call to imported object with method name matching, but not imported as target
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"obj": "pkg.mod"}
    call_node = make_call_node("obj.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 3.00μs -> 2.46μs (22.1% faster)

def test_imported_object_method_with_qualified_target():
    # Test call to imported object with method name matching, imported as target
    finder = FunctionCallFinder(target_function_name="pkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"obj": "pkg.mod"}
    call_node = make_call_node("obj.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.58μs -> 2.21μs (16.9% faster)

def test_call_with_non_string_func():
    # Test call where func is a constant, e.g., 123()
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    node = ast.parse("123()", mode="exec").body[0].value
    codeflash_output = finder._is_target_function_call(node) # 709ns -> 625ns (13.4% faster)

def test_call_with_nested_attributes():
    # Test call to deeply nested attributes, e.g., a.b.c.d.foo()
    finder = FunctionCallFinder(target_function_name="a.b.c.d.foo", target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node("a.b.c.d.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.21μs -> 1.79μs (23.3% faster)

def test_imported_nested_attributes():
    # Test alias.b.c.d.foo() where alias is imported as a
    finder = FunctionCallFinder(target_function_name="a.b.c.d.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"alias": "a"}
    call_node = make_call_node("alias.b.c.d.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 3.29μs -> 2.79μs (17.9% faster)

def test_imported_nested_attributes_no_match():
    # Test alias.b.c.d.bar() where alias is imported as a
    finder = FunctionCallFinder(target_function_name="a.b.c.d.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"alias": "a"}
    call_node = make_call_node("alias.b.c.d.bar()")
    codeflash_output = finder._is_target_function_call(call_node) # 3.33μs -> 2.92μs (14.3% faster)

# -------------------
# Large Scale Test Cases
# -------------------

def test_many_imports_and_calls():
    # Test with many imports and many calls, only one matches
    finder = FunctionCallFinder(target_function_name="pkg.target", target_filepath="somefile.py", source_lines=[])
    # Simulate 500 imports, only one matches
    for i in range(500):
        finder.imports[f"mod{i}"] = f"pkg.mod{i}"
    finder.imports["target"] = "pkg.target"
    # 500 calls to non-targets, one to the target
    for i in range(500):
        call_node = make_call_node(f"mod{i}.foo()")
        codeflash_output = finder._is_target_function_call(call_node) # 578μs -> 511μs (13.0% faster)
    call_node = make_call_node("target()")
    codeflash_output = finder._is_target_function_call(call_node) # 667ns -> 541ns (23.3% faster)

def test_large_chained_attribute_depth():
    # Test with a long chain of attributes
    chain = ".".join([f"a{i}" for i in range(20)]) + ".foo"
    finder = FunctionCallFinder(target_function_name=chain, target_filepath="somefile.py", source_lines=[])
    call_node = make_call_node(chain + "()")
    codeflash_output = finder._is_target_function_call(call_node) # 3.83μs -> 3.33μs (15.0% faster)

def test_large_number_of_calls_with_one_match():
    # Test 999 calls, only the last matches
    finder = FunctionCallFinder(target_function_name="foo", target_filepath="somefile.py", source_lines=[])
    for i in range(998):
        call_node = make_call_node(f"bar{i}()")
        codeflash_output = finder._is_target_function_call(call_node) # 302μs -> 321μs (5.98% slower)
    call_node = make_call_node("foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 291ns -> 292ns (0.342% slower)

def test_large_import_alias_chain():
    # Test a long chain of imports and a matching qualified call
    finder = FunctionCallFinder(target_function_name="pkg.subpkg.mod.foo", target_filepath="somefile.py", source_lines=[])
    finder.imports = {"alias": "pkg.subpkg"}
    call_node = make_call_node("alias.mod.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 3.12μs -> 2.58μs (20.9% faster)

def test_large_imports_with_similar_names():
    # Test many imports with similar names, only one matches
    finder = FunctionCallFinder(target_function_name="pkg.special.foo", target_filepath="somefile.py", source_lines=[])
    for i in range(500):
        finder.imports[f"alias{i}"] = f"pkg.mod{i}"
    finder.imports["special"] = "pkg.special"
    call_node = make_call_node("special.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 2.92μs -> 2.33μs (25.0% faster)
    # Check that a similar but non-matching import does not match
    call_node = make_call_node("alias123.foo()")
    codeflash_output = finder._is_target_function_call(call_node) # 1.96μs -> 1.58μs (23.7% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-FunctionCallFinder._is_target_function_call-mgzp3u9r and push.

Codeflash

**Optimizations applied:**
- Used dict `.get()` lookups instead of retrieving and checking with `in` for imports, reducing key lookups.
- Minimized string operations by only constructing the full path as needed and avoiding unnecessary calls.
- In `_get_call_name`, uses list reversing via slicing for the attribute chain, which is faster than `reversed` and more py3-native.
- No structural or behavioral changes—logic and returned results remain identical. Comment retention and annotations preserved.
@codeflash-ai codeflash-ai bot requested a review from KRRT7 October 20, 2025 22:17
@codeflash-ai codeflash-ai bot added the ⚡️ codeflash Optimization PR opened by Codeflash AI label Oct 20, 2025
@KRRT7 KRRT7 closed this Oct 20, 2025
@codeflash-ai codeflash-ai bot deleted the codeflash/optimize-FunctionCallFinder._is_target_function_call-mgzp3u9r branch October 20, 2025 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant