feat(es/lints): Add no-await-in-loop rule (#4936)

This commit is contained in:
Artur 2022-06-11 08:00:32 +03:00 committed by GitHub
parent 6fdf84da15
commit b041f2911f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 270 additions and 0 deletions

View File

@ -0,0 +1,9 @@
{
"jsc": {
"lints": {
"no-await-in-loop": [
"error"
]
}
}
}

View File

@ -0,0 +1,29 @@
async function foo() { while (baz) { await bar; } }
async function foo() { while (await foo()) { } }
async function foo() { while (baz) { for await (x of xs); } }
async function foo() { for (var bar of baz) { await bar; } }
async function foo() { for (var bar of baz) await bar; }
async function foo() { for (var bar in baz) { await bar; } }
async function foo() { for (var i; i < n; i++) { await bar; } }
async function foo() { for (var i; await foo(i); i++) { } }
async function foo() { for (var i; i < n; i = await bar) { } }
async function foo() { do { await bar; } while (baz); }
async function foo() { do { } while (await bar); }
async function foo() { while (true) { if (bar) { foo(await bar); } } }
async function foo() { while (xyz || 5 > await x) { } }
async function foo() { for await (var x of xs) { while (1) await f(x) } }
// valid
async function foo() { await bar; }
async function foo() { for (var bar in await baz) { } }
async function foo() { for (var bar of await baz) { } }
async function foo() { for await (var bar of await baz) { } }
async function foo() { while (true) { async function foo() { await bar; } } }
async function foo() { for (var i = await bar; i < n; i++) { } }
async function foo() { do { } while (bar); }
async function foo() { while (true) { var y = async function () { await bar; } } }
async function foo() { while (true) { var y = async () => await foo; } }
async function foo() { while (true) { var y = async () => { await foo; } } }
async function foo() { while (true) { class Foo { async foo() { await bar; } } } }
async function foo() { while (true) { class Foo { async foo() { await bar; } } } }
async function foo() { for await (var x of xs) { await f(x) } }

View File

@ -0,0 +1,84 @@
x Unexpected `await` inside a loop
,----
1 | async function foo() { while (baz) { await bar; } }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
2 | async function foo() { while (await foo()) { } }
: ^^^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
3 | async function foo() { while (baz) { for await (x of xs); } }
: ^^^^^^^^^^^^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
4 | async function foo() { for (var bar of baz) { await bar; } }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
5 | async function foo() { for (var bar of baz) await bar; }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
6 | async function foo() { for (var bar in baz) { await bar; } }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
7 | async function foo() { for (var i; i < n; i++) { await bar; } }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
8 | async function foo() { for (var i; await foo(i); i++) { } }
: ^^^^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
9 | async function foo() { for (var i; i < n; i = await bar) { } }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
10 | async function foo() { do { await bar; } while (baz); }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
11 | async function foo() { do { } while (await bar); }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
12 | async function foo() { while (true) { if (bar) { foo(await bar); } } }
: ^^^^^^^^^
`----
x Unexpected `await` inside a loop
,----
13 | async function foo() { while (xyz || 5 > await x) { } }
: ^^^^^^^
`----
x Unexpected `await` inside a loop
,----
14 | async function foo() { for await (var x of xs) { while (1) await f(x) } }
: ^^^^^^^^^^
`----

View File

@ -203,4 +203,8 @@ pub struct LintConfig {
#[cfg(feature = "non_critical_lints")]
#[serde(default, alias = "defaultCaseLast")]
pub default_case_last: RuleConfig<()>,
#[cfg(feature = "non_critical_lints")]
#[serde(default, alias = "noAwaitInLoop")]
pub no_await_in_loop: RuleConfig<()>,
}

View File

@ -20,6 +20,7 @@ pub(crate) mod non_critical_lints {
pub mod dot_notation;
pub mod eqeqeq;
pub mod no_alert;
pub mod no_await_in_loop;
pub mod no_bitwise;
pub mod no_compare_neg_zero;
pub mod no_console;
@ -181,6 +182,10 @@ pub fn all(lint_params: LintParams) -> Vec<Box<dyn Rule>> {
rules.extend(default_case_last::default_case_last(
&lint_config.default_case_last,
));
rules.extend(no_await_in_loop::no_await_in_loop(
&lint_config.no_await_in_loop,
));
}
rules

View File

@ -0,0 +1,139 @@
use swc_common::{errors::HANDLER, Span};
use swc_ecma_ast::*;
use swc_ecma_visit::{Visit, VisitWith};
use crate::{
config::{LintRuleReaction, RuleConfig},
rule::{visitor_rule, Rule},
};
const MESSAGE: &str = "Unexpected `await` inside a loop";
pub fn no_await_in_loop(config: &RuleConfig<()>) -> Option<Box<dyn Rule>> {
let rule_reaction = config.get_rule_reaction();
match rule_reaction {
LintRuleReaction::Off => None,
_ => Some(visitor_rule(NoAwaitInLoop::new(rule_reaction))),
}
}
#[derive(Debug, Default)]
struct NoAwaitInLoop {
expected_reaction: LintRuleReaction,
await_restricted: bool,
}
impl NoAwaitInLoop {
fn new(expected_reaction: LintRuleReaction) -> Self {
Self {
expected_reaction,
await_restricted: false,
}
}
fn emit_report(&self, span: Span) {
HANDLER.with(|handler| match self.expected_reaction {
LintRuleReaction::Error => {
handler.struct_span_err(span, MESSAGE).emit();
}
LintRuleReaction::Warning => {
handler.struct_span_warn(span, MESSAGE).emit();
}
_ => {}
});
}
}
impl Visit for NoAwaitInLoop {
fn visit_for_stmt(&mut self, for_stmt: &ForStmt) {
let prev_await_restriction = self.await_restricted;
for_stmt.init.visit_children_with(self);
self.await_restricted = true;
for_stmt.test.visit_children_with(self);
for_stmt.update.visit_children_with(self);
for_stmt.body.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
fn visit_do_while_stmt(&mut self, do_while_stmt: &DoWhileStmt) {
let prev_await_restriction = self.await_restricted;
self.await_restricted = true;
do_while_stmt.body.visit_children_with(self);
do_while_stmt.test.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
fn visit_for_in_stmt(&mut self, for_in_stmt: &ForInStmt) {
let prev_await_restriction = self.await_restricted;
for_in_stmt.left.visit_children_with(self);
for_in_stmt.right.visit_children_with(self);
self.await_restricted = true;
for_in_stmt.body.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
fn visit_for_of_stmt(&mut self, for_of_stmt: &ForOfStmt) {
let prev_await_restriction = self.await_restricted;
if self.await_restricted {
self.emit_report(for_of_stmt.span);
}
for_of_stmt.left.visit_children_with(self);
for_of_stmt.right.visit_children_with(self);
self.await_restricted = for_of_stmt.await_token.is_none();
for_of_stmt.body.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
fn visit_while_stmt(&mut self, while_stmt: &WhileStmt) {
let prev_await_restriction = self.await_restricted;
self.await_restricted = true;
while_stmt.test.visit_children_with(self);
while_stmt.body.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
fn visit_await_expr(&mut self, await_expr: &AwaitExpr) {
if self.await_restricted {
self.emit_report(await_expr.span);
}
await_expr.visit_children_with(self);
}
fn visit_function(&mut self, function: &Function) {
let prev_await_restriction = self.await_restricted;
self.await_restricted = false;
function.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
fn visit_arrow_expr(&mut self, arrow_expr: &ArrowExpr) {
let prev_await_restriction = self.await_restricted;
self.await_restricted = false;
arrow_expr.visit_children_with(self);
self.await_restricted = prev_await_restriction;
}
}