Skip to content

Commit

Permalink
feat(es/lints): Add no-await-in-loop rule (#4936)
Browse files Browse the repository at this point in the history
  • Loading branch information
ArturAralin committed Jun 11, 2022
1 parent 6fdf84d commit b041f29
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 0 deletions.
9 changes: 9 additions & 0 deletions crates/swc/tests/errors/lints/no-await-in-loop/default/.swcrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"jsc": {
"lints": {
"no-await-in-loop": [
"error"
]
}
}
}
29 changes: 29 additions & 0 deletions crates/swc/tests/errors/lints/no-await-in-loop/default/input.js
Original file line number Diff line number Diff line change
@@ -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) } }
Original file line number Diff line number Diff line change
@@ -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) } }
: ^^^^^^^^^^
`----
4 changes: 4 additions & 0 deletions crates/swc_ecma_lints/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()>,
}
5 changes: 5 additions & 0 deletions crates/swc_ecma_lints/src/rules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
139 changes: 139 additions & 0 deletions crates/swc_ecma_lints/src/rules/no_await_in_loop.rs
Original file line number Diff line number Diff line change
@@ -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;
}
}

1 comment on commit b041f29

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: b041f29 Previous: f993a52 Ratio
es/full/minify/libraries/antd 1801856233 ns/iter (± 67535184) 1507053340 ns/iter (± 43955912) 1.20
es/full/minify/libraries/d3 449663924 ns/iter (± 3775355) 399078971 ns/iter (± 3284809) 1.13
es/full/minify/libraries/echarts 2319792247 ns/iter (± 44284565) 1788066709 ns/iter (± 11368754) 1.30
es/full/minify/libraries/jquery 98193511 ns/iter (± 2413158) 90628154 ns/iter (± 291175) 1.08
es/full/minify/libraries/lodash 135908126 ns/iter (± 1407229) 123771935 ns/iter (± 513729) 1.10
es/full/minify/libraries/moment 57053307 ns/iter (± 1174923) 52801410 ns/iter (± 127484) 1.08
es/full/minify/libraries/react 18215146 ns/iter (± 358057) 17667206 ns/iter (± 30452) 1.03
es/full/minify/libraries/terser 504202583 ns/iter (± 8284465) 459387347 ns/iter (± 1490363) 1.10
es/full/minify/libraries/three 624186581 ns/iter (± 23548179) 493314004 ns/iter (± 8598656) 1.27
es/full/minify/libraries/typescript 4262301967 ns/iter (± 25221516) 3412375968 ns/iter (± 9232633) 1.25
es/full/minify/libraries/victory 762201324 ns/iter (± 15040280) 662193216 ns/iter (± 6429348) 1.15
es/full/minify/libraries/vue 148445357 ns/iter (± 1762410) 135832803 ns/iter (± 521122) 1.09
es/full/codegen/es3 36204 ns/iter (± 1580) 34865 ns/iter (± 142) 1.04
es/full/codegen/es5 35508 ns/iter (± 1234) 34833 ns/iter (± 171) 1.02
es/full/codegen/es2015 34715 ns/iter (± 1428) 34901 ns/iter (± 153) 0.99
es/full/codegen/es2016 34082 ns/iter (± 1371) 34896 ns/iter (± 140) 0.98
es/full/codegen/es2017 34651 ns/iter (± 1586) 34907 ns/iter (± 136) 0.99
es/full/codegen/es2018 35408 ns/iter (± 1679) 34889 ns/iter (± 158) 1.01
es/full/codegen/es2019 34974 ns/iter (± 1303) 34879 ns/iter (± 107) 1.00
es/full/codegen/es2020 34432 ns/iter (± 1374) 34878 ns/iter (± 122) 0.99
es/full/all/es3 208220769 ns/iter (± 3963579) 189442398 ns/iter (± 810410) 1.10
es/full/all/es5 197687643 ns/iter (± 4165791) 178540437 ns/iter (± 914244) 1.11
es/full/all/es2015 158034904 ns/iter (± 3878207) 136962330 ns/iter (± 355443) 1.15
es/full/all/es2016 156554170 ns/iter (± 3243915) 136298934 ns/iter (± 369705) 1.15
es/full/all/es2017 153853669 ns/iter (± 3870914) 135503463 ns/iter (± 688799) 1.14
es/full/all/es2018 152434401 ns/iter (± 4092637) 133083999 ns/iter (± 12047467) 1.15
es/full/all/es2019 151666129 ns/iter (± 3539084) 132415002 ns/iter (± 402311) 1.15
es/full/all/es2020 146675726 ns/iter (± 3248459) 126632982 ns/iter (± 649418) 1.16
es/full/parser 607017 ns/iter (± 55977) 578363 ns/iter (± 45194) 1.05
es/full/base/fixer 29157 ns/iter (± 1279) 25670 ns/iter (± 240) 1.14
es/full/base/resolver_and_hygiene 143821 ns/iter (± 7008) 140095 ns/iter (± 1991) 1.03
serialization of ast node 185 ns/iter (± 6) 182 ns/iter (± 23) 1.02
serialization of serde 192 ns/iter (± 7) 184 ns/iter (± 0) 1.04

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.