cha_core/plugins/
todo_tracker.rs1use crate::{AnalysisContext, Finding, Location, Plugin, Severity, SmellCategory};
2
3pub struct TodoTrackerAnalyzer;
8
9impl Plugin for TodoTrackerAnalyzer {
10 fn name(&self) -> &str {
11 "todo_tracker"
12 }
13
14 fn description(&self) -> &str {
15 "Leftover TODO/FIXME/HACK/XXX comments"
16 }
17
18 fn analyze(&self, ctx: &AnalysisContext) -> Vec<Finding> {
19 ctx.model
20 .comments
21 .iter()
22 .filter_map(|c| check_comment(c, ctx))
23 .collect()
24 }
25}
26
27fn check_comment(c: &crate::CommentInfo, ctx: &AnalysisContext) -> Option<Finding> {
28 let upper = c.text.to_uppercase();
29 let (tag, severity) = if has_tag(&upper, "HACK") {
30 ("HACK", Severity::Warning)
31 } else if has_tag(&upper, "XXX") {
32 ("XXX", Severity::Warning)
33 } else if has_tag(&upper, "FIXME") {
34 ("FIXME", Severity::Hint)
35 } else if has_tag(&upper, "TODO") {
36 ("TODO", Severity::Hint)
37 } else {
38 return None;
39 };
40 Some(Finding {
41 smell_name: "todo_comment".into(),
42 category: SmellCategory::Dispensables,
43 severity,
44 location: Location {
45 path: ctx.file.path.clone(),
46 start_line: c.line,
47 end_line: c.line,
48 name: None,
49 },
50 message: format!(
51 "{tag}: {}",
52 c.text.trim_start_matches(['/', '#', '*', ' ', '-'])
53 ),
54 suggested_refactorings: vec!["Resolve or create a tracking issue".into()],
55 ..Default::default()
56 })
57}
58
59fn has_tag(line: &str, tag: &str) -> bool {
61 if let Some(pos) = line.find(tag) {
62 let after = pos + tag.len();
63 after >= line.len() || !line.as_bytes()[after].is_ascii_alphabetic()
64 } else {
65 false
66 }
67}