debtmap/extraction_patterns/language_specific/
python_patterns.rs1use crate::extraction_patterns::{
2 AccumulationOp, AnalysisContext, ComplexityImpact, ExtractablePattern, ExtractionSuggestion,
3 MatchedPattern, Parameter, PatternMatcher,
4};
5use syn::File;
6
7pub struct PythonPatternMatcher;
8
9impl Default for PythonPatternMatcher {
10 fn default() -> Self {
11 Self::new()
12 }
13}
14
15impl PythonPatternMatcher {
16 pub fn new() -> Self {
17 Self
18 }
19
20 #[allow(dead_code)]
21 fn detect_list_comprehensions(&self) -> Vec<ExtractablePattern> {
22 Vec::new()
24 }
25
26 #[allow(dead_code)]
27 fn detect_guard_patterns(&self) -> Vec<ExtractablePattern> {
28 Vec::new()
30 }
31
32 #[allow(dead_code)]
33 fn detect_data_transformations(&self) -> Vec<ExtractablePattern> {
34 Vec::new()
36 }
37}
38
39impl PatternMatcher for PythonPatternMatcher {
40 fn match_patterns(&self, _ast: &File, _context: &AnalysisContext) -> Vec<MatchedPattern> {
41 Vec::new()
44 }
45
46 fn score_confidence(&self, pattern: &MatchedPattern, context: &AnalysisContext) -> f32 {
47 use crate::extraction_patterns::confidence::ConfidenceScorer;
48 ConfidenceScorer::score_pattern(pattern, context)
49 }
50
51 fn generate_extraction(&self, pattern: &MatchedPattern) -> ExtractionSuggestion {
52 use crate::extraction_patterns::naming::FunctionNameInferrer;
53
54 let suggested_name = FunctionNameInferrer::infer_name(&pattern.pattern, "python");
55
56 let (start_line, end_line) = match &pattern.pattern {
57 ExtractablePattern::AccumulationLoop {
58 start_line,
59 end_line,
60 ..
61 }
62 | ExtractablePattern::GuardChainSequence {
63 start_line,
64 end_line,
65 ..
66 }
67 | ExtractablePattern::TransformationPipeline {
68 start_line,
69 end_line,
70 ..
71 }
72 | ExtractablePattern::SimilarBranches {
73 start_line,
74 end_line,
75 ..
76 }
77 | ExtractablePattern::NestedExtraction {
78 start_line,
79 end_line,
80 ..
81 } => (*start_line, *end_line),
82 };
83
84 let parameters = self.extract_parameters(&pattern.pattern);
85 let return_type = self.infer_return_type(&pattern.pattern);
86 let complexity_reduction =
87 self.calculate_complexity_reduction(&pattern.pattern, &pattern.context);
88 let example = self.generate_example(&pattern.pattern, &suggested_name);
89
90 ExtractionSuggestion {
91 pattern_type: pattern.pattern.clone(),
92 start_line,
93 end_line,
94 suggested_name,
95 confidence: pattern.confidence,
96 parameters,
97 return_type,
98 complexity_reduction,
99 example_transformation: example,
100 }
101 }
102}
103
104impl PythonPatternMatcher {
105 fn extract_parameters(&self, pattern: &ExtractablePattern) -> Vec<Parameter> {
106 match pattern {
107 ExtractablePattern::AccumulationLoop {
108 iterator_binding, ..
109 } => {
110 vec![Parameter {
111 name: format!("{}_list", iterator_binding),
112 type_hint: "List[Any]".to_string(),
113 is_mutable: false,
114 }]
115 }
116 ExtractablePattern::GuardChainSequence { .. } => {
117 vec![Parameter {
118 name: "value".to_string(),
119 type_hint: "Any".to_string(),
120 is_mutable: false,
121 }]
122 }
123 ExtractablePattern::TransformationPipeline { input_binding, .. } => {
124 vec![Parameter {
125 name: input_binding.clone(),
126 type_hint: "Any".to_string(),
127 is_mutable: false,
128 }]
129 }
130 _ => Vec::new(),
131 }
132 }
133
134 fn infer_return_type(&self, pattern: &ExtractablePattern) -> String {
135 match pattern {
136 ExtractablePattern::AccumulationLoop { operation, .. } => match operation {
137 AccumulationOp::Sum | AccumulationOp::Product => "float".to_string(),
138 AccumulationOp::Concatenation => "str".to_string(),
139 AccumulationOp::Collection => "List[Any]".to_string(),
140 AccumulationOp::Custom(_) => "Any".to_string(),
141 },
142 ExtractablePattern::GuardChainSequence { early_return, .. } => {
143 early_return.type_name.clone()
144 }
145 ExtractablePattern::TransformationPipeline { output_type, .. } => output_type.clone(),
146 _ => "None".to_string(),
147 }
148 }
149
150 fn calculate_complexity_reduction(
151 &self,
152 pattern: &ExtractablePattern,
153 context: &AnalysisContext,
154 ) -> ComplexityImpact {
155 let extracted_complexity = match pattern {
156 ExtractablePattern::AccumulationLoop {
157 filter, transform, ..
158 } => {
159 let base = 2;
160 let filter_complexity = if filter.is_some() { 1 } else { 0 };
161 let transform_complexity = if transform.is_some() { 1 } else { 0 };
162 base + filter_complexity + transform_complexity
163 }
164 ExtractablePattern::GuardChainSequence { checks, .. } => checks.len() as u32,
165 ExtractablePattern::TransformationPipeline { stages, .. } => stages.len() as u32 * 2,
166 _ => 3,
167 };
168
169 ComplexityImpact {
170 current_cyclomatic: context.complexity_before,
171 predicted_cyclomatic: context
172 .complexity_before
173 .saturating_sub(extracted_complexity),
174 current_cognitive: context.complexity_before * 2,
175 predicted_cognitive: (context.complexity_before * 2)
176 .saturating_sub(extracted_complexity * 2),
177 extracted_function_complexity: extracted_complexity,
178 }
179 }
180
181 fn generate_example(&self, pattern: &ExtractablePattern, function_name: &str) -> String {
182 match pattern {
183 ExtractablePattern::AccumulationLoop {
184 iterator_binding,
185 operation,
186 ..
187 } => {
188 let _op = match operation {
189 AccumulationOp::Sum => "sum",
190 AccumulationOp::Product => "product",
191 _ => "accumulate",
192 };
193 format!(
194 "# Before:\n\
195 result = 0\n\
196 for {} in items:\n\
197 result += {}\n\n\
198 # After:\n\
199 result = {}(items)",
200 iterator_binding, iterator_binding, function_name
201 )
202 }
203 ExtractablePattern::GuardChainSequence { .. } => {
204 format!(
205 "# Before:\n\
206 if not condition1:\n\
207 raise ValueError('Invalid')\n\
208 if not condition2:\n\
209 raise ValueError('Invalid')\n\n\
210 # After:\n\
211 {}(value)",
212 function_name
213 )
214 }
215 ExtractablePattern::TransformationPipeline { .. } => {
216 format!(
217 "# Before:\n\
218 data = [x * 2 for x in items]\n\
219 data = [x for x in data if x > 10]\n\
220 result = sorted(data)\n\n\
221 # After:\n\
222 result = {}(items)",
223 function_name
224 )
225 }
226 _ => "# Example transformation".to_string(),
227 }
228 }
229}