harper_core/linting/
possessive_your.rs

1use crate::{
2    Token,
3    patterns::{Pattern, SequencePattern},
4};
5
6use super::{Lint, LintKind, PatternLinter, Suggestion};
7
8pub struct PossessiveYour {
9    pattern: Box<dyn Pattern>,
10}
11
12impl Default for PossessiveYour {
13    fn default() -> Self {
14        let pattern =
15            SequencePattern::aco("you")
16                .then_whitespace()
17                .then(|tok: &Token, source: &[char]| {
18                    if tok.kind.is_nominal() && !tok.kind.is_likely_homograph() {
19                        let word = tok.span.get_content_string(source).to_lowercase();
20                        return !matches!(word.as_str(), "guys" | "what's");
21                    }
22                    false
23                });
24
25        Self {
26            pattern: Box::new(pattern),
27        }
28    }
29}
30
31impl PatternLinter for PossessiveYour {
32    fn pattern(&self) -> &dyn Pattern {
33        self.pattern.as_ref()
34    }
35
36    fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
37        let span = matched_tokens.first()?.span;
38        let orig_chars = span.get_content(source);
39
40        Some(Lint {
41            span,
42            lint_kind: LintKind::WordChoice,
43            suggestions: vec![
44                Suggestion::replace_with_match_case("your".chars().collect(), orig_chars),
45                Suggestion::replace_with_match_case("you're a".chars().collect(), orig_chars),
46                Suggestion::replace_with_match_case("you're an".chars().collect(), orig_chars),
47            ],
48            message: "The possessive version of this word is more common in this context."
49                .to_owned(),
50            ..Default::default()
51        })
52    }
53
54    fn description(&self) -> &'static str {
55        "The possessive form of `you` is more likely before nouns."
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use crate::linting::tests::{
62        assert_lint_count, assert_suggestion_result, assert_top3_suggestion_result,
63    };
64
65    use super::PossessiveYour;
66
67    #[test]
68    fn your_comments() {
69        assert_suggestion_result(
70            "You comments may end up in the documentation.",
71            PossessiveYour::default(),
72            "Your comments may end up in the documentation.",
73        );
74    }
75
76    #[test]
77    fn allow_intro_page() {
78        assert_lint_count(
79            "You can try out an editor that uses Harper under-the-hood here.",
80            PossessiveYour::default(),
81            0,
82        );
83    }
84
85    #[test]
86    fn allow_you_guys() {
87        assert_lint_count(
88            "I mean I'm pretty sure you guys can't do anything with this stuff.",
89            PossessiveYour::default(),
90            0,
91        );
92    }
93
94    #[test]
95    fn test_top3_suggestion_your() {
96        assert_top3_suggestion_result(
97            "You combination of artist and teacher.",
98            PossessiveYour::default(),
99            "Your combination of artist and teacher.",
100        );
101    }
102
103    #[test]
104    fn test_top3_suggestion_youre_a() {
105        assert_top3_suggestion_result(
106            "You combination of artist and teacher.",
107            PossessiveYour::default(),
108            "You're a combination of artist and teacher.",
109        );
110    }
111
112    #[test]
113    #[ignore]
114    fn test_top3_suggestion_multiple() {
115        assert_top3_suggestion_result(
116            "You knowledge. You imagination. You icosahedron",
117            PossessiveYour::default(),
118            "Your knowledge. Your imagination. You're an icosahedron",
119        );
120    }
121
122    #[test]
123    fn dont_flag_just_showing_you() {
124        assert_lint_count(
125            "I'm just showing you what's available and how to use it.",
126            PossessiveYour::default(),
127            0,
128        );
129    }
130}