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