harper_core/linting/
back_in_the_day.rs

1use crate::{
2    Lrc, Token, TokenStringExt,
3    patterns::{ExactPhrase, OwnedPatternExt, Pattern, SequencePattern, WordSet},
4};
5
6use super::{Lint, LintKind, PatternLinter, Suggestion};
7
8pub struct BackInTheDay {
9    pattern: Box<dyn Pattern>,
10    // The trailing words that should tell us to ignore the rule.
11    exceptions: Lrc<WordSet>,
12}
13
14impl Default for BackInTheDay {
15    fn default() -> Self {
16        let exceptions = Lrc::new(WordSet::new(&["before", "of", "when"]));
17        let phrase = Lrc::new(ExactPhrase::from_phrase("back in the days"));
18
19        let pattern = SequencePattern::default()
20            .then(phrase.clone())
21            .then_whitespace()
22            .then(exceptions.clone())
23            .or(Box::new(phrase));
24
25        Self {
26            pattern: Box::new(pattern),
27            exceptions,
28        }
29    }
30}
31
32impl PatternLinter for BackInTheDay {
33    fn pattern(&self) -> &dyn Pattern {
34        self.pattern.as_ref()
35    }
36
37    fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
38        if let Some(tail) = matched_tokens.get(8..) {
39            if self.exceptions.matches(tail, source).is_some() {
40                return None;
41            }
42        }
43
44        let span = matched_tokens.span()?;
45        let chars = span.get_content(source);
46
47        Some(Lint {
48            span,
49            lint_kind: LintKind::WordChoice,
50            suggestions: vec![Suggestion::replace_with_match_case(
51                "back in the day".chars().collect(),
52                chars,
53            )],
54            message: "Use the more idiomatic version of this phrase.".to_owned(),
55            priority: 127,
56        })
57    }
58
59    fn description(&self) -> &'static str {
60        "This linter flags instances of the nonstandard phrase `back in the days`. The correct, more accepted form is `back in the day`"
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::BackInTheDay;
67    use crate::linting::tests::{assert_lint_count, assert_suggestion_result};
68
69    #[test]
70    fn detects_gem_update_case() {
71        assert_suggestion_result(
72            "... has been resolved through a gem update back in the days",
73            BackInTheDay::default(),
74            "... has been resolved through a gem update back in the day",
75        );
76    }
77
78    #[test]
79    fn detects_install_case() {
80        assert_suggestion_result(
81            "Back in the days we're used to install it directly from ...",
82            BackInTheDay::default(),
83            "Back in the day we're used to install it directly from ...",
84        );
85    }
86
87    #[test]
88    fn detects_composer_json_case() {
89        assert_suggestion_result(
90            "Back in the days there was only composer.json and ...",
91            BackInTheDay::default(),
92            "Back in the day there was only composer.json and ...",
93        );
94    }
95
96    #[test]
97    fn detects_version_release_case() {
98        assert_suggestion_result(
99            "... should have been released back in the days in a version 11",
100            BackInTheDay::default(),
101            "... should have been released back in the day in a version 11",
102        );
103    }
104
105    #[test]
106    fn avoids_false_positive_springfox() {
107        assert_lint_count(
108            "Back in the days of SpringFox, there were several requests to ...",
109            BackInTheDay::default(),
110            0,
111        );
112    }
113
114    #[test]
115    fn avoids_false_positive_ie() {
116        assert_lint_count(
117            "Back in the days of IE, Powershell used to ...",
118            BackInTheDay::default(),
119            0,
120        );
121    }
122
123    #[test]
124    fn avoids_false_positive_code_usage() {
125        assert_lint_count(
126            "Back in the days when I had 100% of my code in ...",
127            BackInTheDay::default(),
128            0,
129        );
130    }
131    #[test]
132    fn catches_uppercase() {
133        assert_lint_count(
134            "Back in the days, we went for a walk.",
135            BackInTheDay::default(),
136            1,
137        );
138    }
139
140    #[test]
141    fn catches_lowercase() {
142        assert_lint_count(
143            "We used to go for walks back in the days.",
144            BackInTheDay::default(),
145            1,
146        );
147    }
148
149    #[test]
150    fn doesnt_catch_false_positive_of() {
151        assert_lint_count(
152            "Back in the days of CRTs, computers were expensive.",
153            BackInTheDay::default(),
154            0,
155        );
156    }
157
158    #[test]
159    fn doesnt_catch_false_positive_when() {
160        assert_lint_count(
161            "Back in the days when videogame arcades were popular.",
162            BackInTheDay::default(),
163            0,
164        );
165    }
166
167    #[test]
168    fn catches_comma_when() {
169        assert_lint_count(
170            "Back in the days, when we were children, we played outside.",
171            BackInTheDay::default(),
172            1,
173        );
174    }
175
176    #[test]
177    fn doesnt_catch_false_positive_before() {
178        assert_lint_count(
179            "Back in the days before laptops we had \"luggables\".",
180            BackInTheDay::default(),
181            0,
182        );
183    }
184
185    #[test]
186    fn catches_comma_before() {
187        assert_lint_count(
188            "Back in the days, before laptops.",
189            BackInTheDay::default(),
190            1,
191        );
192    }
193
194    #[test]
195    fn doesnt_catch_qualified_days() {
196        assert_lint_count(
197            "Back in the old days we did this by hand.",
198            BackInTheDay::default(),
199            0,
200        );
201    }
202}