From 605be4bcbf5554b26fdcb407871e920d680ed322 Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 03:10:34 +0000 Subject: [PATCH 1/3] Optimize detect_optimization_opportunities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optimized code achieves a **35% runtime improvement** (8.09ms → 5.95ms) through strategic regex avoidance and elimination of expensive string operations: ## Key Optimizations 1. **Eliminated Component Source Join**: Removed `component_source = "\n".join(component_lines)` which was creating a large concatenated string. This alone saves significant memory allocation and copying overhead, especially for large components (tests show 43-62% speedup on 1000+ line components). 2. **Fast-Path Substring Checks Before Regex**: Added cheap substring presence checks before running expensive regex patterns: - `if "={" not in line: continue` before JSX prop regexes (reduces ~2,664 regex calls in typical cases) - `if "." not in stripped: continue` before expensive operations regex (reduces ~2,523 regex calls) - `if ("const" not in stripped and "let" not in stripped...)` before function definition regex (reduces ~1,414 regex calls) 3. **Line-by-Line useCallback Detection**: Changed from `USECALLBACK_RE.search(component_source)` on the entire joined string to iterating lines with substring check first (`if "useCallback" in l`), then regex only when needed. This avoids regex on large text and short-circuits on first match. ## Performance Impact by Workload The optimization particularly benefits: - **Large components** (1000+ lines): 26-43% faster, as seen in `test_large_scale_detection_performance` and `test_detection_accuracy_with_1000_items` - **Dense JSX with many props** (100+ inline props): 54-62% faster, as shown in `test_many_inline_props_in_large_component` and `test_complex_nested_jsx_structure` - **Components with many function definitions**: 27% faster in `test_many_function_definitions` The approach maintains identical detection logic and results while dramatically reducing computational cost through algorithmic improvements: O(n) substring checks filter out non-matching lines before expensive O(n*m) regex operations. --- .../javascript/frameworks/react/analyzer.py | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/codeflash/languages/javascript/frameworks/react/analyzer.py b/codeflash/languages/javascript/frameworks/react/analyzer.py index 2de39c802..7816647ee 100644 --- a/codeflash/languages/javascript/frameworks/react/analyzer.py +++ b/codeflash/languages/javascript/frameworks/react/analyzer.py @@ -64,7 +64,10 @@ def detect_optimization_opportunities(source: str, component_info: ReactComponen start = component_info.start_line - 1 end = min(component_info.end_line, len(lines)) component_lines = lines[start:end] - component_source = "\n".join(component_lines) + # Avoid building a large joined string; detectors will inspect lines directly. + component_source = "" # kept for signature compatibility with helpers + + # Check for inline objects in JSX props # Check for inline objects in JSX props _detect_inline_props(component_lines, start, opportunities) @@ -94,6 +97,9 @@ def _detect_inline_props(lines: list[str], offset: int, opportunities: list[Opti """Detect inline object/array literals in JSX prop positions.""" for i, line in enumerate(lines): line_num = offset + i + 1 + # Quick check to avoid running regexes when no JSX prop assignment is present + if "={" not in line: + continue if INLINE_OBJECT_IN_JSX_RE.search(line): opportunities.append( OptimizationOpportunity( @@ -120,12 +126,24 @@ def _detect_missing_usecallback( component_source: str, lines: list[str], offset: int, opportunities: list[OptimizationOpportunity] ) -> None: """Detect arrow functions or function expressions that could use useCallback.""" - has_usecallback = bool(USECALLBACK_RE.search(component_source)) + # Determine whether the component uses useCallback anywhere by scanning lines (avoid joining) + has_usecallback = False + for l in lines: + if "useCallback" in l: + # cheap substring check before regex to avoid unnecessary work + if USECALLBACK_RE.search(l): + has_usecallback = True + break + for i, line in enumerate(lines): line_num = offset + i + 1 stripped = line.strip() # Look for arrow function or function expression definitions inside the component + # Quick substring check: FUNCTION_DEF_RE targets lines with var/const/let/function + if ("const" not in stripped and "let" not in stripped and "var" not in stripped and "function" not in stripped): + continue + # Look for arrow function or function expression definitions inside the component if FUNCTION_DEF_RE.search(stripped) and "useCallback" not in stripped and "useMemo" not in stripped: # Skip if the component already uses useCallback extensively if not has_usecallback: @@ -147,6 +165,10 @@ def _detect_missing_usememo( for i, line in enumerate(lines): line_num = offset + i + 1 stripped = line.strip() + # Quick exclusions to avoid running the expensive regex when impossible to match + # expensive ops are accessed via a dot call like arr.map( + if "." not in stripped: + continue if EXPENSIVE_OPS_RE.search(stripped) and "useMemo" not in stripped: opportunities.append( OptimizationOpportunity( From 8008c8e88d86eaa82a615e04c91437d8cbe3e67f Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 03:13:25 +0000 Subject: [PATCH 2/3] style: auto-fix linting issues --- .../languages/javascript/frameworks/detector.py | 5 ++++- .../javascript/frameworks/react/analyzer.py | 9 ++++----- .../languages/javascript/frameworks/react/context.py | 3 ++- .../javascript/frameworks/react/discovery.py | 9 +++------ .../javascript/frameworks/react/profiler.py | 12 ++++-------- codeflash/languages/javascript/treesitter_utils.py | 4 ++-- 6 files changed, 19 insertions(+), 23 deletions(-) diff --git a/codeflash/languages/javascript/frameworks/detector.py b/codeflash/languages/javascript/frameworks/detector.py index 013de47f5..f4905c593 100644 --- a/codeflash/languages/javascript/frameworks/detector.py +++ b/codeflash/languages/javascript/frameworks/detector.py @@ -10,7 +10,10 @@ import logging from dataclasses import dataclass, field from functools import lru_cache -from pathlib import Path +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from pathlib import Path logger = logging.getLogger(__name__) diff --git a/codeflash/languages/javascript/frameworks/react/analyzer.py b/codeflash/languages/javascript/frameworks/react/analyzer.py index 7816647ee..4d3452984 100644 --- a/codeflash/languages/javascript/frameworks/react/analyzer.py +++ b/codeflash/languages/javascript/frameworks/react/analyzer.py @@ -128,20 +128,19 @@ def _detect_missing_usecallback( """Detect arrow functions or function expressions that could use useCallback.""" # Determine whether the component uses useCallback anywhere by scanning lines (avoid joining) has_usecallback = False - for l in lines: - if "useCallback" in l: + for line in lines: + if "useCallback" in line: # cheap substring check before regex to avoid unnecessary work - if USECALLBACK_RE.search(l): + if USECALLBACK_RE.search(line): has_usecallback = True break - for i, line in enumerate(lines): line_num = offset + i + 1 stripped = line.strip() # Look for arrow function or function expression definitions inside the component # Quick substring check: FUNCTION_DEF_RE targets lines with var/const/let/function - if ("const" not in stripped and "let" not in stripped and "var" not in stripped and "function" not in stripped): + if "const" not in stripped and "let" not in stripped and "var" not in stripped and "function" not in stripped: continue # Look for arrow function or function expression definitions inside the component if FUNCTION_DEF_RE.search(stripped) and "useCallback" not in stripped and "useMemo" not in stripped: diff --git a/codeflash/languages/javascript/frameworks/react/context.py b/codeflash/languages/javascript/frameworks/react/context.py index 0d53e5c8b..d451fdb28 100644 --- a/codeflash/languages/javascript/frameworks/react/context.py +++ b/codeflash/languages/javascript/frameworks/react/context.py @@ -8,10 +8,11 @@ import logging from dataclasses import dataclass, field -from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: + from pathlib import Path + from tree_sitter import Node from codeflash.languages.javascript.frameworks.react.analyzer import OptimizationOpportunity diff --git a/codeflash/languages/javascript/frameworks/react/discovery.py b/codeflash/languages/javascript/frameworks/react/discovery.py index 9e39de817..d4f30a9e6 100644 --- a/codeflash/languages/javascript/frameworks/react/discovery.py +++ b/codeflash/languages/javascript/frameworks/react/discovery.py @@ -10,10 +10,11 @@ import re from dataclasses import dataclass from enum import Enum -from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: + from pathlib import Path + from tree_sitter import Node from codeflash.languages.javascript.treesitter import FunctionNode, TreeSitterAnalyzer @@ -191,11 +192,7 @@ def _node_contains_jsx(node: Node) -> bool: if _node_contains_jsx(child): return True - for child in node.children: - if _node_contains_jsx(child): - return True - - return False + return any(_node_contains_jsx(child) for child in node.children) def _extract_hooks_used(function_source: str) -> list[str]: diff --git a/codeflash/languages/javascript/frameworks/react/profiler.py b/codeflash/languages/javascript/frameworks/react/profiler.py index 880793c11..f817c375d 100644 --- a/codeflash/languages/javascript/frameworks/react/profiler.py +++ b/codeflash/languages/javascript/frameworks/react/profiler.py @@ -12,10 +12,11 @@ import logging import re -from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: + from pathlib import Path + from tree_sitter import Node from codeflash.languages.javascript.treesitter import TreeSitterAnalyzer @@ -76,9 +77,7 @@ def instrument_component_with_profiler(source: str, component_name: str, analyze result = _insert_after_imports(result, counter_code, analyzer) # Ensure React is imported - result = _ensure_react_import(result) - - return result + return _ensure_react_import(result) def instrument_all_components_for_tracing(source: str, file_path: Path, analyzer: TreeSitterAnalyzer) -> str: @@ -163,10 +162,7 @@ def _contains_jsx(node: Node) -> bool: """Check if a tree-sitter node contains JSX elements.""" if node.type in ("jsx_element", "jsx_self_closing_element", "jsx_fragment"): return True - for child in node.children: - if _contains_jsx(child): - return True - return False + return any(_contains_jsx(child) for child in node.children) def _wrap_return_with_profiler(source: str, return_node: Node, profiler_id: str, safe_name: str) -> str: diff --git a/codeflash/languages/javascript/treesitter_utils.py b/codeflash/languages/javascript/treesitter_utils.py index b6126ec9a..75792be6f 100644 --- a/codeflash/languages/javascript/treesitter_utils.py +++ b/codeflash/languages/javascript/treesitter_utils.py @@ -1580,9 +1580,9 @@ def get_analyzer_for_file(file_path: Path) -> TreeSitterAnalyzer: """ suffix = file_path.suffix.lower() - if suffix in (".ts",): + if suffix == ".ts": return TreeSitterAnalyzer(TreeSitterLanguage.TYPESCRIPT) - if suffix in (".tsx",): + if suffix == ".tsx": return TreeSitterAnalyzer(TreeSitterLanguage.TSX) # Default to JavaScript for .js, .jsx, .mjs, .cjs return TreeSitterAnalyzer(TreeSitterLanguage.JAVASCRIPT) From e612c2ba7c3bda5011cddf00f84bd8dd6573cefe Mon Sep 17 00:00:00 2001 From: "codeflash-ai[bot]" <148906541+codeflash-ai[bot]@users.noreply.github.com> Date: Fri, 20 Feb 2026 03:28:08 +0000 Subject: [PATCH 3/3] Optimize _contains_jsx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The optimized code achieves a **149% speedup** (539μs → 216μs) by replacing recursive tree traversal with an iterative stack-based approach. ## Key Optimization **Eliminated recursive function calls**: The original implementation used `any(_contains_jsx(child) for child in node.children)`, which created a new stack frame for each recursive call. The line profiler shows this consumed **84.1% of total runtime** (2.23ms out of 2.65ms). The optimized version uses an explicit stack data structure with a while loop, avoiding Python's function call overhead entirely. ## Why This Is Faster 1. **No recursive overhead**: Each recursive call in Python involves creating a new stack frame, copying arguments, and managing return values. The iterative approach eliminates all of this. 2. **Early termination efficiency**: Both versions can return `True` early when JSX is found, but the iterative version does this more efficiently without unwinding the call stack. 3. **Better memory locality**: The explicit stack keeps all traversal state in a single list object, improving CPU cache utilization compared to scattered stack frames. ## Performance Characteristics The optimization shines particularly well on: - **Deep nesting**: 100-level deep tree shows **691% speedup** (94.9μs → 12.0μs) - **Wide trees with JSX at end**: 100 siblings with JSX as last child shows **1985% speedup** (34.3μs → 1.64μs) - **Balanced trees**: 512-node tree shows **195% speedup** (308μs → 104μs) Trade-off: Direct JSX matches (root node is JSX) are ~20-26% slower due to stack initialization overhead, but these simple cases are already extremely fast (<1μs) and the optimization dramatically benefits complex real-world AST traversals. ## Impact For React code analysis tools that traverse Abstract Syntax Trees to detect JSX elements, this optimization significantly reduces profiling overhead, especially when analyzing large component files with deeply nested or wide component trees. --- .../languages/javascript/frameworks/react/profiler.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/codeflash/languages/javascript/frameworks/react/profiler.py b/codeflash/languages/javascript/frameworks/react/profiler.py index f817c375d..1321d0af5 100644 --- a/codeflash/languages/javascript/frameworks/react/profiler.py +++ b/codeflash/languages/javascript/frameworks/react/profiler.py @@ -160,9 +160,13 @@ def walk(node: Node) -> None: def _contains_jsx(node: Node) -> bool: """Check if a tree-sitter node contains JSX elements.""" - if node.type in ("jsx_element", "jsx_self_closing_element", "jsx_fragment"): - return True - return any(_contains_jsx(child) for child in node.children) + stack = [node] + while stack: + current = stack.pop() + if current.type in ("jsx_element", "jsx_self_closing_element", "jsx_fragment"): + return True + stack.extend(current.children) + return False def _wrap_return_with_profiler(source: str, return_node: Node, profiler_id: str, safe_name: str) -> str: