mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +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 eqeqeq;
|
||||||
pub mod for_direction;
|
pub mod for_direction;
|
||||||
pub mod getter_return;
|
pub mod getter_return;
|
||||||
|
pub mod guard_for_in;
|
||||||
pub mod max_lines;
|
pub mod max_lines;
|
||||||
pub mod no_array_constructor;
|
pub mod no_array_constructor;
|
||||||
pub mod no_async_promise_executor;
|
pub mod no_async_promise_executor;
|
||||||
|
|
@ -360,6 +361,7 @@ oxc_macros::declare_all_lint_rules! {
|
||||||
eslint::eqeqeq,
|
eslint::eqeqeq,
|
||||||
eslint::for_direction,
|
eslint::for_direction,
|
||||||
eslint::getter_return,
|
eslint::getter_return,
|
||||||
|
eslint::guard_for_in,
|
||||||
eslint::max_lines,
|
eslint::max_lines,
|
||||||
eslint::no_ternary,
|
eslint::no_ternary,
|
||||||
eslint::no_this_before_super,
|
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