harper_core/linting/
oxymorons.rs1use crate::linting::{Lint, LintKind, PatternLinter};
2use crate::patterns::{EitherPattern, ExactPhrase, Pattern};
3use crate::{Token, TokenStringExt};
4
5pub struct Oxymorons {
7 pattern: Box<dyn Pattern>,
8}
9
10impl Oxymorons {
11 pub fn new() -> Self {
12 let phrases = vec![
14 "amateur expert",
15 "increasingly less",
16 "advancing backwards?",
17 "alludes explicitly to",
18 "explicitly alludes to",
19 "totally obsolescent",
20 "completely obsolescent",
21 "generally always",
22 "usually always",
23 "build down",
24 "conspicuous absence",
25 "exact estimate",
26 "found missing",
27 "intense apathy",
28 "mandatory choice",
29 "nonworking mother",
30 "organized mess",
31 ];
32
33 let patterns: Vec<Box<dyn Pattern>> = phrases
35 .into_iter()
36 .map(|s| Box::new(ExactPhrase::from_phrase(s)) as Box<dyn Pattern>)
37 .collect();
38
39 let pattern = Box::new(EitherPattern::new(patterns));
40 Self { pattern }
41 }
42}
43
44impl Default for Oxymorons {
45 fn default() -> Self {
46 Self::new()
47 }
48}
49
50impl PatternLinter for Oxymorons {
51 fn pattern(&self) -> &dyn Pattern {
53 self.pattern.as_ref()
54 }
55
56 fn match_to_lint(&self, matched_tokens: &[Token], source: &[char]) -> Option<Lint> {
57 let span = matched_tokens.span()?;
58 let matched_text: String = span.get_content(source).iter().collect();
59 Some(Lint {
60 span,
61 lint_kind: LintKind::Miscellaneous,
62 suggestions: Vec::new(),
63 message: format!("'{}' is an oxymoron.", matched_text),
64 priority: 31,
65 })
66 }
67
68 fn description(&self) -> &str {
69 "Flags oxymoronic phrases (e.g. `amateur expert`, `increasingly less`, etc.)."
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::Oxymorons;
76 use crate::linting::tests::assert_lint_count;
77
78 #[test]
79 fn detects_amateur_expert() {
80 assert_lint_count("The amateur expert gave his opinion.", Oxymorons::new(), 1);
81 }
82
83 #[test]
84 fn detects_increasingly_less() {
85 assert_lint_count(
86 "The solution was increasingly less effective.",
87 Oxymorons::new(),
88 1,
89 );
90 }
91
92 #[test]
93 fn detects_advancing_backwards() {
94 assert_lint_count("The project is advancing backwards?", Oxymorons::new(), 1);
95 }
96
97 #[test]
98 fn detects_alludes_explicitly_to() {
99 assert_lint_count(
100 "The report alludes explicitly to several issues.",
101 Oxymorons::new(),
102 1,
103 );
104 }
105
106 #[test]
107 fn detects_explicitly_alludes_to() {
108 assert_lint_count(
109 "The report explicitly alludes to several issues.",
110 Oxymorons::new(),
111 1,
112 );
113 }
114
115 #[test]
116 fn does_not_flag_clean_text() {
117 assert_lint_count("The expert provided clear advice.", Oxymorons::new(), 0);
118 }
119
120 #[test]
121 fn lowercase_match() {
122 assert_lint_count(
123 "the amateur expert is often unreliable.",
124 Oxymorons::new(),
125 1,
126 );
127 }
128
129 #[test]
130 fn phrase_with_extra_whitespace() {
131 assert_lint_count("An organized mess was found.", Oxymorons::new(), 1);
132 }
133
134 #[test]
135 fn phrase_split_by_line_break() {
136 assert_lint_count(
137 "nonworking\nmother is not a term to be used.",
138 Oxymorons::new(),
139 1,
140 );
141 }
142}