Skip to main content

smelt_validator/rules/
complexity.rs

1//! Complexity threshold checking
2
3use super::ValidationRule;
4use crate::config::ComplexityConfig;
5use crate::validator::{ValidationSeverity, Violation};
6use smelt_core::{IntentRecord, SemanticDelta};
7
8/// Checks for complexity threshold violations
9pub struct ComplexityChecker {
10    config: ComplexityConfig,
11}
12
13impl ComplexityChecker {
14    /// Create a new complexity checker
15    pub fn new(config: ComplexityConfig) -> Self {
16        Self { config }
17    }
18}
19
20impl ValidationRule for ComplexityChecker {
21    fn name(&self) -> &'static str {
22        "complexity"
23    }
24
25    fn validate(&self, delta: &SemanticDelta, _intent: Option<&IntentRecord>) -> Vec<Violation> {
26        let mut violations = Vec::new();
27
28        // Check overall complexity increase
29        let complexity_delta = delta.impact_summary.complexity_delta;
30        if complexity_delta > self.config.max_complexity_increase {
31            violations.push(Violation {
32                rule: "complexity-increase".to_string(),
33                severity: if self.config.complexity_error {
34                    ValidationSeverity::Error
35                } else {
36                    ValidationSeverity::Warning
37                },
38                message: format!(
39                    "Complexity increased by {} (threshold: {})",
40                    complexity_delta, self.config.max_complexity_increase
41                ),
42                location: None,
43                suggestion: Some(
44                    "Consider refactoring to reduce complexity or splitting into smaller functions"
45                        .to_string(),
46                ),
47            });
48        }
49
50        // Check individual function complexity from changes
51        // Note: Full complexity analysis would require AST access
52        // This checks complexity_delta in BodyModified changes
53        for change in &delta.changes {
54            if let smelt_core::SemanticChange::BodyModified {
55                name,
56                file,
57                complexity_delta: func_delta,
58            } = change
59            {
60                if *func_delta > self.config.max_complexity_increase {
61                    violations.push(Violation {
62                        rule: "function-complexity".to_string(),
63                        severity: if self.config.complexity_error {
64                            ValidationSeverity::Error
65                        } else {
66                            ValidationSeverity::Warning
67                        },
68                        message: format!(
69                            "Function '{}' complexity increased by {}",
70                            name, func_delta
71                        ),
72                        location: Some(file.clone()),
73                        suggestion: Some(
74                            "Extract helper functions or simplify control flow".to_string(),
75                        ),
76                    });
77                }
78            }
79        }
80
81        violations
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use chrono::Utc;
89    use smelt_core::{ImpactSummary, SemanticChange};
90    use uuid::Uuid;
91
92    fn make_delta_with_complexity(complexity_delta: i32) -> SemanticDelta {
93        SemanticDelta {
94            id: Uuid::new_v4(),
95            intent_id: Uuid::new_v4(),
96            timestamp: Utc::now(),
97            from_snapshot: Uuid::new_v4(),
98            to_snapshot: Uuid::new_v4(),
99            changes: Vec::new(),
100            impact_summary: ImpactSummary {
101                complexity_delta,
102                ..Default::default()
103            },
104        }
105    }
106
107    #[test]
108    fn test_complexity_under_threshold() {
109        let checker = ComplexityChecker::new(ComplexityConfig::default());
110
111        let delta = make_delta_with_complexity(5);
112        let violations = checker.validate(&delta, None);
113        assert!(violations.is_empty());
114    }
115
116    #[test]
117    fn test_complexity_over_threshold() {
118        let checker = ComplexityChecker::new(ComplexityConfig::default());
119
120        let delta = make_delta_with_complexity(15);
121        let violations = checker.validate(&delta, None);
122        assert_eq!(violations.len(), 1);
123        assert_eq!(violations[0].rule, "complexity-increase");
124    }
125
126    #[test]
127    fn test_function_complexity_change() {
128        let checker = ComplexityChecker::new(ComplexityConfig::default());
129
130        let delta = SemanticDelta {
131            id: Uuid::new_v4(),
132            intent_id: Uuid::new_v4(),
133            timestamp: Utc::now(),
134            from_snapshot: Uuid::new_v4(),
135            to_snapshot: Uuid::new_v4(),
136            changes: vec![SemanticChange::BodyModified {
137                name: "complex_function".to_string(),
138                file: "lib.rs".to_string(),
139                complexity_delta: 15,
140            }],
141            impact_summary: ImpactSummary::default(),
142        };
143
144        let violations = checker.validate(&delta, None);
145        assert_eq!(violations.len(), 1);
146        assert_eq!(violations[0].rule, "function-complexity");
147    }
148}