pub struct ViolationDetector {
config: EntropyConfig,
}
impl ViolationDetector {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new(config: EntropyConfig) -> Self {
Self { config }
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn detect_violations(
&self,
patterns: &PatternCollection,
metrics: &EntropyMetrics,
) -> Result<Vec<ActionableViolation>> {
let mut violations = Vec::new();
self.detect_repetitive_patterns(patterns, &mut violations)?;
self.detect_low_diversity(patterns, metrics, &mut violations)?;
self.detect_cross_file_duplication(patterns, &mut violations)?;
self.detect_inconsistent_patterns(patterns, &mut violations)?;
violations.retain(|v| v.severity >= self.config.min_severity);
violations = self.deduplicate_violations(violations);
violations.sort_by(|a, b| {
b.priority_score
.partial_cmp(&a.priority_score)
.expect("internal error")
});
Ok(violations)
}
fn detect_repetitive_patterns(
&self,
patterns: &PatternCollection,
violations: &mut Vec<ActionableViolation>,
) -> Result<()> {
for pattern in patterns.patterns.values() {
if pattern.frequency > self.config.max_pattern_repetition {
let severity = self.calculate_repetition_severity(pattern.frequency);
let loc_reduction = self.estimate_loc_reduction(pattern);
violations.push(ActionableViolation {
severity,
pattern: PatternSummary {
pattern_type: pattern.pattern_type,
repetitions: pattern.frequency,
variation_score: pattern.variation_score,
example_code: pattern.example_code.clone(),
},
message: format!(
"{:?} pattern repeated {} times",
pattern.pattern_type, pattern.frequency
),
fix_suggestion: self.generate_fix_suggestion(pattern),
estimated_loc_reduction: loc_reduction,
affected_files: pattern
.locations
.iter()
.map(|l| l.file.clone())
.collect::<Vec<_>>()
.into_iter()
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect(),
priority_score: self.calculate_priority(severity, loc_reduction),
});
}
}
Ok(())
}
fn detect_low_diversity(
&self,
_patterns: &PatternCollection,
metrics: &EntropyMetrics,
violations: &mut Vec<ActionableViolation>,
) -> Result<()> {
if metrics.pattern_diversity < self.config.min_pattern_diversity {
violations.push(ActionableViolation {
severity: Severity::Medium,
pattern: PatternSummary {
pattern_type: PatternType::ControlFlow,
repetitions: 0,
variation_score: 1.0 - metrics.pattern_diversity,
example_code: "Various repetitive patterns".to_string(),
},
message: format!(
"Low pattern diversity: {:.1}% (minimum: {:.1}%)",
metrics.pattern_diversity * 100.0,
self.config.min_pattern_diversity * 100.0
),
fix_suggestion: "Consider extracting common patterns into reusable functions"
.to_string(),
estimated_loc_reduction: (metrics.total_loc as f64 * 0.15) as usize,
affected_files: vec![],
priority_score: 5.0,
});
}
Ok(())
}
fn detect_cross_file_duplication(
&self,
patterns: &PatternCollection,
violations: &mut Vec<ActionableViolation>,
) -> Result<()> {
for pattern in patterns.patterns.values() {
let unique_files: std::collections::HashSet<_> =
pattern.locations.iter().map(|l| &l.file).collect();
if unique_files.len() > 2 {
let severity = if unique_files.len() > 5 {
Severity::High
} else {
Severity::Medium
};
violations.push(ActionableViolation {
severity,
pattern: PatternSummary {
pattern_type: pattern.pattern_type,
repetitions: pattern.frequency,
variation_score: pattern.variation_score,
example_code: pattern.example_code.clone(),
},
message: format!(
"{:?} pattern duplicated across {} files",
pattern.pattern_type,
unique_files.len()
),
fix_suggestion: format!(
"Extract to shared module: {}",
self.suggest_module_name(pattern.pattern_type)
),
estimated_loc_reduction: pattern.estimated_loc * (unique_files.len() - 1),
affected_files: unique_files.into_iter().cloned().collect(),
priority_score: 8.0,
});
}
}
Ok(())
}
fn detect_inconsistent_patterns(
&self,
patterns: &PatternCollection,
violations: &mut Vec<ActionableViolation>,
) -> Result<()> {
for pattern in patterns.patterns.values() {
if pattern.variation_score > self.config.max_inconsistency_score {
violations.push(ActionableViolation {
severity: Severity::Medium,
pattern: PatternSummary {
pattern_type: pattern.pattern_type,
repetitions: pattern.frequency,
variation_score: pattern.variation_score,
example_code: pattern.example_code.clone(),
},
message: format!(
"Inconsistent {:?} implementations (variation: {:.1}%)",
pattern.pattern_type,
pattern.variation_score * 100.0
),
fix_suggestion: format!(
"Standardize {} pattern across codebase",
self.pattern_name(pattern.pattern_type)
),
estimated_loc_reduction: ((pattern.estimated_loc * pattern.frequency) as f64
* 0.3) as usize,
affected_files: pattern.locations.iter().map(|l| l.file.clone()).collect(),
priority_score: 6.0,
});
}
}
Ok(())
}
fn calculate_repetition_severity(&self, frequency: usize) -> Severity {
if frequency > 10 {
Severity::High
} else if frequency > 5 {
Severity::Medium
} else {
Severity::Low
}
}
fn estimate_loc_reduction(&self, pattern: &AstPattern) -> usize {
let instances_to_remove = pattern.frequency.saturating_sub(1);
let avg_size = pattern.estimated_loc;
let reduction_factor = 0.8;
((instances_to_remove * avg_size) as f64 * reduction_factor) as usize
}
fn generate_fix_suggestion(&self, pattern: &AstPattern) -> String {
match pattern.pattern_type {
PatternType::ErrorHandling => {
format!(
"Extract to `handle_{}_error()` function",
self.context_name(pattern)
)
}
PatternType::DataValidation => "Create validation trait or module".to_string(),
PatternType::ResourceManagement => {
"Implement RAII pattern or use guard types".to_string()
}
PatternType::ControlFlow => "Refactor to strategy pattern or polymorphism".to_string(),
PatternType::DataTransformation => {
"Extract to data transformation pipeline".to_string()
}
PatternType::ApiCall => "Create API client abstraction".to_string(),
}
}
fn calculate_priority(&self, severity: Severity, loc_reduction: usize) -> f64 {
let severity_score = match severity {
Severity::High => 10.0,
Severity::Medium => 5.0,
Severity::Low => 1.0,
};
let loc_score = (loc_reduction as f64 / 100.0).min(10.0);
severity_score + loc_score
}
fn suggest_module_name(&self, pattern_type: PatternType) -> &'static str {
match pattern_type {
PatternType::ErrorHandling => "error_handler",
PatternType::DataValidation => "validators",
PatternType::ResourceManagement => "resource_guards",
PatternType::ControlFlow => "control_flow",
PatternType::DataTransformation => "transformers",
PatternType::ApiCall => "api_client",
}
}
fn pattern_name(&self, pattern_type: PatternType) -> &'static str {
match pattern_type {
PatternType::ErrorHandling => "error handling",
PatternType::DataValidation => "validation",
PatternType::ResourceManagement => "resource management",
PatternType::ControlFlow => "control flow",
PatternType::DataTransformation => "data transformation",
PatternType::ApiCall => "API call",
}
}
fn context_name(&self, _pattern: &AstPattern) -> &'static str {
"context"
}
fn deduplicate_violations(
&self,
violations: Vec<ActionableViolation>,
) -> Vec<ActionableViolation> {
use std::collections::HashMap;
let mut unique_violations: HashMap<String, ActionableViolation> = HashMap::new();
for violation in violations {
let key = format!(
"{}:{}:{}",
violation.pattern.pattern_type as u8,
violation.pattern.repetitions,
violation.pattern.example_code.len() );
match unique_violations.get(&key) {
Some(existing) if existing.priority_score >= violation.priority_score => {
}
_ => {
unique_violations.insert(key, violation);
}
}
}
unique_violations.into_values().collect()
}
}