Skip to content

Commit 4a54486

Browse files
misrasaurabh1claude
andcommitted
feat: Integrate JS/TS find_references into optimization flow
- Update get_opt_review_metrics to use ReferenceFinder for JavaScript/TypeScript - Format function references as markdown code blocks (matching Python format) - Extract calling function source code for context - Add 11 new edge case tests covering: - Same function name in different files - Circular imports - Nested directory structures - Unicode in code - Dynamic imports - Type-only imports - JSX component usage - Higher-order functions (debounce/throttle) - Export with 'as' keyword - Very large files - Syntax error handling Total: 46 tests for find_references (all passing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 50c09f4 commit 4a54486

2 files changed

Lines changed: 510 additions & 9 deletions

File tree

codeflash/code_utils/code_extractor.py

Lines changed: 155 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,10 +1563,24 @@ def is_numerical_code(code_string: str, function_name: str | None = None) -> boo
15631563
def get_opt_review_metrics(
15641564
source_code: str, file_path: Path, qualified_name: str, project_root: Path, tests_root: Path, language: Language
15651565
) -> str:
1566-
if language != Language.PYTHON:
1567-
# TODO: {Claude} handle function refrences for other languages
1568-
return ""
15691566
start_time = time.perf_counter()
1567+
1568+
if language == Language.PYTHON:
1569+
calling_fns_details = _get_python_references(source_code, file_path, qualified_name, project_root, tests_root)
1570+
elif language in (Language.JAVASCRIPT, Language.TYPESCRIPT):
1571+
calling_fns_details = _get_javascript_references(file_path, qualified_name, project_root, tests_root)
1572+
else:
1573+
calling_fns_details = ""
1574+
1575+
end_time = time.perf_counter()
1576+
logger.debug(f"Got function references in {end_time - start_time:.2f} seconds")
1577+
return calling_fns_details
1578+
1579+
1580+
def _get_python_references(
1581+
source_code: str, file_path: Path, qualified_name: str, project_root: Path, tests_root: Path
1582+
) -> str:
1583+
"""Get function references for Python code using jedi."""
15701584
try:
15711585
qualified_name_split = qualified_name.rsplit(".", maxsplit=1)
15721586
if len(qualified_name_split) == 1:
@@ -1576,10 +1590,142 @@ def get_opt_review_metrics(
15761590
matches = get_fn_references_jedi(
15771591
source_code, file_path, project_root, target_function, target_class
15781592
) # jedi is not perfect, it doesn't capture aliased references
1579-
calling_fns_details = find_occurances(qualified_name, str(file_path), matches, project_root, tests_root)
1593+
return find_occurances(qualified_name, str(file_path), matches, project_root, tests_root)
15801594
except Exception as e:
1581-
calling_fns_details = ""
1582-
logger.debug(f"Investigate {e}")
1583-
end_time = time.perf_counter()
1584-
logger.debug(f"Got function references in {end_time - start_time:.2f} seconds")
1585-
return calling_fns_details
1595+
logger.debug(f"Error getting Python references: {e}")
1596+
return ""
1597+
1598+
1599+
def _get_javascript_references(
1600+
file_path: Path, qualified_name: str, project_root: Path, tests_root: Path
1601+
) -> str:
1602+
"""Get function references for JavaScript/TypeScript code using tree-sitter.
1603+
1604+
This function finds all call sites of a JavaScript/TypeScript function
1605+
across the codebase and formats them for the optimizer's context.
1606+
"""
1607+
try:
1608+
from codeflash.languages.javascript.find_references import ReferenceFinder
1609+
from codeflash.languages.treesitter_utils import get_analyzer_for_file
1610+
1611+
# Extract function name from qualified name
1612+
# Qualified name could be "functionName" or "ClassName.methodName"
1613+
function_name = qualified_name.rsplit(".", maxsplit=1)[-1]
1614+
1615+
finder = ReferenceFinder(project_root)
1616+
references = finder.find_references(function_name, file_path, max_files=500)
1617+
1618+
if not references:
1619+
return ""
1620+
1621+
# Format references similar to Python format
1622+
fn_call_context = ""
1623+
context_len = 0
1624+
1625+
# Group references by file
1626+
refs_by_file: dict[Path, list] = {}
1627+
for ref in references:
1628+
# Exclude test files
1629+
try:
1630+
if ref.file_path.relative_to(tests_root):
1631+
continue
1632+
except ValueError:
1633+
pass
1634+
1635+
# Exclude the source file's definition
1636+
if ref.file_path == file_path and ref.reference_type == "import":
1637+
continue
1638+
1639+
if ref.file_path not in refs_by_file:
1640+
refs_by_file[ref.file_path] = []
1641+
refs_by_file[ref.file_path].append(ref)
1642+
1643+
for ref_file, file_refs in refs_by_file.items():
1644+
if context_len > MAX_CONTEXT_LEN_REVIEW:
1645+
break
1646+
1647+
try:
1648+
path_relative = ref_file.relative_to(project_root)
1649+
except ValueError:
1650+
continue
1651+
1652+
# Get the file extension for syntax highlighting
1653+
ext = ref_file.suffix.lstrip(".")
1654+
lang = "typescript" if ext in ("ts", "tsx") else "javascript"
1655+
1656+
# Read the file to extract calling function context
1657+
try:
1658+
file_content = ref_file.read_text(encoding="utf-8")
1659+
lines = file_content.splitlines()
1660+
except Exception:
1661+
continue
1662+
1663+
# Get unique caller functions from this file
1664+
callers_seen = set()
1665+
caller_contexts = []
1666+
1667+
for ref in file_refs:
1668+
caller = ref.caller_function or "<module>"
1669+
if caller in callers_seen:
1670+
continue
1671+
callers_seen.add(caller)
1672+
1673+
# Extract context around the reference (the calling function or surrounding lines)
1674+
if ref.caller_function:
1675+
# Try to extract the full calling function
1676+
func_code = _extract_calling_function_js(file_content, ref.caller_function, ref.line)
1677+
if func_code:
1678+
caller_contexts.append(func_code)
1679+
context_len += len(func_code)
1680+
else:
1681+
# Module-level call - just show a few lines of context
1682+
start_line = max(0, ref.line - 3)
1683+
end_line = min(len(lines), ref.line + 2)
1684+
context_code = "\n".join(lines[start_line:end_line])
1685+
caller_contexts.append(context_code)
1686+
context_len += len(context_code)
1687+
1688+
if caller_contexts:
1689+
fn_call_context += f"```{lang}:{path_relative}\n"
1690+
fn_call_context += "\n".join(caller_contexts)
1691+
fn_call_context += "\n```\n"
1692+
1693+
return fn_call_context
1694+
1695+
except Exception as e:
1696+
logger.debug(f"Error getting JavaScript references: {e}")
1697+
return ""
1698+
1699+
1700+
def _extract_calling_function_js(source_code: str, function_name: str, ref_line: int) -> str | None:
1701+
"""Extract the source code of a calling function in JavaScript/TypeScript.
1702+
1703+
Args:
1704+
source_code: Full source code of the file.
1705+
function_name: Name of the function to extract.
1706+
ref_line: Line number where the reference is (helps identify the right function).
1707+
1708+
Returns:
1709+
Source code of the function, or None if not found.
1710+
"""
1711+
try:
1712+
from codeflash.languages.treesitter_utils import TreeSitterAnalyzer, TreeSitterLanguage
1713+
1714+
# Try TypeScript first, fall back to JavaScript
1715+
for lang in [TreeSitterLanguage.TYPESCRIPT, TreeSitterLanguage.TSX, TreeSitterLanguage.JAVASCRIPT]:
1716+
try:
1717+
analyzer = TreeSitterAnalyzer(lang)
1718+
functions = analyzer.find_functions(source_code, include_methods=True)
1719+
1720+
for func in functions:
1721+
if func.name == function_name:
1722+
# Check if the reference line is within this function
1723+
if func.start_line <= ref_line <= func.end_line:
1724+
return func.source_text
1725+
break
1726+
except Exception:
1727+
continue
1728+
1729+
return None
1730+
except Exception:
1731+
return None

0 commit comments

Comments
 (0)