This repository has been archived by the owner on Aug 31, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 657
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rome_js_analyze): noForEach (#4364)
feat(rome_js_analyze): noForEach
- Loading branch information
1 parent
f6ada3a
commit 3902f46
Showing
15 changed files
with
451 additions
and
86 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
98 changes: 98 additions & 0 deletions
98
crates/rome_js_analyze/src/analyzers/nursery/no_for_each.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
use rome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic}; | ||
use rome_console::markup; | ||
use rome_js_syntax::{AnyJsExpression, JsCallExpression}; | ||
use rome_rowan::AstNode; | ||
|
||
declare_rule! { | ||
/// Prefer `for...of` statement instead of `Array.forEach`. | ||
/// | ||
/// Here's a summary of why `forEach` may be disallowed, and why `for...of` is preferred for almost any use-case of `forEach`: | ||
/// - Performance: Using `forEach` can lead to performance issues, especially when working with large arrays. | ||
/// When more requirements are added on, `forEach` typically gets chained with other methods like `filter` or `map`, causing multiple iterations over the same Array. | ||
/// Encouraging for loops discourages chaining and encourages single-iteration logic (e.g. using a continue instead of `filter`). | ||
/// | ||
/// - Readability: While `forEach` is a simple and concise way to iterate over an array, it can make the code less readable, especially when the callback function is complex. | ||
/// In contrast, using a for loop or a `for...of` loop can make the code more explicit and easier to read. | ||
/// | ||
/// - Debugging: `forEach` can make debugging more difficult, because it hides the iteration process. | ||
/// | ||
/// ## Examples | ||
/// | ||
/// ### Invalid | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// els.forEach(el => { | ||
/// el | ||
/// }) | ||
/// ``` | ||
/// | ||
/// ```js,expect_diagnostic | ||
/// els['forEach'](el => { | ||
/// el | ||
/// }) | ||
/// ``` | ||
/// | ||
/// ## Valid | ||
/// | ||
/// ```js | ||
/// for (const el of els) { | ||
/// el | ||
/// } | ||
/// ``` | ||
/// | ||
pub(crate) NoForEach { | ||
version: "next", | ||
name: "noForEach", | ||
recommended: false, | ||
} | ||
} | ||
|
||
impl Rule for NoForEach { | ||
type Query = Ast<JsCallExpression>; | ||
type State = (); | ||
type Signals = Option<Self::State>; | ||
type Options = (); | ||
|
||
fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||
let node = ctx.query(); | ||
|
||
let is_for_each = match node.callee().ok()?.omit_parentheses() { | ||
AnyJsExpression::JsStaticMemberExpression(expression) => { | ||
expression | ||
.member() | ||
.ok()? | ||
.as_js_name()? | ||
.value_token() | ||
.ok()? | ||
.text_trimmed() | ||
== "forEach" | ||
} | ||
AnyJsExpression::JsComputedMemberExpression(expression) => { | ||
expression | ||
.member() | ||
.ok()? | ||
.as_any_js_literal_expression()? | ||
.as_js_string_literal_expression()? | ||
.inner_string_text() | ||
.ok()? | ||
.text() | ||
== "forEach" | ||
} | ||
_ => return None, | ||
}; | ||
|
||
is_for_each.then_some(()) | ||
} | ||
|
||
fn diagnostic(ctx: &RuleContext<Self>, _state: &Self::State) -> Option<RuleDiagnostic> { | ||
let node = ctx.query(); | ||
|
||
Some(RuleDiagnostic::new( | ||
rule_category!(), | ||
node.syntax().text_trimmed_range(), | ||
markup! { | ||
"Prefer for...of instead of Array.forEach" | ||
}, | ||
)) | ||
} | ||
} |
15 changes: 15 additions & 0 deletions
15
crates/rome_js_analyze/tests/specs/nursery/noForEach/invalid.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
els.forEach((el) => { | ||
el; | ||
}); | ||
|
||
(els.forEach)((el) => { | ||
el; | ||
}); | ||
|
||
els['forEach']((el) => { | ||
el; | ||
}); | ||
|
||
(els['forEach'])((el) => { | ||
el; | ||
}); |
95 changes: 95 additions & 0 deletions
95
crates/rome_js_analyze/tests/specs/nursery/noForEach/invalid.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
--- | ||
source: crates/rome_js_analyze/tests/spec_tests.rs | ||
expression: invalid.js | ||
--- | ||
# Input | ||
```js | ||
els.forEach((el) => { | ||
el; | ||
}); | ||
|
||
(els.forEach)((el) => { | ||
el; | ||
}); | ||
|
||
els['forEach']((el) => { | ||
el; | ||
}); | ||
|
||
(els['forEach'])((el) => { | ||
el; | ||
}); | ||
|
||
``` | ||
|
||
# Diagnostics | ||
``` | ||
invalid.js:1:1 lint/nursery/noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
! Prefer for...of instead of Array.forEach | ||
> 1 │ els.forEach((el) => { | ||
│ ^^^^^^^^^^^^^^^^^^^^^ | ||
> 2 │ el; | ||
> 3 │ }); | ||
│ ^^ | ||
4 │ | ||
5 │ (els.forEach)((el) => { | ||
``` | ||
|
||
``` | ||
invalid.js:5:1 lint/nursery/noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
! Prefer for...of instead of Array.forEach | ||
3 │ }); | ||
4 │ | ||
> 5 │ (els.forEach)((el) => { | ||
│ ^^^^^^^^^^^^^^^^^^^^^^^ | ||
> 6 │ el; | ||
> 7 │ }); | ||
│ ^^ | ||
8 │ | ||
9 │ els['forEach']((el) => { | ||
``` | ||
|
||
``` | ||
invalid.js:9:1 lint/nursery/noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
! Prefer for...of instead of Array.forEach | ||
7 │ }); | ||
8 │ | ||
> 9 │ els['forEach']((el) => { | ||
│ ^^^^^^^^^^^^^^^^^^^^^^^^ | ||
> 10 │ el; | ||
> 11 │ }); | ||
│ ^^ | ||
12 │ | ||
13 │ (els['forEach'])((el) => { | ||
``` | ||
|
||
``` | ||
invalid.js:13:1 lint/nursery/noForEach ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ | ||
! Prefer for...of instead of Array.forEach | ||
11 │ }); | ||
12 │ | ||
> 13 │ (els['forEach'])((el) => { | ||
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
> 14 │ el; | ||
> 15 │ }); | ||
│ ^^ | ||
16 │ | ||
``` | ||
|
||
|
3 changes: 3 additions & 0 deletions
3
crates/rome_js_analyze/tests/specs/nursery/noForEach/valid.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
for (const el of els) { | ||
el | ||
} |
13 changes: 13 additions & 0 deletions
13
crates/rome_js_analyze/tests/specs/nursery/noForEach/valid.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
--- | ||
source: crates/rome_js_analyze/tests/spec_tests.rs | ||
expression: valid.js | ||
--- | ||
# Input | ||
```js | ||
for (const el of els) { | ||
el | ||
} | ||
|
||
``` | ||
|
||
|
Oops, something went wrong.