Skip to content

Commit

Permalink
feat(linter): add redundant continue statement rule
Browse files Browse the repository at this point in the history
Signed-off-by: azjezz <azjezz@protonmail.com>
  • Loading branch information
azjezz committed Dec 10, 2024
1 parent 708b92a commit b6ddc75
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
3 changes: 2 additions & 1 deletion crates/linter/src/plugin/redundancy/mod.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
use crate::plugin::redundancy::rules::redundant_block::RedundantBlockRule;
use crate::plugin::redundancy::rules::redundant_closing_tag::RedudnantClosingTagRule;
use crate::plugin::redundancy::rules::redundant_continue::RedundantContinueRule;
use crate::plugin::redundancy::rules::redundant_final_method_modifier::RedundantFinalMethodModifierRule;
use crate::plugin::redundancy::rules::redundant_if_statement::RedundantIfStatementRule;
use crate::plugin::redundancy::rules::redundant_label::RedundantLabelRule;
use crate::plugin::redundancy::rules::redundant_method_override::RedundantMethodOverrideRule;
use crate::plugin::redundancy::rules::redundant_noop::RedundantNoopRule;
use crate::plugin::redundancy::rules::redundant_parentheses::RedundantParenthesesRule;
use crate::plugin::redundancy::rules::redundant_string_concat::RedundantStringConcatRule;

use crate::plugin::Plugin;
use crate::rule::Rule;

Expand All @@ -30,6 +30,7 @@ impl Plugin for RedundancyPlugin {
Box::new(RedundantParenthesesRule),
Box::new(RedundantBlockRule),
Box::new(RedudnantClosingTagRule),
Box::new(RedundantContinueRule),
Box::new(RedundantStringConcatRule),
Box::new(RedundantNoopRule),
Box::new(RedundantMethodOverrideRule),
Expand Down
1 change: 1 addition & 0 deletions crates/linter/src/plugin/redundancy/rules/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod redundant_block;
pub mod redundant_closing_tag;
pub mod redundant_continue;
pub mod redundant_final_method_modifier;
pub mod redundant_if_statement;
pub mod redundant_label;
Expand Down
90 changes: 90 additions & 0 deletions crates/linter/src/plugin/redundancy/rules/redundant_continue.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use mago_ast::*;
use mago_fixer::SafetyClassification;
use mago_reporting::*;
use mago_span::HasSpan;
use mago_walker::Walker;

use crate::context::LintContext;
use crate::rule::Rule;

#[derive(Clone, Debug)]
pub struct RedundantContinueRule;

impl Rule for RedundantContinueRule {
fn get_name(&self) -> &'static str {
"redundant-continue"
}

fn get_default_level(&self) -> Option<Level> {
Some(Level::Help)
}
}

impl RedundantContinueRule {
fn report(&self, r#continue: &Continue, r#loop: impl HasSpan, context: &mut LintContext<'_>) {
let issue = Issue::new(context.level(), "Redundant continue statement in loop body")
.with_annotations([
Annotation::primary(r#continue.span()).with_message(
"This `continue` statement is redundant because it is the last statement in the loop body.",
),
Annotation::secondary(r#loop.span()),
])
.with_help("Remove this `continue` statement, as it does not affect the loop's behavior.");

context.report_with_fix(issue, |plan| {
plan.delete(r#continue.span().to_range(), SafetyClassification::Safe);
});
}
}

impl<'a> Walker<LintContext<'a>> for RedundantContinueRule {
fn walk_in_foreach(&self, foreach: &Foreach, context: &mut LintContext<'a>) {
if let Some(cont) = match &foreach.body {
ForeachBody::Statement(stmt) => statement_is_continue(stmt),
ForeachBody::ColonDelimited(body) => statements_end_with_continue(body.statements.as_slice()),
} {
self.report(cont, foreach, context);
}
}

fn walk_in_for(&self, r#for: &For, context: &mut LintContext<'a>) {
if let Some(cont) = match &r#for.body {
ForBody::Statement(stmt) => statement_is_continue(stmt),
ForBody::ColonDelimited(body) => statements_end_with_continue(body.statements.as_slice()),
} {
self.report(cont, r#for, context);
}
}

fn walk_in_while(&self, r#while: &While, context: &mut LintContext<'a>) {
if let Some(cont) = match &r#while.body {
WhileBody::Statement(stmt) => statement_is_continue(stmt),
WhileBody::ColonDelimited(body) => statements_end_with_continue(body.statements.as_slice()),
} {
self.report(cont, r#while, context);
}
}

fn walk_in_do_while(&self, do_while: &DoWhile, context: &mut LintContext<'a>) {
if let Some(cont) = statement_is_continue(&do_while.statement) {
self.report(cont, do_while, context);
}
}
}

fn statements_end_with_continue(statements: &[Statement]) -> Option<&Continue> {
let last = statements.last()?;

statement_is_continue(last)
}

fn statement_is_continue(statement: &Statement) -> Option<&Continue> {
match statement {
Statement::Block(block) => statement_is_continue(block.statements.last()?),
Statement::Continue(cont) => match cont.level {
None | Some(Expression::Literal(Literal::Integer(LiteralInteger { value: Some(1), .. }))) => Some(cont),
Some(_) => None,
},
_ => None,
}
}

0 comments on commit b6ddc75

Please sign in to comment.