harper_core/linting/
the_how_why.rs1use crate::{
2 Token, TokenStringExt,
3 linting::{Lint, LintKind, PatternLinter, Suggestion},
4 patterns::{EitherPattern, Invert, Pattern, SequencePattern},
5};
6
7pub struct TheHowWhy {
10 pattern: EitherPattern,
11}
12
13impl Default for TheHowWhy {
14 fn default() -> Self {
15 let the_how = SequencePattern::default()
16 .t_aco("the")
17 .then_whitespace()
18 .t_aco("how")
19 .then(Invert::new(
20 SequencePattern::default().then_whitespace().t_aco("to"),
21 ));
22
23 let the_who = SequencePattern::default()
24 .t_aco("the")
25 .then_whitespace()
26 .t_aco("who")
27 .then(Invert::new(
28 SequencePattern::default()
29 .then_whitespace()
30 .t_aco("'s")
31 .then_whitespace()
32 .t_aco("who"),
33 ));
34
35 let the_why = SequencePattern::default()
36 .t_aco("the")
37 .then_whitespace()
38 .t_aco("why");
39
40 let the_when = SequencePattern::default()
41 .t_aco("the")
42 .then_whitespace()
43 .t_aco("when");
44
45 let the_what = SequencePattern::default()
46 .t_aco("the")
47 .then_whitespace()
48 .t_aco("what");
49
50 let pattern = EitherPattern::new(vec![
51 Box::new(the_how),
52 Box::new(the_who),
53 Box::new(the_why),
54 Box::new(the_when),
55 Box::new(the_what),
56 ]);
57
58 Self { pattern }
59 }
60}
61
62impl PatternLinter for TheHowWhy {
63 fn pattern(&self) -> &dyn Pattern {
64 &self.pattern
65 }
66
67 fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
68 let the_token_span = matched_tokens[0..2].span()?;
69 let question_word_token = matched_tokens.get(2)?;
70 let question_word = question_word_token.span.get_content(source);
71
72 Some(Lint {
73 span: the_token_span,
74 lint_kind: LintKind::Miscellaneous,
75 message: format!(
76 "Remove `the` before `{}`. In most contexts, `{}` alone is clearer.",
77 question_word.iter().collect::<String>(),
78 question_word.iter().collect::<String>()
79 ),
80 suggestions: vec![Suggestion::Remove],
81 priority: 31,
82 })
83 }
84
85 fn description(&self) -> &str {
86 "Removes the extra `the` from expressions like `the how`, skipping `how to` and `who's who`."
87 }
88}
89
90#[cfg(test)]
91mod tests {
92 use super::TheHowWhy;
93 use crate::linting::tests::{assert_lint_count, assert_suggestion_result};
94
95 #[test]
96 fn basic_the_how() {
97 assert_suggestion_result(
98 "This is the how it all started.",
99 TheHowWhy::default(),
100 "This is how it all started.",
101 );
102 }
103
104 #[test]
105 fn the_why() {
106 assert_suggestion_result(
107 "The important part is the why it matters.",
108 TheHowWhy::default(),
109 "The important part is why it matters.",
110 );
111 }
112
113 #[test]
114 fn skip_how_to() {
115 assert_lint_count(
116 "I'd like to explain the how to install this properly.",
117 TheHowWhy::default(),
118 0,
119 );
120 }
121
122 #[test]
123 fn skip_whos_who() {
124 assert_lint_count(
125 "We covered the who's who of corporate leadership last time.",
126 TheHowWhy::default(),
127 0,
128 );
129 }
130
131 #[test]
132 fn the_who() {
133 assert_suggestion_result(
134 "We must identify the who is responsible.",
135 TheHowWhy::default(),
136 "We must identify who is responsible.",
137 );
138 }
139
140 #[test]
141 fn the_when() {
142 assert_suggestion_result(
143 "He outlined the when the new phase will start.",
144 TheHowWhy::default(),
145 "He outlined when the new phase will start.",
146 );
147 }
148
149 #[test]
150 fn the_what() {
151 assert_suggestion_result(
152 "The presentation clarifies the what we intend to build.",
153 TheHowWhy::default(),
154 "The presentation clarifies what we intend to build.",
155 );
156 }
157
158 #[test]
159 fn no_false_positive() {
160 assert_lint_count(
161 "These tips examine the how to fix your code quickly, plus the what's next.",
162 TheHowWhy::default(),
163 0,
164 );
165 }
166}