1use std::path::Path;
2use syn::{visit::Visit, ExprClosure, File, GenericParam, ItemImpl, ItemTrait, Lifetime};
3
4use crate::analyzer::{CodeIssue, Severity};
5use crate::rules::Rule;
6use crate::utils::get_position;
7
8pub struct ComplexClosureRule;
9
10impl Rule for ComplexClosureRule {
11 fn name(&self) -> &'static str {
12 "complex-closure"
13 }
14
15 fn check(
16 &self,
17 file_path: &Path,
18 syntax_tree: &File,
19 _content: &str,
20 lang: &str,
21 is_test_file: bool,
22 ) -> Vec<CodeIssue> {
23 if is_test_file {
24 return Vec::new();
25 }
26 let mut visitor = ClosureVisitor::new(file_path.to_path_buf(), lang);
27 visitor.visit_file(syntax_tree);
28 visitor.issues
29 }
30}
31
32pub struct LifetimeAbuseRule;
33
34impl Rule for LifetimeAbuseRule {
35 fn name(&self) -> &'static str {
36 "lifetime-abuse"
37 }
38
39 fn check(
40 &self,
41 file_path: &Path,
42 syntax_tree: &File,
43 _content: &str,
44 lang: &str,
45 is_test_file: bool,
46 ) -> Vec<CodeIssue> {
47 if is_test_file {
48 return Vec::new();
49 }
50
51 let mut visitor = LifetimeVisitor::new(file_path.to_path_buf(), lang);
52 visitor.visit_file(syntax_tree);
53 visitor.issues
54 }
55}
56
57pub struct TraitComplexityRule;
58
59impl Rule for TraitComplexityRule {
60 fn name(&self) -> &'static str {
61 "trait-complexity"
62 }
63
64 fn check(
65 &self,
66 file_path: &Path,
67 syntax_tree: &File,
68 _content: &str,
69 lang: &str,
70 is_test_file: bool,
71 ) -> Vec<CodeIssue> {
72 if is_test_file {
73 return Vec::new();
74 }
75 let mut visitor = TraitVisitor::new(file_path.to_path_buf(), lang);
76 visitor.visit_file(syntax_tree);
77 visitor.issues
78 }
79}
80
81pub struct GenericAbuseRule;
82
83impl Rule for GenericAbuseRule {
84 fn name(&self) -> &'static str {
85 "generic-abuse"
86 }
87
88 fn check(
89 &self,
90 file_path: &Path,
91 syntax_tree: &File,
92 _content: &str,
93 lang: &str,
94 is_test_file: bool,
95 ) -> Vec<CodeIssue> {
96 if is_test_file {
97 return Vec::new();
98 }
99 let mut visitor = GenericVisitor::new(file_path.to_path_buf(), lang);
100 visitor.visit_file(syntax_tree);
101 visitor.issues
102 }
103}
104
105struct ClosureVisitor {
106 file_path: std::path::PathBuf,
107 issues: Vec<CodeIssue>,
108 closure_depth: usize,
109 lang: String,
110}
111
112impl ClosureVisitor {
113 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
114 Self {
115 file_path,
116 issues: Vec::new(),
117 closure_depth: 0,
118 lang: lang.to_string(),
119 }
120 }
121
122 fn check_closure_complexity(&mut self, closure: &ExprClosure) {
123 if self.closure_depth > 2 {
125 let messages = if self.lang == "zh-CN" {
126 vec![
127 "闭包套闭包?你这是在写俄罗斯套娃还是在考验读者的智商?",
128 "嵌套闭包比我的人际关系还复杂",
129 "这闭包嵌套得像洋葱一样,剥一层哭一次",
130 "闭包嵌套过深,建议拆分成独立函数",
131 ]
132 } else {
133 vec![
134 "Closures within closures? Are you writing Russian nesting dolls or testing our IQ?",
135 "Nested closures are more complex than my relationships",
136 "These closures are nested like an onion - peel one layer, cry once",
137 "Closures too deeply nested - consider splitting into separate functions",
138 ]
139 };
140
141 let (line, column) = get_position(closure);
142 self.issues.push(CodeIssue {
143 file_path: self.file_path.clone(),
144 line,
145 column,
146 rule_name: "complex-closure".to_string(),
147 message: messages[self.issues.len() % messages.len()].to_string(),
148 severity: Severity::Spicy,
149 });
150 }
151
152 if closure.inputs.len() > 5 {
154 let messages = if self.lang == "zh-CN" {
155 vec![
156 "这个闭包的参数比我的借口还多",
157 "闭包参数过多,你确定不是在写函数?",
158 "这么多参数的闭包,建议改成正经函数",
159 ]
160 } else {
161 vec![
162 "This closure has more parameters than my excuses",
163 "Too many parameters for a closure - are you sure this isn't a function?",
164 "So many parameters - consider making this a proper function",
165 ]
166 };
167
168 let (line, column) = get_position(closure);
169 self.issues.push(CodeIssue {
170 file_path: self.file_path.clone(),
171 line,
172 column,
173 rule_name: "complex-closure".to_string(),
174 message: messages[self.issues.len() % messages.len()].to_string(),
175 severity: Severity::Mild,
176 });
177 }
178 }
179}
180
181impl<'ast> Visit<'ast> for ClosureVisitor {
182 fn visit_expr_closure(&mut self, closure: &'ast ExprClosure) {
183 self.closure_depth += 1;
184 self.check_closure_complexity(closure);
185 syn::visit::visit_expr_closure(self, closure);
186 self.closure_depth -= 1;
187 }
188}
189
190struct LifetimeVisitor {
191 file_path: std::path::PathBuf,
192 issues: Vec<CodeIssue>,
193 lifetime_count: usize,
194 lang: String,
195}
196
197impl LifetimeVisitor {
198 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
199 Self {
200 file_path,
201 issues: Vec::new(),
202 lifetime_count: 0,
203 lang: lang.to_string(),
204 }
205 }
206}
207
208impl<'ast> Visit<'ast> for LifetimeVisitor {
209 fn visit_lifetime(&mut self, lifetime: &'ast Lifetime) {
210 if lifetime.ident == "static" {
212 return;
213 }
214
215 self.lifetime_count += 1;
216
217 if self.lifetime_count > 20 && self.issues.is_empty() {
221 let messages = if self.lang == "zh-CN" {
222 vec![
223 "生命周期标注比我的生命还复杂",
224 "这么多生命周期,你是在写哲学论文吗?",
225 "生命周期滥用,建议重新设计数据结构",
226 "生命周期多到让人怀疑人生",
227 ]
228 } else {
229 vec![
230 "Lifetime annotations are more complex than my life",
231 "So many lifetimes - are you writing a philosophy paper?",
232 "Lifetime abuse - consider redesigning your data structure",
233 "Too many lifetimes - making people question their existence",
234 ]
235 };
236
237 let (line, column) = get_position(lifetime);
238 self.issues.push(CodeIssue {
239 file_path: self.file_path.clone(),
240 line,
241 column,
242 rule_name: "lifetime-abuse".to_string(),
243 message: messages[self.issues.len() % messages.len()].to_string(),
244 severity: Severity::Spicy,
245 });
246 }
247
248 syn::visit::visit_lifetime(self, lifetime);
249 }
250}
251
252struct TraitVisitor {
253 file_path: std::path::PathBuf,
254 issues: Vec<CodeIssue>,
255 lang: String,
256}
257
258impl TraitVisitor {
259 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
260 Self {
261 file_path,
262 issues: Vec::new(),
263 lang: lang.to_string(),
264 }
265 }
266
267 fn check_trait_complexity(&mut self, trait_item: &ItemTrait) {
268 if trait_item.items.len() > 10 {
270 let messages = if self.lang == "zh-CN" {
271 vec![
272 "这个 trait 的方法比我的借口还多",
273 "trait 方法过多,违反了单一职责原则",
274 "这个 trait 比瑞士军刀还要全能",
275 "trait 臃肿,建议拆分成多个小 trait",
276 ]
277 } else {
278 vec![
279 "This trait has more methods than my excuses",
280 "Too many trait methods - violates single responsibility principle",
281 "This trait is more versatile than a Swiss Army knife",
282 "Bloated trait - consider splitting into smaller traits",
283 ]
284 };
285
286 let (line, column) = get_position(trait_item);
287 self.issues.push(CodeIssue {
288 file_path: self.file_path.clone(),
289 line,
290 column,
291 rule_name: "trait-complexity".to_string(),
292 message: messages[self.issues.len() % messages.len()].to_string(),
293 severity: Severity::Spicy,
294 });
295 }
296
297 if trait_item.generics.params.len() > 3 {
299 let messages = if self.lang == "zh-CN" {
300 vec![
301 "泛型参数比我的密码还复杂",
302 "这么多泛型,你是在写数学公式吗?",
303 "泛型滥用,建议简化设计",
304 ]
305 } else {
306 vec![
307 "Generic parameters are more complex than my password",
308 "So many generics - are you writing a math formula?",
309 "Generic abuse - simplify your design",
310 ]
311 };
312
313 let (line, column) = get_position(trait_item);
314 self.issues.push(CodeIssue {
315 file_path: self.file_path.clone(),
316 line,
317 column,
318 rule_name: "trait-complexity".to_string(),
319 message: messages[self.issues.len() % messages.len()].to_string(),
320 severity: Severity::Mild,
321 });
322 }
323 }
324}
325
326impl<'ast> Visit<'ast> for TraitVisitor {
327 fn visit_item_trait(&mut self, trait_item: &'ast ItemTrait) {
328 self.check_trait_complexity(trait_item);
329 syn::visit::visit_item_trait(self, trait_item);
330 }
331}
332
333struct GenericVisitor {
334 file_path: std::path::PathBuf,
335 issues: Vec<CodeIssue>,
336 lang: String,
337}
338
339impl GenericVisitor {
340 fn new(file_path: std::path::PathBuf, lang: &str) -> Self {
341 Self {
342 file_path,
343 issues: Vec::new(),
344 lang: lang.to_string(),
345 }
346 }
347
348 fn check_generic_abuse(&mut self, generics: &syn::Generics) {
349 if generics.params.len() > 5 {
350 let messages = if self.lang == "zh-CN" {
351 vec![
352 "泛型参数比我的购物清单还长",
353 "这么多泛型,编译器都要哭了",
354 "泛型滥用,建议重新设计架构",
355 "泛型多到让人怀疑这还是 Rust 吗",
356 ]
357 } else {
358 vec![
359 "Generic parameters are longer than my shopping list",
360 "So many generics - even the compiler is crying",
361 "Generic abuse - consider redesigning your architecture",
362 "So many generics - making people question if this is still Rust",
363 ]
364 };
365
366 let (line, column) = get_position(generics);
367 self.issues.push(CodeIssue {
368 file_path: self.file_path.clone(),
369 line,
370 column,
371 rule_name: "generic-abuse".to_string(),
372 message: messages[self.issues.len() % messages.len()].to_string(),
373 severity: Severity::Spicy,
374 });
375 }
376
377 for param in &generics.params {
379 if let GenericParam::Type(type_param) = param {
380 let name = type_param.ident.to_string();
381 if name.len() == 1 && !matches!(name.as_str(), "T" | "U" | "V" | "E" | "K") {
382 let messages = if self.lang == "zh-CN" {
383 vec![
384 format!("泛型参数 '{name}' 的命名创意约等于零"),
385 format!("泛型 '{name}' 的名字比我的耐心还短"),
386 format!("用 '{name}' 做泛型名?建议用更有意义的名字"),
387 ]
388 } else {
389 vec![
390 format!("Generic parameter '{name}' has approximately zero creativity"),
391 format!("Generic '{name}' is shorter than my patience"),
392 format!("Using '{name}' as a generic name? Consider something more meaningful"),
393 ]
394 };
395
396 let (line, column) = get_position(type_param);
397 self.issues.push(CodeIssue {
398 file_path: self.file_path.clone(),
399 line,
400 column,
401 rule_name: "generic-abuse".to_string(),
402 message: messages[self.issues.len() % messages.len()].clone(),
403 severity: Severity::Mild,
404 });
405 }
406 }
407 }
408 }
409}
410
411impl<'ast> Visit<'ast> for GenericVisitor {
412 fn visit_item_fn(&mut self, func: &'ast syn::ItemFn) {
413 self.check_generic_abuse(&func.sig.generics);
414 syn::visit::visit_item_fn(self, func);
415 }
416
417 fn visit_item_struct(&mut self, struct_item: &'ast syn::ItemStruct) {
418 self.check_generic_abuse(&struct_item.generics);
419 syn::visit::visit_item_struct(self, struct_item);
420 }
421
422 fn visit_item_enum(&mut self, enum_item: &'ast syn::ItemEnum) {
423 self.check_generic_abuse(&enum_item.generics);
424 syn::visit::visit_item_enum(self, enum_item);
425 }
426
427 fn visit_item_impl(&mut self, impl_item: &'ast ItemImpl) {
428 self.check_generic_abuse(&impl_item.generics);
429 syn::visit::visit_item_impl(self, impl_item);
430 }
431}