Skip to content

Commit 77c79d7

Browse files
fix: reject Java optimizations with unused additions and unchanged target method
Adds a wiring check in replace_function() that detects when the AI generates "optimizations" adding fields/helpers that the target method never references. Previously these passed through because benchmark noise produced fake speedups. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3c38a80 commit 77c79d7

2 files changed

Lines changed: 308 additions & 0 deletions

File tree

codeflash/languages/java/replacement.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,60 @@ def _parse_optimization_source(new_source: str, target_method_name: str, analyze
158158
)
159159

160160

161+
def _has_unused_additions(parsed: ParsedOptimization, original_method_source: str, analyzer: JavaAnalyzer) -> bool:
162+
"""Check if an optimization adds fields/helpers that the target method never references.
163+
164+
Returns True if the optimization has additions (fields/helpers) that are completely
165+
unreferenced by the target method body. This catches fake optimizations where the AI
166+
adds new code but leaves the target method unchanged.
167+
168+
Returns False (additions are used / no problem) when:
169+
- There are no additions
170+
- At least one addition's identifier appears in the target method body
171+
- The target method itself was changed
172+
"""
173+
has_additions = bool(parsed.new_fields or parsed.helpers_before_target or parsed.helpers_after_target)
174+
if not has_additions:
175+
return False
176+
177+
# Normalize whitespace for comparison: strip leading/trailing whitespace from each line
178+
def normalize_ws(s: str) -> str:
179+
return "\n".join(line.strip() for line in s.strip().splitlines() if line.strip())
180+
181+
if normalize_ws(parsed.target_method_source) != normalize_ws(original_method_source):
182+
# Target method was actually changed — additions may or may not be used,
183+
# but the optimization is not a no-op on the target function.
184+
return False
185+
186+
# Target method is unchanged. Check if any addition identifiers are referenced.
187+
addition_names: set[str] = set()
188+
189+
for field_src in parsed.new_fields:
190+
dummy_class = f"class __Dummy__ {{\n{field_src}\n}}"
191+
for field_info in analyzer.find_fields(dummy_class):
192+
addition_names.add(field_info.name)
193+
194+
for helper_src in parsed.helpers_before_target + parsed.helpers_after_target:
195+
for method_info in analyzer.find_methods(helper_src):
196+
addition_names.add(method_info.name)
197+
198+
if not addition_names:
199+
return False
200+
201+
# Check if any addition name appears in the target method body as a word boundary match
202+
target_body = parsed.target_method_source
203+
for name in addition_names:
204+
if re.search(rf"\b{re.escape(name)}\b", target_body):
205+
return False # At least one addition is referenced
206+
207+
logger.info(
208+
"Rejecting optimization: target method '%s' is unchanged and new additions %s are unreferenced.",
209+
"target",
210+
addition_names,
211+
)
212+
return True
213+
214+
161215
def _dedent_member(source: str) -> str:
162216
"""Strip the common leading whitespace from a class member source."""
163217
return textwrap.dedent(source).strip()
@@ -459,6 +513,23 @@ def replace_function(
459513
logger.error("Could not find method %s in source", func_name)
460514
return source
461515

516+
# Extract original method source for comparison
517+
orig_start = (target_method.javadoc_start_line or target_method.start_line) - 1
518+
orig_end = target_method.end_line
519+
orig_lines = source.splitlines(keepends=True)
520+
original_method_source = "".join(orig_lines[orig_start:orig_end])
521+
522+
# Reject optimizations that add unused code while leaving the target method unchanged.
523+
# This catches fake optimizations (e.g., adding a static field that the method never references)
524+
# that produce spurious speedups from benchmark noise.
525+
if _has_unused_additions(parsed, original_method_source, analyzer):
526+
logger.warning(
527+
"Optimization for '%s' adds unreferenced fields/helpers without changing the target method. "
528+
"Returning original source.",
529+
func_name,
530+
)
531+
return source
532+
462533
# Get the class name for inserting new members
463534
class_name = target_method.class_name or function.class_name
464535

tests/test_languages/test_java/test_replacement.py

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1929,3 +1929,240 @@ def test_anonymous_iterator_methods_not_hoisted_to_class(self, tmp_path, java_su
19291929
}
19301930
"""
19311931
assert new_code == expected_code
1932+
1933+
1934+
class TestUnusedAdditionsRejection:
1935+
"""Tests that optimizations adding unused fields/helpers with unchanged target method are rejected."""
1936+
1937+
def test_unchanged_method_with_unused_field_rejected(self, tmp_path: Path, java_support: JavaSupport):
1938+
"""An optimization that adds a field but doesn't change the method should be rejected."""
1939+
java_file = (tmp_path / "SystemUtils.java").resolve()
1940+
original_code = """public class SystemUtils {
1941+
public static String getJavaIoTmpdir() {
1942+
return System.getProperty("java.io.tmpdir");
1943+
}
1944+
}
1945+
"""
1946+
java_file.write_text(original_code, encoding="utf-8")
1947+
1948+
# AI adds NULL_SUPPLIER but doesn't change getJavaIoTmpdir
1949+
optimized_markdown = f"""```java:{java_file.relative_to(tmp_path)}
1950+
public class SystemUtils {{
1951+
private static final String CACHED_TMPDIR = System.getProperty("java.io.tmpdir");
1952+
1953+
public static String getJavaIoTmpdir() {{
1954+
return System.getProperty("java.io.tmpdir");
1955+
}}
1956+
}}
1957+
```"""
1958+
1959+
optimized_code = CodeStringsMarkdown.parse_markdown_code(optimized_markdown, expected_language="java")
1960+
1961+
from codeflash.discovery.functions_to_optimize import FunctionParent, FunctionToOptimize
1962+
1963+
function_to_optimize = FunctionToOptimize(
1964+
function_name="getJavaIoTmpdir",
1965+
file_path=java_file,
1966+
starting_line=2,
1967+
ending_line=4,
1968+
parents=[FunctionParent(name="SystemUtils", type="ClassDef")],
1969+
qualified_name="SystemUtils.getJavaIoTmpdir",
1970+
is_method=True,
1971+
)
1972+
1973+
result = replace_function_definitions_for_language(
1974+
function_names=["getJavaIoTmpdir"],
1975+
optimized_code=optimized_code,
1976+
module_abspath=java_file,
1977+
project_root_path=tmp_path,
1978+
lang_support=java_support,
1979+
function_to_optimize=function_to_optimize,
1980+
)
1981+
1982+
# Should reject: method unchanged, field unreferenced
1983+
assert result is False
1984+
assert java_file.read_text(encoding="utf-8") == original_code
1985+
1986+
def test_unchanged_method_with_unused_helper_rejected(self, tmp_path: Path, java_support: JavaSupport):
1987+
"""An optimization that adds a helper method but doesn't change the target should be rejected."""
1988+
java_file = (tmp_path / "Calculator.java").resolve()
1989+
original_code = """public class Calculator {
1990+
public int add(int a, int b) {
1991+
return a + b;
1992+
}
1993+
}
1994+
"""
1995+
java_file.write_text(original_code, encoding="utf-8")
1996+
1997+
# AI adds a helper method but doesn't change add()
1998+
optimized_markdown = f"""```java:{java_file.relative_to(tmp_path)}
1999+
public class Calculator {{
2000+
private static int fastAdd(int a, int b) {{
2001+
return Math.addExact(a, b);
2002+
}}
2003+
2004+
public int add(int a, int b) {{
2005+
return a + b;
2006+
}}
2007+
}}
2008+
```"""
2009+
2010+
optimized_code = CodeStringsMarkdown.parse_markdown_code(optimized_markdown, expected_language="java")
2011+
2012+
from codeflash.discovery.functions_to_optimize import FunctionParent, FunctionToOptimize
2013+
2014+
function_to_optimize = FunctionToOptimize(
2015+
function_name="add",
2016+
file_path=java_file,
2017+
starting_line=2,
2018+
ending_line=4,
2019+
parents=[FunctionParent(name="Calculator", type="ClassDef")],
2020+
qualified_name="Calculator.add",
2021+
is_method=True,
2022+
)
2023+
2024+
result = replace_function_definitions_for_language(
2025+
function_names=["add"],
2026+
optimized_code=optimized_code,
2027+
module_abspath=java_file,
2028+
project_root_path=tmp_path,
2029+
lang_support=java_support,
2030+
function_to_optimize=function_to_optimize,
2031+
)
2032+
2033+
# Should reject: method unchanged, helper unreferenced
2034+
assert result is False
2035+
assert java_file.read_text(encoding="utf-8") == original_code
2036+
2037+
def test_changed_method_with_used_field_accepted(self, tmp_path: Path, java_support: JavaSupport):
2038+
"""An optimization that adds a field AND uses it in the changed method should be accepted."""
2039+
java_file = (tmp_path / "SystemUtils.java").resolve()
2040+
original_code = """public class SystemUtils {
2041+
public static String getJavaIoTmpdir() {
2042+
return System.getProperty("java.io.tmpdir");
2043+
}
2044+
}
2045+
"""
2046+
java_file.write_text(original_code, encoding="utf-8")
2047+
2048+
# AI adds CACHED_TMPDIR and actually uses it in the method
2049+
optimized_markdown = f"""```java:{java_file.relative_to(tmp_path)}
2050+
public class SystemUtils {{
2051+
private static final String CACHED_TMPDIR = System.getProperty("java.io.tmpdir");
2052+
2053+
public static String getJavaIoTmpdir() {{
2054+
return CACHED_TMPDIR;
2055+
}}
2056+
}}
2057+
```"""
2058+
2059+
optimized_code = CodeStringsMarkdown.parse_markdown_code(optimized_markdown, expected_language="java")
2060+
2061+
from codeflash.discovery.functions_to_optimize import FunctionParent, FunctionToOptimize
2062+
2063+
function_to_optimize = FunctionToOptimize(
2064+
function_name="getJavaIoTmpdir",
2065+
file_path=java_file,
2066+
starting_line=2,
2067+
ending_line=4,
2068+
parents=[FunctionParent(name="SystemUtils", type="ClassDef")],
2069+
qualified_name="SystemUtils.getJavaIoTmpdir",
2070+
is_method=True,
2071+
)
2072+
2073+
result = replace_function_definitions_for_language(
2074+
function_names=["getJavaIoTmpdir"],
2075+
optimized_code=optimized_code,
2076+
module_abspath=java_file,
2077+
project_root_path=tmp_path,
2078+
lang_support=java_support,
2079+
function_to_optimize=function_to_optimize,
2080+
)
2081+
2082+
# Should accept: method changed to use CACHED_TMPDIR
2083+
assert result is True
2084+
new_code = java_file.read_text(encoding="utf-8")
2085+
assert "CACHED_TMPDIR" in new_code
2086+
assert "private static final String CACHED_TMPDIR" in new_code
2087+
2088+
def test_changed_method_without_additions_accepted(self, tmp_path: Path, java_support: JavaSupport):
2089+
"""A normal optimization that just changes the method body should be accepted."""
2090+
java_file = (tmp_path / "Calculator.java").resolve()
2091+
original_code = """public class Calculator {
2092+
public int add(int a, int b) {
2093+
return a + b;
2094+
}
2095+
}
2096+
"""
2097+
java_file.write_text(original_code, encoding="utf-8")
2098+
2099+
optimized_markdown = f"""```java:{java_file.relative_to(tmp_path)}
2100+
public class Calculator {{
2101+
public int add(int a, int b) {{
2102+
return Math.addExact(a, b);
2103+
}}
2104+
}}
2105+
```"""
2106+
2107+
optimized_code = CodeStringsMarkdown.parse_markdown_code(optimized_markdown, expected_language="java")
2108+
2109+
result = replace_function_definitions_for_language(
2110+
function_names=["add"],
2111+
optimized_code=optimized_code,
2112+
module_abspath=java_file,
2113+
project_root_path=tmp_path,
2114+
lang_support=java_support,
2115+
)
2116+
2117+
# Should accept: method was changed
2118+
assert result is True
2119+
2120+
def test_unchanged_method_with_used_helper_accepted(self, tmp_path: Path, java_support: JavaSupport):
2121+
"""Method unchanged but references the new helper — should be accepted (helper IS used)."""
2122+
java_file = (tmp_path / "Calculator.java").resolve()
2123+
original_code = """public class Calculator {
2124+
public int add(int a, int b) {
2125+
return a + b;
2126+
}
2127+
}
2128+
"""
2129+
java_file.write_text(original_code, encoding="utf-8")
2130+
2131+
# AI adds helper AND rewrites method to use it
2132+
optimized_markdown = f"""```java:{java_file.relative_to(tmp_path)}
2133+
public class Calculator {{
2134+
private static int fastAdd(int a, int b) {{
2135+
return Math.addExact(a, b);
2136+
}}
2137+
2138+
public int add(int a, int b) {{
2139+
return fastAdd(a, b);
2140+
}}
2141+
}}
2142+
```"""
2143+
2144+
optimized_code = CodeStringsMarkdown.parse_markdown_code(optimized_markdown, expected_language="java")
2145+
2146+
from codeflash.discovery.functions_to_optimize import FunctionParent, FunctionToOptimize
2147+
2148+
function_to_optimize = FunctionToOptimize(
2149+
function_name="add",
2150+
file_path=java_file,
2151+
starting_line=2,
2152+
ending_line=4,
2153+
parents=[FunctionParent(name="Calculator", type="ClassDef")],
2154+
qualified_name="Calculator.add",
2155+
is_method=True,
2156+
)
2157+
2158+
result = replace_function_definitions_for_language(
2159+
function_names=["add"],
2160+
optimized_code=optimized_code,
2161+
module_abspath=java_file,
2162+
project_root_path=tmp_path,
2163+
lang_support=java_support,
2164+
function_to_optimize=function_to_optimize,
2165+
)
2166+
2167+
# Should accept: method changed to call fastAdd
2168+
assert result is True

0 commit comments

Comments
 (0)