harper_core/linting/
nobody.rs

1use crate::{
2    Token, TokenStringExt,
3    patterns::{Pattern, SequencePattern},
4};
5
6use super::{Lint, LintKind, PatternLinter, Suggestion};
7
8pub struct Nobody {
9    pattern: Box<dyn Pattern>,
10}
11
12impl Default for Nobody {
13    fn default() -> Self {
14        let pattern = SequencePattern::aco("no")
15            .then_whitespace()
16            .t_aco("body")
17            .then_whitespace()
18            .then_verb();
19        Self {
20            pattern: Box::new(pattern),
21        }
22    }
23}
24
25impl PatternLinter for Nobody {
26    fn pattern(&self) -> &dyn Pattern {
27        self.pattern.as_ref()
28    }
29
30    fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
31        let span = matched_tokens[0..3].span()?;
32        let orig_chars = span.get_content(source);
33        Some(Lint {
34            span,
35            lint_kind: LintKind::WordChoice,
36            suggestions: vec![Suggestion::replace_with_match_case(
37                "nobody".chars().collect(),
38                orig_chars,
39            )],
40            message: format!("Did you mean the closed compound `{}`?", "nobody"),
41            ..Default::default()
42        })
43    }
44
45    fn description(&self) -> &'static str {
46        "Looks for incorrect spacing inside the closed compound `nobody`."
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    use crate::linting::tests::assert_suggestion_result;
53
54    use super::Nobody;
55
56    #[test]
57    fn both_valid_and_invalid() {
58        assert_suggestion_result(
59            "No body told me. I have a head but no body.",
60            Nobody::default(),
61            "Nobody told me. I have a head but no body.",
62        );
63    }
64}