harper_core/linting/
correct_number_suffix.rs1use super::{Lint, LintKind, Linter, Suggestion};
2use crate::{Document, OrdinalSuffix, Span, TokenKind};
3use crate::{Number, TokenStringExt};
4
5#[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}