Skip to main content

wowlab_tidy/languages/rust/rules/style/
whitespace.rs

1#[cfg(test)]
2use googletest::prelude::*;
3
4use crate::{Example, FileCtx, Fix, Violation, violation};
5
6#[rustfmt::skip]
7const EXAMPLES: &[Example] = &[
8    Example {
9        label: "clean file",
10        code: "fn main() {}",
11        pass: true,
12    },
13    Example {
14        label: "trailing whitespace",
15        code: "let x = 1;   \nlet y = 2;",
16        pass: false,
17    },
18    Example {
19        label: "tab character",
20        code: "\tlet x = 1;",
21        pass: false,
22    },
23];
24
25crate::line_rule!(
26    style,
27    "Enforce no trailing whitespace, no tabs, no CRLF line endings.",
28    "Trailing whitespace, tabs, and CRLF endings cause noisy diffs and merge conflicts.",
29    Low,
30    fix_style,
31);
32
33fn check_style(ctx: &FileCtx<'_>) -> Vec<Violation> {
34    let mut out = Vec::new();
35
36    for (i, line) in ctx.lines.iter().enumerate() {
37        let lineno = i + 1;
38
39        if *line != line.trim_end() {
40            out.push(violation(ctx.rel, lineno, "trailing whitespace"));
41        }
42
43        if line.contains('\t') {
44            out.push(violation(ctx.rel, lineno, "tab character (use spaces)"));
45        }
46
47        if line.contains('\r') {
48            out.push(violation(ctx.rel, lineno, "CR line ending (use LF)"));
49        }
50    }
51
52    out
53}
54
55fn fix_style(ctx: &FileCtx<'_>, v: &Violation) -> Option<Fix> {
56    let line = ctx.line(v.line)?;
57    let fixed = line.replace('\t', "    ").replace('\r', "");
58
59    Some(Fix::replace_line(v.line, fixed.trim_end()))
60}
61
62crate::tidy_test!(check_style, {
63    crate::example_tests!(EXAMPLES, check_style);
64    crate::fix_tests!(line, check_style, fix_style);
65
66    #[gtest]
67    fn cr_line_ending() -> Result<()> {
68        let v = run("let x = 1;\r");
69        verify_false!(v.is_empty())?;
70        verify_true!(v.iter().any(|v| v.message.contains("CR line ending")))?;
71
72        Ok(())
73    }
74
75    #[gtest]
76    fn multiple_violations_on_one_line() -> Result<()> {
77        let v = run("\tlet x = 1;  ");
78        verify_eq!(v.len(), 2)?;
79
80        Ok(())
81    }
82});