harper_core/linting/
hedging.rs

1use crate::linting::{Lint, LintKind, PatternLinter};
2use crate::patterns::{EitherPattern, ExactPhrase, Pattern};
3use crate::{Token, TokenStringExt};
4
5/// A linter that detects hedging language.
6pub struct Hedging {
7    pattern: Box<dyn Pattern>,
8}
9
10impl Default for Hedging {
11    fn default() -> Self {
12        let phrases = vec!["I would argue that", ", so to speak", "to a certain degree"];
13
14        let patterns: Vec<Box<dyn Pattern>> = phrases
15            .into_iter()
16            .map(|s| Box::new(ExactPhrase::from_phrase(s)) as Box<dyn Pattern>)
17            .collect();
18
19        let pattern = Box::new(EitherPattern::new(patterns));
20        Self { pattern }
21    }
22}
23
24impl PatternLinter for Hedging {
25    fn pattern(&self) -> &dyn Pattern {
26        self.pattern.as_ref()
27    }
28
29    fn match_to_lint(&self, matched_tokens: &[Token], _source: &[char]) -> Option<Lint> {
30        let span = matched_tokens.span()?;
31        Some(Lint {
32            span,
33            lint_kind: LintKind::Miscellaneous,
34            suggestions: Vec::new(),
35            message: "You're hedging.".to_string(),
36            priority: 31,
37        })
38    }
39
40    fn description(&self) -> &str {
41        "Flags hedging language (e.g. `I would argue that`, `..., so to speak`, `to a certain degree`)."
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::Hedging;
48    use crate::linting::tests::assert_lint_count;
49
50    #[test]
51    fn detects_hedging_phrase() {
52        assert_lint_count("I would argue that this is correct.", Hedging::default(), 1);
53    }
54
55    #[test]
56    fn does_not_flag_clean_text() {
57        assert_lint_count("This is clear and direct.", Hedging::default(), 0);
58    }
59
60    #[test]
61    fn lowercase_hedging() {
62        assert_lint_count(
63            "i would argue that the outcome is uncertain.",
64            Hedging::default(),
65            1,
66        );
67    }
68
69    #[test]
70    fn incomplete_phrase_not_flagged() {
71        assert_lint_count("I would argue the data is clear.", Hedging::default(), 0);
72    }
73
74    #[test]
75    fn phrase_with_trailing_comma() {
76        let text = "I would argue that, this method works.";
77        assert_lint_count(text, Hedging::default(), 1);
78    }
79
80    #[test]
81    fn phrase_with_extra_whitespace() {
82        assert_lint_count(
83            "to   a   certain   degree the results are ambiguous.",
84            Hedging::default(),
85            1,
86        );
87    }
88
89    #[test]
90    fn does_not_flag_similar_but_incorrect_phrase() {
91        assert_lint_count(
92            "He spoke so to speakingly about the event.",
93            Hedging::default(),
94            0,
95        );
96    }
97
98    #[test]
99    fn phrase_split_by_line_break() {
100        assert_lint_count(
101            "I would argue\nthat this approach fails.",
102            Hedging::default(),
103            1,
104        );
105    }
106}