mirror of
https://github.com/swc-project/swc.git
synced 2024-12-24 14:16:12 +03:00
fix(es/transforms/compat): Fix block scoping (#2916)
swc_ecma_transforms_compat: - `block_scoping`: Track if we are in nested loops. - `block_scoping`: Don't treat `break` nor `continue` in nested loops as leaper. (https://github.com/vercel/next.js/issues/31757, Closes #2799, Closes #2915) - `block_scoping`: Don't recurse into nested loops while looking for functions. (Closes #2622)
This commit is contained in:
parent
57fb69262d
commit
028d0ce2c6
@ -163,6 +163,7 @@ impl BlockScoping {
|
||||
has_return: false,
|
||||
mutated,
|
||||
in_switch_case: false,
|
||||
in_nested_loop: false,
|
||||
};
|
||||
|
||||
body_stmt.visit_mut_with(&mut flow_helper);
|
||||
@ -699,6 +700,8 @@ struct FlowHelper<'a> {
|
||||
all: &'a Vec<Id>,
|
||||
mutated: AHashMap<Id, SyntaxContext>,
|
||||
in_switch_case: bool,
|
||||
|
||||
in_nested_loop: bool,
|
||||
}
|
||||
|
||||
impl<'a> FlowHelper<'a> {
|
||||
@ -738,6 +741,38 @@ impl VisitMut for FlowHelper<'_> {
|
||||
n.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
/// https://github.com/swc-project/swc/pull/2916
|
||||
fn visit_mut_do_while_stmt(&mut self, s: &mut DoWhileStmt) {
|
||||
let old = self.in_nested_loop;
|
||||
self.in_nested_loop = true;
|
||||
s.visit_mut_children_with(self);
|
||||
self.in_nested_loop = old;
|
||||
}
|
||||
|
||||
/// https://github.com/swc-project/swc/pull/2916
|
||||
fn visit_mut_for_in_stmt(&mut self, s: &mut ForInStmt) {
|
||||
let old = self.in_nested_loop;
|
||||
self.in_nested_loop = true;
|
||||
s.visit_mut_children_with(self);
|
||||
self.in_nested_loop = old;
|
||||
}
|
||||
|
||||
/// https://github.com/swc-project/swc/pull/2916
|
||||
fn visit_mut_for_of_stmt(&mut self, s: &mut ForOfStmt) {
|
||||
let old = self.in_nested_loop;
|
||||
self.in_nested_loop = true;
|
||||
s.visit_mut_children_with(self);
|
||||
self.in_nested_loop = old;
|
||||
}
|
||||
|
||||
/// https://github.com/swc-project/swc/pull/2916
|
||||
fn visit_mut_for_stmt(&mut self, s: &mut ForStmt) {
|
||||
let old = self.in_nested_loop;
|
||||
self.in_nested_loop = true;
|
||||
s.visit_mut_children_with(self);
|
||||
self.in_nested_loop = old;
|
||||
}
|
||||
|
||||
/// noop
|
||||
fn visit_mut_function(&mut self, _f: &mut Function) {}
|
||||
|
||||
@ -746,6 +781,10 @@ impl VisitMut for FlowHelper<'_> {
|
||||
|
||||
match node {
|
||||
Stmt::Continue(..) => {
|
||||
if self.in_nested_loop {
|
||||
return;
|
||||
}
|
||||
|
||||
self.has_continue = true;
|
||||
*node = Stmt::Return(ReturnStmt {
|
||||
span,
|
||||
@ -761,7 +800,7 @@ impl VisitMut for FlowHelper<'_> {
|
||||
});
|
||||
}
|
||||
Stmt::Break(..) => {
|
||||
if self.in_switch_case {
|
||||
if self.in_switch_case || self.in_nested_loop {
|
||||
return;
|
||||
}
|
||||
self.has_break = true;
|
||||
@ -816,6 +855,14 @@ impl VisitMut for FlowHelper<'_> {
|
||||
}
|
||||
n.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
/// https://github.com/swc-project/swc/pull/2916
|
||||
fn visit_mut_while_stmt(&mut self, s: &mut WhileStmt) {
|
||||
let old = self.in_nested_loop;
|
||||
self.in_nested_loop = true;
|
||||
s.visit_mut_children_with(self);
|
||||
self.in_nested_loop = old;
|
||||
}
|
||||
}
|
||||
|
||||
struct MutationHandler<'a> {
|
||||
@ -938,7 +985,32 @@ impl Visit for FunctionFinder {
|
||||
self.found = true;
|
||||
}
|
||||
|
||||
/// Do not recurse into nested loop.
|
||||
///
|
||||
/// https://github.com/swc-project/swc/issues/2622
|
||||
fn visit_do_while_stmt(&mut self, _: &DoWhileStmt, _: &dyn Node) {}
|
||||
|
||||
/// Do not recurse into nested loop.
|
||||
///
|
||||
/// https://github.com/swc-project/swc/issues/2622
|
||||
fn visit_for_in_stmt(&mut self, _: &ForInStmt, _: &dyn Node) {}
|
||||
|
||||
/// Do not recurse into nested loop.
|
||||
///
|
||||
/// https://github.com/swc-project/swc/issues/2622
|
||||
fn visit_for_of_stmt(&mut self, _: &ForOfStmt, _: &dyn Node) {}
|
||||
|
||||
/// Do not recurse into nested loop.
|
||||
///
|
||||
/// https://github.com/swc-project/swc/issues/2622
|
||||
fn visit_for_stmt(&mut self, _: &ForStmt, _: &dyn Node) {}
|
||||
|
||||
fn visit_function(&mut self, _: &Function, _: &dyn Node) {
|
||||
self.found = true
|
||||
}
|
||||
|
||||
/// Do not recurse into nested loop.
|
||||
///
|
||||
/// https://github.com/swc-project/swc/issues/2622
|
||||
fn visit_while_stmt(&mut self, _: &WhileStmt, _: &dyn Node) {}
|
||||
}
|
||||
|
@ -1,6 +1,11 @@
|
||||
use std::{fs::read_to_string, path::PathBuf};
|
||||
use swc_common::{chain, comments::NoopComments, Mark};
|
||||
use swc_ecma_parser::Syntax;
|
||||
use swc_ecma_transforms_compat::es2015::for_of::{for_of, Config};
|
||||
use swc_ecma_transforms_base::resolver::resolver_with_mark;
|
||||
use swc_ecma_transforms_compat::es2015::{
|
||||
self,
|
||||
for_of::{for_of, Config},
|
||||
};
|
||||
use swc_ecma_transforms_testing::{compare_stdout, test, test_exec};
|
||||
|
||||
fn syntax() -> Syntax {
|
||||
@ -579,9 +584,32 @@ fn fixture(input: PathBuf) {
|
||||
compare_stdout(
|
||||
Syntax::default(),
|
||||
|_| {
|
||||
for_of(Config {
|
||||
assume_array: false,
|
||||
})
|
||||
let top_level_mark = Mark::fresh(Mark::root());
|
||||
|
||||
chain!(
|
||||
resolver_with_mark(top_level_mark),
|
||||
for_of(Config {
|
||||
assume_array: false,
|
||||
})
|
||||
)
|
||||
},
|
||||
&input,
|
||||
);
|
||||
}
|
||||
|
||||
#[testing::fixture("tests/fixture/for-of/**/exec.js")]
|
||||
fn fixture_es2015(input: PathBuf) {
|
||||
let input = read_to_string(&input).unwrap();
|
||||
|
||||
compare_stdout(
|
||||
Syntax::default(),
|
||||
|_| {
|
||||
let top_level_mark = Mark::fresh(Mark::root());
|
||||
|
||||
chain!(
|
||||
resolver_with_mark(top_level_mark),
|
||||
es2015::es2015(top_level_mark, Some(NoopComments), Default::default())
|
||||
)
|
||||
},
|
||||
&input,
|
||||
);
|
||||
|
@ -0,0 +1,8 @@
|
||||
for (let a = 0; a < 2; a++) {
|
||||
for (let b = 0; b < 2; b++) {
|
||||
() => { };
|
||||
for (let c = 0; c < 2; c++) {
|
||||
console.log(b);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
const block = () => {
|
||||
for (const value in { a: 1, b: 2, c: 3 }) {
|
||||
for (let i = 0; i < 10; i++) {
|
||||
function something() { }
|
||||
continue
|
||||
|
||||
return { value }
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(block());
|
||||
console.log('OK?')
|
@ -0,0 +1,17 @@
|
||||
|
||||
function foo(baz) {
|
||||
for (const g in baz) {
|
||||
console.log(g);
|
||||
|
||||
for (let j = 0; j < g.length; j++) {
|
||||
console.log(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
console.log(foo({
|
||||
a: [1],
|
||||
b: [2, 2],
|
||||
c: [3, 3, 3]
|
||||
}))
|
@ -0,0 +1,10 @@
|
||||
let message = 0;
|
||||
for (let x of [1, 2, 3, 4, 5]) {
|
||||
for (let y of ["a", "b", "c", "d"]) {
|
||||
console.log("Message", ++message, x, y);
|
||||
[].forEach(() => { });
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("WHY", message == 5);
|
||||
|
@ -0,0 +1,9 @@
|
||||
let message = 0;
|
||||
for (let x of [1, 2, 3, 4, 5]) {
|
||||
for (let y of ["a", "b", "c", "d"]) {
|
||||
console.log("Message", ++message, x, y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("WHY", message == 5);
|
||||
|
Loading…
Reference in New Issue
Block a user