garbage_code_hunter/rules/
mod.rs1use std::path::Path;
2use syn::File;
3
4use crate::analyzer::CodeIssue;
5use crate::context::{FileContext, ProjectConfig};
6
7pub mod advanced_rust;
8pub mod code_smells;
9pub mod complexity;
10pub mod comprehensive_rust;
11pub mod duplication;
12pub mod file_structure;
13pub mod garbage_naming;
14pub mod naming;
15pub mod rust_patterns;
16pub mod rust_specific;
17pub mod struct_patterns;
18pub mod student_code;
19
20pub trait Rule {
21 fn name(&self) -> &'static str;
22
23 fn check(
25 &self,
26 file_path: &Path,
27 syntax_tree: &File,
28 content: &str,
29 lang: &str,
30 is_test_file: bool,
31 ) -> Vec<CodeIssue> {
32 self.check_with_context(
33 file_path,
34 syntax_tree,
35 content,
36 lang,
37 is_test_file,
38 &FileContext::from_path(file_path),
39 &ProjectConfig::default(),
40 )
41 }
42
43 #[allow(clippy::too_many_arguments)]
45 fn check_with_context(
46 &self,
47 file_path: &Path,
48 syntax_tree: &File,
49 content: &str,
50 lang: &str,
51 is_test_file: bool,
52 _context: &FileContext,
53 _config: &ProjectConfig,
54 ) -> Vec<CodeIssue> {
55 self.check(file_path, syntax_tree, content, lang, is_test_file)
56 }
57}
58
59pub struct RuleEngine {
60 rules: Vec<Box<dyn Rule>>,
61 config: ProjectConfig,
62}
63
64impl Default for RuleEngine {
65 fn default() -> Self {
66 Self::new()
67 }
68}
69
70impl RuleEngine {
71 pub fn new() -> Self {
72 Self::with_config(ProjectConfig::default())
73 }
74
75 pub fn with_config(config: ProjectConfig) -> Self {
76 let rules: Vec<Box<dyn Rule>> = vec![
77 Box::new(naming::TerribleNamingRule),
79 Box::new(naming::SingleLetterVariableRule),
80 Box::new(garbage_naming::MeaninglessNamingRule),
82 Box::new(garbage_naming::HungarianNotationRule),
83 Box::new(garbage_naming::AbbreviationAbuseRule),
84 Box::new(student_code::PrintlnDebuggingRule),
86 Box::new(student_code::PanicAbuseRule),
87 Box::new(student_code::TodoCommentRule),
88 Box::new(code_smells::MagicNumberRule),
90 Box::new(code_smells::GodFunctionRule),
91 Box::new(code_smells::CommentedCodeRule),
92 Box::new(code_smells::DeadCodeRule),
93 Box::new(rust_patterns::StringAbuseRule),
95 Box::new(rust_patterns::VecAbuseRule),
96 Box::new(complexity::DeepNestingRule),
97 Box::new(complexity::LongFunctionRule),
98 Box::new(duplication::CodeDuplicationRule),
99 Box::new(rust_specific::UnwrapAbuseRule),
100 Box::new(rust_specific::UnnecessaryCloneRule),
101 Box::new(advanced_rust::ComplexClosureRule),
103 Box::new(advanced_rust::LifetimeAbuseRule),
104 Box::new(advanced_rust::TraitComplexityRule),
105 Box::new(advanced_rust::GenericAbuseRule),
106 Box::new(comprehensive_rust::ChannelAbuseRule),
108 Box::new(comprehensive_rust::AsyncAbuseRule),
109 Box::new(comprehensive_rust::DynTraitAbuseRule),
110 Box::new(comprehensive_rust::UnsafeAbuseRule),
111 Box::new(comprehensive_rust::FFIAbuseRule),
112 Box::new(comprehensive_rust::MacroAbuseRule),
113 Box::new(comprehensive_rust::ModuleComplexityRule),
114 Box::new(comprehensive_rust::PatternMatchingAbuseRule),
115 Box::new(struct_patterns::ReferenceAbuseRule),
116 Box::new(struct_patterns::BoxAbuseRule),
117 Box::new(struct_patterns::SliceAbuseRule),
118 Box::new(file_structure::FileStructureRule),
120 Box::new(file_structure::ImportChaosRule),
121 Box::new(file_structure::ModuleNestingRule),
122 ];
123
124 Self { rules, config }
125 }
126
127 pub fn check_file_with_context(
129 &self,
130 file_path: &Path,
131 syntax_tree: &File,
132 content: &str,
133 lang: &str,
134 is_test_file: bool,
135 ) -> Vec<CodeIssue> {
136 let context = FileContext::from_path(file_path);
137 let mut issues = Vec::new();
138
139 for rule in &self.rules {
140 if context.should_skip_rule(rule.name()) {
141 continue;
142 }
143
144 let rule_issues = rule.check_with_context(
145 file_path,
146 syntax_tree,
147 content,
148 lang,
149 is_test_file,
150 &context,
151 &self.config,
152 );
153
154 issues.extend(rule_issues);
155 }
156
157 issues
158 }
159
160 pub fn check_file(
162 &self,
163 file_path: &Path,
164 syntax_tree: &File,
165 content: &str,
166 lang: &str,
167 is_test_file: bool,
168 ) -> Vec<CodeIssue> {
169 self.check_file_with_context(file_path, syntax_tree, content, lang, is_test_file)
170 }
171
172 pub fn rule_names(&self) -> Vec<&'static str> {
173 self.rules.iter().map(|r| r.name()).collect()
174 }
175}