harper_core/linting/
use_genitive.rs1use crate::linting::{LintKind, PatternLinter, Suggestion};
2use crate::patterns::{EitherPattern, Invert, Pattern, SequencePattern, WordPatternGroup};
3use crate::{Lint, Lrc, Token};
4
5pub struct UseGenitive {
7 pattern: Box<dyn Pattern>,
8}
9
10impl UseGenitive {
11 fn new() -> Self {
12 let environment = Lrc::new(SequencePattern::default().then_whitespace().then(
14 EitherPattern::new(vec![
15 Box::new(
16 SequencePattern::default()
17 .then_one_or_more_adjectives()
18 .then_whitespace()
19 .then_noun(),
20 ),
21 Box::new(SequencePattern::default().then_noun()),
22 ]),
23 ));
24
25 let trigger_words = ["there", "they're"];
26
27 let mut primary_pattern = WordPatternGroup::default();
28
29 for word in trigger_words {
30 primary_pattern.add(
31 word,
32 Box::new(
33 SequencePattern::default()
34 .then_exact_word(word)
35 .then(environment.clone()),
36 ),
37 )
38 }
39
40 let full_pattern = SequencePattern::default()
42 .then(Invert::new(EitherPattern::new(vec![
43 Box::new(SequencePattern::default().t_aco("is")),
44 Box::new(SequencePattern::default().t_aco("were")),
45 Box::new(SequencePattern::default().then_adjective()),
46 ])))
47 .then_whitespace()
48 .then(primary_pattern);
49
50 Self {
51 pattern: Box::new(full_pattern),
52 }
53 }
54}
55
56impl PatternLinter for UseGenitive {
57 fn pattern(&self) -> &dyn crate::patterns::Pattern {
58 self.pattern.as_ref()
59 }
60
61 fn match_to_lint(&self, matched_tokens: &[Token], _source: &[char]) -> Option<Lint> {
62 Some(Lint {
63 span: matched_tokens[2].span,
64 lint_kind: LintKind::Miscellaneous,
65 suggestions: vec![Suggestion::ReplaceWith(vec!['t', 'h', 'e', 'i', 'r'])],
66 message: "Use the genitive case.".to_string(),
67 priority: 31,
68 })
69 }
70
71 fn description(&self) -> &'static str {
72 "Looks for situations where the genitive case of \"there\" should be used."
73 }
74}
75
76impl Default for UseGenitive {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use crate::linting::tests::{assert_lint_count, assert_suggestion_result};
85
86 use super::UseGenitive;
87
88 #[test]
89 fn catches_adjective_noun() {
90 assert_suggestion_result(
91 "What are there big problems?",
92 UseGenitive::default(),
93 "What are their big problems?",
94 )
95 }
96
97 #[test]
98 fn catches_just_noun() {
99 assert_suggestion_result(
100 "What are there problems?",
101 UseGenitive::default(),
102 "What are their problems?",
103 )
104 }
105
106 #[test]
107 fn allows_clause_termination() {
108 assert_lint_count("Look there!", UseGenitive::default(), 0)
109 }
110
111 #[test]
112 fn allows_there_are() {
113 assert_lint_count(
114 "Since there are people here, we should be socially aware.",
115 UseGenitive::default(),
116 0,
117 )
118 }
119
120 #[test]
121 fn allows_there_at_beginning() {
122 assert_lint_count(
123 "There is a cute cat sitting on the chair at home.",
124 UseGenitive::default(),
125 0,
126 )
127 }
128
129 #[test]
130 fn catches_they_are() {
131 assert_suggestion_result(
132 "The students received they're test results today.",
133 UseGenitive::default(),
134 "The students received their test results today.",
135 )
136 }
137
138 #[test]
139 fn allows_grantlemons_issue_267_cat() {
140 assert_lint_count("Were there cats at her house?", UseGenitive::default(), 0);
141 }
142
143 #[test]
144 fn allows_grantlemons_issue_267_apple() {
145 assert_lint_count(
146 "Were there any apples at the store?",
147 UseGenitive::default(),
148 0,
149 );
150 }
151
152 #[test]
153 fn allows_grantlemons_issue_267_fruit() {
154 assert_lint_count(
155 "Were there many kinds of fruit at the store?",
156 UseGenitive::default(),
157 0,
158 );
159 }
160
161 #[test]
162 fn allows_grantlemons_issue_267_people() {
163 assert_lint_count(
164 "Were there more than, or less than six people at the party?",
165 UseGenitive::default(),
166 0,
167 );
168 }
169
170 #[test]
171 fn allows_faster_at_running() {
172 assert_lint_count(
173 "Melissa was faster at running than her friend.",
174 UseGenitive::default(),
175 0,
176 );
177 }
178}