1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
/// Detects violations from patterns
pub struct ViolationDetector {
config: EntropyConfig,
}
impl ViolationDetector {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
/// Create a new instance.
pub fn new(config: EntropyConfig) -> Self {
Self { config }
}
/// Detect actionable violations from patterns
#[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();
// Check for repetitive patterns
self.detect_repetitive_patterns(patterns, &mut violations)?;
// Check for low diversity
self.detect_low_diversity(patterns, metrics, &mut violations)?;
// Check for cross-file duplication
self.detect_cross_file_duplication(patterns, &mut violations)?;
// Check for inconsistent patterns
self.detect_inconsistent_patterns(patterns, &mut violations)?;
// Filter by minimum severity
violations.retain(|v| v.severity >= self.config.min_severity);
// TOYOTA WAY FIX: Deduplicate violations to prevent false inflation
// Issue: Same pattern reported by multiple detection methods
violations = self.deduplicate_violations(violations);
// Sort by priority, then by message and affected file. Without the
// tie-breaks a stable sort just preserves whatever order dedup produced,
// so equal-priority violations came out shuffled between runs.
violations.sort_by(|a, b| {
b.priority_score
.partial_cmp(&a.priority_score)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.message.cmp(&b.message))
.then_with(|| a.affected_files.cmp(&b.affected_files))
});
Ok(violations)
}
/// Distinct files a pattern occurs in, in a fixed order.
fn sorted_affected_files(pattern: &AstPattern) -> Vec<PathBuf> {
let mut files: Vec<PathBuf> = pattern.locations.iter().map(|l| l.file.clone()).collect();
files.sort();
files.dedup();
files
}
/// Detect repetitive pattern 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: Some(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: Some(loc_reduction),
// Was collected through a HashSet, so the file list (and the
// "file" a quality-gate row was attributed to, which is the
// first entry) changed between runs.
affected_files: Self::sorted_affected_files(pattern),
priority_score: self.calculate_priority(severity, loc_reduction),
});
}
}
Ok(())
}
/// Detect low diversity violations
///
/// An unmeasured diversity (`None`) can never be below a threshold — a gate
/// must not fail on a number that was never computed.
fn detect_low_diversity(
&self,
_patterns: &PatternCollection,
metrics: &EntropyMetrics,
violations: &mut Vec<ActionableViolation>,
) -> Result<()> {
let Some(diversity) = metrics.pattern_diversity else {
return Ok(());
};
if diversity < self.config.min_pattern_diversity {
violations.push(ActionableViolation {
severity: Severity::Medium,
// No pattern: this finding is about the project's pattern
// *distribution*, not about any one construct. It used to carry
// a placeholder summary whose `variation_score` was `1 - diversity`,
// so the same object reported "diversity LOW (11.9%)" and
// "variation_score HIGH (0.88)" — one number stated twice, in
// opposite directions (#650).
pattern: None,
message: format!(
"Low pattern diversity: {:.1}% (minimum: {:.1}%)",
diversity * 100.0,
self.config.min_pattern_diversity * 100.0
),
fix_suggestion: "Consider extracting common patterns into reusable functions"
.to_string(),
// Was `total_loc * 0.15`, invariant to the diversity it claimed
// to derive from: 358 LOC -> "saves 53", 1200 -> 180,
// 158020 -> 23703, and a project measured at 0.0% diversity got
// the same 15% as one measured at 11.9%. Nothing here is
// measured, so nothing is reported.
estimated_loc_reduction: None,
affected_files: vec![],
priority_score: 5.0,
});
}
Ok(())
}
/// Detect cross-file duplication
fn detect_cross_file_duplication(
&self,
patterns: &PatternCollection,
violations: &mut Vec<ActionableViolation>,
) -> Result<()> {
// Find patterns that appear in multiple files
for pattern in patterns.patterns.values() {
let unique_files = Self::sorted_affected_files(pattern);
if unique_files.len() > 2 {
let severity = if unique_files.len() > 5 {
Severity::High
} else {
Severity::Medium
};
violations.push(ActionableViolation {
severity,
pattern: Some(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: Some(self.estimate_loc_reduction(pattern)),
affected_files: unique_files,
priority_score: 8.0,
});
}
}
Ok(())
}
/// Detect inconsistent pattern implementations
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: Some(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 already covers all `frequency` instances;
// multiplying by frequency again counted every line twice over.
estimated_loc_reduction: Some((pattern.estimated_loc as f64 * 0.3) as usize),
affected_files: Self::sorted_affected_files(pattern),
priority_score: 6.0,
});
}
}
Ok(())
}
/// Calculate severity based on repetition count
fn calculate_repetition_severity(&self, frequency: usize) -> Severity {
if frequency > 10 {
Severity::High
} else if frequency > 5 {
Severity::Medium
} else {
Severity::Low
}
}
/// Estimate LOC reduction from fixing a pattern
///
/// `estimated_loc` is the LOC covered by *all* `frequency` instances, so the
/// per-instance size has to be divided back out. The old code multiplied the
/// whole-group figure by `frequency - 1` again, which is where numbers like
/// "saves 302 lines" for a ten-line pattern came from.
fn estimate_loc_reduction(&self, pattern: &AstPattern) -> usize {
if pattern.frequency <= 1 {
return 0;
}
let instances_to_remove = pattern.frequency - 1;
let per_instance = pattern.estimated_loc as f64 / pattern.frequency as f64;
let reduction_factor = 0.8; // Assume 80% can be eliminated
(instances_to_remove as f64 * per_instance * reduction_factor) as usize
}
/// Generate fix suggestion for a pattern
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(),
}
}
/// Calculate priority score for ordering violations
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
}
/// Suggest module name for extracted pattern
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",
}
}
/// Get human-readable pattern name
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",
}
}
/// Extract context name from pattern
fn context_name(&self, _pattern: &AstPattern) -> &'static str {
// Extract meaningful name from pattern
// Simplified - would analyze actual AST
"context"
}
/// Deduplicate violations to prevent the same pattern being reported multiple times
///
/// Issue: Same pattern can be detected by multiple methods (repetitive, cross-file, etc.)
/// causing inflated violation counts. This deduplicates based on pattern type and core message.
fn deduplicate_violations(
&self,
violations: Vec<ActionableViolation>,
) -> Vec<ActionableViolation> {
use std::collections::BTreeMap;
// BTreeMap: `into_values()` on a HashMap emitted the survivors in a
// per-process random order, which the stable priority sort below then
// preserved for equal priorities.
let mut unique_violations: BTreeMap<String, ActionableViolation> = BTreeMap::new();
for violation in violations {
let key = Self::dedup_key(&violation);
// Keep the violation with highest severity/priority
match unique_violations.get(&key) {
Some(existing) if existing.priority_score >= violation.priority_score => {
// Keep existing
}
_ => {
// Replace or insert new
unique_violations.insert(key, violation);
}
}
}
unique_violations.into_values().collect()
}
/// Identity of the *finding* a violation is about, so that the same pattern
/// surfaced by two detectors collapses to one row.
///
/// Keyed on the full `example_code`, not `example_code.len()`: with the
/// length, two structurally different patterns of the same type, the same
/// repetition count and coincidentally equal snippet lengths collapsed into
/// one and the other was dropped without trace.
///
/// A project-level finding has no pattern, so it is keyed on its own message.
fn dedup_key(violation: &ActionableViolation) -> String {
violation.pattern.as_ref().map_or_else(
|| format!("project:{}", violation.message),
|p| {
format!(
"pattern:{}:{}:{}",
p.pattern_type as u8, p.repetitions, p.example_code
)
},
)
}
}