use crate::frontend::ast::ExprKind;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::time::{Duration, SystemTime};
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum AnalysisDepth {
Shallow,
Standard,
Deep,
}
#[derive(Debug, Clone)]
pub struct QualityScore {
pub value: f64, pub components: ScoreComponents,
pub grade: Grade, pub confidence: f64, pub cache_hit_rate: f64, }
#[derive(Debug, Clone)]
pub struct ScoreComponents {
pub correctness: f64, pub performance: f64, pub maintainability: f64, pub safety: f64, pub idiomaticity: f64, }
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum Grade {
APlus, A, AMinus, BPlus, B, BMinus, CPlus, C, CMinus, D, F, }
impl Grade {
pub fn from_score(value: f64) -> Self {
match value {
v if v >= 0.97 => Grade::APlus,
v if v >= 0.93 => Grade::A,
v if v >= 0.90 => Grade::AMinus,
v if v >= 0.87 => Grade::BPlus,
v if v >= 0.83 => Grade::B,
v if v >= 0.80 => Grade::BMinus,
v if v >= 0.77 => Grade::CPlus,
v if v >= 0.73 => Grade::C,
v if v >= 0.70 => Grade::CMinus,
v if v >= 0.60 => Grade::D,
_ => Grade::F,
}
}
pub fn to_rank(&self) -> u8 {
use Grade::{AMinus, APlus, BMinus, BPlus, CMinus, CPlus, A, B, C, D, F};
match self {
F => 0,
D => 1,
CMinus => 2,
C => 3,
CPlus => 4,
BMinus => 5,
B => 6,
BPlus => 7,
AMinus => 8,
A => 9,
APlus => 10,
}
}
}
impl std::fmt::Display for Grade {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Grade::APlus => write!(f, "A+"),
Grade::A => write!(f, "A"),
Grade::AMinus => write!(f, "A-"),
Grade::BPlus => write!(f, "B+"),
Grade::B => write!(f, "B"),
Grade::BMinus => write!(f, "B-"),
Grade::CPlus => write!(f, "C+"),
Grade::C => write!(f, "C"),
Grade::CMinus => write!(f, "C-"),
Grade::D => write!(f, "D"),
Grade::F => write!(f, "F"),
}
}
}
#[derive(Debug, Clone)]
pub struct ScoreConfig {
pub correctness_weight: f64,
pub performance_weight: f64,
pub maintainability_weight: f64,
pub safety_weight: f64,
pub idiomaticity_weight: f64,
}
impl Default for ScoreConfig {
fn default() -> Self {
Self {
correctness_weight: 0.35,
performance_weight: 0.25,
maintainability_weight: 0.20,
safety_weight: 0.15,
idiomaticity_weight: 0.05,
}
}
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct CacheKey {
pub file_path: PathBuf,
pub content_hash: u64,
pub depth: AnalysisDepth,
}
#[derive(Debug, Clone)]
pub struct CacheEntry {
pub score: QualityScore,
pub timestamp: SystemTime,
pub dependencies: Vec<PathBuf>,
}
#[derive(Debug)]
pub struct DependencyTracker {
dependencies: HashMap<PathBuf, Vec<PathBuf>>,
file_times: HashMap<PathBuf, SystemTime>,
}
impl Default for DependencyTracker {
fn default() -> Self {
Self::new()
}
}
impl DependencyTracker {
pub fn new() -> Self {
Self {
dependencies: HashMap::new(),
file_times: HashMap::new(),
}
}
pub fn track_dependency(&mut self, file: PathBuf, dependency: PathBuf) {
self.dependencies.entry(file).or_default().push(dependency);
}
pub fn is_stale(&self, file: &PathBuf) -> bool {
if let Some(dependencies) = self.dependencies.get(file) {
for dep in dependencies {
if self.is_file_modified(dep) {
return true;
}
}
}
false
}
fn is_file_modified(&self, file: &PathBuf) -> bool {
let Ok(metadata) = fs::metadata(file) else {
return true;
};
let Ok(modified) = metadata.modified() else {
return true;
};
if let Some(&cached_time) = self.file_times.get(file) {
modified > cached_time
} else {
true
}
}
pub fn update_file_time(&mut self, file: PathBuf) {
if let Ok(metadata) = fs::metadata(&file) {
if let Ok(modified) = metadata.modified() {
self.file_times.insert(file, modified);
}
}
}
}
pub struct ScoreEngine {
config: ScoreConfig,
cache: HashMap<CacheKey, CacheEntry>,
dependency_tracker: DependencyTracker,
}
impl ScoreEngine {
pub fn new(config: ScoreConfig) -> Self {
Self {
config,
cache: HashMap::new(),
dependency_tracker: DependencyTracker::new(),
}
}
pub fn score(&self, ast: &crate::frontend::ast::Expr, depth: AnalysisDepth) -> QualityScore {
let components = match depth {
AnalysisDepth::Shallow => Self::score_shallow(ast),
AnalysisDepth::Standard => Self::score_standard(ast),
AnalysisDepth::Deep => Self::score_deep(ast),
};
let value = self.calculate_weighted_score(&components);
let grade = Grade::from_score(value);
let confidence = Self::calculate_confidence(depth);
QualityScore {
value,
components,
grade,
confidence,
cache_hit_rate: 0.0, }
}
pub fn score_incremental(
&mut self,
ast: &crate::frontend::ast::Expr,
file_path: PathBuf,
content: &str,
depth: AnalysisDepth,
) -> QualityScore {
let content_hash = Self::hash_content(content);
let cache_key = CacheKey {
file_path: file_path.clone(),
content_hash,
depth,
};
if let Some(entry) = self.cache.get(&cache_key) {
if !self.dependency_tracker.is_stale(&file_path) {
let mut score = entry.score.clone();
score.cache_hit_rate = 1.0;
return score;
}
}
let start = std::time::Instant::now();
let is_small_file = content.len() < 1024;
let effective_depth = if is_small_file && depth != AnalysisDepth::Deep {
AnalysisDepth::Shallow
} else {
depth
};
let components = match effective_depth {
AnalysisDepth::Shallow => Self::score_shallow(ast),
AnalysisDepth::Standard => Self::score_standard(ast),
AnalysisDepth::Deep => Self::score_deep(ast),
};
let value = self.calculate_weighted_score(&components);
let grade = Grade::from_score(value);
let confidence = if is_small_file && depth != effective_depth {
Self::calculate_confidence(effective_depth) * 0.9 } else {
Self::calculate_confidence(depth)
};
let elapsed = start.elapsed();
let score = QualityScore {
value,
components,
grade,
confidence,
cache_hit_rate: 0.0,
};
let is_worth_caching = elapsed > Duration::from_millis(10) || !is_small_file;
if is_worth_caching {
let entry = CacheEntry {
score: score.clone(),
timestamp: SystemTime::now(),
dependencies: Self::extract_dependencies(ast),
};
self.cache.insert(cache_key, entry);
}
self.dependency_tracker.update_file_time(file_path);
if elapsed > Duration::from_millis(100) || self.cache.len() > 1000 {
self.optimize_cache();
}
score
}
pub fn score_progressive(
&mut self,
ast: &crate::frontend::ast::Expr,
file_path: PathBuf,
content: &str,
time_budget: Duration,
) -> QualityScore {
let start = std::time::Instant::now();
let mut score =
self.score_incremental(ast, file_path.clone(), content, AnalysisDepth::Shallow);
if start.elapsed() < time_budget / 3 {
score =
self.score_incremental(ast, file_path.clone(), content, AnalysisDepth::Standard);
if start.elapsed() < time_budget * 2 / 3 {
score = self.score_incremental(ast, file_path, content, AnalysisDepth::Deep);
}
}
score
}
fn hash_content(content: &str) -> u64 {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
hasher.finish()
}
fn extract_dependencies(_ast: &crate::frontend::ast::Expr) -> Vec<PathBuf> {
Vec::new()
}
fn optimize_cache(&mut self) {
let now = SystemTime::now();
let cutoff = Duration::from_secs(300); let max_entries = 500; self.cache.retain(|_, entry| {
if let Ok(age) = now.duration_since(entry.timestamp) {
age < cutoff
} else {
false
}
});
if self.cache.len() > max_entries {
let mut entries: Vec<_> = self
.cache
.iter()
.map(|(k, v)| (k.clone(), v.timestamp))
.collect();
entries.sort_by_key(|(_, timestamp)| *timestamp);
let to_remove = self.cache.len() - max_entries;
let keys_to_remove: Vec<_> = entries
.iter()
.take(to_remove)
.map(|(k, _)| k.clone())
.collect();
for key in keys_to_remove {
self.cache.remove(&key);
}
}
}
pub fn clear_cache(&mut self) {
self.cache.clear();
self.dependency_tracker = DependencyTracker::new();
}
pub fn cache_stats(&self) -> CacheStats {
CacheStats {
entries: self.cache.len(),
memory_usage_estimate: self.cache.len() * 1024, }
}
fn score_shallow(ast: &crate::frontend::ast::Expr) -> ScoreComponents {
let metrics = analyze_ast_metrics(ast);
let correctness = 1.0;
let mut performance = 1.0;
let mut maintainability = 1.0;
let safety = 1.0;
let idiomaticity = 1.0;
if metrics.max_depth > 10 {
maintainability *= 0.9;
}
if metrics.function_count > 50 {
maintainability *= 0.95;
}
if metrics.max_nesting > 5 {
performance *= 0.9;
maintainability *= 0.9;
}
if metrics.line_count > 1000 {
maintainability *= 0.95;
}
ScoreComponents {
correctness,
performance,
maintainability,
safety,
idiomaticity,
}
}
fn score_standard(ast: &crate::frontend::ast::Expr) -> ScoreComponents {
let mut components = Self::score_shallow(ast);
components.correctness *= 0.95;
components.safety *= 0.95;
components
}
fn score_deep(ast: &crate::frontend::ast::Expr) -> ScoreComponents {
let mut components = Self::score_standard(ast);
components.correctness *= 0.98;
components
}
fn calculate_weighted_score(&self, components: &ScoreComponents) -> f64 {
components.correctness * self.config.correctness_weight
+ components.performance * self.config.performance_weight
+ components.maintainability * self.config.maintainability_weight
+ components.safety * self.config.safety_weight
+ components.idiomaticity * self.config.idiomaticity_weight
}
fn calculate_confidence(depth: AnalysisDepth) -> f64 {
match depth {
AnalysisDepth::Shallow => 0.6,
AnalysisDepth::Standard => 0.8,
AnalysisDepth::Deep => 0.95,
}
}
}
#[derive(Debug)]
struct AstMetrics {
function_count: usize,
max_depth: usize,
max_nesting: usize,
line_count: usize,
cyclomatic_complexity: usize,
}
fn analyze_ast_metrics(ast: &crate::frontend::ast::Expr) -> AstMetrics {
let mut metrics = AstMetrics {
function_count: 0,
max_depth: 0,
max_nesting: 0,
line_count: 0,
cyclomatic_complexity: 1, };
analyze_expr(ast, &mut metrics, 0, 0);
metrics
}
fn analyze_expr(
expr: &crate::frontend::ast::Expr,
metrics: &mut AstMetrics,
depth: usize,
nesting: usize,
) {
metrics.max_depth = metrics.max_depth.max(depth);
metrics.max_nesting = metrics.max_nesting.max(nesting);
match &expr.kind {
ExprKind::Function { body, .. } => analyze_function(body, metrics, depth),
ExprKind::Block(exprs) => analyze_block(exprs, metrics, depth, nesting),
ExprKind::If {
condition,
then_branch,
else_branch,
} => analyze_if(
condition,
then_branch,
else_branch.as_deref(),
metrics,
depth,
nesting,
),
ExprKind::While {
condition, body, ..
} => analyze_while(condition, body, metrics, depth, nesting),
ExprKind::For { iter, body, .. } => analyze_for(iter, body, metrics, depth, nesting),
ExprKind::Match {
expr: match_expr,
arms,
} => analyze_match(match_expr, arms, metrics, depth, nesting),
_ => {}
}
}
fn analyze_function(body: &crate::frontend::ast::Expr, metrics: &mut AstMetrics, depth: usize) {
metrics.function_count += 1;
analyze_expr(body, metrics, depth + 1, 0);
}
fn analyze_block(
exprs: &[crate::frontend::ast::Expr],
metrics: &mut AstMetrics,
depth: usize,
nesting: usize,
) {
for e in exprs {
analyze_expr(e, metrics, depth + 1, nesting);
}
}
fn analyze_if(
condition: &crate::frontend::ast::Expr,
then_branch: &crate::frontend::ast::Expr,
else_branch: Option<&crate::frontend::ast::Expr>,
metrics: &mut AstMetrics,
depth: usize,
nesting: usize,
) {
metrics.cyclomatic_complexity += 1;
analyze_expr(condition, metrics, depth + 1, nesting + 1);
analyze_expr(then_branch, metrics, depth + 1, nesting + 1);
if let Some(else_expr) = else_branch {
analyze_expr(else_expr, metrics, depth + 1, nesting + 1);
}
}
fn analyze_while(
condition: &crate::frontend::ast::Expr,
body: &crate::frontend::ast::Expr,
metrics: &mut AstMetrics,
depth: usize,
nesting: usize,
) {
metrics.cyclomatic_complexity += 1;
analyze_expr(condition, metrics, depth + 1, nesting + 1);
analyze_expr(body, metrics, depth + 1, nesting + 1);
}
fn analyze_for(
iter: &crate::frontend::ast::Expr,
body: &crate::frontend::ast::Expr,
metrics: &mut AstMetrics,
depth: usize,
nesting: usize,
) {
metrics.cyclomatic_complexity += 1;
analyze_expr(iter, metrics, depth + 1, nesting + 1);
analyze_expr(body, metrics, depth + 1, nesting + 1);
}
fn analyze_match(
match_expr: &crate::frontend::ast::Expr,
arms: &[crate::frontend::ast::MatchArm],
metrics: &mut AstMetrics,
depth: usize,
nesting: usize,
) {
analyze_expr(match_expr, metrics, depth + 1, nesting);
for arm in arms {
metrics.cyclomatic_complexity += 1;
analyze_expr(&arm.body, metrics, depth + 1, nesting + 1);
}
}
impl QualityScore {
pub fn explain_delta(&self, baseline: &QualityScore) -> ScoreExplanation {
let delta = self.value - baseline.value;
let mut changes = Vec::new();
let mut tradeoffs = Vec::new();
let components = [
(
"Correctness",
self.components.correctness,
baseline.components.correctness,
),
(
"Performance",
self.components.performance,
baseline.components.performance,
),
(
"Maintainability",
self.components.maintainability,
baseline.components.maintainability,
),
("Safety", self.components.safety, baseline.components.safety),
(
"Idiomaticity",
self.components.idiomaticity,
baseline.components.idiomaticity,
),
];
for (name, current, baseline) in components {
let diff = current - baseline;
if diff.abs() > 0.01 {
changes.push(format!(
"{}: {}{:.1}%",
name,
if diff > 0.0 { "+" } else { "" },
diff * 100.0
));
}
}
if self.components.performance > baseline.components.performance
&& self.components.maintainability < baseline.components.maintainability
{
tradeoffs.push("Performance improved at the cost of maintainability".to_string());
}
if self.components.safety > baseline.components.safety
&& self.components.performance < baseline.components.performance
{
tradeoffs.push("Safety improved at the cost of performance".to_string());
}
ScoreExplanation {
delta,
changes,
tradeoffs,
grade_change: format!("{} → {}", baseline.grade, self.grade),
}
}
}
pub struct ScoreExplanation {
pub delta: f64,
pub changes: Vec<String>,
pub tradeoffs: Vec<String>,
pub grade_change: String,
}
#[derive(Debug, Clone)]
pub struct CacheStats {
pub entries: usize,
pub memory_usage_estimate: usize,
}
pub fn score_correctness(ast: &crate::frontend::ast::Expr) -> f64 {
let mut score = 1.0;
let pattern_completeness = analyze_pattern_completeness(ast);
score *= pattern_completeness;
let error_handling_quality = analyze_error_handling(ast);
score *= error_handling_quality;
let type_consistency = analyze_type_consistency(ast);
score *= type_consistency;
let logical_soundness = analyze_logical_soundness(ast);
score *= logical_soundness;
score.clamp(0.0, 1.0)
}
fn analyze_pattern_completeness(ast: &crate::frontend::ast::Expr) -> f64 {
let mut total_matches = 0;
let mut complete_matches = 0;
analyze_pattern_completeness_recursive(ast, &mut total_matches, &mut complete_matches);
if total_matches == 0 {
1.0 } else {
#[allow(clippy::cast_precision_loss)]
let score = (complete_matches as f64) / (total_matches as f64);
score
}
}
fn analyze_pattern_completeness_recursive(
expr: &crate::frontend::ast::Expr,
total_matches: &mut usize,
complete_matches: &mut usize,
) {
match &expr.kind {
ExprKind::Match {
expr: match_expr,
arms,
} => {
*total_matches += 1;
let has_wildcard = arms
.iter()
.any(|arm| matches!(arm.pattern, crate::frontend::ast::Pattern::Wildcard));
if has_wildcard || arms.len() >= 2 {
*complete_matches += 1;
}
analyze_pattern_completeness_recursive(match_expr, total_matches, complete_matches);
for arm in arms {
analyze_pattern_completeness_recursive(&arm.body, total_matches, complete_matches);
}
}
ExprKind::If {
condition,
then_branch,
else_branch,
} => {
analyze_pattern_completeness_recursive(condition, total_matches, complete_matches);
analyze_pattern_completeness_recursive(then_branch, total_matches, complete_matches);
if let Some(else_expr) = else_branch {
analyze_pattern_completeness_recursive(else_expr, total_matches, complete_matches);
}
}
ExprKind::Block(exprs) => {
for e in exprs {
analyze_pattern_completeness_recursive(e, total_matches, complete_matches);
}
}
ExprKind::Function { body, .. } => {
analyze_pattern_completeness_recursive(body, total_matches, complete_matches);
}
_ => {}
}
}
fn analyze_error_handling(ast: &crate::frontend::ast::Expr) -> f64 {
let mut total_fallible_ops = 0;
let mut handled_ops = 0;
analyze_error_handling_recursive(ast, &mut total_fallible_ops, &mut handled_ops);
if total_fallible_ops == 0 {
1.0 } else {
#[allow(clippy::cast_precision_loss)]
let base_score = (handled_ops as f64) / (total_fallible_ops as f64);
if handled_ops > 0 {
(base_score + 0.3).min(1.0)
} else {
0.7 }
}
}
fn analyze_error_handling_recursive(
expr: &crate::frontend::ast::Expr,
total_fallible_ops: &mut usize,
handled_ops: &mut usize,
) {
match &expr.kind {
ExprKind::Match {
expr: match_expr,
arms,
} => {
if arms.len() >= 2 {
*total_fallible_ops += 1;
*handled_ops += 1;
}
analyze_error_handling_recursive(match_expr, total_fallible_ops, handled_ops);
for arm in arms {
analyze_error_handling_recursive(&arm.body, total_fallible_ops, handled_ops);
}
}
ExprKind::Block(exprs) => {
for e in exprs {
analyze_error_handling_recursive(e, total_fallible_ops, handled_ops);
}
}
ExprKind::Function { body, .. } => {
analyze_error_handling_recursive(body, total_fallible_ops, handled_ops);
}
_ => {}
}
}
fn analyze_type_consistency(_ast: &crate::frontend::ast::Expr) -> f64 {
0.95
}
fn analyze_logical_soundness(ast: &crate::frontend::ast::Expr) -> f64 {
let mut score = 1.0;
let has_unreachable = has_unreachable_code(ast);
if has_unreachable {
score *= 0.8; }
let has_infinite_loops = has_potential_infinite_loops(ast);
if has_infinite_loops {
score *= 0.9; }
score
}
fn has_unreachable_code(ast: &crate::frontend::ast::Expr) -> bool {
match &ast.kind {
ExprKind::Block(exprs) => check_block_unreachable(exprs),
ExprKind::If {
condition,
then_branch,
else_branch,
} => check_if_unreachable(condition, then_branch, else_branch.as_deref()),
_ => false,
}
}
fn check_block_unreachable(exprs: &[crate::frontend::ast::Expr]) -> bool {
for (i, expr) in exprs.iter().enumerate() {
if i < exprs.len() - 1 && is_diverging_expr(expr) {
return true; }
if has_unreachable_code(expr) {
return true;
}
}
false
}
fn check_if_unreachable(
condition: &crate::frontend::ast::Expr,
then_branch: &crate::frontend::ast::Expr,
else_branch: Option<&crate::frontend::ast::Expr>,
) -> bool {
has_unreachable_code(condition)
|| has_unreachable_code(then_branch)
|| else_branch
.as_ref()
.is_some_and(|e| has_unreachable_code(e))
}
fn is_diverging_expr(expr: &crate::frontend::ast::Expr) -> bool {
match &expr.kind {
ExprKind::Call { func, .. } => {
if let ExprKind::Identifier(name) = &func.kind {
matches!(name.as_str(), "panic" | "unreachable" | "exit")
} else {
false
}
}
_ => false,
}
}
fn has_potential_infinite_loops(ast: &crate::frontend::ast::Expr) -> bool {
match &ast.kind {
ExprKind::While {
condition, body, ..
} => {
if let ExprKind::Literal(crate::frontend::ast::Literal::Bool(true)) = &condition.kind {
!has_break_statement(body)
} else {
has_potential_infinite_loops(condition) || has_potential_infinite_loops(body)
}
}
ExprKind::Block(exprs) => exprs.iter().any(has_potential_infinite_loops),
ExprKind::Function { body, .. } => has_potential_infinite_loops(body),
_ => false,
}
}
fn has_break_statement(ast: &crate::frontend::ast::Expr) -> bool {
match &ast.kind {
ExprKind::Break { .. } => true,
ExprKind::Block(exprs) => exprs.iter().any(has_break_statement),
ExprKind::If {
condition,
then_branch,
else_branch,
} => {
has_break_statement(condition)
|| has_break_statement(then_branch)
|| else_branch.as_ref().is_some_and(|e| has_break_statement(e))
}
_ => false,
}
}
pub fn score_performance(ast: &crate::frontend::ast::Expr) -> f64 {
let metrics = analyze_ast_metrics(ast);
let mut score = 1.0;
let complexity_score = analyze_algorithmic_complexity(ast);
score *= complexity_score;
if metrics.cyclomatic_complexity > 10 {
#[allow(clippy::cast_precision_loss)]
let penalty = ((metrics.cyclomatic_complexity - 10) as f64 * 0.02).min(0.3);
score *= 1.0 - penalty;
}
if metrics.max_nesting > 3 {
#[allow(clippy::cast_precision_loss)]
let penalty = ((metrics.max_nesting - 3) as f64 * 0.05).min(0.2);
score *= 1.0 - penalty;
}
let allocation_score = analyze_allocation_patterns(ast);
score *= allocation_score;
score.clamp(0.0, 1.0)
}
fn analyze_algorithmic_complexity(ast: &crate::frontend::ast::Expr) -> f64 {
let mut nested_loops = 0;
let mut recursive_calls = 0;
analyze_complexity_recursive(ast, &mut nested_loops, &mut recursive_calls, 0);
let mut score = 1.0;
if nested_loops > 0 {
#[allow(clippy::cast_precision_loss)]
let penalty = (f64::from(nested_loops) * 0.15).min(0.5);
score *= 1.0 - penalty;
}
if recursive_calls > 2 {
score *= 0.8; }
score
}
fn analyze_complexity_recursive(
expr: &crate::frontend::ast::Expr,
nested_loops: &mut i32,
recursive_calls: &mut i32,
current_nesting: i32,
) {
match &expr.kind {
ExprKind::For { iter, body, .. }
| ExprKind::While {
condition: iter,
body,
..
} => analyze_loop_complexity(iter, body, nested_loops, recursive_calls, current_nesting),
ExprKind::Call { func, args } => {
analyze_call_complexity(func, args, nested_loops, recursive_calls, current_nesting);
}
ExprKind::Block(exprs) => {
analyze_block_complexity(exprs, nested_loops, recursive_calls, current_nesting);
}
ExprKind::Function { body, .. } => {
analyze_complexity_recursive(body, nested_loops, recursive_calls, 0);
}
ExprKind::If {
condition,
then_branch,
else_branch,
} => analyze_if_complexity(
condition,
then_branch,
else_branch.as_deref(),
nested_loops,
recursive_calls,
current_nesting,
),
_ => {}
}
}
fn analyze_loop_complexity(
iter: &crate::frontend::ast::Expr,
body: &crate::frontend::ast::Expr,
nested_loops: &mut i32,
recursive_calls: &mut i32,
current_nesting: i32,
) {
if current_nesting > 0 {
*nested_loops += 1;
}
analyze_complexity_recursive(iter, nested_loops, recursive_calls, current_nesting);
analyze_complexity_recursive(body, nested_loops, recursive_calls, current_nesting + 1);
}
fn analyze_call_complexity(
func: &crate::frontend::ast::Expr,
args: &[crate::frontend::ast::Expr],
nested_loops: &mut i32,
recursive_calls: &mut i32,
current_nesting: i32,
) {
if let ExprKind::Identifier(_) = &func.kind {
*recursive_calls += 1;
}
analyze_complexity_recursive(func, nested_loops, recursive_calls, current_nesting);
for arg in args {
analyze_complexity_recursive(arg, nested_loops, recursive_calls, current_nesting);
}
}
fn analyze_block_complexity(
exprs: &[crate::frontend::ast::Expr],
nested_loops: &mut i32,
recursive_calls: &mut i32,
current_nesting: i32,
) {
for e in exprs {
analyze_complexity_recursive(e, nested_loops, recursive_calls, current_nesting);
}
}
fn analyze_if_complexity(
condition: &crate::frontend::ast::Expr,
then_branch: &crate::frontend::ast::Expr,
else_branch: Option<&crate::frontend::ast::Expr>,
nested_loops: &mut i32,
recursive_calls: &mut i32,
current_nesting: i32,
) {
analyze_complexity_recursive(condition, nested_loops, recursive_calls, current_nesting);
analyze_complexity_recursive(then_branch, nested_loops, recursive_calls, current_nesting);
if let Some(else_expr) = else_branch {
analyze_complexity_recursive(else_expr, nested_loops, recursive_calls, current_nesting);
}
}
fn analyze_allocation_patterns(ast: &crate::frontend::ast::Expr) -> f64 {
let mut allocations = 0;
let mut large_allocations = 0;
count_allocations_recursive(ast, &mut allocations, &mut large_allocations);
let mut score = 1.0;
if allocations > 10 {
#[allow(clippy::cast_precision_loss)]
let penalty = (f64::from(allocations - 10) * 0.01).min(0.3);
score *= 1.0 - penalty;
}
if large_allocations > 0 {
#[allow(clippy::cast_precision_loss)]
let penalty = (f64::from(large_allocations) * 0.1).min(0.4);
score *= 1.0 - penalty;
}
score
}
fn count_allocations_recursive(
expr: &crate::frontend::ast::Expr,
allocations: &mut i32,
large_allocations: &mut i32,
) {
match &expr.kind {
ExprKind::List(items) => {
*allocations += 1;
if items.len() > 100 {
*large_allocations += 1;
}
for item in items {
count_allocations_recursive(item, allocations, large_allocations);
}
}
ExprKind::StringInterpolation { parts } => {
*allocations += 1; for part in parts {
if let crate::frontend::ast::StringPart::Expr(e) = part {
count_allocations_recursive(e, allocations, large_allocations);
}
}
}
ExprKind::Block(exprs) => {
for e in exprs {
count_allocations_recursive(e, allocations, large_allocations);
}
}
ExprKind::Function { body, .. } => {
count_allocations_recursive(body, allocations, large_allocations);
}
_ => {}
}
}
pub fn score_maintainability(ast: &crate::frontend::ast::Expr) -> f64 {
let metrics = analyze_ast_metrics(ast);
let mut score = 1.0;
let coupling_score = analyze_coupling(ast);
score *= coupling_score;
let cohesion_score = analyze_cohesion(ast, &metrics);
score *= cohesion_score;
let duplication_score = analyze_duplication(ast);
score *= duplication_score;
let naming_score = analyze_naming_quality(ast);
score *= naming_score;
score.clamp(0.0, 1.0)
}
fn analyze_coupling(ast: &crate::frontend::ast::Expr) -> f64 {
let mut external_calls = 0;
let mut total_functions = 0;
count_coupling_metrics(ast, &mut external_calls, &mut total_functions);
if total_functions == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
let coupling_ratio = f64::from(external_calls) / f64::from(total_functions);
if coupling_ratio > 5.0 {
0.7 } else if coupling_ratio > 2.0 {
0.85
} else {
1.0 }
}
fn count_coupling_metrics(
expr: &crate::frontend::ast::Expr,
external_calls: &mut i32,
total_functions: &mut i32,
) {
match &expr.kind {
ExprKind::Function { body, .. } => {
*total_functions += 1;
count_coupling_metrics(body, external_calls, total_functions);
}
ExprKind::Call { func, args } => {
*external_calls += 1;
count_coupling_metrics(func, external_calls, total_functions);
for arg in args {
count_coupling_metrics(arg, external_calls, total_functions);
}
}
ExprKind::Block(exprs) => {
for e in exprs {
count_coupling_metrics(e, external_calls, total_functions);
}
}
_ => {}
}
}
fn analyze_cohesion(_ast: &crate::frontend::ast::Expr, metrics: &AstMetrics) -> f64 {
let mut score = 1.0;
if metrics.line_count > 100 {
score *= 0.8;
}
if metrics.max_depth > 15 {
score *= 0.85;
}
if metrics.function_count > 30 {
score *= 0.9;
}
score
}
fn analyze_duplication(ast: &crate::frontend::ast::Expr) -> f64 {
let mut expression_patterns = std::collections::HashMap::new();
collect_expression_patterns(ast, &mut expression_patterns);
let duplicated_patterns = expression_patterns
.values()
.filter(|&&count| count > 1)
.count();
if duplicated_patterns > 5 {
0.8 } else if duplicated_patterns > 2 {
0.9 } else {
1.0 }
}
fn collect_expression_patterns(
expr: &crate::frontend::ast::Expr,
patterns: &mut std::collections::HashMap<String, i32>,
) {
let pattern = format!("{:?}", std::mem::discriminant(&expr.kind));
*patterns.entry(pattern).or_insert(0) += 1;
match &expr.kind {
ExprKind::Block(exprs) => {
for e in exprs {
collect_expression_patterns(e, patterns);
}
}
ExprKind::Function { body, .. } => {
collect_expression_patterns(body, patterns);
}
ExprKind::If {
condition,
then_branch,
else_branch,
} => {
collect_expression_patterns(condition, patterns);
collect_expression_patterns(then_branch, patterns);
if let Some(else_expr) = else_branch {
collect_expression_patterns(else_expr, patterns);
}
}
_ => {}
}
}
fn analyze_naming_quality(ast: &crate::frontend::ast::Expr) -> f64 {
let mut good_names = 0;
let mut total_names = 0;
analyze_names_recursive(ast, &mut good_names, &mut total_names);
if total_names == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
let good_ratio = f64::from(good_names) / f64::from(total_names);
good_ratio.max(0.5) }
fn analyze_names_recursive(
expr: &crate::frontend::ast::Expr,
good_names: &mut i32,
total_names: &mut i32,
) {
match &expr.kind {
ExprKind::Function { name, .. } => {
*total_names += 1;
if is_good_name(name) {
*good_names += 1;
}
}
ExprKind::Let { name, body, .. } => {
*total_names += 1;
if is_good_name(name) {
*good_names += 1;
}
analyze_names_recursive(body, good_names, total_names);
}
ExprKind::Block(exprs) => {
for e in exprs {
analyze_names_recursive(e, good_names, total_names);
}
}
_ => {}
}
}
fn is_good_name(name: &str) -> bool {
if name.len() < 2 || name.starts_with('_') {
return false;
}
name.len() >= 3 && !name.chars().all(|c| c.is_ascii_uppercase())
}
pub fn score_safety(ast: &crate::frontend::ast::Expr) -> f64 {
let mut score = 1.0;
let error_handling_quality = analyze_error_handling(ast);
score *= error_handling_quality;
let null_safety_score = analyze_null_safety(ast);
score *= null_safety_score;
let resource_score = analyze_resource_management(ast);
score *= resource_score;
score *= 0.95; score.clamp(0.0, 1.0)
}
fn analyze_null_safety(ast: &crate::frontend::ast::Expr) -> f64 {
let mut option_uses = 0;
let mut unsafe_accesses = 0;
analyze_null_safety_recursive(ast, &mut option_uses, &mut unsafe_accesses);
if option_uses + unsafe_accesses == 0 {
return 1.0; }
if unsafe_accesses == 0 {
1.0 } else {
#[allow(clippy::cast_precision_loss)]
let safety_ratio = f64::from(option_uses) / f64::from(option_uses + unsafe_accesses);
safety_ratio.max(0.5) }
}
fn analyze_null_safety_recursive(
expr: &crate::frontend::ast::Expr,
option_uses: &mut i32,
unsafe_accesses: &mut i32,
) {
match &expr.kind {
ExprKind::Some { .. } | ExprKind::None => {
*option_uses += 1;
}
ExprKind::Match { arms, .. } => {
if arms.len() >= 2 {
*option_uses += 1;
}
for arm in arms {
analyze_null_safety_recursive(&arm.body, option_uses, unsafe_accesses);
}
}
ExprKind::Block(exprs) => {
for e in exprs {
analyze_null_safety_recursive(e, option_uses, unsafe_accesses);
}
}
ExprKind::Function { body, .. } => {
analyze_null_safety_recursive(body, option_uses, unsafe_accesses);
}
_ => {}
}
}
fn analyze_resource_management(ast: &crate::frontend::ast::Expr) -> f64 {
let mut resource_allocations = 0;
let mut proper_cleanup = 0;
analyze_resources_recursive(ast, &mut resource_allocations, &mut proper_cleanup);
if resource_allocations == 0 {
return 1.0; }
#[allow(clippy::cast_precision_loss)]
let cleanup_ratio = f64::from(proper_cleanup) / f64::from(resource_allocations);
cleanup_ratio.max(0.7) }
fn analyze_resources_recursive(
expr: &crate::frontend::ast::Expr,
allocations: &mut i32,
cleanup: &mut i32,
) {
match &expr.kind {
ExprKind::Block(exprs) => {
for e in exprs {
analyze_resources_recursive(e, allocations, cleanup);
}
}
ExprKind::Function { body, .. } => {
analyze_resources_recursive(body, allocations, cleanup);
}
_ => {}
}
}
pub fn score_idiomaticity(ast: &crate::frontend::ast::Expr) -> f64 {
let mut score = 1.0;
let pattern_score = analyze_pattern_usage(ast);
score *= pattern_score;
let iterator_score = analyze_iterator_usage(ast);
score *= iterator_score;
let lambda_score = analyze_lambda_usage(ast);
score *= lambda_score;
score.clamp(0.0, 1.0)
}
fn analyze_pattern_usage(ast: &crate::frontend::ast::Expr) -> f64 {
let mut matches = 0;
let mut conditionals = 0;
count_pattern_vs_conditional(ast, &mut matches, &mut conditionals);
let total = matches + conditionals;
if total == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
let pattern_ratio = f64::from(matches) / f64::from(total);
if pattern_ratio > 0.7 {
1.0
} else if pattern_ratio > 0.4 {
0.9
} else {
0.8
}
}
fn count_pattern_vs_conditional(
expr: &crate::frontend::ast::Expr,
matches: &mut i32,
conditionals: &mut i32,
) {
match &expr.kind {
ExprKind::Match { arms, .. } => {
*matches += 1;
for arm in arms {
count_pattern_vs_conditional(&arm.body, matches, conditionals);
}
}
ExprKind::If {
condition,
then_branch,
else_branch,
} => {
*conditionals += 1;
count_pattern_vs_conditional(condition, matches, conditionals);
count_pattern_vs_conditional(then_branch, matches, conditionals);
if let Some(else_expr) = else_branch {
count_pattern_vs_conditional(else_expr, matches, conditionals);
}
}
ExprKind::Block(exprs) => {
for e in exprs {
count_pattern_vs_conditional(e, matches, conditionals);
}
}
ExprKind::Function { body, .. } => {
count_pattern_vs_conditional(body, matches, conditionals);
}
_ => {}
}
}
fn analyze_iterator_usage(ast: &crate::frontend::ast::Expr) -> f64 {
let mut iterators = 0;
let mut loops = 0;
count_iterator_vs_loops(ast, &mut iterators, &mut loops);
let total = iterators + loops;
if total == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
let iterator_ratio = f64::from(iterators) / f64::from(total);
if iterator_ratio > 0.6 {
1.0
} else if iterator_ratio > 0.3 {
0.9
} else {
0.8
}
}
fn count_iterator_vs_loops(
expr: &crate::frontend::ast::Expr,
iterators: &mut i32,
loops: &mut i32,
) {
match &expr.kind {
ExprKind::For { .. } => {
*iterators += 1; }
ExprKind::While { .. } => {
*loops += 1;
}
ExprKind::Block(exprs) => {
for e in exprs {
count_iterator_vs_loops(e, iterators, loops);
}
}
ExprKind::Function { body, .. } => {
count_iterator_vs_loops(body, iterators, loops);
}
_ => {}
}
}
fn analyze_lambda_usage(ast: &crate::frontend::ast::Expr) -> f64 {
let mut lambdas = 0;
let mut total_functions = 0;
count_lambda_usage(ast, &mut lambdas, &mut total_functions);
if total_functions == 0 {
return 1.0;
}
#[allow(clippy::cast_precision_loss)]
let lambda_ratio = f64::from(lambdas) / f64::from(total_functions);
if lambda_ratio > 0.3 {
1.0
} else if lambda_ratio > 0.1 {
0.95
} else {
0.9 }
}
fn count_lambda_usage(
expr: &crate::frontend::ast::Expr,
lambdas: &mut i32,
total_functions: &mut i32,
) {
match &expr.kind {
ExprKind::Lambda { .. } => {
*lambdas += 1;
*total_functions += 1;
}
ExprKind::Function { body, .. } => {
*total_functions += 1;
count_lambda_usage(body, lambdas, total_functions);
}
ExprKind::Block(exprs) => {
for e in exprs {
count_lambda_usage(e, lambdas, total_functions);
}
}
_ => {}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_grade_from_score() {
assert_eq!(Grade::from_score(1.0), Grade::APlus);
assert_eq!(Grade::from_score(0.97), Grade::APlus);
assert_eq!(Grade::from_score(0.95), Grade::A);
assert_eq!(Grade::from_score(0.93), Grade::A);
assert_eq!(Grade::from_score(0.91), Grade::AMinus);
assert_eq!(Grade::from_score(0.90), Grade::AMinus);
assert_eq!(Grade::from_score(0.88), Grade::BPlus);
assert_eq!(Grade::from_score(0.85), Grade::B);
assert_eq!(Grade::from_score(0.81), Grade::BMinus);
assert_eq!(Grade::from_score(0.78), Grade::CPlus);
assert_eq!(Grade::from_score(0.75), Grade::C);
assert_eq!(Grade::from_score(0.71), Grade::CMinus);
assert_eq!(Grade::from_score(0.65), Grade::D);
assert_eq!(Grade::from_score(0.5), Grade::F);
assert_eq!(Grade::from_score(0.0), Grade::F);
}
#[test]
fn test_grade_to_rank() {
assert_eq!(Grade::F.to_rank(), 0);
assert_eq!(Grade::D.to_rank(), 1);
assert_eq!(Grade::CMinus.to_rank(), 2);
assert_eq!(Grade::C.to_rank(), 3);
assert_eq!(Grade::CPlus.to_rank(), 4);
assert_eq!(Grade::BMinus.to_rank(), 5);
assert_eq!(Grade::B.to_rank(), 6);
assert_eq!(Grade::BPlus.to_rank(), 7);
assert_eq!(Grade::AMinus.to_rank(), 8);
assert_eq!(Grade::A.to_rank(), 9);
assert_eq!(Grade::APlus.to_rank(), 10);
}
#[test]
fn test_grade_display() {
assert_eq!(format!("{}", Grade::APlus), "A+");
assert_eq!(format!("{}", Grade::A), "A");
assert_eq!(format!("{}", Grade::AMinus), "A-");
assert_eq!(format!("{}", Grade::BPlus), "B+");
assert_eq!(format!("{}", Grade::B), "B");
assert_eq!(format!("{}", Grade::BMinus), "B-");
assert_eq!(format!("{}", Grade::CPlus), "C+");
assert_eq!(format!("{}", Grade::C), "C");
assert_eq!(format!("{}", Grade::CMinus), "C-");
assert_eq!(format!("{}", Grade::D), "D");
assert_eq!(format!("{}", Grade::F), "F");
}
#[test]
fn test_analysis_depth_equality() {
assert_eq!(AnalysisDepth::Shallow, AnalysisDepth::Shallow);
assert_eq!(AnalysisDepth::Standard, AnalysisDepth::Standard);
assert_eq!(AnalysisDepth::Deep, AnalysisDepth::Deep);
assert_ne!(AnalysisDepth::Shallow, AnalysisDepth::Deep);
}
#[test]
fn test_score_components_creation() {
let components = ScoreComponents {
correctness: 0.9,
performance: 0.85,
maintainability: 0.8,
safety: 0.95,
idiomaticity: 0.7,
};
assert_eq!(components.correctness, 0.9);
assert_eq!(components.performance, 0.85);
assert_eq!(components.maintainability, 0.8);
assert_eq!(components.safety, 0.95);
assert_eq!(components.idiomaticity, 0.7);
}
#[test]
fn test_quality_score_creation() {
let components = ScoreComponents {
correctness: 0.9,
performance: 0.85,
maintainability: 0.8,
safety: 0.95,
idiomaticity: 0.7,
};
let score = QualityScore {
value: 0.88,
components,
grade: Grade::BPlus,
confidence: 0.95,
cache_hit_rate: 0.2,
};
assert_eq!(score.value, 0.88);
assert_eq!(score.grade, Grade::BPlus);
assert_eq!(score.confidence, 0.95);
assert_eq!(score.cache_hit_rate, 0.2);
assert_eq!(score.components.correctness, 0.9);
}
#[test]
fn test_quality_score_confidence_levels() {
let mut score = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.0,
cache_hit_rate: 0.0,
};
score.confidence = 0.0;
assert!(score.confidence < 0.5);
score.confidence = 0.5;
assert_eq!(score.confidence, 0.5);
score.confidence = 1.0;
assert_eq!(score.confidence, 1.0);
}
#[test]
fn test_quality_score_cache_hit_rate() {
let mut score = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.9,
cache_hit_rate: 0.0,
};
score.cache_hit_rate = 0.0;
assert_eq!(score.cache_hit_rate, 0.0);
score.cache_hit_rate = 0.5;
assert_eq!(score.cache_hit_rate, 0.5);
score.cache_hit_rate = 1.0;
assert_eq!(score.cache_hit_rate, 1.0);
}
#[test]
fn test_grade_edge_cases() {
assert_eq!(Grade::from_score(0.969_999), Grade::A);
assert_eq!(Grade::from_score(0.97), Grade::APlus);
assert_eq!(Grade::from_score(0.929_999), Grade::AMinus);
assert_eq!(Grade::from_score(0.93), Grade::A);
assert_eq!(Grade::from_score(0.599_999), Grade::F);
assert_eq!(Grade::from_score(0.60), Grade::D);
assert_eq!(Grade::from_score(-0.1), Grade::F);
assert_eq!(Grade::from_score(1.1), Grade::APlus);
}
#[test]
fn test_analysis_depth_display() {
let depths = vec![
AnalysisDepth::Shallow,
AnalysisDepth::Standard,
AnalysisDepth::Deep,
];
for depth in &depths {
assert_eq!(*depth, *depth);
}
assert_ne!(AnalysisDepth::Shallow, AnalysisDepth::Standard);
assert_ne!(AnalysisDepth::Standard, AnalysisDepth::Deep);
}
#[test]
fn test_score_components_weights() {
let components = ScoreComponents {
correctness: 0.35,
performance: 0.25,
maintainability: 0.20,
safety: 0.15,
idiomaticity: 0.05,
};
let sum = components.correctness
+ components.performance
+ components.maintainability
+ components.safety
+ components.idiomaticity;
assert!((sum - 1.0).abs() < 0.001);
}
#[test]
fn test_quality_score_normalization() {
let score = QualityScore {
value: 0.5,
components: ScoreComponents {
correctness: 0.5,
performance: 0.5,
maintainability: 0.5,
safety: 0.5,
idiomaticity: 0.5,
},
grade: Grade::F,
confidence: 0.5,
cache_hit_rate: 0.5,
};
assert!(score.value >= 0.0 && score.value <= 1.0);
assert!(score.confidence >= 0.0 && score.confidence <= 1.0);
assert!(score.cache_hit_rate >= 0.0 && score.cache_hit_rate <= 1.0);
}
#[test]
fn test_grade_ordering() {
assert!(Grade::APlus.to_rank() > Grade::A.to_rank());
assert!(Grade::A.to_rank() > Grade::AMinus.to_rank());
assert!(Grade::AMinus.to_rank() > Grade::BPlus.to_rank());
assert!(Grade::BPlus.to_rank() > Grade::B.to_rank());
assert!(Grade::B.to_rank() > Grade::BMinus.to_rank());
assert!(Grade::BMinus.to_rank() > Grade::CPlus.to_rank());
assert!(Grade::CPlus.to_rank() > Grade::C.to_rank());
assert!(Grade::C.to_rank() > Grade::CMinus.to_rank());
assert!(Grade::CMinus.to_rank() > Grade::D.to_rank());
assert!(Grade::D.to_rank() > Grade::F.to_rank());
}
#[test]
fn test_incremental_cache_behavior() {
let mut score = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.9,
cache_hit_rate: 0.0,
};
score.cache_hit_rate = 0.25;
assert!(score.cache_hit_rate > 0.0);
score.cache_hit_rate = 0.50;
assert!(score.cache_hit_rate > 0.25);
score.cache_hit_rate = 0.75;
assert!(score.cache_hit_rate > 0.50);
}
#[test]
fn test_analysis_depth_performance_tradeoffs() {
let shallow = AnalysisDepth::Shallow;
let standard = AnalysisDepth::Standard;
let deep = AnalysisDepth::Deep;
assert_ne!(shallow, standard);
assert_ne!(standard, deep);
assert_ne!(shallow, deep);
}
#[test]
fn test_score_component_independence() {
let components1 = ScoreComponents {
correctness: 1.0,
performance: 0.0,
maintainability: 0.5,
safety: 0.7,
idiomaticity: 0.3,
};
let components2 = ScoreComponents {
correctness: 0.0,
performance: 1.0,
maintainability: 0.5,
safety: 0.7,
idiomaticity: 0.3,
};
assert_ne!(components1.correctness, components2.correctness);
assert_ne!(components1.performance, components2.performance);
assert_eq!(components1.maintainability, components2.maintainability);
}
#[test]
fn test_grade_display_format() {
assert_eq!(format!("{}", Grade::APlus), "A+");
assert_eq!(format!("{}", Grade::A), "A");
assert_eq!(format!("{}", Grade::AMinus), "A-");
assert_eq!(format!("{}", Grade::BPlus), "B+");
assert_eq!(format!("{}", Grade::B), "B");
assert_eq!(format!("{}", Grade::BMinus), "B-");
assert_eq!(format!("{}", Grade::CPlus), "C+");
assert_eq!(format!("{}", Grade::C), "C");
assert_eq!(format!("{}", Grade::CMinus), "C-");
assert_eq!(format!("{}", Grade::D), "D");
assert_eq!(format!("{}", Grade::F), "F");
}
#[test]
fn test_confidence_levels() {
let low_confidence = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.2,
cache_hit_rate: 0.0,
};
let high_confidence = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.95,
cache_hit_rate: 0.0,
};
assert!(low_confidence.confidence < 0.5);
assert!(high_confidence.confidence > 0.9);
}
#[test]
fn test_quality_score_with_extreme_components() {
let extreme_score = QualityScore {
value: 0.0,
components: ScoreComponents {
correctness: 0.0,
performance: 0.0,
maintainability: 0.0,
safety: 0.0,
idiomaticity: 0.0,
},
grade: Grade::F,
confidence: 1.0,
cache_hit_rate: 0.0,
};
assert_eq!(extreme_score.value, 0.0);
assert_eq!(extreme_score.grade, Grade::F);
let perfect_score = QualityScore {
value: 1.0,
components: ScoreComponents {
correctness: 1.0,
performance: 1.0,
maintainability: 1.0,
safety: 1.0,
idiomaticity: 1.0,
},
grade: Grade::APlus,
confidence: 1.0,
cache_hit_rate: 1.0,
};
assert_eq!(perfect_score.value, 1.0);
assert_eq!(perfect_score.grade, Grade::APlus);
}
#[test]
fn test_grade_transitions() {
let scores = vec![
(0.97, Grade::APlus),
(0.93, Grade::A),
(0.90, Grade::AMinus),
(0.87, Grade::BPlus),
(0.83, Grade::B),
(0.80, Grade::BMinus),
(0.77, Grade::CPlus),
(0.73, Grade::C),
(0.70, Grade::CMinus),
(0.60, Grade::D),
(0.59, Grade::F),
];
for (score, expected_grade) in scores {
assert_eq!(Grade::from_score(score), expected_grade);
}
}
#[test]
fn test_score_component_balance() {
let balanced = ScoreComponents {
correctness: 0.8,
performance: 0.8,
maintainability: 0.8,
safety: 0.8,
idiomaticity: 0.8,
};
let unbalanced = ScoreComponents {
correctness: 1.0,
performance: 0.2,
maintainability: 0.9,
safety: 0.3,
idiomaticity: 1.0,
};
assert_eq!(balanced.correctness, balanced.performance);
assert_eq!(balanced.performance, balanced.maintainability);
assert_ne!(unbalanced.correctness, unbalanced.performance);
assert_ne!(unbalanced.safety, unbalanced.idiomaticity);
}
#[test]
fn test_analysis_depth_hash_equality() {
use std::collections::HashSet;
let mut depth_set = HashSet::new();
depth_set.insert(AnalysisDepth::Shallow);
depth_set.insert(AnalysisDepth::Standard);
depth_set.insert(AnalysisDepth::Deep);
assert_eq!(depth_set.len(), 3);
assert!(depth_set.contains(&AnalysisDepth::Shallow));
assert!(depth_set.contains(&AnalysisDepth::Standard));
assert!(depth_set.contains(&AnalysisDepth::Deep));
}
#[test]
fn test_grade_rank_consistency() {
let test_scores = vec![
0.98, 0.95, 0.92, 0.88, 0.85, 0.82, 0.78, 0.75, 0.72, 0.65, 0.50,
];
for score in test_scores {
let grade1 = Grade::from_score(score);
let grade2 = Grade::from_score(score + 0.001);
assert!(grade2.to_rank() >= grade1.to_rank());
}
}
#[test]
fn test_cache_hit_rate_impact() {
let score1 = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.9,
cache_hit_rate: 0.0,
};
let score2 = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.9,
cache_hit_rate: 1.0,
};
assert_eq!(score1.grade, score2.grade);
assert_eq!(score1.value, score2.value);
}
#[test]
fn test_incremental_scoring_architecture() {
let initial_score = QualityScore {
value: 0.7,
components: ScoreComponents {
correctness: 0.6,
performance: 0.7,
maintainability: 0.8,
safety: 0.7,
idiomaticity: 0.6,
},
grade: Grade::CMinus,
confidence: 0.5,
cache_hit_rate: 0.0,
};
let improved_score = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.8,
safety: 0.9,
idiomaticity: 0.7,
},
grade: Grade::B,
confidence: 0.8,
cache_hit_rate: 0.5,
};
assert!(improved_score.value > initial_score.value);
assert!(improved_score.confidence > initial_score.confidence);
assert!(improved_score.cache_hit_rate > initial_score.cache_hit_rate);
}
#[test]
fn test_component_weight_distribution() {
let expected_weights = [
("correctness", 0.35),
("performance", 0.25),
("maintainability", 0.20),
("safety", 0.15),
("idiomaticity", 0.05),
];
let total: f64 = expected_weights.iter().map(|(_, w)| w).sum();
assert!((total - 1.0).abs() < 0.001);
assert!(expected_weights[0].1 > expected_weights[1].1);
assert!(expected_weights[1].1 > expected_weights[2].1);
}
#[test]
fn test_edge_case_scores() {
assert_eq!(Grade::from_score(f64::NAN), Grade::F);
assert_eq!(Grade::from_score(f64::INFINITY), Grade::APlus);
assert_eq!(Grade::from_score(f64::NEG_INFINITY), Grade::F);
assert_eq!(Grade::from_score(f64::MIN), Grade::F);
assert_eq!(Grade::from_score(f64::MAX), Grade::APlus);
}
#[test]
fn test_score_serialization_compatibility() {
let grades = vec![
Grade::APlus,
Grade::A,
Grade::AMinus,
Grade::BPlus,
Grade::B,
Grade::BMinus,
Grade::CPlus,
Grade::C,
Grade::CMinus,
Grade::D,
Grade::F,
];
for grade in grades {
let json = serde_json::to_string(&grade).unwrap();
let deserialized: Grade = serde_json::from_str(&json).unwrap();
assert_eq!(grade, deserialized);
}
}
#[test]
fn test_score_engine_new() {
let config = ScoreConfig::default();
let engine = ScoreEngine::new(config);
drop(engine);
}
#[test]
fn test_score_engine_score_shallow() {
let config = ScoreConfig::default();
let engine = ScoreEngine::new(config);
let mut parser = crate::frontend::parser::Parser::new("42");
let ast = parser.parse().expect("parse should succeed");
let score = engine.score(&ast, AnalysisDepth::Shallow);
assert!(score.value >= 0.0 && score.value <= 1.0);
}
#[test]
fn test_score_engine_score_standard() {
let config = ScoreConfig::default();
let engine = ScoreEngine::new(config);
let mut parser = crate::frontend::parser::Parser::new("let x = 1");
let ast = parser.parse().expect("parse should succeed");
let score = engine.score(&ast, AnalysisDepth::Standard);
assert!(score.confidence >= 0.0);
}
#[test]
fn test_score_engine_score_deep() {
let config = ScoreConfig::default();
let engine = ScoreEngine::new(config);
let mut parser = crate::frontend::parser::Parser::new("fun foo() { 1 + 2 }");
let ast = parser.parse().expect("parse should succeed");
let score = engine.score(&ast, AnalysisDepth::Deep);
assert!(matches!(
score.grade,
Grade::APlus
| Grade::A
| Grade::AMinus
| Grade::BPlus
| Grade::B
| Grade::BMinus
| Grade::CPlus
| Grade::C
| Grade::CMinus
| Grade::D
| Grade::F
));
}
#[test]
fn test_dependency_tracker_new() {
let tracker = DependencyTracker::new();
drop(tracker);
}
#[test]
fn test_dependency_tracker_is_stale_nonexistent() {
let tracker = DependencyTracker::new();
let path = PathBuf::from("/nonexistent/path/file.rs");
let is_stale = tracker.is_stale(&path);
assert!(!is_stale);
}
#[test]
fn test_score_config_default() {
let config = ScoreConfig::default();
let total = config.correctness_weight
+ config.performance_weight
+ config.maintainability_weight
+ config.safety_weight
+ config.idiomaticity_weight;
assert!((total - 1.0).abs() < 0.01);
}
#[test]
fn test_cache_key_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let key1 = CacheKey {
file_path: PathBuf::from("test.rs"),
content_hash: 12345,
depth: AnalysisDepth::Shallow,
};
let key2 = CacheKey {
file_path: PathBuf::from("test.rs"),
content_hash: 12345,
depth: AnalysisDepth::Shallow,
};
let mut hasher1 = DefaultHasher::new();
let mut hasher2 = DefaultHasher::new();
key1.hash(&mut hasher1);
key2.hash(&mut hasher2);
assert_eq!(hasher1.finish(), hasher2.finish());
}
#[test]
fn test_grade_ordering_via_rank() {
assert!(Grade::APlus.to_rank() > Grade::A.to_rank());
assert!(Grade::A.to_rank() > Grade::AMinus.to_rank());
assert!(Grade::AMinus.to_rank() > Grade::BPlus.to_rank());
assert!(Grade::BPlus.to_rank() > Grade::B.to_rank());
assert!(Grade::B.to_rank() > Grade::BMinus.to_rank());
assert!(Grade::BMinus.to_rank() > Grade::CPlus.to_rank());
assert!(Grade::CPlus.to_rank() > Grade::C.to_rank());
assert!(Grade::C.to_rank() > Grade::CMinus.to_rank());
assert!(Grade::CMinus.to_rank() > Grade::D.to_rank());
assert!(Grade::D.to_rank() > Grade::F.to_rank());
}
#[test]
fn test_analysis_depth_hash() {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
let mut hasher = DefaultHasher::new();
AnalysisDepth::Shallow.hash(&mut hasher);
let hash1 = hasher.finish();
let mut hasher = DefaultHasher::new();
AnalysisDepth::Standard.hash(&mut hasher);
let hash2 = hasher.finish();
assert_ne!(hash1, hash2);
}
#[test]
fn test_score_components_clone() {
let original = ScoreComponents {
correctness: 0.9,
performance: 0.85,
maintainability: 0.8,
safety: 0.95,
idiomaticity: 0.7,
};
let cloned = original.clone();
assert_eq!(original.correctness, cloned.correctness);
assert_eq!(original.performance, cloned.performance);
}
#[test]
fn test_quality_score_clone() {
let components = ScoreComponents {
correctness: 0.9,
performance: 0.85,
maintainability: 0.8,
safety: 0.95,
idiomaticity: 0.7,
};
let original = QualityScore {
value: 0.88,
components,
grade: Grade::BPlus,
confidence: 0.95,
cache_hit_rate: 0.2,
};
let cloned = original.clone();
assert_eq!(original.value, cloned.value);
assert_eq!(original.grade, cloned.grade);
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod property_tests_scoring {
use super::*;
use proptest::prelude::*;
proptest! {
#![proptest_config(ProptestConfig::with_cases(50))]
#[test]
fn prop_grade_from_score_valid(value in 0.0f64..=1.0) {
let grade = Grade::from_score(value);
let rank = grade.to_rank();
prop_assert!((0..=10).contains(&rank));
}
#[test]
fn prop_grade_rank_consistent(value in 0.0f64..=1.0) {
let grade1 = Grade::from_score(value);
let grade2 = Grade::from_score(value);
prop_assert_eq!(grade1.to_rank(), grade2.to_rank());
}
#[test]
fn prop_score_config_default_valid(_dummy: u8) {
let _config = ScoreConfig::default();
prop_assert!(true);
}
#[test]
fn prop_score_engine_new_never_panics(_dummy: u8) {
let config = ScoreConfig::default();
let _engine = ScoreEngine::new(config);
prop_assert!(true);
}
#[test]
fn prop_dependency_tracker_new_never_panics(_dummy: u8) {
let tracker = DependencyTracker::new();
let _is_stale = tracker.is_stale(&PathBuf::from("test.rs"));
prop_assert!(true);
}
#[test]
fn prop_score_parsed_integer(n in -1000i64..1000) {
let code = format!("{n}");
let mut parser = crate::frontend::parser::Parser::new(&code);
if let Ok(ast) = parser.parse() {
let score = score_correctness(&ast);
prop_assert!((0.0..=100.0).contains(&score));
}
}
#[test]
fn prop_score_parsed_let(n in -100i64..100) {
let code = format!("let x = {n}");
let mut parser = crate::frontend::parser::Parser::new(&code);
if let Ok(ast) = parser.parse() {
let score = score_performance(&ast);
prop_assert!((0.0..=100.0).contains(&score));
}
}
#[test]
fn prop_score_parsed_function(_dummy: u8) {
let code = "fun add(a, b) { a + b }";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let score = score_maintainability(&ast);
prop_assert!((0.0..=100.0).contains(&score));
}
}
#[test]
fn prop_score_safety_bounded(_dummy: u8) {
let code = "let x = 42";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let score = score_safety(&ast);
prop_assert!((0.0..=100.0).contains(&score));
}
}
#[test]
fn prop_score_idiomaticity_bounded(_dummy: u8) {
let code = "true && false";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let score = score_idiomaticity(&ast);
prop_assert!((0.0..=100.0).contains(&score));
}
}
}
}
#[cfg(test)]
mod scoring_tests_r162 {
use super::*;
#[test]
fn test_grade_from_score_boundaries_r162() {
assert_eq!(Grade::from_score(1.0), Grade::APlus);
assert_eq!(Grade::from_score(0.97), Grade::APlus);
assert_eq!(Grade::from_score(0.969), Grade::A);
assert_eq!(Grade::from_score(0.93), Grade::A);
assert_eq!(Grade::from_score(0.929), Grade::AMinus);
assert_eq!(Grade::from_score(0.90), Grade::AMinus);
assert_eq!(Grade::from_score(0.899), Grade::BPlus);
assert_eq!(Grade::from_score(0.87), Grade::BPlus);
assert_eq!(Grade::from_score(0.869), Grade::B);
assert_eq!(Grade::from_score(0.83), Grade::B);
assert_eq!(Grade::from_score(0.829), Grade::BMinus);
assert_eq!(Grade::from_score(0.80), Grade::BMinus);
assert_eq!(Grade::from_score(0.799), Grade::CPlus);
assert_eq!(Grade::from_score(0.77), Grade::CPlus);
assert_eq!(Grade::from_score(0.769), Grade::C);
assert_eq!(Grade::from_score(0.73), Grade::C);
assert_eq!(Grade::from_score(0.729), Grade::CMinus);
assert_eq!(Grade::from_score(0.70), Grade::CMinus);
assert_eq!(Grade::from_score(0.699), Grade::D);
assert_eq!(Grade::from_score(0.60), Grade::D);
assert_eq!(Grade::from_score(0.599), Grade::F);
assert_eq!(Grade::from_score(0.0), Grade::F);
}
#[test]
fn test_grade_to_rank_all_grades_r162() {
assert_eq!(Grade::F.to_rank(), 0);
assert_eq!(Grade::D.to_rank(), 1);
assert_eq!(Grade::CMinus.to_rank(), 2);
assert_eq!(Grade::C.to_rank(), 3);
assert_eq!(Grade::CPlus.to_rank(), 4);
assert_eq!(Grade::BMinus.to_rank(), 5);
assert_eq!(Grade::B.to_rank(), 6);
assert_eq!(Grade::BPlus.to_rank(), 7);
assert_eq!(Grade::AMinus.to_rank(), 8);
assert_eq!(Grade::A.to_rank(), 9);
assert_eq!(Grade::APlus.to_rank(), 10);
}
#[test]
fn test_grade_display_all_r162() {
assert_eq!(format!("{}", Grade::APlus), "A+");
assert_eq!(format!("{}", Grade::A), "A");
assert_eq!(format!("{}", Grade::AMinus), "A-");
assert_eq!(format!("{}", Grade::BPlus), "B+");
assert_eq!(format!("{}", Grade::B), "B");
assert_eq!(format!("{}", Grade::BMinus), "B-");
assert_eq!(format!("{}", Grade::CPlus), "C+");
assert_eq!(format!("{}", Grade::C), "C");
assert_eq!(format!("{}", Grade::CMinus), "C-");
assert_eq!(format!("{}", Grade::D), "D");
assert_eq!(format!("{}", Grade::F), "F");
}
#[test]
fn test_grade_negative_score_r162() {
assert_eq!(Grade::from_score(-1.0), Grade::F);
assert_eq!(Grade::from_score(-0.5), Grade::F);
assert_eq!(Grade::from_score(-100.0), Grade::F);
}
#[test]
fn test_grade_over_1_score_r162() {
assert_eq!(Grade::from_score(1.1), Grade::APlus);
assert_eq!(Grade::from_score(2.0), Grade::APlus);
assert_eq!(Grade::from_score(100.0), Grade::APlus);
}
#[test]
fn test_analysis_depth_clone_r162() {
let depth = AnalysisDepth::Standard;
let cloned = depth;
assert_eq!(depth, cloned);
}
#[test]
fn test_analysis_depth_debug_r162() {
let shallow = AnalysisDepth::Shallow;
let standard = AnalysisDepth::Standard;
let deep = AnalysisDepth::Deep;
assert!(format!("{:?}", shallow).contains("Shallow"));
assert!(format!("{:?}", standard).contains("Standard"));
assert!(format!("{:?}", deep).contains("Deep"));
}
#[test]
fn test_analysis_depth_eq_r162() {
assert_eq!(AnalysisDepth::Shallow, AnalysisDepth::Shallow);
assert_eq!(AnalysisDepth::Standard, AnalysisDepth::Standard);
assert_eq!(AnalysisDepth::Deep, AnalysisDepth::Deep);
assert_ne!(AnalysisDepth::Shallow, AnalysisDepth::Standard);
assert_ne!(AnalysisDepth::Standard, AnalysisDepth::Deep);
assert_ne!(AnalysisDepth::Shallow, AnalysisDepth::Deep);
}
#[test]
fn test_analysis_depth_hash_r162() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(AnalysisDepth::Shallow);
set.insert(AnalysisDepth::Standard);
set.insert(AnalysisDepth::Deep);
assert_eq!(set.len(), 3);
set.insert(AnalysisDepth::Shallow);
assert_eq!(set.len(), 3);
}
#[test]
fn test_score_components_clone_r162() {
let components = ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.7,
safety: 0.6,
idiomaticity: 0.5,
};
let cloned = components.clone();
assert_eq!(cloned.correctness, 0.9);
assert_eq!(cloned.performance, 0.8);
assert_eq!(cloned.maintainability, 0.7);
assert_eq!(cloned.safety, 0.6);
assert_eq!(cloned.idiomaticity, 0.5);
}
#[test]
fn test_score_components_debug_r162() {
let components = ScoreComponents {
correctness: 0.95,
performance: 0.85,
maintainability: 0.75,
safety: 0.65,
idiomaticity: 0.55,
};
let debug_str = format!("{:?}", components);
assert!(debug_str.contains("ScoreComponents"));
assert!(debug_str.contains("correctness"));
assert!(debug_str.contains("performance"));
assert!(debug_str.contains("maintainability"));
assert!(debug_str.contains("safety"));
assert!(debug_str.contains("idiomaticity"));
}
#[test]
fn test_quality_score_clone_r162() {
let score = QualityScore {
value: 0.85,
components: ScoreComponents {
correctness: 0.9,
performance: 0.8,
maintainability: 0.7,
safety: 0.85,
idiomaticity: 0.75,
},
grade: Grade::B,
confidence: 0.95,
cache_hit_rate: 0.5,
};
let cloned = score.clone();
assert_eq!(cloned.value, 0.85);
assert_eq!(cloned.grade, Grade::B);
assert_eq!(cloned.confidence, 0.95);
assert_eq!(cloned.cache_hit_rate, 0.5);
}
#[test]
fn test_quality_score_debug_r162() {
let score = QualityScore {
value: 0.75,
components: ScoreComponents {
correctness: 0.8,
performance: 0.7,
maintainability: 0.6,
safety: 0.75,
idiomaticity: 0.65,
},
grade: Grade::CPlus,
confidence: 0.8,
cache_hit_rate: 0.25,
};
let debug_str = format!("{:?}", score);
assert!(debug_str.contains("QualityScore"));
assert!(debug_str.contains("value"));
assert!(debug_str.contains("grade"));
assert!(debug_str.contains("confidence"));
}
#[test]
fn test_score_config_default_r162() {
let config = ScoreConfig::default();
assert!(config.correctness_weight >= 0.0 && config.correctness_weight <= 1.0);
assert!(config.performance_weight >= 0.0 && config.performance_weight <= 1.0);
assert!(config.maintainability_weight >= 0.0 && config.maintainability_weight <= 1.0);
assert!(config.safety_weight >= 0.0 && config.safety_weight <= 1.0);
assert!(config.idiomaticity_weight >= 0.0 && config.idiomaticity_weight <= 1.0);
}
#[test]
fn test_dependency_tracker_new_r162() {
let tracker = DependencyTracker::new();
let path = PathBuf::from("nonexistent.rs");
let _is_stale = tracker.is_stale(&path);
}
#[test]
fn test_dependency_tracker_is_stale_nonexistent_r162() {
let tracker = DependencyTracker::new();
let path = PathBuf::from("definitely_does_not_exist_12345.rs");
let is_stale = tracker.is_stale(&path);
assert!(!is_stale);
}
#[test]
fn test_grade_rank_ordering_r162() {
assert!(Grade::APlus.to_rank() > Grade::A.to_rank());
assert!(Grade::A.to_rank() > Grade::AMinus.to_rank());
assert!(Grade::AMinus.to_rank() > Grade::BPlus.to_rank());
assert!(Grade::BPlus.to_rank() > Grade::B.to_rank());
assert!(Grade::B.to_rank() > Grade::BMinus.to_rank());
assert!(Grade::BMinus.to_rank() > Grade::CPlus.to_rank());
assert!(Grade::CPlus.to_rank() > Grade::C.to_rank());
assert!(Grade::C.to_rank() > Grade::CMinus.to_rank());
assert!(Grade::CMinus.to_rank() > Grade::D.to_rank());
assert!(Grade::D.to_rank() > Grade::F.to_rank());
}
#[test]
fn test_grade_serialize_deserialize_r162() {
let grade = Grade::AMinus;
let serialized = serde_json::to_string(&grade).unwrap();
let deserialized: Grade = serde_json::from_str(&serialized).unwrap();
assert_eq!(grade, deserialized);
}
#[test]
fn test_grade_all_serialize_r162() {
for grade in [
Grade::APlus,
Grade::A,
Grade::AMinus,
Grade::BPlus,
Grade::B,
Grade::BMinus,
Grade::CPlus,
Grade::C,
Grade::CMinus,
Grade::D,
Grade::F,
] {
let serialized = serde_json::to_string(&grade).unwrap();
let deserialized: Grade = serde_json::from_str(&serialized).unwrap();
assert_eq!(grade, deserialized);
}
}
#[test]
fn test_score_empty_program_r162() {
let code = "";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let correctness = score_correctness(&ast);
let performance = score_performance(&ast);
let maintainability = score_maintainability(&ast);
let safety = score_safety(&ast);
let idiomaticity = score_idiomaticity(&ast);
assert!((0.0..=100.0).contains(&correctness));
assert!((0.0..=100.0).contains(&performance));
assert!((0.0..=100.0).contains(&maintainability));
assert!((0.0..=100.0).contains(&safety));
assert!((0.0..=100.0).contains(&idiomaticity));
}
}
#[test]
fn test_score_simple_literal_r162() {
let code = "42";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let correctness = score_correctness(&ast);
assert!((0.0..=100.0).contains(&correctness));
}
}
#[test]
fn test_score_string_literal_r162() {
let code = r#""hello world""#;
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let idiomaticity = score_idiomaticity(&ast);
assert!((0.0..=100.0).contains(&idiomaticity));
}
}
#[test]
fn test_score_boolean_literal_r162() {
let code = "true";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let safety = score_safety(&ast);
assert!((0.0..=100.0).contains(&safety));
}
}
#[test]
fn test_score_binary_expression_r162() {
let code = "1 + 2 * 3";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let performance = score_performance(&ast);
assert!((0.0..=100.0).contains(&performance));
}
}
#[test]
fn test_score_nested_functions_r162() {
let code = "fun outer() { fun inner() { 42 } }";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let maintainability = score_maintainability(&ast);
assert!((0.0..=100.0).contains(&maintainability));
}
}
#[test]
fn test_score_if_expression_r162() {
let code = "if true { 1 } else { 2 }";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let correctness = score_correctness(&ast);
assert!((0.0..=100.0).contains(&correctness));
}
}
#[test]
fn test_score_while_loop_r162() {
let code = "while false { 1 }";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let safety = score_safety(&ast);
assert!((0.0..=100.0).contains(&safety));
}
}
#[test]
fn test_score_array_literal_r162() {
let code = "[1, 2, 3, 4, 5]";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let performance = score_performance(&ast);
assert!((0.0..=100.0).contains(&performance));
}
}
#[test]
fn test_score_lambda_expression_r162() {
let code = "|x| x * 2";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let maintainability = score_maintainability(&ast);
assert!((0.0..=100.0).contains(&maintainability));
}
}
#[test]
fn test_score_idiomaticity_compound_expression_r162() {
let code = "fun map_values(arr) { arr.map(|x| x * 2) }";
let mut parser = crate::frontend::parser::Parser::new(code);
if let Ok(ast) = parser.parse() {
let idiomaticity = score_idiomaticity(&ast);
assert!((0.0..=100.0).contains(&idiomaticity));
}
}
}