harper_core/linting/
correct_number_suffix.rs

1use super::{Lint, LintKind, Linter, Suggestion};
2use crate::{Document, OrdinalSuffix, Span, TokenKind};
3use crate::{Number, TokenStringExt};
4
5/// Detect incorrect number suffix (e.g. "2st").
6#[derive(Debug, Clone, Copy, Default)]
7pub struct CorrectNumberSuffix;
8
9impl Linter for CorrectNumberSuffix {
10    fn lint(&mut self, document: &Document) -> Vec<Lint> {
11        let mut output = Vec::new();
12
13        for number_tok in document.iter_numbers() {
14            let Some(suffix_span) = Span::new_with_len(number_tok.span.end, 2).pulled_by(2) else {
15                continue;
16            };
17
18            if let TokenKind::Number(Number {
19                value,
20                suffix: Some(suffix),
21                ..
22            }) = number_tok.kind
23                && let Some(correct_suffix) = OrdinalSuffix::correct_suffix_for(value)
24                && suffix != correct_suffix
25            {
26                output.push(Lint {
27                    span: suffix_span,
28                    lint_kind: LintKind::Miscellaneous,
29                    message: "This number needs a different suffix to sound right.".to_string(),
30                    suggestions: vec![Suggestion::ReplaceWith(correct_suffix.to_chars())],
31                    ..Default::default()
32                })
33            }
34        }
35
36        output
37    }
38
39    fn description(&self) -> &'static str {
40        "When making quick edits, it is common for authors to change the value of a number without changing its suffix. This rule looks for these cases, for example: `2st`."
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::CorrectNumberSuffix;
47    use crate::linting::tests::assert_lint_count;
48
49    #[test]
50    fn passes_correct_cases() {
51        assert_lint_count("2nd", CorrectNumberSuffix, 0);
52        assert_lint_count("101st", CorrectNumberSuffix, 0);
53        assert_lint_count("1012th", CorrectNumberSuffix, 0);
54    }
55
56    #[test]
57    fn detects_incorrect_cases() {
58        assert_lint_count("2st", CorrectNumberSuffix, 1);
59        assert_lint_count("101nd", CorrectNumberSuffix, 1);
60        assert_lint_count("1012rd", CorrectNumberSuffix, 1);
61    }
62}