-
Notifications
You must be signed in to change notification settings - Fork 3
Enhanced Solidity syntax fixer with additional automated fixes and compatibility updates #108
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -43,4 +43,109 @@ def check_compilation(self, file_path: str) -> Tuple[bool, str]: | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return False, result.stderr | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return False, f"Error during compilation: {str(e)}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return False, f"Error during compilation: {str(e)}" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def detect_and_fix_syntax_issues(self, content: str, file_path: str) -> str: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"""Detect and fix common Solidity syntax issues""" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
original_content = content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 1: Abstract contracts with unimplemented functions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pattern = r'contract\s+(\w+)\s*{' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
matches = re.finditer(pattern, content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for match in matches: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contract_name = match.group(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contract_start = match.start() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Look for unimplemented functions in this contract | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Find the contract body | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
brace_count = 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contract_end = contract_start | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
for i, char in enumerate(content[contract_start:]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if char == '{': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
brace_count += 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
elif char == '}': | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
brace_count -= 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if brace_count == 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contract_end = contract_start + i | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
break | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
contract_body = content[contract_start:contract_end + 1] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Check for unimplemented functions (ending with semicolon) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func_pattern = r'function\s+\w+\([^)]*\)\s*[^{]*;' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if re.search(func_pattern, contract_body): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# This contract has unimplemented functions, make it abstract | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new_contract_def = f'abstract contract {contract_name} {{' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content = content.replace(match.group(0), new_contract_def) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append(f"Made contract '{contract_name}' abstract (has unimplemented functions)") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 2: Add virtual keyword to unimplemented functions | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
virtual_pattern = r'function\s+(\w+)\s*\([^)]*\)\s*([^{;]*);' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def add_virtual(match): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
func_name = match.group(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
modifiers = match.group(2).strip() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if 'virtual' not in modifiers: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if modifiers: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new_func = f"function {func_name}({match.group(0).split('(')[1].split(')')[0]}) {modifiers} virtual;" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
new_func = f"function {func_name}({match.group(0).split('(')[1].split(')')[0]}) public virtual;" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append(f"Added 'virtual' keyword to function '{func_name}'") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return new_func | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return match.group(0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content = re.sub(virtual_pattern, add_virtual, content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+84
to
+97
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Complex regex replacement with potential parsing issues. The function parameter extraction using string splitting is fragile and may break with complex parameter lists or nested parentheses. Apply this diff for more robust parameter handling: - # Fix 2: Add virtual keyword to unimplemented functions
- virtual_pattern = r'function\s+(\w+)\s*\([^)]*\)\s*([^{;]*);'
- def add_virtual(match):
- func_name = match.group(1)
- modifiers = match.group(2).strip()
- if 'virtual' not in modifiers:
- if modifiers:
- new_func = f"function {func_name}({match.group(0).split('(')[1].split(')')[0]}) {modifiers} virtual;"
- else:
- new_func = f"function {func_name}({match.group(0).split('(')[1].split(')')[0]}) public virtual;"
- fixes.append(f"Added 'virtual' keyword to function '{func_name}'")
- return new_func
- return match.group(0)
-
- content = re.sub(virtual_pattern, add_virtual, content)
+ # Fix 2: Add virtual keyword to unimplemented functions
+ virtual_pattern = r'function\s+(\w+)\s*\(([^)]*)\)\s*([^{;]*);'
+ def add_virtual(match):
+ func_name = match.group(1)
+ params = match.group(2)
+ modifiers = match.group(3).strip()
+ if 'virtual' not in modifiers:
+ if modifiers:
+ new_func = f"function {func_name}({params}) {modifiers} virtual;"
+ else:
+ new_func = f"function {func_name}({params}) public virtual;"
+ fixes.append(f"Added 'virtual' keyword to function '{func_name}'")
+ return new_func
+ return match.group(0)
+
+ content = re.sub(virtual_pattern, add_virtual, content) 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 3: Update deprecated .value() syntax to {value: ...} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value_pattern = r'(\w+)\.call\.value\(([^)]+)\)\(\)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def fix_call_value(match): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
address = match.group(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value = match.group(2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append(f"Updated deprecated .value() syntax to modern {{value: ...}} format") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return f'{address}.call{{value: {value}}}("")' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content = re.sub(value_pattern, fix_call_value, content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 4: Add success handling for call operations | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
call_pattern = r'require\s*\(\s*(\w+)\.call\{value:\s*([^}]+)\}\s*\(""\)\s*\)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
def fix_call_handling(match): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
address = match.group(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
value = match.group(2) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append(f"Added proper success handling for call operation") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return f'(bool success, ) = {address}.call{{value: {value}}}("");\n require(success)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content = re.sub(call_pattern, fix_call_handling, content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+109
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Multiple issues with call pattern handling.
Apply this diff to fix the issues: - # Fix 4: Add success handling for call operations
- call_pattern = r'require\s*\(\s*(\w+)\.call\{value:\s*([^}]+)\}\s*\(""\)\s*\)'
- def fix_call_handling(match):
- address = match.group(1)
- value = match.group(2)
- fixes.append(f"Added proper success handling for call operation")
- return f'(bool success, ) = {address}.call{{value: {value}}}("");\n require(success)'
-
- content = re.sub(call_pattern, fix_call_handling, content)
+ # Fix 4: Add success handling for call operations
+ call_pattern = r'require\s*\(\s*(\w+)\.call\{value:\s*([^}]+)\}\s*\(""\)\s*\)'
+ def fix_call_handling(match):
+ address = match.group(1)
+ value = match.group(2)
+ fixes.append("Added proper success handling for call operation")
+ return f'(bool success, ) = {address}.call{{value: {value}}}("");\n require(success)'
+
+ content = re.sub(call_pattern, fix_call_handling, content) 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff (0.13.1)114-114: f-string without any placeholders Remove extraneous (F541) 🤖 Prompt for AI Agents
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 5: Update pragma version if it's too old and causing issues | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pragma_pattern = r'pragma\s+solidity\s+([^;]+);' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
pragma_match = re.search(pragma_pattern, content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if pragma_match: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
version = pragma_match.group(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# If version is older than 0.6.0 and we have modern syntax, update it | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if '^0.5' in version or '^0.4' in version: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if 'abstract' in content or 'virtual' in content or '{value:' in content: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content = re.sub(pragma_pattern, 'pragma solidity ^0.8.0;', content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append(f"Updated pragma version from '{version}' to '^0.8.0' for compatibility") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 6: Add SPDX license if missing | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if 'SPDX-License-Identifier' not in content: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
lines = content.split('\n') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
lines.insert(0, '// SPDX-License-Identifier: MIT') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
content = '\n'.join(lines) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append("Added missing SPDX license identifier") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Fix 7: Fix require statements that use old call patterns | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
old_require_pattern = r'require\s*\(\s*address\([^)]+\)\.call\.value\([^)]+\)\(\)\s*\)' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if re.search(old_require_pattern, content): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# This is handled by the earlier fixes, but let's ensure consistency | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
fixes.append("Fixed deprecated require statement with old call pattern") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
# Store fixes for this file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if fixes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
self.fixes_applied.append({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"file": file_path, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"fixes": fixes, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
"timestamp": datetime.now().isoformat() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return content |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Potential issues with contract parsing and abstract keyword insertion.
The contract parsing logic has several concerns:
contract A is B {
)Consider this more robust approach:
🤖 Prompt for AI Agents