Skip to content

Commit 64ffda2

Browse files
fix context extraction: skip object properties that are matching helper functions names
1 parent 348a6ec commit 64ffda2

File tree

2 files changed

+242
-0
lines changed

2 files changed

+242
-0
lines changed

codeflash/languages/treesitter_utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,11 @@ def _walk_tree_for_functions(
258258
if func_info.is_arrow and not include_arrow_functions:
259259
should_include = False
260260

261+
# Skip arrow functions that are object properties (e.g., { foo: () => {} })
262+
# These are not standalone functions - they're values in object literals
263+
if func_info.is_arrow and node.parent and node.parent.type == "pair":
264+
should_include = False
265+
261266
if should_include:
262267
functions.append(func_info)
263268

@@ -464,6 +469,7 @@ def _walk_tree_for_imports(
464469
source_bytes: Source code bytes.
465470
imports: List to append found imports to.
466471
in_function: Whether we're currently inside a function/method body.
472+
467473
"""
468474
# Track when we enter function/method bodies
469475
# These node types contain function/method bodies where require() should not be treated as imports

tests/test_languages/test_code_context_extraction.py

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import pytest
2626

27+
from codeflash.context.code_context_extractor import get_code_optimization_context_for_language
28+
from codeflash.discovery.functions_to_optimize import FunctionToOptimize
2729
from codeflash.languages.base import Language
2830
from codeflash.languages.javascript.support import JavaScriptSupport, TypeScriptSupport
2931

@@ -1694,6 +1696,240 @@ def test_function_with_computed_property_names(self, js_support, temp_project):
16941696
};"""
16951697
assert context.read_only_context == expected_read_only
16961698

1699+
def test_with_tricky_helpers(self, ts_support, temp_project):
1700+
"""Test function returning object with computed property names."""
1701+
code = """import { WebClient, ChatPostMessageArguments } from "@slack/web-api"
1702+
1703+
// Dependencies interface for easier testing
1704+
export interface SendSlackMessageDependencies {
1705+
WebClient: typeof WebClient
1706+
getSlackToken: () => string | undefined
1707+
getSlackChannelId: () => string | undefined
1708+
console: typeof console
1709+
}
1710+
1711+
// Default dependencies
1712+
let dependencies: SendSlackMessageDependencies = {
1713+
WebClient,
1714+
getSlackToken: () => process.env.SLACK_TOKEN,
1715+
getSlackChannelId: () => process.env.SLACK_CHANNEL_ID,
1716+
console,
1717+
}
1718+
1719+
// For testing - allow dependency injection
1720+
export function setSendSlackMessageDependencies(deps: Partial<SendSlackMessageDependencies>) {
1721+
dependencies = { ...dependencies, ...deps }
1722+
}
1723+
1724+
export function resetSendSlackMessageDependencies() {
1725+
dependencies = {
1726+
WebClient,
1727+
getSlackToken: () => process.env.SLACK_TOKEN,
1728+
getSlackChannelId: () => process.env.SLACK_CHANNEL_ID,
1729+
console,
1730+
}
1731+
}
1732+
1733+
// Initialize web client
1734+
let web: WebClient | null = null
1735+
1736+
export function initializeWebClient() {
1737+
const SLACK_TOKEN = dependencies.getSlackToken()
1738+
const SLACK_CHANNEL_ID = dependencies.getSlackChannelId()
1739+
1740+
if (!SLACK_TOKEN) {
1741+
throw new Error("Missing SLACK_TOKEN")
1742+
}
1743+
1744+
if (!SLACK_CHANNEL_ID) {
1745+
throw new Error("Missing SLACK_CHANNEL_ID")
1746+
}
1747+
1748+
if (!web) {
1749+
web = new dependencies.WebClient(SLACK_TOKEN, {})
1750+
}
1751+
1752+
return web
1753+
}
1754+
1755+
// For testing - allow resetting the web client
1756+
export function resetWebClient() {
1757+
web = null
1758+
}
1759+
1760+
/**
1761+
* Send a message to Slack
1762+
*
1763+
* @param {string|object} message - Text message or Block Kit message object
1764+
* @param {string|null} channel - Channel ID, defaults to SLACK_CHANNEL_ID
1765+
* @param {boolean} returnData - Whether to return the full Slack API response
1766+
* @returns {Promise<boolean|object>} - True or API response
1767+
*/
1768+
export const sendSlackMessage = async (
1769+
message: any,
1770+
channel: string | null = null,
1771+
returnData: boolean = false,
1772+
): Promise<boolean | object> => {
1773+
return new Promise(async (resolve, reject) => {
1774+
try {
1775+
const webClient = initializeWebClient()
1776+
const SLACK_CHANNEL_ID = dependencies.getSlackChannelId()
1777+
const channelId = channel || SLACK_CHANNEL_ID
1778+
1779+
// Configure the message payload depending on the input type
1780+
let payload: ChatPostMessageArguments
1781+
1782+
if (typeof message === "string") {
1783+
payload = {
1784+
channel: channelId,
1785+
text: message,
1786+
}
1787+
} else if (message && typeof message === "object") {
1788+
if (message.blocks) {
1789+
payload = {
1790+
channel: channelId,
1791+
text: message.text || "Notification from CodeFlash",
1792+
blocks: message.blocks,
1793+
}
1794+
} else {
1795+
dependencies.console.warn("Object passed to sendSlackMessage without blocks property")
1796+
payload = {
1797+
channel: channelId,
1798+
text: JSON.stringify(message),
1799+
}
1800+
}
1801+
} else {
1802+
dependencies.console.error("Invalid message type", typeof message)
1803+
payload = {
1804+
channel: channelId,
1805+
text: "Invalid message",
1806+
}
1807+
}
1808+
1809+
// console.log("Sending payload to Slack:", JSON.stringify(payload, null, 2));
1810+
1811+
const resp = await webClient.chat.postMessage(payload)
1812+
return resolve(returnData ? resp : true)
1813+
} catch (error) {
1814+
dependencies.console.error("Error sending Slack message:", error)
1815+
return resolve(returnData ? { error } : true)
1816+
}
1817+
})
1818+
}
1819+
"""
1820+
file_path = temp_project / "slack_util.ts"
1821+
file_path.write_text(code, encoding="utf-8")
1822+
target_func = "sendSlackMessage"
1823+
1824+
functions = ts_support.discover_functions(file_path)
1825+
func_info = next(f for f in functions if f.name == target_func)
1826+
fto = FunctionToOptimize(
1827+
function_name=target_func,
1828+
file_path=file_path,
1829+
parents=func_info.parents,
1830+
starting_line=func_info.start_line,
1831+
ending_line=func_info.end_line,
1832+
starting_col=func_info.start_col,
1833+
ending_col=func_info.end_col,
1834+
is_async=func_info.is_async,
1835+
language="typescript",
1836+
)
1837+
1838+
ctx = get_code_optimization_context_for_language(
1839+
fto, temp_project
1840+
)
1841+
1842+
# The read_writable_code should contain the target function AND helper functions
1843+
expected_read_writable = """```typescript:slack_util.ts
1844+
import { WebClient, ChatPostMessageArguments } from "@slack/web-api"
1845+
1846+
export const sendSlackMessage = async (
1847+
message: any,
1848+
channel: string | null = null,
1849+
returnData: boolean = false,
1850+
): Promise<boolean | object> => {
1851+
return new Promise(async (resolve, reject) => {
1852+
try {
1853+
const webClient = initializeWebClient()
1854+
const SLACK_CHANNEL_ID = dependencies.getSlackChannelId()
1855+
const channelId = channel || SLACK_CHANNEL_ID
1856+
1857+
// Configure the message payload depending on the input type
1858+
let payload: ChatPostMessageArguments
1859+
1860+
if (typeof message === "string") {
1861+
payload = {
1862+
channel: channelId,
1863+
text: message,
1864+
}
1865+
} else if (message && typeof message === "object") {
1866+
if (message.blocks) {
1867+
payload = {
1868+
channel: channelId,
1869+
text: message.text || "Notification from CodeFlash",
1870+
blocks: message.blocks,
1871+
}
1872+
} else {
1873+
dependencies.console.warn("Object passed to sendSlackMessage without blocks property")
1874+
payload = {
1875+
channel: channelId,
1876+
text: JSON.stringify(message),
1877+
}
1878+
}
1879+
} else {
1880+
dependencies.console.error("Invalid message type", typeof message)
1881+
payload = {
1882+
channel: channelId,
1883+
text: "Invalid message",
1884+
}
1885+
}
1886+
1887+
// console.log("Sending payload to Slack:", JSON.stringify(payload, null, 2));
1888+
1889+
const resp = await webClient.chat.postMessage(payload)
1890+
return resolve(returnData ? resp : true)
1891+
} catch (error) {
1892+
dependencies.console.error("Error sending Slack message:", error)
1893+
return resolve(returnData ? { error } : true)
1894+
}
1895+
})
1896+
}
1897+
1898+
1899+
export function initializeWebClient() {
1900+
const SLACK_TOKEN = dependencies.getSlackToken()
1901+
const SLACK_CHANNEL_ID = dependencies.getSlackChannelId()
1902+
1903+
if (!SLACK_TOKEN) {
1904+
throw new Error("Missing SLACK_TOKEN")
1905+
}
1906+
1907+
if (!SLACK_CHANNEL_ID) {
1908+
throw new Error("Missing SLACK_CHANNEL_ID")
1909+
}
1910+
1911+
if (!web) {
1912+
web = new dependencies.WebClient(SLACK_TOKEN, {})
1913+
}
1914+
1915+
return web
1916+
}
1917+
```"""
1918+
1919+
# The read_only_context should contain global variables (dependencies object, web client)
1920+
# but NOT have invalid floating object properties
1921+
expected_read_only = """let dependencies: SendSlackMessageDependencies = {
1922+
WebClient,
1923+
getSlackToken: () => process.env.SLACK_TOKEN,
1924+
getSlackChannelId: () => process.env.SLACK_CHANNEL_ID,
1925+
console,
1926+
}
1927+
let web: WebClient | null = null"""
1928+
1929+
assert ctx.read_writable_code.markdown == expected_read_writable
1930+
assert ctx.read_only_context_code == expected_read_only
1931+
1932+
16971933

16981934
class TestContextProperties:
16991935
"""Tests for CodeContext object properties."""

0 commit comments

Comments
 (0)