Skip to main content

wowlab_tidy/languages/rust/rules/hygiene/
todo.rs

1use crate::{Example, FileCtx, Violation, infra::parse, violation};
2
3#[rustfmt::skip]
4const EXAMPLES: &[Example] = &[
5    Example {
6        label: "bare TODO",
7        code: "// TODO: fix this later",
8        pass: false,
9    },
10    Example {
11        label: "tracked TODO",
12        code: "// TODO(#123) fix this",
13        pass: true,
14    },
15    Example {
16        label: "bare FIXME",
17        code: "// FIXME this is broken",
18        pass: false,
19    },
20    Example {
21        label: "bare HACK",
22        code: "// HACK: workaround",
23        pass: false,
24    },
25    Example {
26        label: "bare XXX",
27        code: "// XXX",
28        pass: false,
29    },
30    Example {
31        label: "no comment",
32        code: "let todo = 5;",
33        pass: true,
34    },
35    Example {
36        label: "inline TODO",
37        code: "let x = 1; // TODO fix",
38        pass: false,
39    },
40    Example {
41        label: "tracked FIXME",
42        code: "// FIXME(perf regression in v2)",
43        pass: true,
44    },
45];
46
47crate::line_rule!(
48    todo,
49    "Require TODO/FIXME/HACK/XXX to have parenthesized context.",
50    "TODO without context (who, ticket, deadline) becomes permanent. Parenthesized context ensures accountability.",
51);
52
53const BANNED: &[&str] = &["TODO", "FIXME", "HACK", "XXX"];
54
55fn check_todo(ctx: &FileCtx<'_>) -> Vec<Violation> {
56    let mut out = Vec::new();
57
58    for (i, line) in ctx.lines.iter().enumerate() {
59        let Some(comment) = parse::find_comment_start(line) else {
60            continue;
61        };
62
63        for keyword in BANNED {
64            if let Some(after) = parse::find_keyword_suffix(comment, keyword) {
65                if parse::matches(after, '(') {
66                    continue;
67                }
68
69                out.push(violation(
70                    ctx.rel,
71                    i + 1,
72                    format!(
73                        "{keyword} without tracking context \
74                         (use {keyword}(#issue) or {keyword}(reason))"
75                    ),
76                ));
77            }
78        }
79    }
80
81    out
82}
83
84crate::tidy_test!(check_todo, {
85    crate::example_tests!(EXAMPLES, check_todo);
86});