From 5c76aea82bd93ddaee63bef77ff667d14019a5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EB=8F=99=EC=9C=A4?= Date: Fri, 11 Sep 2020 23:33:36 +0900 Subject: [PATCH] Fix resolver (#1064) swc_ecma_transforms: - resolver: properly handle let and const - resolver: handle catch parameters - resolver: handle parameter properties swc_ecma_visit: - reduced compile time of debug build --- ecmascript/Cargo.toml | 2 +- ecmascript/transforms/Cargo.toml | 2 +- ecmascript/transforms/src/resolver.rs | 238 ++++++++++++++++---- ecmascript/transforms/src/resolver/tests.rs | 88 ++++++++ ecmascript/visit/Cargo.toml | 2 +- ecmascript/visit/src/lib.rs | 6 +- 6 files changed, 293 insertions(+), 45 deletions(-) diff --git a/ecmascript/Cargo.toml b/ecmascript/Cargo.toml index 4ae66772934..0584173e95c 100644 --- a/ecmascript/Cargo.toml +++ b/ecmascript/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "Apache-2.0/MIT" name = "swc_ecmascript" repository = "https://github.com/swc-project/swc.git" -version = "0.7.3" +version = "0.7.4" [features] codegen = ["swc_ecma_codegen"] diff --git a/ecmascript/transforms/Cargo.toml b/ecmascript/transforms/Cargo.toml index e9ba7a67b54..25485a90892 100644 --- a/ecmascript/transforms/Cargo.toml +++ b/ecmascript/transforms/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "Apache-2.0/MIT" name = "swc_ecma_transforms" repository = "https://github.com/swc-project/swc.git" -version = "0.23.8" +version = "0.23.9" [features] const-modules = ["dashmap"] diff --git a/ecmascript/transforms/src/resolver.rs b/ecmascript/transforms/src/resolver.rs index a7ed7dad417..5034ad5f75c 100644 --- a/ecmascript/transforms/src/resolver.rs +++ b/ecmascript/transforms/src/resolver.rs @@ -125,6 +125,15 @@ impl<'a> Resolver<'a> { } } + fn visit_mut_stmt_within_same_scope(&mut self, s: &mut Stmt) { + match s { + Stmt::Block(s) => { + s.visit_mut_children_with(self); + } + _ => s.visit_mut_with(self), + } + } + /// Returns a [Mark] for an identifier reference. fn mark_for_ref(&self, sym: &JsWord) -> Option { if self.handle_types && self.in_type { @@ -168,9 +177,14 @@ impl<'a> Resolver<'a> { None } - fn visit_mut_binding_ident(&mut self, ident: &mut Ident) { + fn visit_mut_binding_ident(&mut self, ident: &mut Ident, kind: Option) { if cfg!(debug_assertions) && LOG { - eprintln!("resolver: Binding {}{:?}", ident.sym, ident.span.ctxt()); + eprintln!( + "resolver: Binding {}{:?} {:?}", + ident.sym, + ident.span.ctxt(), + kind + ); } if ident.span.ctxt() != SyntaxContext::empty() { @@ -231,14 +245,19 @@ impl<'a> Resolver<'a> { } } - let (should_insert, mark) = if let Some((ref cur, override_mark)) = self.cur_defining { - if *cur != ident.sym { - (true, self.mark) - } else { - (false, override_mark) + let (should_insert, mark) = match kind { + None | Some(VarDeclKind::Var) => { + if let Some((ref cur, override_mark)) = self.cur_defining { + if *cur != ident.sym { + (true, self.mark) + } else { + (false, override_mark) + } + } else { + (true, self.mark) + } } - } else { - (true, self.mark) + _ => (true, self.mark), }; let mut mark = mark; @@ -247,13 +266,23 @@ impl<'a> Resolver<'a> { if self.hoist { let mut cursor = Some(&self.current); - while let Some(c) = cursor { - if c.kind == ScopeKind::Fn { - c.hoisted_symbols.borrow_mut().insert(ident.sym.clone()); - break; + match kind { + Some(VarDeclKind::Var) | None => { + while let Some(c) = cursor { + if c.kind == ScopeKind::Fn { + c.hoisted_symbols.borrow_mut().insert(ident.sym.clone()); + break; + } + cursor = c.parent; + mark = mark.parent(); + } + } + Some(VarDeclKind::Let) | Some(VarDeclKind::Const) => { + self.current + .hoisted_symbols + .borrow_mut() + .insert(ident.sym.clone()); } - cursor = c.parent; - mark = mark.parent(); } } else { self.current.declared_symbols.insert(ident.sym.clone()); @@ -373,7 +402,7 @@ impl<'a> VisitMut for Resolver<'a> { return; } self.in_type = true; - self.visit_mut_binding_ident(&mut param.name); + self.visit_mut_binding_ident(&mut param.name, None); param.default.visit_mut_with(self); param.constraint.visit_mut_with(self); } @@ -428,7 +457,7 @@ impl<'a> VisitMut for Resolver<'a> { } self.in_type = false; - self.visit_mut_binding_ident(&mut decl.id); + self.visit_mut_binding_ident(&mut decl.id, None); decl.members.visit_mut_with(self); } @@ -506,7 +535,7 @@ impl<'a> VisitMut for Resolver<'a> { } self.in_type = true; - self.visit_mut_binding_ident(&mut n.id); + self.visit_mut_binding_ident(&mut n.id, None); let child_mark = Mark::fresh(self.mark); // Child folder let mut child = Resolver::new( @@ -528,7 +557,7 @@ impl<'a> VisitMut for Resolver<'a> { } self.in_type = true; - self.visit_mut_binding_ident(&mut n.id); + self.visit_mut_binding_ident(&mut n.id, None); let child_mark = Mark::fresh(self.mark); // Child folder let mut child = Resolver::new( @@ -549,7 +578,7 @@ impl<'a> VisitMut for Resolver<'a> { } self.in_type = true; - self.visit_mut_binding_ident(&mut n.id); + self.visit_mut_binding_ident(&mut n.id, None); n.module_ref.visit_mut_with(self); } @@ -560,7 +589,7 @@ impl<'a> VisitMut for Resolver<'a> { } self.in_type = true; - self.visit_mut_binding_ident(&mut n.id); + self.visit_mut_binding_ident(&mut n.id, None); n.body.visit_mut_with(self); } @@ -570,7 +599,7 @@ impl<'a> VisitMut for Resolver<'a> { return; } - self.in_type = true; + self.in_type = false; self.ident_type = IdentType::Binding; n.visit_mut_children_with(self) } @@ -605,10 +634,10 @@ impl<'a> VisitMut for Resolver<'a> { let old_hoist = self.hoist; let old = folder.ident_type; folder.ident_type = IdentType::Binding; - self.hoist = false; + folder.hoist = false; e.params.visit_mut_with(&mut folder); folder.ident_type = old; - self.hoist = old_hoist; + folder.hoist = old_hoist; e.body.visit_mut_with(&mut folder); @@ -621,6 +650,61 @@ impl<'a> VisitMut for Resolver<'a> { param.visit_mut_children_with(self); } + fn visit_mut_for_stmt(&mut self, n: &mut ForStmt) { + let child_mark = Mark::fresh(self.mark); + let mut child = Resolver::new( + child_mark, + Scope::new(ScopeKind::Block, Some(&self.current)), + self.cur_defining.take(), + self.handle_types, + ); + + self.ident_type = IdentType::Binding; + n.init.visit_mut_with(&mut child); + self.ident_type = IdentType::Ref; + n.test.visit_mut_with(&mut child); + self.ident_type = IdentType::Ref; + n.update.visit_mut_with(&mut child); + + child.visit_mut_stmt_within_same_scope(&mut *n.body); + + self.cur_defining = child.cur_defining; + } + + fn visit_mut_for_of_stmt(&mut self, n: &mut ForOfStmt) { + let child_mark = Mark::fresh(self.mark); + let mut child = Resolver::new( + child_mark, + Scope::new(ScopeKind::Block, Some(&self.current)), + self.cur_defining.take(), + self.handle_types, + ); + + n.left.visit_mut_with(&mut child); + n.right.visit_mut_with(&mut child); + + child.visit_mut_stmt_within_same_scope(&mut *n.body); + + self.cur_defining = child.cur_defining; + } + + fn visit_mut_for_in_stmt(&mut self, n: &mut ForInStmt) { + let child_mark = Mark::fresh(self.mark); + let mut child = Resolver::new( + child_mark, + Scope::new(ScopeKind::Block, Some(&self.current)), + self.cur_defining.take(), + self.handle_types, + ); + + n.left.visit_mut_with(&mut child); + n.right.visit_mut_with(&mut child); + + child.visit_mut_stmt_within_same_scope(&mut *n.body); + + self.cur_defining = child.cur_defining; + } + fn visit_mut_block_stmt(&mut self, block: &mut BlockStmt) { let child_mark = Mark::fresh(self.mark); @@ -713,8 +797,12 @@ impl<'a> VisitMut for Resolver<'a> { c.params.visit_mut_with(&mut folder); self.ident_type = old; - c.body.visit_mut_with(&mut folder); - c.key.visit_mut_with(&mut folder); + match &mut c.body { + Some(body) => { + body.visit_mut_children_with(&mut folder); + } + None => {} + } } /// Leftmost one of a member expression should be resolved. @@ -762,7 +850,7 @@ impl<'a> VisitMut for Resolver<'a> { fn visit_mut_fn_expr(&mut self, e: &mut FnExpr) { if let Some(ident) = &mut e.ident { - self.visit_mut_binding_ident(ident) + self.visit_mut_binding_ident(ident, None) } let child_mark = Mark::fresh(self.mark); @@ -790,9 +878,7 @@ impl<'a> VisitMut for Resolver<'a> { f.params.visit_mut_with(self); self.ident_type = IdentType::Ref; - f.body - .as_mut() - .map(|stmt| stmt.visit_mut_children_with(self)); + f.body.visit_mut_with(self); f.return_type.visit_mut_with(self); } @@ -805,7 +891,7 @@ impl<'a> VisitMut for Resolver<'a> { self.ident_type = ident_type; match self.ident_type { - IdentType::Binding => self.visit_mut_binding_ident(i), + IdentType::Binding => self.visit_mut_binding_ident(i, None), IdentType::Ref => { let Ident { span, sym, .. } = i; @@ -857,7 +943,7 @@ impl<'a> VisitMut for Resolver<'a> { i.span = span; // Support hoisting - self.visit_mut_binding_ident(i) + self.visit_mut_binding_ident(i, None) } } // We currently does not touch labels @@ -951,7 +1037,11 @@ impl<'a> VisitMut for Resolver<'a> { // Phase 1: Handle hoisting { - let mut hoister = Hoister { resolver: self }; + let mut hoister = Hoister { + resolver: self, + kind: None, + in_block: false, + }; stmts.visit_mut_children_with(&mut hoister) } @@ -962,7 +1052,11 @@ impl<'a> VisitMut for Resolver<'a> { fn visit_mut_stmts(&mut self, stmts: &mut Vec) { // Phase 1: Handle hoisting { - let mut hoister = Hoister { resolver: self }; + let mut hoister = Hoister { + resolver: self, + kind: None, + in_block: false, + }; stmts.visit_mut_children_with(&mut hoister) } @@ -974,28 +1068,49 @@ impl<'a> VisitMut for Resolver<'a> { /// The folder which handles var / function hoisting. struct Hoister<'a, 'b> { resolver: &'a mut Resolver<'b>, + kind: Option, + /// Hoister should not touch let / const in the block. + in_block: bool, } impl VisitMut for Hoister<'_, '_> { noop_visit_mut_type!(); fn visit_mut_fn_decl(&mut self, node: &mut FnDecl) { - self.resolver.visit_mut_binding_ident(&mut node.ident); + self.resolver + .visit_mut_binding_ident(&mut node.ident, Some(VarDeclKind::Var)); } + fn visit_mut_expr(&mut self, _: &mut Expr) {} + #[inline] fn visit_mut_arrow_expr(&mut self, _: &mut ArrowExpr) {} + #[inline] + fn visit_mut_tagged_tpl(&mut self, _: &mut TaggedTpl) {} + + #[inline] + fn visit_mut_tpl(&mut self, _: &mut Tpl) {} + #[inline] fn visit_mut_function(&mut self, _: &mut Function) {} fn visit_mut_var_decl(&mut self, node: &mut VarDecl) { - if node.kind != VarDeclKind::Var { - return; + if self.in_block { + match node.kind { + VarDeclKind::Const | VarDeclKind::Let => return, + _ => {} + } } + + let old_kind = self.kind; + self.kind = Some(node.kind); + self.resolver.hoist = false; - node.visit_mut_children_with(self) + node.visit_mut_children_with(self); + + self.kind = old_kind; } #[inline] @@ -1005,14 +1120,59 @@ impl VisitMut for Hoister<'_, '_> { fn visit_mut_pat(&mut self, node: &mut Pat) { match node { - Pat::Ident(i) => self.resolver.visit_mut_binding_ident(i), + Pat::Ident(i) => self.resolver.visit_mut_binding_ident(i, self.kind), _ => node.visit_mut_children_with(self), } } + #[inline] + fn visit_mut_catch_clause(&mut self, _: &mut CatchClause) {} + #[inline] fn visit_mut_pat_or_expr(&mut self, _: &mut PatOrExpr) {} #[inline] fn visit_mut_param(&mut self, _: &mut Param) {} + + #[inline] + fn visit_mut_constructor(&mut self, _: &mut Constructor) {} + + fn visit_mut_var_decl_or_expr(&mut self, n: &mut VarDeclOrExpr) { + match n { + VarDeclOrExpr::VarDecl(VarDecl { + kind: VarDeclKind::Let, + .. + }) + | VarDeclOrExpr::VarDecl(VarDecl { + kind: VarDeclKind::Const, + .. + }) => {} + _ => { + n.visit_mut_children_with(self); + } + } + } + + fn visit_mut_var_decl_or_pat(&mut self, n: &mut VarDeclOrPat) { + match n { + VarDeclOrPat::VarDecl(VarDecl { + kind: VarDeclKind::Let, + .. + }) + | VarDeclOrPat::VarDecl(VarDecl { + kind: VarDeclKind::Const, + .. + }) => {} + _ => { + n.visit_mut_children_with(self); + } + } + } + + fn visit_mut_block_stmt(&mut self, n: &mut BlockStmt) { + let old_in_block = self.in_block; + self.in_block = true; + n.visit_mut_children_with(self); + self.in_block = old_in_block; + } } diff --git a/ecmascript/transforms/src/resolver/tests.rs b/ecmascript/transforms/src/resolver/tests.rs index 3d826c933d0..55ef7fa1414 100644 --- a/ecmascript/transforms/src/resolver/tests.rs +++ b/ecmascript/transforms/src/resolver/tests.rs @@ -1477,3 +1477,91 @@ to_ts!( }); " ); + +// See: https://github.com/denoland/deno_lint/pull/304 +to_ts!( + let_scoping, + " + function wrapper() { + const usage = () => { + return a; + }; + let a; + } + ", + " + function wrapper() { + const usage__2 = ()=>{ + return a__2; + }; + let a__2; + } + " +); + +to_ts!( + ts_resolver_parameter_property, + r#" + class PartWriter implements Deno.Writer { + constructor( + private writer: Deno.Writer, + readonly boundary: string, + public headers: Headers, + isFirstBoundary: boolean, + ) { + let buf = ""; + if (isFirstBoundary) { + buf += `--${boundary}\r\n`; + } else { + buf += `\r\n--${boundary}\r\n`; + } + for (const [key, value] of headers.entries()) { + buf += `${key}: ${value}\r\n`; + } + buf += `\r\n`; + this.partHeader = buf; + } + } + "#, + r#" + class PartWriter { + constructor(private writer__2: Deno.Writer., readonly boundary__2: string, public headers__2: Headers, isFirstBoundary__2: boolean){ + let buf__2 = ""; + if (isFirstBoundary__2) { + buf__2 += `--${boundary__2}\r\n`; + } else { + buf__2 += `\r\n--${boundary__2}\r\n`; + } + for (const [key__3, value__3] of headers__2.entries()){ + buf__2 += `${key__3}: ${value__3}\r\n`; + } + buf__2 += `\r\n`; + this.partHeader = buf__2; + } + } + "# +); + +to_ts!( + ts_resolver_catch_param, + r#" +function wrapper(...args) { + try { + return target(...args); + } catch (err) { + switch (err.name) { + } + } +} + "#, + " + function wrapper(...args__2) { + try { + return target(...args__2); + } catch (err__3) { + switch(err__3.name){ + } + } + } + " +); diff --git a/ecmascript/visit/Cargo.toml b/ecmascript/visit/Cargo.toml index 293a6448161..74af656be0d 100644 --- a/ecmascript/visit/Cargo.toml +++ b/ecmascript/visit/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" license = "Apache-2.0/MIT" name = "swc_ecma_visit" repository = "https://github.com/swc-project/swc.git" -version = "0.17.1" +version = "0.17.2" [dependencies] num-bigint = {version = "0.2", features = ["serde"]} diff --git a/ecmascript/visit/src/lib.rs b/ecmascript/visit/src/lib.rs index 2982712fb95..bc686794e8c 100644 --- a/ecmascript/visit/src/lib.rs +++ b/ecmascript/visit/src/lib.rs @@ -182,7 +182,7 @@ where #[macro_export] macro_rules! noop_fold_type { ($name:ident, $N:tt) => { - #[inline(always)] + #[inline] fn $name(&mut self, node: $crate::swc_ecma_ast::$N) -> $crate::swc_ecma_ast::$N { node } @@ -261,7 +261,7 @@ macro_rules! noop_fold_type { #[macro_export] macro_rules! noop_visit_type { ($name:ident, $N:tt) => { - #[inline(always)] + #[inline] fn $name(&mut self, _: &$crate::swc_ecma_ast::$N, _: &dyn $crate::Node) {} }; () => { @@ -338,7 +338,7 @@ macro_rules! noop_visit_type { #[macro_export] macro_rules! noop_visit_mut_type { ($name:ident, $N:ident) => { - #[inline(always)] + #[inline] fn $name(&mut self, _: &mut $crate::swc_ecma_ast::$N) {} }; () => {