mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter): eslint/guard-for-in (#2746)
Rule details: https://eslint.org/docs/latest/rules/guard-for-in --------- Co-authored-by: j.buendia <j.buendia>
This commit is contained in:
parent
95ac265504
commit
22c84c546a
3 changed files with 147 additions and 0 deletions
|
|
@ -43,6 +43,7 @@ mod eslint {
|
|||
pub mod eqeqeq;
|
||||
pub mod for_direction;
|
||||
pub mod getter_return;
|
||||
pub mod guard_for_in;
|
||||
pub mod max_lines;
|
||||
pub mod no_array_constructor;
|
||||
pub mod no_async_promise_executor;
|
||||
|
|
@ -360,6 +361,7 @@ oxc_macros::declare_all_lint_rules! {
|
|||
eslint::eqeqeq,
|
||||
eslint::for_direction,
|
||||
eslint::getter_return,
|
||||
eslint::guard_for_in,
|
||||
eslint::max_lines,
|
||||
eslint::no_ternary,
|
||||
eslint::no_this_before_super,
|
||||
|
|
|
|||
100
crates/oxc_linter/src/rules/eslint/guard_for_in.rs
Normal file
100
crates/oxc_linter/src/rules/eslint/guard_for_in.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use oxc_ast::ast::Statement;
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_diagnostics::{
|
||||
miette::{self, Diagnostic},
|
||||
thiserror::Error,
|
||||
};
|
||||
use oxc_macros::declare_oxc_lint;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{context::LintContext, rule::Rule, AstNode};
|
||||
|
||||
#[derive(Debug, Error, Diagnostic)]
|
||||
#[error("eslint(guard-for-in): Require `for-in` loops to include an `if` statement")]
|
||||
#[diagnostic(severity(warning), help("The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype."))]
|
||||
struct GuardForInDiagnostic(#[label] pub Span);
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct GuardForIn;
|
||||
|
||||
declare_oxc_lint!(
|
||||
/// ### What it does
|
||||
/// This rule is aimed at preventing unexpected behavior that could arise from using a for in loop without filtering the results in the loop. As such, it will warn when for in loops do not filter their results with an if statement.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
///
|
||||
/// ### Example
|
||||
/// ```javascript
|
||||
/// for (key in foo) {
|
||||
// doSomething(key);
|
||||
// }
|
||||
/// ```
|
||||
GuardForIn,
|
||||
style
|
||||
);
|
||||
|
||||
impl Rule for GuardForIn {
|
||||
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
|
||||
if let AstKind::ForInStatement(for_in_statement) = node.kind() {
|
||||
match &for_in_statement.body {
|
||||
Statement::EmptyStatement(_) | Statement::IfStatement(_) => return,
|
||||
Statement::BlockStatement(block_body) if block_body.body.is_empty() => return,
|
||||
Statement::BlockStatement(block_body)
|
||||
if block_body.body.len() == 1
|
||||
&& matches!(block_body.body[0], Statement::IfStatement(_)) =>
|
||||
{
|
||||
return
|
||||
}
|
||||
Statement::BlockStatement(block_body) if block_body.body.len() >= 1 => {
|
||||
let block_statement = &block_body.body[0];
|
||||
if let Statement::IfStatement(i) = block_statement {
|
||||
if let Statement::ContinueStatement(_) = &i.consequent {
|
||||
return;
|
||||
}
|
||||
if let Statement::BlockStatement(consequent_block) = &i.consequent {
|
||||
if consequent_block.body.len() == 1
|
||||
&& matches!(
|
||||
&consequent_block.body[0],
|
||||
Statement::ContinueStatement(_)
|
||||
)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ctx.diagnostic(GuardForInDiagnostic(Span::new(
|
||||
for_in_statement.span.start,
|
||||
for_in_statement.span.end,
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test() {
|
||||
use crate::tester::Tester;
|
||||
|
||||
let pass = vec![
|
||||
"for (var x in o);",
|
||||
"for (var x in o) {}",
|
||||
"for (var x in o) if (x) f();",
|
||||
"for (var x in o) { if (x) { f(); } }",
|
||||
"for (var x in o) { if (x) continue; f(); }",
|
||||
"for (var x in o) { if (x) { continue; } f(); }",
|
||||
];
|
||||
|
||||
let fail = vec![
|
||||
"for (var x in o) { if (x) { f(); continue; } g(); }",
|
||||
"for (var x in o) { if (x) { continue; f(); } g(); }",
|
||||
"for (var x in o) { if (x) { f(); } g(); }",
|
||||
"for (var x in o) { if (x) f(); g(); }",
|
||||
"for (var x in o) { foo() }",
|
||||
"for (var x in o) foo();",
|
||||
];
|
||||
|
||||
Tester::new(GuardForIn::NAME, pass, fail).test_and_snapshot();
|
||||
}
|
||||
45
crates/oxc_linter/src/snapshots/guard_for_in.snap
Normal file
45
crates/oxc_linter/src/snapshots/guard_for_in.snap
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
---
|
||||
source: crates/oxc_linter/src/tester.rs
|
||||
expression: guard_for_in
|
||||
---
|
||||
⚠ eslint(guard-for-in): Require `for-in` loops to include an `if` statement
|
||||
╭─[guard_for_in.tsx:1:1]
|
||||
1 │ for (var x in o) { if (x) { f(); continue; } g(); }
|
||||
· ───────────────────────────────────────────────────
|
||||
╰────
|
||||
help: The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.
|
||||
|
||||
⚠ eslint(guard-for-in): Require `for-in` loops to include an `if` statement
|
||||
╭─[guard_for_in.tsx:1:1]
|
||||
1 │ for (var x in o) { if (x) { continue; f(); } g(); }
|
||||
· ───────────────────────────────────────────────────
|
||||
╰────
|
||||
help: The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.
|
||||
|
||||
⚠ eslint(guard-for-in): Require `for-in` loops to include an `if` statement
|
||||
╭─[guard_for_in.tsx:1:1]
|
||||
1 │ for (var x in o) { if (x) { f(); } g(); }
|
||||
· ─────────────────────────────────────────
|
||||
╰────
|
||||
help: The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.
|
||||
|
||||
⚠ eslint(guard-for-in): Require `for-in` loops to include an `if` statement
|
||||
╭─[guard_for_in.tsx:1:1]
|
||||
1 │ for (var x in o) { if (x) f(); g(); }
|
||||
· ─────────────────────────────────────
|
||||
╰────
|
||||
help: The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.
|
||||
|
||||
⚠ eslint(guard-for-in): Require `for-in` loops to include an `if` statement
|
||||
╭─[guard_for_in.tsx:1:1]
|
||||
1 │ for (var x in o) { foo() }
|
||||
· ──────────────────────────
|
||||
╰────
|
||||
help: The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.
|
||||
|
||||
⚠ eslint(guard-for-in): Require `for-in` loops to include an `if` statement
|
||||
╭─[guard_for_in.tsx:1:1]
|
||||
1 │ for (var x in o) foo();
|
||||
· ───────────────────────
|
||||
╰────
|
||||
help: The body of a for-in should be wrapped in an if statement to filter unwanted properties from the prototype.
|
||||
Loading…
Reference in a new issue