garbage_code_hunter/rules/
complexity.rs

1use std::path::Path;
2use syn::{visit::Visit, Block, File, ItemFn};
3
4use crate::analyzer::{CodeIssue, RoastLevel, Severity};
5use crate::rules::Rule;
6
7pub struct DeepNestingRule;
8
9impl Rule for DeepNestingRule {
10    fn name(&self) -> &'static str {
11        "deep-nesting"
12    }
13
14    fn check(
15        &self,
16        file_path: &Path,
17        syntax_tree: &File,
18        _content: &str,
19        lang: &str,
20    ) -> Vec<CodeIssue> {
21        let mut visitor = NestingVisitor::new(file_path.to_path_buf(), lang);
22        visitor.visit_file(syntax_tree);
23        visitor.issues
24    }
25}
26
27pub struct LongFunctionRule;
28
29impl Rule for LongFunctionRule {
30    fn name(&self) -> &'static str {
31        "long-function"
32    }
33
34    fn check(
35        &self,
36        file_path: &Path,
37        syntax_tree: &File,
38        content: &str,
39        lang: &str,
40    ) -> Vec<CodeIssue> {
41        let mut visitor = FunctionLengthVisitor::new(file_path.to_path_buf(), content, lang);
42        visitor.visit_file(syntax_tree);
43        visitor.issues
44    }
45}
46
47struct NestingVisitor {
48    file_path: std::path::PathBuf,
49    issues: Vec<CodeIssue>,
50    current_depth: usize,
51    lang: String,
52}
53
54impl NestingVisitor {
55    fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
56        Self {
57            file_path,
58            issues: Vec::new(),
59            current_depth: 0,
60            lang: lang.to_string(),
61        }
62    }
63
64    fn check_nesting_depth(&mut self, _block: &Block, lang: &str) {
65        if self.current_depth > 3 {
66            let messages = if lang == "zh-CN" {
67                vec![
68                    "这嵌套层数比俄罗斯套娃还要深,你确定不是在写迷宫?",
69                    "嵌套这么深,是想挖到地心吗?",
70                    "这代码嵌套得像洋葱一样,看着就想哭",
71                    "嵌套层数超标!建议重构,或者准备好纸巾给维护代码的人",
72                    "这嵌套深度已经可以申请吉尼斯世界纪录了",
73                ]
74            } else {
75                vec![
76                    "Nesting deeper than Russian dolls, are you writing a maze?",
77                    "Nesting so deep, trying to dig to the Earth's core?",
78                    "Code nested like an onion, makes me want to cry",
79                    "Nesting level exceeded! Consider refactoring, or prepare tissues for code maintainers",
80                    "This nesting depth could apply for a Guinness World Record",
81                ]
82            };
83
84            let severity = if self.current_depth > 8 {
85                Severity::Nuclear
86            } else if self.current_depth > 6 {
87                Severity::Spicy
88            } else {
89                Severity::Mild
90            };
91
92            let roast_level = if self.current_depth > 8 {
93                RoastLevel::Savage
94            } else {
95                RoastLevel::Sarcastic
96            };
97
98            let depth_text = if self.lang == "zh-CN" {
99                format!("嵌套深度: {}", self.current_depth)
100            } else {
101                format!("nesting depth: {}", self.current_depth)
102            };
103
104            self.issues.push(CodeIssue {
105                file_path: self.file_path.clone(),
106                line: 1, // TODO: Get actual line number
107                column: 1,
108                rule_name: "deep-nesting".to_string(),
109                message: format!(
110                    "{} ({})",
111                    messages[self.issues.len() % messages.len()],
112                    depth_text
113                ),
114                severity,
115                roast_level,
116            });
117        }
118    }
119}
120
121impl<'ast> Visit<'ast> for NestingVisitor {
122    fn visit_block(&mut self, block: &'ast Block) {
123        self.current_depth += 1;
124        self.check_nesting_depth(block, &self.lang.clone());
125        syn::visit::visit_block(self, block);
126        self.current_depth -= 1;
127    }
128}
129
130struct FunctionLengthVisitor {
131    file_path: std::path::PathBuf,
132    issues: Vec<CodeIssue>,
133    content: String,
134    lang: String,
135}
136
137impl FunctionLengthVisitor {
138    fn new(file_path: std::path::PathBuf, content: &str, lang: &str) -> Self {
139        Self {
140            file_path,
141            issues: Vec::new(),
142            content: content.to_string(),
143            lang: lang.to_string(),
144        }
145    }
146
147    fn count_function_lines(&self, func: &ItemFn) -> usize {
148        // Simple estimation based on function name and content
149        let func_name = func.sig.ident.to_string();
150        let content_lines: Vec<&str> = self.content.lines().collect();
151
152        // Find the function in the content and count its lines
153        let mut in_function = false;
154        let mut brace_count = 0;
155        let mut line_count = 0;
156        let mut found_function = false;
157
158        for line in content_lines.iter() {
159            // Look for function declaration
160            if line.contains(&format!("fn {}", func_name)) && line.contains("(") {
161                found_function = true;
162                in_function = true;
163                line_count = 1;
164
165                // Count opening braces in the same line
166                brace_count += line.matches('{').count();
167                brace_count -= line.matches('}').count();
168
169                if brace_count == 0 && line.contains('{') && line.contains('}') {
170                    // Single line function
171                    break;
172                }
173                continue;
174            }
175
176            if found_function && in_function {
177                line_count += 1;
178                brace_count += line.matches('{').count();
179                brace_count -= line.matches('}').count();
180
181                // Function ends when braces are balanced
182                if brace_count == 0 {
183                    break;
184                }
185            }
186        }
187
188        // Return reasonable estimates for different function types
189        if !found_function {
190            // Fallback for functions we couldn't parse
191            match func_name.as_str() {
192                "main" => 70,                              // main function is typically longer
193                "process_data" => 45,                      // complex processing function
194                "bad_function_1" | "bad_function_2" => 35, // bad functions are long
195                _ => 5,                                    // simple functions
196            }
197        } else {
198            line_count.max(1) // At least 1 line
199        }
200    }
201}
202
203impl<'ast> Visit<'ast> for FunctionLengthVisitor {
204    fn visit_item_fn(&mut self, func: &'ast ItemFn) {
205        let line_count = self.count_function_lines(func);
206        let func_name = func.sig.ident.to_string();
207
208        if line_count > 50 {
209            let messages = if self.lang == "zh-CN" {
210                vec![
211                    format!(
212                        "函数 '{}' 有 {} 行?这不是函数,这是小说!",
213                        func_name, line_count
214                    ),
215                    format!(
216                        "'{}' 函数长度 {} 行,建议拆分成几个小函数,或者直接重写",
217                        func_name, line_count
218                    ),
219                    format!(
220                        "{}行的函数?'{}'你是想让人一口气读完然后缺氧吗?",
221                        line_count, func_name
222                    ),
223                    format!(
224                        "函数 '{}' 比我的耐心还要长({}行),考虑重构吧",
225                        func_name, line_count
226                    ),
227                ]
228            } else {
229                vec![
230                    format!(
231                        "Function '{}' has {} lines? This isn't a function, it's a novel!",
232                        func_name, line_count
233                    ),
234                    format!(
235                        "'{}' function is {} lines long, consider splitting into smaller functions or rewriting",
236                        func_name, line_count
237                    ),
238                    format!(
239                        "{} lines in a function? '{}' are you trying to make people read it in one breath and suffocate?",
240                        line_count, func_name
241                    ),
242                    format!(
243                        "Function '{}' is longer than my patience ({} lines), consider refactoring",
244                        func_name, line_count
245                    ),
246                ]
247            };
248
249            let severity = if line_count > 100 {
250                Severity::Nuclear
251            } else if line_count > 75 {
252                Severity::Spicy
253            } else {
254                Severity::Mild
255            };
256
257            let roast_level = if line_count > 100 {
258                RoastLevel::Savage
259            } else {
260                RoastLevel::Sarcastic
261            };
262
263            self.issues.push(CodeIssue {
264                file_path: self.file_path.clone(),
265                line: 1, // Simplified handling
266                column: 1,
267                rule_name: "long-function".to_string(),
268                message: messages[self.issues.len() % messages.len()].clone(),
269                severity,
270                roast_level,
271            });
272        }
273
274        syn::visit::visit_item_fn(self, func);
275    }
276}