harper-core 2.0.0

The language checker for developers.
Documentation
use crate::expr::Expr;
use crate::expr::SequenceExpr;
use crate::{Token, TokenStringExt};

use super::{ExprLinter, Lint, LintKind, Suggestion};
use crate::linting::expr_linter::Chunk;

pub struct DeterminerWithoutNoun {
    expr: SequenceExpr,
}

impl Default for DeterminerWithoutNoun {
    fn default() -> Self {
        let expr = SequenceExpr::default()
            .then_kind_where(|kind| kind.is_determiner())
            .t_ws()
            .then_conjunction();

        Self { expr }
    }
}

impl ExprLinter for DeterminerWithoutNoun {
    type Unit = Chunk;

    fn expr(&self) -> &dyn Expr {
        &self.expr
    }

    fn match_to_lint(&self, matched_tokens: &[Token], _source: &[char]) -> Option<Lint> {
        let span = matched_tokens.span()?;
        Some(Lint {
            span,
            lint_kind: LintKind::Miscellaneous,
            suggestions: Vec::<Suggestion>::new(),
            message: "A determiner should not be immediately followed by a conjunction."
                .to_string(),
            priority: 32,
        })
    }

    fn description(&self) -> &'static str {
        "Flags sequences where a determiner (`a`, `an`, `the`, etc.) is directly followed by a coordinating or subordinating conjunction (`and`, `or`, `but`, `because`, etc.), indicating a missing noun."
    }
}

#[cfg(test)]
mod tests {
    use super::DeterminerWithoutNoun;
    use crate::linting::tests::assert_lint_count;

    #[test]
    fn flags_determiner_followed_by_conjunction() {
        assert_lint_count(
            "The and other options were ignored.",
            DeterminerWithoutNoun::default(),
            1,
        );
    }

    #[test]
    fn flags_indefinite_article_followed_by_conjunction() {
        assert_lint_count("A because I said so.", DeterminerWithoutNoun::default(), 1);
        assert_lint_count("An because I said so.", DeterminerWithoutNoun::default(), 1);
    }

    #[test]
    fn allows_correct_use_with_noun() {
        assert_lint_count("The dog barked.", DeterminerWithoutNoun::default(), 0);
    }

    #[test]
    fn allows_determiner_noun_then_conjunction() {
        assert_lint_count(
            "The dog and the cat played.",
            DeterminerWithoutNoun::default(),
            0,
        );
    }
}