reslove PR comments
Some checks failed
CF-API CI / skip-validate (pull_request) Blocked by required conditions
CF-API CI / validate (pull_request) Blocked by required conditions
Codeflash AiService / No aiservice changes detected (pull_request) Blocked by required conditions
Codeflash AiService / Wait for prek checks (pull_request) Blocked by required conditions
Codeflash AiService / Optimize new code in this PR (pull_request) Blocked by required conditions
django-unit-tests / No aiservice changes detected (pull_request) Blocked by required conditions
django-unit-tests / Wait for prek checks (pull_request) Blocked by required conditions
django-unit-tests / unit-tests (pull_request) Blocked by required conditions
django-unit-tests / django-unit-tests-status (pull_request) Blocked by required conditions
end-to-end-tests / Wait for prek checks (pull_request) Blocked by required conditions
end-to-end-tests / Wait for unit tests (pull_request) Blocked by required conditions
end-to-end-tests / No relevant changes detected (pull_request) Blocked by required conditions
end-to-end-tests / coverage (pull_request) Blocked by required conditions
end-to-end-tests / futurehouse (pull_request) Blocked by required conditions
end-to-end-tests / init-optimization (pull_request) Blocked by required conditions
end-to-end-tests / tracer-replay (pull_request) Blocked by required conditions
end-to-end-tests / bubblesort-unittest (pull_request) Blocked by required conditions
end-to-end-tests / topological-sort (pull_request) Blocked by required conditions
end-to-end-tests / bubblesort-pytest-no-git (pull_request) Blocked by required conditions
end-to-end-tests / E2E Tests Status (pull_request) Blocked by required conditions
Mypy Type Checking for Aiservice / skip-type-check (pull_request) Blocked by required conditions
Mypy Type Checking for Aiservice / type-check-aiservice (pull_request) Blocked by required conditions
Mypy Type Checking for Aiservice / mypy-aiservice-status (pull_request) Blocked by required conditions
Next.js Build Check / skip-build (pull_request) Blocked by required conditions
Next.js Build Check / build (pull_request) Blocked by required conditions
CF-API CI / check-changes (pull_request) Failing after 2s
Claude Code / pr-review (pull_request) Failing after 2s
Claude Code / claude-mention (pull_request) Has been skipped
Codeflash AiService / check-changes (pull_request) Failing after 2s
django-unit-tests / check-changes (pull_request) Failing after 2s
end-to-end-tests / check-changes (pull_request) Failing after 2s
Mypy Type Checking for Aiservice / check-changes (pull_request) Failing after 2s
Next.js Build Check / check-changes (pull_request) Failing after 2s
Prek (pre-commit checks) checks / prek (pull_request) Failing after 2s
Some checks failed
CF-API CI / skip-validate (pull_request) Blocked by required conditions
CF-API CI / validate (pull_request) Blocked by required conditions
Codeflash AiService / No aiservice changes detected (pull_request) Blocked by required conditions
Codeflash AiService / Wait for prek checks (pull_request) Blocked by required conditions
Codeflash AiService / Optimize new code in this PR (pull_request) Blocked by required conditions
django-unit-tests / No aiservice changes detected (pull_request) Blocked by required conditions
django-unit-tests / Wait for prek checks (pull_request) Blocked by required conditions
django-unit-tests / unit-tests (pull_request) Blocked by required conditions
django-unit-tests / django-unit-tests-status (pull_request) Blocked by required conditions
end-to-end-tests / Wait for prek checks (pull_request) Blocked by required conditions
end-to-end-tests / Wait for unit tests (pull_request) Blocked by required conditions
end-to-end-tests / No relevant changes detected (pull_request) Blocked by required conditions
end-to-end-tests / coverage (pull_request) Blocked by required conditions
end-to-end-tests / futurehouse (pull_request) Blocked by required conditions
end-to-end-tests / init-optimization (pull_request) Blocked by required conditions
end-to-end-tests / tracer-replay (pull_request) Blocked by required conditions
end-to-end-tests / bubblesort-unittest (pull_request) Blocked by required conditions
end-to-end-tests / topological-sort (pull_request) Blocked by required conditions
end-to-end-tests / bubblesort-pytest-no-git (pull_request) Blocked by required conditions
end-to-end-tests / E2E Tests Status (pull_request) Blocked by required conditions
Mypy Type Checking for Aiservice / skip-type-check (pull_request) Blocked by required conditions
Mypy Type Checking for Aiservice / type-check-aiservice (pull_request) Blocked by required conditions
Mypy Type Checking for Aiservice / mypy-aiservice-status (pull_request) Blocked by required conditions
Next.js Build Check / skip-build (pull_request) Blocked by required conditions
Next.js Build Check / build (pull_request) Blocked by required conditions
CF-API CI / check-changes (pull_request) Failing after 2s
Claude Code / pr-review (pull_request) Failing after 2s
Claude Code / claude-mention (pull_request) Has been skipped
Codeflash AiService / check-changes (pull_request) Failing after 2s
django-unit-tests / check-changes (pull_request) Failing after 2s
end-to-end-tests / check-changes (pull_request) Failing after 2s
Mypy Type Checking for Aiservice / check-changes (pull_request) Failing after 2s
Next.js Build Check / check-changes (pull_request) Failing after 2s
Prek (pre-commit checks) checks / prek (pull_request) Failing after 2s
This commit is contained in:
parent
2a69779684
commit
c315f9ffff
2 changed files with 88 additions and 181 deletions
|
|
@ -94,7 +94,7 @@ def extract_code_and_explanation(
|
|||
return "", content
|
||||
|
||||
|
||||
def _remove_strings_and_comments(code: str) -> str:
|
||||
def _remove_strings_and_comments(code: str) -> str | None:
|
||||
"""Remove string literals and comments from Java code.
|
||||
|
||||
This allows accurate counting of braces/brackets/parentheses
|
||||
|
|
@ -104,7 +104,8 @@ def _remove_strings_and_comments(code: str) -> str:
|
|||
code: The Java source code
|
||||
|
||||
Returns:
|
||||
Code with strings and comments replaced by spaces
|
||||
Code with strings and comments replaced by spaces,
|
||||
or None if unterminated string/comment is detected
|
||||
|
||||
"""
|
||||
result = []
|
||||
|
|
@ -131,6 +132,9 @@ def _remove_strings_and_comments(code: str) -> str:
|
|||
result.append(" ")
|
||||
result.append(" ")
|
||||
i += 2
|
||||
else:
|
||||
# Unterminated multi-line comment
|
||||
return None
|
||||
# Check for string literal (double quotes)
|
||||
elif code[i] == '"':
|
||||
result.append(" ")
|
||||
|
|
@ -147,6 +151,9 @@ def _remove_strings_and_comments(code: str) -> str:
|
|||
if i < length:
|
||||
result.append(" ")
|
||||
i += 1
|
||||
else:
|
||||
# Unterminated string literal
|
||||
return None
|
||||
# Check for character literal (single quotes)
|
||||
elif code[i] == "'":
|
||||
result.append(" ")
|
||||
|
|
@ -163,6 +170,9 @@ def _remove_strings_and_comments(code: str) -> str:
|
|||
if i < length:
|
||||
result.append(" ")
|
||||
i += 1
|
||||
else:
|
||||
# Unterminated character literal
|
||||
return None
|
||||
else:
|
||||
result.append(code[i])
|
||||
i += 1
|
||||
|
|
@ -178,6 +188,7 @@ def validate_java_syntax(code: str) -> bool:
|
|||
|
||||
Checks performed:
|
||||
- Non-empty code
|
||||
- No unterminated strings, comments, or character literals
|
||||
- Balanced and properly nested braces {}, brackets [], and parentheses ()
|
||||
- Strings and comments are ignored when checking balance
|
||||
|
||||
|
|
@ -195,6 +206,10 @@ def validate_java_syntax(code: str) -> bool:
|
|||
# Remove strings and comments to avoid false positives
|
||||
clean_code = _remove_strings_and_comments(code)
|
||||
|
||||
# Check for unterminated strings/comments
|
||||
if clean_code is None:
|
||||
return False
|
||||
|
||||
# Check for balanced and properly nested delimiters using a stack
|
||||
stack: list[str] = []
|
||||
matching = {")": "(", "]": "[", "}": "{"}
|
||||
|
|
|
|||
|
|
@ -1,183 +1,11 @@
|
|||
"""Tests for Java optimizer module.
|
||||
"""Tests for Java optimizer module."""
|
||||
|
||||
Tests the code extraction and normalization functions without importing
|
||||
the full optimizer module (to avoid LLM dependencies in tests).
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
# Pattern to extract code blocks from LLM response (handles both ```java and ```java:filename.java)
|
||||
JAVA_CODE_PATTERN = re.compile(r"```(?:java)(?::[^\n]*)?\s*\n(.*?)```", re.MULTILINE | re.DOTALL)
|
||||
|
||||
# Pattern to extract code blocks with file paths (multi-file context)
|
||||
JAVA_CODE_WITH_PATH_PATTERN = re.compile(r"```(?:java):([^\n]+)\n(.*?)```", re.MULTILINE | re.DOTALL)
|
||||
|
||||
|
||||
def extract_code_and_explanation(
|
||||
content: str, is_multi_file: bool = False
|
||||
) -> tuple[str | dict[str, str], str]:
|
||||
"""Extract code and explanation from LLM response.
|
||||
|
||||
Args:
|
||||
content: The raw LLM response content
|
||||
is_multi_file: Whether to expect multi-file format
|
||||
|
||||
Returns:
|
||||
Tuple of (code, explanation) where code is a string for single file
|
||||
or dict[str, str] for multi-file
|
||||
|
||||
"""
|
||||
if is_multi_file:
|
||||
# Extract all code blocks with file paths
|
||||
matches = JAVA_CODE_WITH_PATH_PATTERN.findall(content)
|
||||
if matches:
|
||||
file_to_code: dict[str, str] = {}
|
||||
first_match_pos = content.find("```")
|
||||
explanation = content[:first_match_pos].strip() if first_match_pos > 0 else ""
|
||||
|
||||
for file_path, code in matches:
|
||||
file_to_code[file_path.strip()] = code.strip()
|
||||
|
||||
return file_to_code, explanation
|
||||
|
||||
# Fall back to single file extraction
|
||||
return extract_code_and_explanation(content, is_multi_file=False)
|
||||
|
||||
# Single file extraction
|
||||
match = JAVA_CODE_PATTERN.search(content)
|
||||
if match:
|
||||
code = match.group(1).strip()
|
||||
# Explanation is everything before the code block
|
||||
explanation_end = match.start()
|
||||
explanation = content[:explanation_end].strip()
|
||||
return code, explanation
|
||||
|
||||
# No code block found, return empty code
|
||||
return "", content
|
||||
|
||||
|
||||
def is_multi_context_java(source_code: str) -> bool:
|
||||
"""Check if source code contains multiple Java file blocks."""
|
||||
return source_code.count("```java:") >= 1
|
||||
|
||||
|
||||
def _remove_strings_and_comments(code: str) -> str:
|
||||
"""Remove string literals and comments from Java code.
|
||||
|
||||
This allows accurate counting of braces/brackets/parentheses
|
||||
without being affected by characters inside strings or comments.
|
||||
|
||||
Args:
|
||||
code: The Java source code
|
||||
|
||||
Returns:
|
||||
Code with strings and comments replaced by spaces
|
||||
|
||||
"""
|
||||
result = []
|
||||
i = 0
|
||||
length = len(code)
|
||||
|
||||
while i < length:
|
||||
# Check for single-line comment
|
||||
if i + 1 < length and code[i] == "/" and code[i + 1] == "/":
|
||||
# Skip until end of line
|
||||
while i < length and code[i] != "\n":
|
||||
result.append(" ")
|
||||
i += 1
|
||||
# Check for multi-line comment
|
||||
elif i + 1 < length and code[i] == "/" and code[i + 1] == "*":
|
||||
result.append(" ")
|
||||
result.append(" ")
|
||||
i += 2
|
||||
# Skip until */
|
||||
while i + 1 < length and not (code[i] == "*" and code[i + 1] == "/"):
|
||||
result.append(" " if code[i] != "\n" else "\n")
|
||||
i += 1
|
||||
if i + 1 < length:
|
||||
result.append(" ")
|
||||
result.append(" ")
|
||||
i += 2
|
||||
# Check for string literal (double quotes)
|
||||
elif code[i] == '"':
|
||||
result.append(" ")
|
||||
i += 1
|
||||
# Skip until closing quote, handling escapes
|
||||
while i < length and code[i] != '"':
|
||||
if code[i] == "\\" and i + 1 < length:
|
||||
result.append(" ")
|
||||
result.append(" ")
|
||||
i += 2
|
||||
else:
|
||||
result.append(" ")
|
||||
i += 1
|
||||
if i < length:
|
||||
result.append(" ")
|
||||
i += 1
|
||||
# Check for character literal (single quotes)
|
||||
elif code[i] == "'":
|
||||
result.append(" ")
|
||||
i += 1
|
||||
# Skip until closing quote, handling escapes
|
||||
while i < length and code[i] != "'":
|
||||
if code[i] == "\\" and i + 1 < length:
|
||||
result.append(" ")
|
||||
result.append(" ")
|
||||
i += 2
|
||||
else:
|
||||
result.append(" ")
|
||||
i += 1
|
||||
if i < length:
|
||||
result.append(" ")
|
||||
i += 1
|
||||
else:
|
||||
result.append(code[i])
|
||||
i += 1
|
||||
|
||||
return "".join(result)
|
||||
|
||||
|
||||
def validate_java_syntax(code: str) -> bool:
|
||||
"""Basic Java syntax validation.
|
||||
|
||||
This is a simple check that the code has basic Java structure.
|
||||
Full validation happens at the client side with the Java compiler.
|
||||
|
||||
Checks performed:
|
||||
- Non-empty code
|
||||
- Balanced and properly nested braces {}, brackets [], and parentheses ()
|
||||
- Strings and comments are ignored when checking balance
|
||||
|
||||
Args:
|
||||
code: The Java source code to validate
|
||||
|
||||
Returns:
|
||||
True if the code passes basic validation
|
||||
|
||||
"""
|
||||
# Check for basic Java structure
|
||||
if not code.strip():
|
||||
return False
|
||||
|
||||
# Remove strings and comments to avoid false positives
|
||||
clean_code = _remove_strings_and_comments(code)
|
||||
|
||||
# Check for balanced and properly nested delimiters using a stack
|
||||
stack: list[str] = []
|
||||
matching = {")": "(", "]": "[", "}": "{"}
|
||||
opening = set(matching.values())
|
||||
closing = set(matching.keys())
|
||||
|
||||
for char in clean_code:
|
||||
if char in opening:
|
||||
stack.append(char)
|
||||
elif char in closing:
|
||||
if not stack or stack[-1] != matching[char]:
|
||||
return False
|
||||
stack.pop()
|
||||
|
||||
# Stack should be empty if all delimiters are balanced
|
||||
return len(stack) == 0
|
||||
from languages.java.optimizer import (
|
||||
_remove_strings_and_comments,
|
||||
extract_code_and_explanation,
|
||||
is_multi_context_java,
|
||||
validate_java_syntax,
|
||||
)
|
||||
|
||||
|
||||
class TestExtractCodeAndExplanation:
|
||||
|
|
@ -885,6 +713,45 @@ public class Test {
|
|||
"""Test newlines-only code fails."""
|
||||
assert not validate_java_syntax("\n\n\n")
|
||||
|
||||
def test_unterminated_string_fails(self) -> None:
|
||||
"""Test that unterminated string literal fails validation."""
|
||||
code = '''
|
||||
public class Test {
|
||||
String s = "this string never ends
|
||||
}
|
||||
'''
|
||||
assert not validate_java_syntax(code)
|
||||
|
||||
def test_unterminated_char_literal_fails(self) -> None:
|
||||
"""Test that unterminated character literal fails validation."""
|
||||
code = """
|
||||
public class Test {
|
||||
char c = 'x
|
||||
}
|
||||
"""
|
||||
assert not validate_java_syntax(code)
|
||||
|
||||
def test_unterminated_multiline_comment_fails(self) -> None:
|
||||
"""Test that unterminated multi-line comment fails validation."""
|
||||
code = """
|
||||
public class Test {
|
||||
/* This comment never ends
|
||||
public void method() {
|
||||
}
|
||||
}
|
||||
"""
|
||||
assert not validate_java_syntax(code)
|
||||
|
||||
def test_unterminated_string_with_balanced_braces_fails(self) -> None:
|
||||
"""Test that unterminated string fails even if braces would be balanced."""
|
||||
# Without the fix, this would incorrectly return True because
|
||||
# the unterminated string would consume everything to EOF
|
||||
code = '''
|
||||
public class Test {
|
||||
String s = "unterminated
|
||||
'''
|
||||
assert not validate_java_syntax(code)
|
||||
|
||||
|
||||
class TestRemoveStringsAndComments:
|
||||
"""Test the _remove_strings_and_comments helper function."""
|
||||
|
|
@ -938,3 +805,28 @@ class TestRemoveStringsAndComments:
|
|||
result = _remove_strings_and_comments(code)
|
||||
# The code after the string should be preserved
|
||||
assert "int x = 1;" in result
|
||||
|
||||
def test_returns_none_for_unterminated_string(self) -> None:
|
||||
"""Test that unterminated string returns None."""
|
||||
code = 'String s = "unterminated'
|
||||
result = _remove_strings_and_comments(code)
|
||||
assert result is None
|
||||
|
||||
def test_returns_none_for_unterminated_char_literal(self) -> None:
|
||||
"""Test that unterminated character literal returns None."""
|
||||
code = "char c = 'x"
|
||||
result = _remove_strings_and_comments(code)
|
||||
assert result is None
|
||||
|
||||
def test_returns_none_for_unterminated_multiline_comment(self) -> None:
|
||||
"""Test that unterminated multi-line comment returns None."""
|
||||
code = "int x = 1; /* comment never ends"
|
||||
result = _remove_strings_and_comments(code)
|
||||
assert result is None
|
||||
|
||||
def test_single_line_comment_at_eof_is_valid(self) -> None:
|
||||
"""Test that single-line comment at EOF is valid (no newline needed)."""
|
||||
code = "int x = 1; // comment at end"
|
||||
result = _remove_strings_and_comments(code)
|
||||
assert result is not None
|
||||
assert "int x = 1;" in result
|
||||
|
|
|
|||
Loading…
Reference in a new issue