Add TypeScript, Go, and Rust analyzers
This commit is contained in:
187
vibeguard/analyzers/languages/rust.py
Normal file
187
vibeguard/analyzers/languages/rust.py
Normal file
@@ -0,0 +1,187 @@
|
||||
"""Rust analyzer for VibeGuard."""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from vibeguard.analyzers.base import BaseAnalyzer
|
||||
|
||||
|
||||
class RustAnalyzer(BaseAnalyzer):
|
||||
"""Analyzer for Rust code."""
|
||||
|
||||
LANGUAGE_NAME = "rust"
|
||||
FILE_EXTENSIONS = [".rs"]
|
||||
|
||||
def parse_file(self, path: Path) -> str | None:
|
||||
"""Parse a Rust file and return the content."""
|
||||
try:
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
return f.read()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def analyze(self, content: str, path: Path) -> list[dict[str, Any]]:
|
||||
"""Analyze Rust content for anti-patterns."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
lines = content.split("\n")
|
||||
|
||||
for i, line in enumerate(lines, 1):
|
||||
issues.extend(self._check_clone_on_copy(line, i, path))
|
||||
issues.extend(self._check_unused_imports(line, i, path))
|
||||
issues.extend(self._check_expect_usage(line, i, path))
|
||||
issues.extend(self._check_magic_strings(line, i, path))
|
||||
issues.extend(self._check_hardcoded_values(line, i, path))
|
||||
|
||||
issues.extend(self._check_unnecessary_boxing(content, path))
|
||||
issues.extend(self._check_into_iter_consumed(content, path))
|
||||
|
||||
return issues
|
||||
|
||||
def _check_clone_on_copy(
|
||||
self, line: str, line_num: int, path: Path
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Check for unnecessary .clone() on Copy types."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
if re.search(r"\.clone\(\)", line):
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "UNNECESSARY_CLONE",
|
||||
"severity": "warning",
|
||||
"file": str(path),
|
||||
"line": line_num,
|
||||
"message": "Unnecessary .clone() - type implements Copy trait",
|
||||
"suggestion": "Remove .clone() as the type will be automatically copied",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_unused_imports(
|
||||
self, line: str, line_num: int, path: Path
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Check for unused imports."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
if re.search(r"#\[allow\(unused\)\]", line):
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "ALLOW_UNUSED",
|
||||
"severity": "info",
|
||||
"file": str(path),
|
||||
"line": line_num,
|
||||
"message": "#[allow(unused)] - remove unused code instead",
|
||||
"suggestion": "Remove the unused imports or code instead of suppressing warnings",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_expect_usage(
|
||||
self, line: str, line_num: int, path: Path
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Check for .expect() usage which can panic."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
if re.search(r"\.expect\(", line):
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "EXPECT_PANIC",
|
||||
"severity": "warning",
|
||||
"file": str(path),
|
||||
"line": line_num,
|
||||
"message": ".expect() can panic - consider proper error handling",
|
||||
"suggestion": "Use ? operator or proper error handling instead of expect",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_magic_strings(
|
||||
self, line: str, line_num: int, path: Path
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Check for magic strings."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
if re.search(r'"[^"]{30,}"', line):
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "MAGIC_STRING",
|
||||
"severity": "warning",
|
||||
"file": str(path),
|
||||
"line": line_num,
|
||||
"message": "Long magic string detected - consider using constants",
|
||||
"suggestion": "Extract to a named constant",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_hardcoded_values(
|
||||
self, line: str, line_num: int, path: Path
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Check for hardcoded values."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
if re.search(r"[^0-9a-zA-Z](?:1[0-9]{3}|[2-9][0-9]{3,})(?![0-9])", line):
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "HARDCODED_VALUE",
|
||||
"severity": "warning",
|
||||
"file": str(path),
|
||||
"line": line_num,
|
||||
"message": "Large hardcoded numeric value detected",
|
||||
"suggestion": "Extract to a named constant with descriptive name",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_unnecessary_boxing(self, content: str, path: Path) -> list[dict[str, Any]]:
|
||||
"""Check for unnecessary Box<T> usage."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
pattern = re.compile(r"Box::new\([^)]+\)")
|
||||
matches = pattern.findall(content)
|
||||
for match in matches:
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "UNNECESSARY_BOXING",
|
||||
"severity": "info",
|
||||
"file": str(path),
|
||||
"line": 0,
|
||||
"message": "Unnecessary Box::new() - consider if Box is needed",
|
||||
"suggestion": "Only use Box for recursive types, trait objects, or large stacks",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
|
||||
def _check_into_iter_consumed(self, content: str, path: Path) -> list[dict[str, Any]]:
|
||||
"""Check for into_iter() which consumes the collection."""
|
||||
issues: list[dict[str, Any]] = []
|
||||
|
||||
pattern = re.compile(r"\.into_iter\(\)")
|
||||
matches = pattern.findall(content)
|
||||
for match in matches:
|
||||
issues.append(
|
||||
{
|
||||
"pattern": "INTO_ITER_CONSUMPTION",
|
||||
"severity": "info",
|
||||
"file": str(path),
|
||||
"line": 0,
|
||||
"message": "into_iter() consumes the collection - consider iter() or iter_mut()",
|
||||
"suggestion": "Use .iter() for borrowing, .iter_mut() for mutable borrowing",
|
||||
"language": self.LANGUAGE_NAME,
|
||||
}
|
||||
)
|
||||
|
||||
return issues
|
||||
Reference in New Issue
Block a user