mirror of
https://github.com/swc-project/swc.git
synced 2024-12-23 21:54:36 +03:00
Fix stack overflow (#1032)
swc_ecma_codegen: - Fix codegen of large binary expressions. swc_ecma_parser: - Fix stack overflow while parsing large binary expressions. swc_ecma_transforms: - typescrip::strip: Fix stack overflow while handling large binary operations. - hygiene: Fix stack overflow by migrating it to `VisitMut`. - hygiene: Improve performance. - fixer: Fix stack overflow by migrating it to `VisitMut`. - resolver: Migrate to `VisitMut`.
This commit is contained in:
parent
c0cb9e4401
commit
eb2162cbd2
1
.github/workflows/cargo.yml
vendored
1
.github/workflows/cargo.yml
vendored
@ -79,6 +79,7 @@ jobs:
|
||||
- name: Run cargo test
|
||||
run: |
|
||||
export PATH="$PATH:$HOME/npm/bin"
|
||||
EXEC=0 cargo test --color always --all --exclude node --exclude wasm
|
||||
cargo test --color always --all --exclude node --exclude wasm
|
||||
|
||||
#
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "swc_ecma_codegen"
|
||||
version = "0.35.0"
|
||||
version = "0.35.1"
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
|
@ -16,6 +16,11 @@ mod tests {
|
||||
assert_min(r#" 'foobar' "#, r#"'foobar';"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn bin_expr() {
|
||||
assert_min("1+2+3+4+5", "1+2+3+4+5;");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn template_expression() {
|
||||
assert_min("``", "``;");
|
||||
|
@ -690,10 +690,9 @@ impl<'a> Emitter<'a> {
|
||||
emit!(node.right);
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
fn emit_bin_expr(&mut self, node: &BinExpr) -> Result {
|
||||
self.emit_leading_comments_of_pos(node.span().lo())?;
|
||||
|
||||
/// Prints operator and right node of a binary expression.
|
||||
#[inline(never)]
|
||||
fn emit_bin_expr_trailing(&mut self, node: &BinExpr) -> Result {
|
||||
// let indent_before_op = needs_indention(node, &node.left, node.op);
|
||||
// let indent_after_op = needs_indention(node, node.op, &node.right);
|
||||
let need_space = match node.op {
|
||||
@ -701,19 +700,17 @@ impl<'a> Emitter<'a> {
|
||||
_ => false,
|
||||
};
|
||||
|
||||
emit!(node.left);
|
||||
|
||||
let need_pre_space = need_space
|
||||
|| match *node.left {
|
||||
Expr::Update(UpdateExpr { prefix: false, .. }) => true,
|
||||
_ => false,
|
||||
};
|
||||
if need_pre_space {
|
||||
space!();
|
||||
space!(self);
|
||||
} else {
|
||||
formatting_space!();
|
||||
formatting_space!(self);
|
||||
}
|
||||
operator!(node.op.as_str());
|
||||
operator!(self, node.op.as_str());
|
||||
|
||||
let need_post_space = need_space
|
||||
|| match *node.right {
|
||||
@ -721,11 +718,47 @@ impl<'a> Emitter<'a> {
|
||||
_ => false,
|
||||
};
|
||||
if need_post_space {
|
||||
space!();
|
||||
space!(self);
|
||||
} else {
|
||||
formatting_space!();
|
||||
formatting_space!(self);
|
||||
}
|
||||
emit!(node.right);
|
||||
emit!(self, node.right);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
fn emit_bin_expr(&mut self, node: &BinExpr) -> Result {
|
||||
self.emit_leading_comments_of_pos(node.span().lo())?;
|
||||
|
||||
{
|
||||
let mut left = Some(node);
|
||||
let mut lefts = vec![];
|
||||
while let Some(l) = left {
|
||||
lefts.push(l);
|
||||
|
||||
match &*l.left {
|
||||
Expr::Bin(b) => {
|
||||
left = Some(b);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
|
||||
let len = lefts.len();
|
||||
|
||||
for (i, left) in lefts.into_iter().rev().enumerate() {
|
||||
if i == 0 {
|
||||
emit!(left.left);
|
||||
}
|
||||
// Check if it's last
|
||||
if i + 1 != len {
|
||||
self.emit_bin_expr_trailing(left)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.emit_bin_expr_trailing(node)?;
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "swc_ecma_parser"
|
||||
version = "0.37.0"
|
||||
version = "0.37.1"
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
|
@ -39,10 +39,50 @@ impl<'a, I: Tokens> Parser<I> {
|
||||
///
|
||||
/// `parseExprOp`
|
||||
pub(in crate::parser) fn parse_bin_op_recursively(
|
||||
&mut self,
|
||||
mut left: Box<Expr>,
|
||||
mut min_prec: u8,
|
||||
) -> PResult<Box<Expr>> {
|
||||
loop {
|
||||
let (next_left, next_prec) = self.parse_bin_op_recursively_inner(left, min_prec)?;
|
||||
|
||||
match &*next_left {
|
||||
Expr::Bin(BinExpr {
|
||||
span,
|
||||
left,
|
||||
op: op!("&&"),
|
||||
..
|
||||
})
|
||||
| Expr::Bin(BinExpr {
|
||||
span,
|
||||
left,
|
||||
op: op!("||"),
|
||||
..
|
||||
}) => match &**left {
|
||||
Expr::Bin(BinExpr { op: op!("??"), .. }) => {
|
||||
self.emit_err(*span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
|
||||
min_prec = match next_prec {
|
||||
Some(v) => v,
|
||||
None => return Ok(next_left),
|
||||
};
|
||||
|
||||
left = next_left;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `(left, Some(next_prec))` or `(expr, None)`.
|
||||
fn parse_bin_op_recursively_inner(
|
||||
&mut self,
|
||||
left: Box<Expr>,
|
||||
min_prec: u8,
|
||||
) -> PResult<Box<Expr>> {
|
||||
) -> PResult<(Box<Expr>, Option<u8>)> {
|
||||
const PREC_OF_IN: u8 = 7;
|
||||
|
||||
if self.input.syntax().typescript()
|
||||
@ -69,21 +109,21 @@ impl<'a, I: Tokens> Parser<I> {
|
||||
}))
|
||||
};
|
||||
|
||||
return self.parse_bin_op_recursively(node, min_prec);
|
||||
return self.parse_bin_op_recursively_inner(node, min_prec);
|
||||
}
|
||||
|
||||
let ctx = self.ctx();
|
||||
// Return left on eof
|
||||
let word = match cur!(false) {
|
||||
Ok(cur) => cur,
|
||||
Err(..) => return Ok(left),
|
||||
Err(..) => return Ok((left, None)),
|
||||
};
|
||||
let op = match *word {
|
||||
Word(Word::Keyword(Keyword::In)) if ctx.include_in_expr => op!("in"),
|
||||
Word(Word::Keyword(Keyword::InstanceOf)) => op!("instanceof"),
|
||||
Token::BinOp(op) => op.into(),
|
||||
_ => {
|
||||
return Ok(left);
|
||||
return Ok((left, None));
|
||||
}
|
||||
};
|
||||
|
||||
@ -100,7 +140,7 @@ impl<'a, I: Tokens> Parser<I> {
|
||||
op.precedence()
|
||||
);
|
||||
|
||||
return Ok(left);
|
||||
return Ok((left, None));
|
||||
}
|
||||
bump!();
|
||||
trace!(
|
||||
@ -173,18 +213,7 @@ impl<'a, I: Tokens> Parser<I> {
|
||||
right,
|
||||
}));
|
||||
|
||||
let expr = self.parse_bin_op_recursively(node, min_prec)?;
|
||||
|
||||
if op == op!("??") {
|
||||
match *expr {
|
||||
Expr::Bin(BinExpr { span, op, .. }) if op == op!("&&") || op == op!("||") => {
|
||||
self.emit_err(span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Ok(expr)
|
||||
return Ok((node, Some(min_prec)));
|
||||
}
|
||||
|
||||
/// Parse unary expression and update expression.
|
||||
|
@ -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.1"
|
||||
version = "0.23.2"
|
||||
|
||||
[features]
|
||||
const-modules = ["dashmap"]
|
||||
@ -25,6 +25,7 @@ jsdoc = {version = "0.5.0", path = "../jsdoc"}
|
||||
log = "0.4.8"
|
||||
once_cell = "1"
|
||||
ordered-float = "1.0.1"
|
||||
phf = {version = "0.8.0", features = ["macros"]}
|
||||
regex = "1"
|
||||
retain_mut = "=0.1.1"
|
||||
scoped-tls = "1"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
use swc_common::Spanned;
|
||||
// use swc_common::Spanned;
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{Fold, FoldWith};
|
||||
use swc_ecma_visit::Fold;
|
||||
|
||||
/// This transform validates span on debug mode and does nothing on release
|
||||
/// mode.
|
||||
@ -8,148 +8,109 @@ pub struct Validator {
|
||||
pub name: &'static str,
|
||||
}
|
||||
|
||||
macro_rules! ne {
|
||||
($v:expr, $T:ty, $l:expr, $r:expr) => {{
|
||||
// debug_assert_ne!(
|
||||
// $l,
|
||||
// $r,
|
||||
// "{}: {}: {} should not be same as {}",
|
||||
// $v.name,
|
||||
// stringify!($T),
|
||||
// stringify!($l),
|
||||
// stringify!($r),
|
||||
// );
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! lte {
|
||||
($v:expr, $T:ty, $l:expr, $r:expr) => {{
|
||||
// debug_assert!(
|
||||
// $l <= $r,
|
||||
// "{}: {}: {} should be <= {}; l={:?}; r={:?}",
|
||||
// $v.name,
|
||||
// stringify!($T),
|
||||
// stringify!($l),
|
||||
// stringify!($r),
|
||||
// $l,
|
||||
// $r
|
||||
// );
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! gte {
|
||||
($v:expr, $T:ty, $l:expr, $r:expr) => {{
|
||||
// debug_assert!(
|
||||
// $l >= $r,
|
||||
// "{}: {}: {} should be >= {}; l={:?}; r={:?}",
|
||||
// $v.name,
|
||||
// stringify!($T),
|
||||
// stringify!($l),
|
||||
// stringify!($r),
|
||||
// $l,
|
||||
// $r
|
||||
// );
|
||||
}};
|
||||
}
|
||||
|
||||
impl Fold for Validator {
|
||||
fn fold_assign_expr(&mut self, node: AssignExpr) -> AssignExpr {
|
||||
if node.span.is_dummy() {
|
||||
return node.fold_children_with(self);
|
||||
}
|
||||
// fn fold_assign_expr(&mut self, node: AssignExpr) -> AssignExpr {
|
||||
// if node.span.is_dummy() {
|
||||
// return node.fold_children_with(self);
|
||||
// }
|
||||
//
|
||||
// if !node.left.span().is_dummy() {
|
||||
// gte!(self, AssignExpr, node.left.span().lo(), node.span().lo());
|
||||
// }
|
||||
//
|
||||
// // if !node.right.span().is_dummy() {
|
||||
// // eq!(self, AssignExpr, node.right.span().hi(),
|
||||
// node.span().hi()); // }
|
||||
//
|
||||
// node.fold_children_with(self)
|
||||
// }
|
||||
//
|
||||
// fn fold_bin_expr(&mut self, node: BinExpr) -> BinExpr {
|
||||
// if node.span.is_dummy() {
|
||||
// return node.fold_children_with(self);
|
||||
// }
|
||||
//
|
||||
// if !node.left.span().is_dummy() {
|
||||
// gte!(self, BinExpr, node.left.span().lo(), node.span().lo());
|
||||
// }
|
||||
//
|
||||
// if !node.right.span().is_dummy() {
|
||||
// gte!(self, BinExpr, node.span().hi(), node.right.span().hi());
|
||||
// }
|
||||
//
|
||||
// node.fold_children_with(self)
|
||||
// }
|
||||
//
|
||||
// fn fold_cond_expr(&mut self, node: CondExpr) -> CondExpr {
|
||||
// if node.span.is_dummy() {
|
||||
// return node.fold_children_with(self);
|
||||
// }
|
||||
//
|
||||
// if !node.test.span().is_dummy() {
|
||||
// gte!(self, CondExpr, node.test.span().lo(), node.span().lo());
|
||||
// }
|
||||
//
|
||||
// if !node.alt.span().is_dummy() {
|
||||
// lte!(self, CondExpr, node.alt.span().hi(), node.span().hi());
|
||||
// }
|
||||
//
|
||||
// node.fold_children_with(self)
|
||||
// }
|
||||
//
|
||||
// fn fold_member_expr(&mut self, node: MemberExpr) -> MemberExpr {
|
||||
// if node.span.is_dummy() {
|
||||
// return node.fold_children_with(self);
|
||||
// }
|
||||
//
|
||||
// if !node.obj.span().is_dummy() {
|
||||
// ne!(self, MemberExpr, node.span(), node.obj.span());
|
||||
// }
|
||||
//
|
||||
// if !node.prop.span().is_dummy() {
|
||||
// ne!(self, MemberExpr, node.span(), node.prop.span());
|
||||
// }
|
||||
//
|
||||
// if !node.obj.span().is_dummy() {
|
||||
// lte!(self, MemberExpr, node.span().lo(), node.obj.span().lo());
|
||||
// }
|
||||
//
|
||||
// if !node.computed && !node.prop.span().is_dummy() {
|
||||
// gte!(self, MemberExpr, node.span().hi(), node.prop.span().hi());
|
||||
// }
|
||||
//
|
||||
// node.fold_children_with(self)
|
||||
// }
|
||||
//
|
||||
// fn fold_unary_expr(&mut self, node: UnaryExpr) -> UnaryExpr {
|
||||
// if node.span.is_dummy() {
|
||||
// return node.fold_children_with(self);
|
||||
// }
|
||||
//
|
||||
// if !node.arg.span().is_dummy() {
|
||||
// lte!(self, UnaryExpr, node.arg.span().hi(), node.span().hi())
|
||||
// }
|
||||
//
|
||||
// node.fold_children_with(self)
|
||||
// }
|
||||
//
|
||||
// fn fold_update_expr(&mut self, node: UpdateExpr) -> UpdateExpr {
|
||||
// if node.span.is_dummy() {
|
||||
// return node.fold_children_with(self);
|
||||
// }
|
||||
//
|
||||
// if node.prefix {
|
||||
// if !node.arg.span().is_dummy() {
|
||||
// lte!(self, UpdateExpr, node.arg.span().hi(), node.span().hi())
|
||||
// }
|
||||
// } else if !node.arg.span().is_dummy() {
|
||||
// gte!(self, UpdateExpr, node.arg.span().lo(), node.span().lo())
|
||||
// }
|
||||
//
|
||||
// node.fold_children_with(self)
|
||||
// }
|
||||
|
||||
if !node.left.span().is_dummy() {
|
||||
gte!(self, AssignExpr, node.left.span().lo(), node.span().lo());
|
||||
}
|
||||
|
||||
// if !node.right.span().is_dummy() {
|
||||
// eq!(self, AssignExpr, node.right.span().hi(), node.span().hi());
|
||||
// }
|
||||
|
||||
node.fold_children_with(self)
|
||||
}
|
||||
|
||||
fn fold_bin_expr(&mut self, node: BinExpr) -> BinExpr {
|
||||
if node.span.is_dummy() {
|
||||
return node.fold_children_with(self);
|
||||
}
|
||||
|
||||
if !node.left.span().is_dummy() {
|
||||
gte!(self, BinExpr, node.left.span().lo(), node.span().lo());
|
||||
}
|
||||
|
||||
if !node.right.span().is_dummy() {
|
||||
gte!(self, BinExpr, node.span().hi(), node.right.span().hi());
|
||||
}
|
||||
|
||||
node.fold_children_with(self)
|
||||
}
|
||||
|
||||
fn fold_cond_expr(&mut self, node: CondExpr) -> CondExpr {
|
||||
if node.span.is_dummy() {
|
||||
return node.fold_children_with(self);
|
||||
}
|
||||
|
||||
if !node.test.span().is_dummy() {
|
||||
gte!(self, CondExpr, node.test.span().lo(), node.span().lo());
|
||||
}
|
||||
|
||||
if !node.alt.span().is_dummy() {
|
||||
lte!(self, CondExpr, node.alt.span().hi(), node.span().hi());
|
||||
}
|
||||
|
||||
node.fold_children_with(self)
|
||||
}
|
||||
|
||||
fn fold_member_expr(&mut self, node: MemberExpr) -> MemberExpr {
|
||||
if node.span.is_dummy() {
|
||||
return node.fold_children_with(self);
|
||||
}
|
||||
|
||||
if !node.obj.span().is_dummy() {
|
||||
ne!(self, MemberExpr, node.span(), node.obj.span());
|
||||
}
|
||||
|
||||
if !node.prop.span().is_dummy() {
|
||||
ne!(self, MemberExpr, node.span(), node.prop.span());
|
||||
}
|
||||
|
||||
if !node.obj.span().is_dummy() {
|
||||
lte!(self, MemberExpr, node.span().lo(), node.obj.span().lo());
|
||||
}
|
||||
|
||||
if !node.computed && !node.prop.span().is_dummy() {
|
||||
gte!(self, MemberExpr, node.span().hi(), node.prop.span().hi());
|
||||
}
|
||||
|
||||
node.fold_children_with(self)
|
||||
}
|
||||
|
||||
fn fold_unary_expr(&mut self, node: UnaryExpr) -> UnaryExpr {
|
||||
if node.span.is_dummy() {
|
||||
return node.fold_children_with(self);
|
||||
}
|
||||
|
||||
if !node.arg.span().is_dummy() {
|
||||
lte!(self, UnaryExpr, node.arg.span().hi(), node.span().hi())
|
||||
}
|
||||
|
||||
node.fold_children_with(self)
|
||||
}
|
||||
|
||||
fn fold_update_expr(&mut self, node: UpdateExpr) -> UpdateExpr {
|
||||
if node.span.is_dummy() {
|
||||
return node.fold_children_with(self);
|
||||
}
|
||||
|
||||
if node.prefix {
|
||||
if !node.arg.span().is_dummy() {
|
||||
lte!(self, UpdateExpr, node.arg.span().hi(), node.span().hi())
|
||||
}
|
||||
} else if !node.arg.span().is_dummy() {
|
||||
gte!(self, UpdateExpr, node.arg.span().lo(), node.span().lo())
|
||||
}
|
||||
|
||||
node.fold_children_with(self)
|
||||
#[inline(always)] // prevent stack overflow on debug build
|
||||
fn fold_module(&mut self, module: Module) -> Module {
|
||||
module
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,10 @@ use swc_ecma_utils::ExprExt;
|
||||
pub(crate) trait MapWithMut: Sized {
|
||||
fn dummy() -> Self;
|
||||
|
||||
fn take(&mut self) -> Self {
|
||||
replace(self, Self::dummy())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map_with_mut<F>(&mut self, op: F)
|
||||
where
|
||||
@ -71,6 +75,22 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl MapWithMut for Ident {
|
||||
fn dummy() -> Self {
|
||||
Ident::new(js_word!(""), DUMMY_SP)
|
||||
}
|
||||
}
|
||||
|
||||
impl MapWithMut for ObjectPatProp {
|
||||
fn dummy() -> Self {
|
||||
ObjectPatProp::Assign(AssignPatProp {
|
||||
span: DUMMY_SP,
|
||||
key: Ident::dummy(),
|
||||
value: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) trait PatOrExprExt: AsOptExpr {
|
||||
fn as_ref(&self) -> &PatOrExpr;
|
||||
fn as_mut(&mut self) -> &mut PatOrExpr;
|
||||
|
@ -1,22 +1,15 @@
|
||||
use crate::{
|
||||
ext::{AsOptExpr, PatOrExprExt},
|
||||
util::ExprFactory,
|
||||
};
|
||||
use crate::ext::{AsOptExpr, MapWithMut, PatOrExprExt};
|
||||
use fxhash::FxHashMap;
|
||||
use swc_common::{
|
||||
comments::Comments,
|
||||
util::{map::Map, move_map::MoveMap},
|
||||
Span, Spanned,
|
||||
};
|
||||
use swc_common::{comments::Comments, Span, Spanned};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
|
||||
|
||||
pub fn fixer<'a>(comments: Option<&'a dyn Comments>) -> impl 'a + Fold {
|
||||
Fixer {
|
||||
as_folder(Fixer {
|
||||
comments,
|
||||
ctx: Default::default(),
|
||||
span_map: Default::default(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
struct Fixer<'a> {
|
||||
@ -56,123 +49,90 @@ impl Default for Context {
|
||||
|
||||
macro_rules! array {
|
||||
($name:ident, $T:tt) => {
|
||||
fn $name(&mut self, e: $T) -> $T {
|
||||
fn $name(&mut self, e: &mut $T) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: false }.into();
|
||||
let elems = e.elems.fold_with(self);
|
||||
e.elems.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
$T { elems, ..e }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl Fold for Fixer<'_> {
|
||||
noop_fold_type!();
|
||||
impl VisitMut for Fixer<'_> {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
array!(fold_array_lit, ArrayLit);
|
||||
array!(visit_mut_array_lit, ArrayLit);
|
||||
// array!(ArrayPat);
|
||||
|
||||
fn fold_new_expr(&mut self, node: NewExpr) -> NewExpr {
|
||||
let NewExpr {
|
||||
span,
|
||||
mut callee,
|
||||
args,
|
||||
type_args,
|
||||
} = node;
|
||||
|
||||
fn visit_mut_new_expr(&mut self, node: &mut NewExpr) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: false };
|
||||
let args = args.fold_with(self);
|
||||
node.args.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Callee { is_new: true };
|
||||
callee = callee.fold_with(self);
|
||||
match *callee {
|
||||
Expr::Call(..) | Expr::Bin(..) => callee = Box::new(self.wrap(*callee)),
|
||||
node.callee.visit_mut_with(self);
|
||||
match *node.callee {
|
||||
Expr::Call(..) | Expr::Bin(..) => self.wrap(&mut node.callee),
|
||||
_ => {}
|
||||
}
|
||||
self.ctx = old;
|
||||
|
||||
NewExpr {
|
||||
span,
|
||||
callee,
|
||||
args,
|
||||
type_args,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_call_expr(&mut self, node: CallExpr) -> CallExpr {
|
||||
let CallExpr {
|
||||
span,
|
||||
callee,
|
||||
args,
|
||||
type_args,
|
||||
} = node;
|
||||
|
||||
fn visit_mut_call_expr(&mut self, node: &mut CallExpr) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: false };
|
||||
let args = args.fold_with(self);
|
||||
node.args.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Callee { is_new: false };
|
||||
let callee = callee.fold_with(self);
|
||||
node.callee.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
CallExpr {
|
||||
span,
|
||||
callee,
|
||||
args,
|
||||
type_args,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_arrow_expr(&mut self, node: ArrowExpr) -> ArrowExpr {
|
||||
fn visit_mut_arrow_expr(&mut self, node: &mut ArrowExpr) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Default;
|
||||
let mut node = node.fold_children_with(self);
|
||||
node.body = match node.body {
|
||||
BlockStmtOrExpr::Expr(e) if e.is_seq() => {
|
||||
BlockStmtOrExpr::Expr(Box::new(self.wrap(*e)))
|
||||
node.visit_mut_children_with(self);
|
||||
match &mut node.body {
|
||||
BlockStmtOrExpr::Expr(ref mut e) if e.is_seq() => {
|
||||
self.wrap(&mut **e);
|
||||
}
|
||||
_ => node.body,
|
||||
_ => {}
|
||||
};
|
||||
self.ctx = old;
|
||||
node
|
||||
}
|
||||
|
||||
fn fold_assign_pat_prop(&mut self, node: AssignPatProp) -> AssignPatProp {
|
||||
let key = node.key.fold_children_with(self);
|
||||
fn visit_mut_assign_pat_prop(&mut self, node: &mut AssignPatProp) {
|
||||
node.key.visit_mut_children_with(self);
|
||||
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: false };
|
||||
let value = node.value.fold_with(self);
|
||||
node.value.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
AssignPatProp { key, value, ..node }
|
||||
}
|
||||
|
||||
fn fold_block_stmt_or_expr(&mut self, body: BlockStmtOrExpr) -> BlockStmtOrExpr {
|
||||
let body = body.fold_children_with(self);
|
||||
fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
|
||||
body.visit_mut_children_with(self);
|
||||
|
||||
match body {
|
||||
BlockStmtOrExpr::Expr(expr) if expr.is_object() => {
|
||||
BlockStmtOrExpr::Expr(Box::new(self.wrap(*expr)))
|
||||
BlockStmtOrExpr::Expr(ref mut expr) if expr.is_object() => {
|
||||
self.wrap(&mut **expr);
|
||||
}
|
||||
|
||||
_ => body,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_class(&mut self, node: Class) -> Class {
|
||||
fn visit_mut_class(&mut self, node: &mut Class) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Default;
|
||||
let mut node: Class = node.fold_children_with(self);
|
||||
node.super_class = match node.super_class {
|
||||
Some(e) if e.is_seq() || e.is_await_expr() => Some(Box::new(self.wrap(*e))),
|
||||
_ => node.super_class,
|
||||
node.visit_mut_children_with(self);
|
||||
match &mut node.super_class {
|
||||
Some(ref mut e) if e.is_seq() || e.is_await_expr() => self.wrap(&mut **e),
|
||||
_ => {}
|
||||
};
|
||||
self.ctx = old;
|
||||
|
||||
@ -180,52 +140,159 @@ impl Fold for Fixer<'_> {
|
||||
ClassMember::Empty(..) => false,
|
||||
_ => true,
|
||||
});
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
fn fold_export_default_expr(&mut self, node: ExportDefaultExpr) -> ExportDefaultExpr {
|
||||
fn visit_mut_export_default_expr(&mut self, node: &mut ExportDefaultExpr) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Default;
|
||||
let mut node = node.fold_children_with(self);
|
||||
node.expr = match *node.expr {
|
||||
Expr::Arrow(..) | Expr::Seq(..) => Box::new(self.wrap(*node.expr)),
|
||||
_ => node.expr,
|
||||
node.visit_mut_children_with(self);
|
||||
match &mut *node.expr {
|
||||
Expr::Arrow(..) | Expr::Seq(..) => self.wrap(&mut node.expr),
|
||||
_ => {}
|
||||
};
|
||||
self.ctx = old;
|
||||
node
|
||||
}
|
||||
|
||||
fn fold_expr(&mut self, expr: Expr) -> Expr {
|
||||
let expr = expr.fold_children_with(self);
|
||||
let expr = self.unwrap_expr(expr);
|
||||
fn visit_mut_expr(&mut self, e: &mut Expr) {
|
||||
e.visit_mut_children_with(self);
|
||||
self.unwrap_expr(e);
|
||||
|
||||
match expr {
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
computed,
|
||||
obj,
|
||||
prop,
|
||||
}) if obj.as_expr().map(|e| e.is_object()).unwrap_or(false)
|
||||
&& match self.ctx {
|
||||
Context::ForcedExpr { is_var_decl: true } => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
MemberExpr {
|
||||
span,
|
||||
computed,
|
||||
obj,
|
||||
prop,
|
||||
self.wrap_with_paren_if_required(e)
|
||||
}
|
||||
|
||||
fn visit_mut_expr_or_spread(&mut self, e: &mut ExprOrSpread) {
|
||||
e.visit_mut_children_with(self);
|
||||
|
||||
if e.spread.is_none() {
|
||||
match *e.expr {
|
||||
Expr::Yield(..) => {
|
||||
self.wrap(&mut e.expr);
|
||||
}
|
||||
.into()
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_if_stmt(&mut self, node: &mut IfStmt) {
|
||||
node.visit_mut_children_with(self);
|
||||
|
||||
match *node.cons {
|
||||
Stmt::If(..) => {
|
||||
node.cons = Box::new(Stmt::Block(BlockStmt {
|
||||
span: node.cons.span(),
|
||||
stmts: vec![*node.cons.take()],
|
||||
}));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_key_value_pat_prop(&mut self, node: &mut KeyValuePatProp) {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: false };
|
||||
node.key.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
node.value.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_key_value_prop(&mut self, prop: &mut KeyValueProp) {
|
||||
prop.visit_mut_children_with(self);
|
||||
|
||||
match *prop.value {
|
||||
Expr::Seq(..) => self.wrap(&mut prop.value),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_prop_name(&mut self, name: &mut PropName) {
|
||||
name.visit_mut_children_with(self);
|
||||
|
||||
match name {
|
||||
PropName::Computed(c) if c.expr.is_seq() => {
|
||||
self.wrap(&mut c.expr);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_stmt(&mut self, stmt: &mut Stmt) {
|
||||
match stmt {
|
||||
Stmt::Expr(expr) => {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Default;
|
||||
expr.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
}
|
||||
_ => stmt.visit_mut_children_with(self),
|
||||
};
|
||||
|
||||
match stmt {
|
||||
Stmt::Expr(ExprStmt { ref mut expr, .. }) => {
|
||||
self.handle_expr_stmt(&mut **expr);
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_var_declarator(&mut self, node: &mut VarDeclarator) {
|
||||
node.name.visit_mut_children_with(self);
|
||||
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: true };
|
||||
node.init.visit_mut_with(self);
|
||||
self.ctx = old;
|
||||
}
|
||||
|
||||
fn visit_mut_module(&mut self, n: &mut Module) {
|
||||
debug_assert!(self.span_map.is_empty());
|
||||
self.span_map.clear();
|
||||
|
||||
let n = n.visit_mut_children_with(self);
|
||||
if let Some(c) = self.comments {
|
||||
for (to, from) in self.span_map.drain() {
|
||||
c.move_leading(from.lo, to.lo);
|
||||
c.move_trailing(from.hi, to.hi);
|
||||
}
|
||||
}
|
||||
|
||||
n
|
||||
}
|
||||
|
||||
fn visit_mut_script(&mut self, n: &mut Script) {
|
||||
debug_assert!(self.span_map.is_empty());
|
||||
self.span_map.clear();
|
||||
|
||||
let n = n.visit_mut_children_with(self);
|
||||
if let Some(c) = self.comments {
|
||||
for (to, from) in self.span_map.drain() {
|
||||
c.move_leading(from.lo, to.lo);
|
||||
c.move_trailing(from.hi, to.hi);
|
||||
}
|
||||
}
|
||||
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
impl Fixer<'_> {
|
||||
fn wrap_with_paren_if_required(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Member(MemberExpr { obj, .. })
|
||||
if obj.as_expr().map(|e| e.is_object()).unwrap_or(false)
|
||||
&& match self.ctx {
|
||||
Context::ForcedExpr { is_var_decl: true } => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
computed,
|
||||
obj: ExprOrSuper::Expr(obj),
|
||||
prop,
|
||||
obj: ExprOrSuper::Expr(ref mut obj),
|
||||
..
|
||||
}) if obj.is_fn_expr()
|
||||
|| obj.is_cond()
|
||||
|| obj.is_unary()
|
||||
@ -238,18 +305,12 @@ impl Fold for Fixer<'_> {
|
||||
|| obj.is_class()
|
||||
|| obj.is_yield_expr()
|
||||
|| obj.is_await_expr()
|
||||
|| match *obj {
|
||||
|| match **obj {
|
||||
Expr::New(NewExpr { args: None, .. }) => true,
|
||||
_ => false,
|
||||
} =>
|
||||
{
|
||||
MemberExpr {
|
||||
span,
|
||||
computed,
|
||||
obj: self.wrap(*obj).as_obj(),
|
||||
prop,
|
||||
}
|
||||
.into()
|
||||
self.wrap(&mut **obj);
|
||||
}
|
||||
|
||||
// Flatten seq expr
|
||||
@ -270,23 +331,25 @@ impl Fold for Fixer<'_> {
|
||||
.filter_map(|(i, e)| {
|
||||
let is_last = i + 1 == exprs_len;
|
||||
if is_last {
|
||||
Some(e)
|
||||
Some(e.take())
|
||||
} else {
|
||||
ignore_return_value(e)
|
||||
ignore_return_value(e.take())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
if exprs.len() == 1 {
|
||||
return *exprs.pop().unwrap();
|
||||
*e = *exprs.pop().unwrap();
|
||||
return;
|
||||
}
|
||||
Expr::Seq(SeqExpr { span, exprs })
|
||||
Expr::Seq(SeqExpr { span: *span, exprs })
|
||||
} else {
|
||||
let mut buf = Vec::with_capacity(len);
|
||||
for (i, expr) in exprs.into_iter().enumerate() {
|
||||
let is_last = i + 1 == exprs_len;
|
||||
|
||||
match *expr {
|
||||
Expr::Seq(SeqExpr { exprs, .. }) => {
|
||||
match **expr {
|
||||
Expr::Seq(SeqExpr { ref mut exprs, .. }) => {
|
||||
let exprs = exprs.take();
|
||||
if !is_last {
|
||||
buf.extend(exprs.into_iter().filter_map(ignore_return_value));
|
||||
} else {
|
||||
@ -301,360 +364,166 @@ impl Fold for Fixer<'_> {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => buf.push(expr),
|
||||
_ => buf.push(expr.take()),
|
||||
}
|
||||
}
|
||||
|
||||
if buf.len() == 1 {
|
||||
return *buf.pop().unwrap();
|
||||
*e = *buf.pop().unwrap();
|
||||
return;
|
||||
}
|
||||
buf.shrink_to_fit();
|
||||
Expr::Seq(SeqExpr { span, exprs: buf })
|
||||
|
||||
Expr::Seq(SeqExpr {
|
||||
span: *span,
|
||||
exprs: buf,
|
||||
})
|
||||
};
|
||||
|
||||
match self.ctx {
|
||||
Context::ForcedExpr { .. } => Expr::Paren(ParenExpr {
|
||||
span,
|
||||
expr: Box::new(expr),
|
||||
}),
|
||||
_ => expr,
|
||||
}
|
||||
Context::ForcedExpr { .. } => {
|
||||
*e = Expr::Paren(ParenExpr {
|
||||
span: *span,
|
||||
expr: Box::new(expr),
|
||||
})
|
||||
}
|
||||
_ => *e = expr,
|
||||
};
|
||||
}
|
||||
|
||||
Expr::Bin(mut expr) => {
|
||||
expr.right = match *expr.right {
|
||||
e @ Expr::Assign(..)
|
||||
| e @ Expr::Seq(..)
|
||||
| e @ Expr::Yield(..)
|
||||
| e @ Expr::Cond(..)
|
||||
| e @ Expr::Arrow(..) => Box::new(self.wrap(e)),
|
||||
Expr::Bin(ref mut expr) => {
|
||||
match &mut *expr.right {
|
||||
Expr::Assign(..)
|
||||
| Expr::Seq(..)
|
||||
| Expr::Yield(..)
|
||||
| Expr::Cond(..)
|
||||
| Expr::Arrow(..) => {
|
||||
self.wrap(&mut expr.right);
|
||||
}
|
||||
Expr::Bin(BinExpr { op: op_of_rhs, .. }) => {
|
||||
if op_of_rhs.precedence() <= expr.op.precedence() {
|
||||
Box::new(self.wrap(*expr.right))
|
||||
} else {
|
||||
expr.right
|
||||
self.wrap(&mut expr.right);
|
||||
}
|
||||
}
|
||||
_ => expr.right,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
match *expr.left {
|
||||
match &mut *expr.left {
|
||||
// While simplifying, (1 + x) * Nan becomes `1 + x * Nan`.
|
||||
// But it should be `(1 + x) * Nan`
|
||||
Expr::Bin(BinExpr { op: op_of_lhs, .. }) => {
|
||||
if op_of_lhs.precedence() < expr.op.precedence() {
|
||||
Expr::Bin(BinExpr {
|
||||
left: Box::new(self.wrap(*expr.left)),
|
||||
..expr
|
||||
})
|
||||
} else {
|
||||
Expr::Bin(expr)
|
||||
self.wrap(&mut expr.left);
|
||||
}
|
||||
}
|
||||
|
||||
e @ Expr::Seq(..)
|
||||
| e @ Expr::Update(..)
|
||||
| e
|
||||
@
|
||||
Expr::Unary(UnaryExpr {
|
||||
Expr::Seq(..)
|
||||
| Expr::Update(..)
|
||||
| Expr::Unary(UnaryExpr {
|
||||
op: op!("delete"), ..
|
||||
})
|
||||
| e
|
||||
@
|
||||
Expr::Unary(UnaryExpr {
|
||||
| Expr::Unary(UnaryExpr {
|
||||
op: op!("void"), ..
|
||||
})
|
||||
| e @ Expr::Yield(..)
|
||||
| e @ Expr::Cond(..)
|
||||
| e @ Expr::Assign(..)
|
||||
| e @ Expr::Arrow(..) => Expr::Bin(BinExpr {
|
||||
left: Box::new(self.wrap(e)),
|
||||
..expr
|
||||
}),
|
||||
e @ Expr::Object(..)
|
||||
| Expr::Yield(..)
|
||||
| Expr::Cond(..)
|
||||
| Expr::Assign(..)
|
||||
| Expr::Arrow(..) => {
|
||||
self.wrap(&mut expr.left);
|
||||
}
|
||||
Expr::Object(..)
|
||||
if expr.op == op!("instanceof")
|
||||
|| expr.op == op!("==")
|
||||
|| expr.op == op!("===")
|
||||
|| expr.op == op!("!=")
|
||||
|| expr.op == op!("!==") =>
|
||||
{
|
||||
Expr::Bin(BinExpr {
|
||||
left: Box::new(e.wrap_with_paren()),
|
||||
..expr
|
||||
})
|
||||
self.wrap(&mut expr.left)
|
||||
}
|
||||
_ => Expr::Bin(expr),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Cond(expr) => {
|
||||
let test = match *expr.test {
|
||||
e @ Expr::Seq(..)
|
||||
| e @ Expr::Assign(..)
|
||||
| e @ Expr::Cond(..)
|
||||
| e @ Expr::Arrow(..) => Box::new(self.wrap(e)),
|
||||
match &mut *expr.test {
|
||||
Expr::Seq(..) | Expr::Assign(..) | Expr::Cond(..) | Expr::Arrow(..) => {
|
||||
self.wrap(&mut expr.test)
|
||||
}
|
||||
|
||||
e @ Expr::Object(..) | e @ Expr::Fn(..) | e @ Expr::Class(..) => {
|
||||
Expr::Object(..) | Expr::Fn(..) | Expr::Class(..) => {
|
||||
if self.ctx == Context::Default {
|
||||
Box::new(self.wrap(e))
|
||||
} else {
|
||||
Box::new(e)
|
||||
self.wrap(&mut expr.test)
|
||||
}
|
||||
}
|
||||
_ => expr.test,
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let cons = match *expr.cons {
|
||||
e @ Expr::Seq(..) => Box::new(self.wrap(e)),
|
||||
_ => expr.cons,
|
||||
match *expr.cons {
|
||||
Expr::Seq(..) => self.wrap(&mut expr.cons),
|
||||
_ => {}
|
||||
};
|
||||
|
||||
let alt = match *expr.alt {
|
||||
e @ Expr::Seq(..) => Box::new(self.wrap(e)),
|
||||
_ => expr.alt,
|
||||
match *expr.alt {
|
||||
Expr::Seq(..) => self.wrap(&mut expr.alt),
|
||||
_ => {}
|
||||
};
|
||||
let expr = Expr::Cond(CondExpr {
|
||||
test,
|
||||
cons,
|
||||
alt,
|
||||
..expr
|
||||
});
|
||||
|
||||
match self.ctx {
|
||||
Context::Callee { is_new: true } => self.wrap(expr),
|
||||
_ => expr,
|
||||
Context::Callee { is_new: true } => self.wrap(e),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Unary(expr) => {
|
||||
let arg = match *expr.arg {
|
||||
e @ Expr::Assign(..)
|
||||
| e @ Expr::Bin(..)
|
||||
| e @ Expr::Seq(..)
|
||||
| e @ Expr::Cond(..)
|
||||
| e @ Expr::Arrow(..)
|
||||
| e @ Expr::Yield(..) => Box::new(self.wrap(e)),
|
||||
_ => expr.arg,
|
||||
};
|
||||
|
||||
Expr::Unary(UnaryExpr { arg, ..expr })
|
||||
}
|
||||
Expr::Unary(expr) => match *expr.arg {
|
||||
Expr::Assign(..)
|
||||
| Expr::Bin(..)
|
||||
| Expr::Seq(..)
|
||||
| Expr::Cond(..)
|
||||
| Expr::Arrow(..)
|
||||
| Expr::Yield(..) => self.wrap(&mut expr.arg),
|
||||
_ => {}
|
||||
},
|
||||
|
||||
Expr::Assign(expr) => {
|
||||
let right = match *expr.right {
|
||||
match &mut *expr.right {
|
||||
// `foo = (bar = baz)` => foo = bar = baz
|
||||
Expr::Assign(AssignExpr { ref left, .. }) if left.as_ident().is_some() => {
|
||||
expr.right
|
||||
}
|
||||
Expr::Assign(AssignExpr { ref left, .. }) if left.as_ident().is_some() => {}
|
||||
|
||||
// Handle `foo = bar = init()
|
||||
Expr::Seq(right) => Box::new(self.wrap(right)),
|
||||
_ => expr.right,
|
||||
};
|
||||
|
||||
Expr::Assign(AssignExpr { right, ..expr })
|
||||
Expr::Seq(..) => self.wrap(&mut expr.right),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: ExprOrSuper::Expr(callee),
|
||||
args,
|
||||
type_args,
|
||||
}) if callee.is_arrow() => Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: self.wrap(*callee).as_callee(),
|
||||
args,
|
||||
type_args,
|
||||
}),
|
||||
callee: ExprOrSuper::Expr(ref mut callee),
|
||||
..
|
||||
}) if callee.is_arrow() => {
|
||||
self.wrap(&mut **callee);
|
||||
}
|
||||
|
||||
// Function expression cannot start with `function`
|
||||
Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: ExprOrSuper::Expr(callee),
|
||||
args,
|
||||
type_args,
|
||||
callee: ExprOrSuper::Expr(ref mut callee),
|
||||
..
|
||||
}) if callee.is_fn_expr() => match self.ctx {
|
||||
Context::ForcedExpr { .. } => Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: callee.as_callee(),
|
||||
args,
|
||||
type_args,
|
||||
}),
|
||||
Context::ForcedExpr { .. } => {}
|
||||
|
||||
Context::Callee { is_new: true } => self.wrap(CallExpr {
|
||||
span,
|
||||
callee: callee.as_callee(),
|
||||
args,
|
||||
type_args,
|
||||
}),
|
||||
Context::Callee { is_new: true } => self.wrap(e),
|
||||
|
||||
_ => Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: self.wrap(*callee).as_callee(),
|
||||
args,
|
||||
type_args,
|
||||
}),
|
||||
_ => self.wrap(&mut **callee),
|
||||
},
|
||||
Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: ExprOrSuper::Expr(callee),
|
||||
args,
|
||||
type_args,
|
||||
}) if callee.is_assign() => Expr::Call(CallExpr {
|
||||
span,
|
||||
callee: self.wrap(*callee).as_callee(),
|
||||
args,
|
||||
type_args,
|
||||
}),
|
||||
_ => expr,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_expr_or_spread(&mut self, e: ExprOrSpread) -> ExprOrSpread {
|
||||
let e = e.fold_children_with(self);
|
||||
|
||||
if e.spread.is_none() {
|
||||
match *e.expr {
|
||||
Expr::Yield(..) => {
|
||||
return ExprOrSpread {
|
||||
spread: None,
|
||||
expr: Box::new(self.wrap(*e.expr)),
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
e
|
||||
}
|
||||
|
||||
fn fold_if_stmt(&mut self, node: IfStmt) -> IfStmt {
|
||||
let node: IfStmt = node.fold_children_with(self);
|
||||
|
||||
match *node.cons {
|
||||
Stmt::If(..) => IfStmt {
|
||||
cons: Box::new(Stmt::Block(BlockStmt {
|
||||
span: node.cons.span(),
|
||||
stmts: vec![*node.cons],
|
||||
})),
|
||||
..node
|
||||
},
|
||||
|
||||
_ => node,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_key_value_pat_prop(&mut self, node: KeyValuePatProp) -> KeyValuePatProp {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: false };
|
||||
let key = node.key.fold_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
let value = node.value.fold_with(self);
|
||||
|
||||
KeyValuePatProp { key, value }
|
||||
}
|
||||
|
||||
fn fold_key_value_prop(&mut self, prop: KeyValueProp) -> KeyValueProp {
|
||||
let prop = prop.fold_children_with(self);
|
||||
|
||||
match *prop.value {
|
||||
Expr::Seq(..) => KeyValueProp {
|
||||
value: Box::new(self.wrap(*prop.value)),
|
||||
..prop
|
||||
},
|
||||
_ => prop,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_prop_name(&mut self, mut name: PropName) -> PropName {
|
||||
name = name.fold_children_with(self);
|
||||
|
||||
match name {
|
||||
PropName::Computed(c) if c.expr.is_seq() => {
|
||||
return PropName::Computed(ComputedPropName {
|
||||
span: c.span,
|
||||
expr: Box::new(self.wrap(*c.expr)),
|
||||
});
|
||||
}
|
||||
callee: ExprOrSuper::Expr(ref mut callee),
|
||||
..
|
||||
}) if callee.is_assign() => self.wrap(&mut **callee),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
name
|
||||
}
|
||||
|
||||
fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
|
||||
let stmt = match stmt {
|
||||
Stmt::Expr(expr) => {
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::Default;
|
||||
let expr = expr.fold_with(self);
|
||||
self.ctx = old;
|
||||
Stmt::Expr(expr)
|
||||
}
|
||||
_ => stmt.fold_children_with(self),
|
||||
};
|
||||
|
||||
let stmt = match stmt {
|
||||
Stmt::Expr(ExprStmt { span, expr }) => Stmt::Expr(ExprStmt {
|
||||
span,
|
||||
expr: expr.map(|e| self.handle_expr_stmt(e)),
|
||||
}),
|
||||
|
||||
_ => stmt,
|
||||
};
|
||||
|
||||
stmt
|
||||
}
|
||||
|
||||
fn fold_var_declarator(&mut self, node: VarDeclarator) -> VarDeclarator {
|
||||
let name = node.name.fold_children_with(self);
|
||||
|
||||
let old = self.ctx;
|
||||
self.ctx = Context::ForcedExpr { is_var_decl: true };
|
||||
let init = node.init.fold_with(self);
|
||||
self.ctx = old;
|
||||
|
||||
VarDeclarator { name, init, ..node }
|
||||
}
|
||||
|
||||
fn fold_module(&mut self, n: Module) -> Module {
|
||||
debug_assert!(self.span_map.is_empty());
|
||||
self.span_map.clear();
|
||||
|
||||
let n = n.fold_children_with(self);
|
||||
if let Some(c) = self.comments {
|
||||
for (to, from) in self.span_map.drain() {
|
||||
c.move_leading(from.lo, to.lo);
|
||||
c.move_trailing(from.hi, to.hi);
|
||||
}
|
||||
}
|
||||
|
||||
n
|
||||
}
|
||||
|
||||
fn fold_script(&mut self, n: Script) -> Script {
|
||||
debug_assert!(self.span_map.is_empty());
|
||||
self.span_map.clear();
|
||||
|
||||
let n = n.fold_children_with(self);
|
||||
if let Some(c) = self.comments {
|
||||
for (to, from) in self.span_map.drain() {
|
||||
c.move_leading(from.lo, to.lo);
|
||||
c.move_trailing(from.hi, to.hi);
|
||||
}
|
||||
}
|
||||
|
||||
n
|
||||
}
|
||||
}
|
||||
|
||||
impl Fixer<'_> {
|
||||
fn wrap<T>(&mut self, e: T) -> Expr
|
||||
where
|
||||
T: Into<Expr>,
|
||||
{
|
||||
let expr = Box::new(e.into());
|
||||
let span = expr.span();
|
||||
/// Wrap with a paren.
|
||||
fn wrap(&mut self, e: &mut Expr) {
|
||||
let span = e.span();
|
||||
|
||||
let span = if let Some(span) = self.span_map.remove(&span) {
|
||||
span
|
||||
@ -662,71 +531,61 @@ impl Fixer<'_> {
|
||||
span
|
||||
};
|
||||
|
||||
Expr::Paren(ParenExpr { expr, span })
|
||||
let expr = Box::new(e.take());
|
||||
*e = Expr::Paren(ParenExpr { expr, span })
|
||||
}
|
||||
|
||||
/// Removes paren
|
||||
fn unwrap_expr(&mut self, mut e: Expr) -> Expr {
|
||||
fn unwrap_expr(&mut self, e: &mut Expr) {
|
||||
match e {
|
||||
Expr::Seq(SeqExpr { ref mut exprs, .. }) if exprs.len() == 1 => {
|
||||
self.unwrap_expr(*exprs.pop().unwrap())
|
||||
self.unwrap_expr(exprs.last_mut().unwrap());
|
||||
*e = *exprs.last_mut().unwrap().take();
|
||||
}
|
||||
Expr::Paren(ParenExpr {
|
||||
span: paren_span,
|
||||
expr,
|
||||
ref mut expr,
|
||||
..
|
||||
}) => {
|
||||
let e = self.unwrap_expr(*expr);
|
||||
let expr_span = expr.span();
|
||||
let paren_span = *paren_span;
|
||||
self.unwrap_expr(&mut **expr);
|
||||
*e = *expr.take();
|
||||
|
||||
self.span_map.insert(e.span(), paren_span);
|
||||
e
|
||||
self.span_map.insert(expr_span, paren_span);
|
||||
}
|
||||
_ => e,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_expr_stmt(&mut self, expr: Expr) -> Expr {
|
||||
fn handle_expr_stmt(&mut self, expr: &mut Expr) {
|
||||
match expr {
|
||||
// It's important for arrow pass to work properly.
|
||||
Expr::Object(..) | Expr::Class(..) | Expr::Fn(..) => self.wrap(expr),
|
||||
|
||||
// ({ a } = foo)
|
||||
Expr::Assign(AssignExpr {
|
||||
span,
|
||||
left: PatOrExpr::Pat(left),
|
||||
op,
|
||||
right,
|
||||
}) if left.is_object() => self.wrap(AssignExpr {
|
||||
span,
|
||||
left: PatOrExpr::Pat(left),
|
||||
op,
|
||||
right,
|
||||
}),
|
||||
..
|
||||
}) if left.is_object() => self.wrap(expr),
|
||||
|
||||
Expr::Seq(SeqExpr { span, exprs }) => {
|
||||
Expr::Seq(SeqExpr { exprs, .. }) => {
|
||||
debug_assert!(
|
||||
exprs.len() != 1,
|
||||
"SeqExpr should be unwrapped if exprs.len() == 1, but length is 1"
|
||||
);
|
||||
|
||||
let mut i = 0;
|
||||
let len = exprs.len();
|
||||
Expr::Seq(SeqExpr {
|
||||
span,
|
||||
exprs: exprs.move_map(|expr| {
|
||||
i += 1;
|
||||
let is_last = len == i;
|
||||
exprs.into_iter().enumerate().for_each(|(i, mut expr)| {
|
||||
let is_last = len == i + 1;
|
||||
|
||||
if !is_last {
|
||||
expr.map(|e| self.handle_expr_stmt(e))
|
||||
} else {
|
||||
expr
|
||||
}
|
||||
}),
|
||||
})
|
||||
if !is_last {
|
||||
self.handle_expr_stmt(&mut expr);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
_ => expr,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
use self::ops::{Operator, ScopeOp};
|
||||
use self::ops::{Operations, Operator};
|
||||
use crate::{
|
||||
compat::es2015::classes::native::is_native,
|
||||
compat::es2015::classes::native::{is_native, is_native_word},
|
||||
scope::{IdentType, ScopeKind},
|
||||
};
|
||||
use fxhash::FxHashMap;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
use std::cell::RefCell;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{chain, Span, SyntaxContext};
|
||||
use swc_common::{chain, SyntaxContext};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{as_folder, noop_fold_type, noop_visit_mut_type, Fold, FoldWith, VisitMut};
|
||||
use swc_ecma_visit::{as_folder, noop_visit_mut_type, Fold, VisitMut, VisitMutWith};
|
||||
|
||||
mod ops;
|
||||
#[cfg(test)]
|
||||
@ -16,6 +17,16 @@ mod tests;
|
||||
|
||||
const LOG: bool = false;
|
||||
|
||||
trait ToBoxedStr {
|
||||
fn to_boxed_str(&self) -> Box<str>;
|
||||
}
|
||||
|
||||
impl ToBoxedStr for JsWord {
|
||||
fn to_boxed_str(&self) -> Box<str> {
|
||||
(**self).to_owned().into_boxed_str()
|
||||
}
|
||||
}
|
||||
|
||||
struct Hygiene<'a> {
|
||||
current: Scope<'a>,
|
||||
ident_type: IdentType,
|
||||
@ -31,14 +42,19 @@ impl<'a> Hygiene<'a> {
|
||||
eprintln!("Declaring {}{:?} ", ident.sym, ctxt);
|
||||
}
|
||||
|
||||
let can_declare_without_renaming = self.current.can_declare(ident.sym.clone(), ctxt);
|
||||
let can_declare_without_renaming =
|
||||
self.current.can_declare(&ident.sym.to_boxed_str(), ctxt);
|
||||
let sym = self.current.change_symbol(ident.sym, ctxt);
|
||||
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("Changed symbol to {}{:?} ", sym, ctxt);
|
||||
}
|
||||
|
||||
self.current
|
||||
.declared_symbols
|
||||
.borrow_mut()
|
||||
.entry(sym.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.entry(sym.to_boxed_str())
|
||||
.or_default()
|
||||
.push(ctxt);
|
||||
|
||||
if can_declare_without_renaming {
|
||||
@ -86,7 +102,7 @@ impl<'a> Hygiene<'a> {
|
||||
let mut i = 0;
|
||||
loop {
|
||||
i += 1;
|
||||
let sym: JsWord = format!("{}{}", sym, i).into();
|
||||
let sym = format!("{}{}", sym, i);
|
||||
|
||||
if !self.current.is_declared(&sym) {
|
||||
break sym;
|
||||
@ -99,14 +115,13 @@ impl<'a> Hygiene<'a> {
|
||||
}
|
||||
|
||||
let sym = self.current.change_symbol(sym, ctxt);
|
||||
let scope = self.current.scope_of(sym.clone(), ctxt);
|
||||
let boxed_sym = sym.to_boxed_str();
|
||||
let scope = self.current.scope_of(&boxed_sym, ctxt);
|
||||
|
||||
// Update symbol list
|
||||
let mut declared_symbols = scope.declared_symbols.borrow_mut();
|
||||
|
||||
let is_not_renamed = scope.ops.borrow().iter().all(|op| match *op {
|
||||
ScopeOp::Rename { ref from, .. } => from.0 != sym || from.1 != ctxt,
|
||||
});
|
||||
let is_not_renamed = !scope.ops.borrow().rename.contains_key(&(sym.clone(), ctxt));
|
||||
|
||||
debug_assert!(
|
||||
is_not_renamed,
|
||||
@ -116,7 +131,7 @@ impl<'a> Hygiene<'a> {
|
||||
scope.ops.borrow(),
|
||||
);
|
||||
|
||||
let old = declared_symbols.entry(sym.clone()).or_default();
|
||||
let old = declared_symbols.entry(sym.to_boxed_str()).or_default();
|
||||
assert!(
|
||||
old.contains(&ctxt),
|
||||
"{:?} does not contain {}{:?}",
|
||||
@ -127,23 +142,26 @@ impl<'a> Hygiene<'a> {
|
||||
old.retain(|c| *c != ctxt);
|
||||
// debug_assert!(old.is_empty() || old.len() == 1);
|
||||
|
||||
let new = declared_symbols.entry(renamed.clone()).or_default();
|
||||
let new = declared_symbols
|
||||
.entry(renamed.clone().into_boxed_str())
|
||||
.or_insert_with(|| Vec::with_capacity(2));
|
||||
new.push(ctxt);
|
||||
debug_assert!(new.len() == 1);
|
||||
|
||||
scope.ops.borrow_mut().push(ScopeOp::Rename {
|
||||
from: (sym, ctxt),
|
||||
to: renamed,
|
||||
});
|
||||
scope
|
||||
.ops
|
||||
.borrow_mut()
|
||||
.rename
|
||||
.insert((sym, ctxt), renamed.into());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hygiene() -> impl Fold + 'static {
|
||||
chain!(
|
||||
Hygiene {
|
||||
as_folder(Hygiene {
|
||||
current: Default::default(),
|
||||
ident_type: IdentType::Ref,
|
||||
},
|
||||
}),
|
||||
as_folder(MarkClearer)
|
||||
)
|
||||
}
|
||||
@ -153,27 +171,27 @@ struct MarkClearer;
|
||||
impl VisitMut for MarkClearer {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn visit_mut_span(&mut self, span: &mut Span) {
|
||||
*span = span.with_ctxt(SyntaxContext::empty());
|
||||
fn visit_mut_ident(&mut self, ident: &mut Ident) {
|
||||
ident.span.ctxt = SyntaxContext::empty();
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Hygiene<'a> {
|
||||
fn apply_ops<N>(&mut self, node: N) -> N
|
||||
fn apply_ops<N>(&mut self, node: &mut N)
|
||||
where
|
||||
for<'o> N: FoldWith<Operator<'o>>,
|
||||
for<'o> N: VisitMutWith<Operator<'o>>,
|
||||
{
|
||||
let ops = self.current.ops.borrow();
|
||||
|
||||
if ops.is_empty() {
|
||||
return node;
|
||||
if ops.rename.is_empty() {
|
||||
return;
|
||||
}
|
||||
node.fold_with(&mut Operator(&ops))
|
||||
node.visit_mut_with(&mut Operator(&ops))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Hygiene<'a> {
|
||||
fn fold_fn(&mut self, ident: Option<Ident>, mut node: Function) -> Function {
|
||||
fn visit_mut_fn(&mut self, ident: Option<Ident>, node: &mut Function) {
|
||||
match ident {
|
||||
Some(ident) => {
|
||||
self.add_declared_ref(ident);
|
||||
@ -187,13 +205,15 @@ impl<'a> Hygiene<'a> {
|
||||
};
|
||||
|
||||
folder.ident_type = IdentType::Ref;
|
||||
node.decorators = node.decorators.fold_with(&mut folder);
|
||||
node.decorators.visit_mut_with(&mut folder);
|
||||
|
||||
folder.ident_type = IdentType::Binding;
|
||||
node.params = node.params.fold_with(&mut folder);
|
||||
node.params.visit_mut_with(&mut folder);
|
||||
|
||||
folder.ident_type = IdentType::Ref;
|
||||
node.body = node.body.map(|stmt| stmt.fold_children_with(&mut folder));
|
||||
node.body
|
||||
.as_mut()
|
||||
.map(|stmt| stmt.visit_mut_children_with(&mut folder));
|
||||
|
||||
folder.apply_ops(node)
|
||||
}
|
||||
@ -208,9 +228,9 @@ struct Scope<'a> {
|
||||
pub kind: ScopeKind,
|
||||
|
||||
/// All references declared in this scope
|
||||
pub declared_symbols: RefCell<HashMap<JsWord, Vec<SyntaxContext>>>,
|
||||
pub declared_symbols: RefCell<FxHashMap<Box<str>, Vec<SyntaxContext>>>,
|
||||
|
||||
pub(crate) ops: RefCell<Vec<ScopeOp>>,
|
||||
pub(crate) ops: RefCell<Operations>,
|
||||
}
|
||||
|
||||
impl<'a> Default for Scope<'a> {
|
||||
@ -230,8 +250,8 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn scope_of(&self, sym: JsWord, ctxt: SyntaxContext) -> &'a Scope<'_> {
|
||||
if let Some(prev) = self.declared_symbols.borrow().get(&sym) {
|
||||
fn scope_of(&self, sym: &Box<str>, ctxt: SyntaxContext) -> &'a Scope<'_> {
|
||||
if let Some(prev) = self.declared_symbols.borrow().get(sym) {
|
||||
if prev.contains(&ctxt) {
|
||||
return self;
|
||||
}
|
||||
@ -243,11 +263,11 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn can_declare(&self, sym: JsWord, ctxt: SyntaxContext) -> bool {
|
||||
fn can_declare(&self, sym: &Box<str>, ctxt: SyntaxContext) -> bool {
|
||||
match self.parent {
|
||||
None => {}
|
||||
Some(parent) => {
|
||||
if !parent.can_declare(sym.clone(), ctxt) {
|
||||
if !parent.can_declare(sym, ctxt) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -257,7 +277,7 @@ impl<'a> Scope<'a> {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(ctxts) = self.declared_symbols.borrow().get(&sym) {
|
||||
if let Some(ctxts) = self.declared_symbols.borrow().get(sym) {
|
||||
ctxts.contains(&ctxt)
|
||||
} else {
|
||||
// No variable named `sym` is declared
|
||||
@ -279,7 +299,7 @@ impl<'a> Scope<'a> {
|
||||
|
||||
let mut ctxts = smallvec![];
|
||||
{
|
||||
if let Some(cxs) = self.declared_symbols.get_mut().get(&sym) {
|
||||
if let Some(cxs) = self.declared_symbols.get_mut().get(&*sym) {
|
||||
if cxs.len() != 1 || cxs[0] != ctxt {
|
||||
ctxts.extend_from_slice(&cxs);
|
||||
}
|
||||
@ -289,7 +309,7 @@ impl<'a> Scope<'a> {
|
||||
let mut cur = self.parent;
|
||||
|
||||
while let Some(scope) = cur {
|
||||
if let Some(cxs) = scope.declared_symbols.borrow().get(&sym) {
|
||||
if let Some(cxs) = scope.declared_symbols.borrow().get(&*sym) {
|
||||
if cxs.len() != 1 || cxs[0] != ctxt {
|
||||
ctxts.extend_from_slice(&cxs);
|
||||
}
|
||||
@ -308,16 +328,11 @@ impl<'a> Scope<'a> {
|
||||
let mut cur = Some(self);
|
||||
|
||||
while let Some(scope) = cur {
|
||||
for op in scope.ops.borrow().iter() {
|
||||
match *op {
|
||||
ScopeOp::Rename { ref from, ref to } if from.0 == *sym && from.1 == ctxt => {
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("Changing symbol: {}{:?} -> {}", sym, ctxt, to);
|
||||
}
|
||||
sym = to.clone()
|
||||
}
|
||||
_ => {}
|
||||
if let Some(to) = scope.ops.borrow().rename.get(&(sym.clone(), ctxt)) {
|
||||
if cfg!(debug_assertions) && LOG {
|
||||
eprintln!("Changing symbol: {}{:?} -> {}", sym, ctxt, to);
|
||||
}
|
||||
sym = to.clone()
|
||||
}
|
||||
|
||||
cur = scope.parent;
|
||||
@ -326,14 +341,13 @@ impl<'a> Scope<'a> {
|
||||
sym
|
||||
}
|
||||
|
||||
fn is_declared(&self, sym: &JsWord) -> bool {
|
||||
fn is_declared(&self, sym: &str) -> bool {
|
||||
if self.declared_symbols.borrow().contains_key(sym) {
|
||||
return true;
|
||||
}
|
||||
for op in self.ops.borrow().iter() {
|
||||
match *op {
|
||||
ScopeOp::Rename { ref to, .. } if sym == to => return true,
|
||||
_ => {}
|
||||
for (_, to) in &self.ops.borrow().rename {
|
||||
if to == sym {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
match self.parent {
|
||||
@ -344,6 +358,7 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[deprecated = "Not a public api"]
|
||||
macro_rules! track_ident {
|
||||
() => {
|
||||
fn fold_export_specifier(&mut self, s: ExportSpecifier) -> ExportSpecifier {
|
||||
@ -492,168 +507,260 @@ macro_rules! track_ident {
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a> Fold for Hygiene<'a> {
|
||||
noop_fold_type!();
|
||||
macro_rules! track_ident_mut {
|
||||
() => {
|
||||
fn visit_mut_export_specifier(&mut self, s: &mut ExportSpecifier) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Ref;
|
||||
s.visit_mut_children_with(self);
|
||||
self.ident_type = old;
|
||||
}
|
||||
|
||||
track_ident!();
|
||||
fn visit_mut_import_specifier(&mut self, s: &mut ImportSpecifier) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Binding;
|
||||
|
||||
fn fold_arrow_expr(&mut self, mut node: ArrowExpr) -> ArrowExpr {
|
||||
match s {
|
||||
ImportSpecifier::Named(ImportNamedSpecifier { imported: None, .. })
|
||||
| ImportSpecifier::Namespace(..)
|
||||
| ImportSpecifier::Default(..) => s.visit_mut_children_with(self),
|
||||
ImportSpecifier::Named(s) => s.local.visit_mut_with(self),
|
||||
};
|
||||
|
||||
self.ident_type = old;
|
||||
}
|
||||
|
||||
fn visit_mut_setter_prop(&mut self, f: &mut SetterProp) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Binding;
|
||||
f.param.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
f.body.visit_mut_with(self);
|
||||
}
|
||||
|
||||
// impl<'a> Fold for $T<'a> {
|
||||
// fn fold(&mut self, f: GetterProp) -> GetterProp {
|
||||
// let body = f.body.visit_mut_with(self);
|
||||
|
||||
// GetterProp { body, ..c }
|
||||
// }
|
||||
// }
|
||||
|
||||
fn visit_mut_labeled_stmt(&mut self, s: &mut LabeledStmt) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Label;
|
||||
s.label.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
s.body.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_break_stmt(&mut self, s: &mut BreakStmt) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Label;
|
||||
s.label.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
}
|
||||
|
||||
fn visit_mut_continue_stmt(&mut self, s: &mut ContinueStmt) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Label;
|
||||
s.label.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
}
|
||||
|
||||
fn visit_mut_class_decl(&mut self, n: &mut ClassDecl) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Binding;
|
||||
n.ident.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
n.class.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_class_expr(&mut self, n: &mut ClassExpr) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Binding;
|
||||
n.ident.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
n.class.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_key_value_pat_prop(&mut self, n: &mut KeyValuePatProp) {
|
||||
n.key.visit_mut_with(self);
|
||||
n.value.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_class(&mut self, c: &mut Class) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Ref;
|
||||
c.decorators.visit_mut_with(self);
|
||||
|
||||
self.ident_type = IdentType::Ref;
|
||||
c.super_class.visit_mut_with(self);
|
||||
|
||||
self.ident_type = IdentType::Binding;
|
||||
c.type_params.visit_mut_with(self);
|
||||
|
||||
self.ident_type = IdentType::Ref;
|
||||
c.super_type_params.visit_mut_with(self);
|
||||
|
||||
self.ident_type = IdentType::Ref;
|
||||
c.implements.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
c.body.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_prop_name(&mut self, n: &mut PropName) {
|
||||
match n {
|
||||
PropName::Computed(c) => {
|
||||
c.visit_mut_with(self);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a> VisitMut for Hygiene<'a> {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
track_ident_mut!();
|
||||
|
||||
fn visit_mut_arrow_expr(&mut self, node: &mut ArrowExpr) {
|
||||
let mut folder = Hygiene {
|
||||
current: Scope::new(ScopeKind::Fn, Some(&self.current)),
|
||||
ident_type: IdentType::Ref,
|
||||
};
|
||||
|
||||
folder.ident_type = IdentType::Binding;
|
||||
node.params = node.params.fold_with(&mut folder);
|
||||
node.params.visit_mut_with(&mut folder);
|
||||
|
||||
folder.ident_type = IdentType::Ref;
|
||||
node.body = node.body.fold_with(&mut folder);
|
||||
node.body.visit_mut_with(&mut folder);
|
||||
|
||||
folder.apply_ops(node)
|
||||
}
|
||||
|
||||
fn fold_block_stmt(&mut self, node: BlockStmt) -> BlockStmt {
|
||||
fn visit_mut_block_stmt(&mut self, node: &mut BlockStmt) {
|
||||
let mut folder = Hygiene {
|
||||
current: Scope::new(ScopeKind::Block, Some(&self.current)),
|
||||
ident_type: IdentType::Ref,
|
||||
};
|
||||
let node = node.fold_children_with(&mut folder);
|
||||
node.visit_mut_children_with(&mut folder);
|
||||
|
||||
folder.apply_ops(node)
|
||||
}
|
||||
|
||||
fn fold_catch_clause(&mut self, c: CatchClause) -> CatchClause {
|
||||
fn visit_mut_catch_clause(&mut self, c: &mut CatchClause) {
|
||||
let mut folder = Hygiene {
|
||||
current: Scope::new(ScopeKind::Fn, Some(&self.current)),
|
||||
ident_type: IdentType::Ref,
|
||||
};
|
||||
folder.ident_type = IdentType::Binding;
|
||||
let param = c.param.fold_with(&mut folder);
|
||||
c.param.visit_mut_with(&mut folder);
|
||||
folder.ident_type = IdentType::Ref;
|
||||
|
||||
let body = c.body.fold_with(&mut folder);
|
||||
|
||||
CatchClause { param, body, ..c }
|
||||
c.body.visit_mut_with(&mut folder);
|
||||
}
|
||||
|
||||
fn fold_constructor(&mut self, c: Constructor) -> Constructor {
|
||||
fn visit_mut_constructor(&mut self, c: &mut Constructor) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Binding;
|
||||
let params = c.params.fold_with(self);
|
||||
c.params.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
let body = c.body.map(|bs| bs.fold_children_with(self));
|
||||
let key = c.key.fold_with(self);
|
||||
|
||||
let c = Constructor {
|
||||
params,
|
||||
body,
|
||||
key,
|
||||
..c
|
||||
};
|
||||
c.body.as_mut().map(|bs| bs.visit_mut_children_with(self));
|
||||
c.key.visit_mut_with(self);
|
||||
|
||||
self.apply_ops(c)
|
||||
}
|
||||
|
||||
fn fold_expr(&mut self, node: Expr) -> Expr {
|
||||
fn visit_mut_expr(&mut self, node: &mut Expr) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Ref;
|
||||
let node = match node {
|
||||
Expr::Ident(..) => node.fold_children_with(self),
|
||||
match node {
|
||||
Expr::Ident(..) => node.visit_mut_children_with(self),
|
||||
Expr::Member(e) => {
|
||||
if e.computed {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: e.obj.fold_with(self),
|
||||
prop: e.prop.fold_with(self),
|
||||
..e
|
||||
})
|
||||
e.obj.visit_mut_with(self);
|
||||
e.prop.visit_mut_with(self);
|
||||
} else {
|
||||
Expr::Member(MemberExpr {
|
||||
obj: e.obj.fold_with(self),
|
||||
..e
|
||||
})
|
||||
e.obj.visit_mut_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
Expr::This(..) => node,
|
||||
Expr::This(..) => {}
|
||||
|
||||
_ => node.fold_children_with(self),
|
||||
_ => node.visit_mut_children_with(self),
|
||||
};
|
||||
|
||||
self.ident_type = old;
|
||||
|
||||
node
|
||||
}
|
||||
|
||||
fn fold_fn_decl(&mut self, mut node: FnDecl) -> FnDecl {
|
||||
node.function = self.fold_fn(Some(node.ident.clone()), node.function);
|
||||
|
||||
node
|
||||
fn visit_mut_fn_decl(&mut self, node: &mut FnDecl) {
|
||||
self.visit_mut_fn(Some(node.ident.clone()), &mut node.function);
|
||||
}
|
||||
|
||||
fn fold_fn_expr(&mut self, mut node: FnExpr) -> FnExpr {
|
||||
node.function = self.fold_fn(node.ident.clone(), node.function);
|
||||
|
||||
node
|
||||
fn visit_mut_fn_expr(&mut self, node: &mut FnExpr) {
|
||||
self.visit_mut_fn(node.ident.clone(), &mut node.function);
|
||||
}
|
||||
|
||||
/// Invoked for `IdetifierRefrence` / `BindingIdentifier`
|
||||
fn fold_ident(&mut self, i: Ident) -> Ident {
|
||||
fn visit_mut_ident(&mut self, i: &mut Ident) {
|
||||
if i.sym == js_word!("arguments") || i.sym == js_word!("undefined") {
|
||||
return i;
|
||||
return;
|
||||
}
|
||||
|
||||
match self.ident_type {
|
||||
IdentType::Binding => self.add_declared_ref(i.clone()),
|
||||
IdentType::Ref => {
|
||||
// Special cases
|
||||
if is_native(&i.sym) {
|
||||
return i;
|
||||
if is_native_word(&i.sym) {
|
||||
return;
|
||||
}
|
||||
|
||||
self.add_used_ref(&i);
|
||||
}
|
||||
IdentType::Label => {
|
||||
// We currently does not touch labels
|
||||
return i;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
i
|
||||
}
|
||||
|
||||
fn fold_module(&mut self, module: Module) -> Module {
|
||||
let module = validate!(module.fold_children_with(self));
|
||||
fn visit_mut_module(&mut self, module: &mut Module) {
|
||||
module.visit_mut_children_with(self);
|
||||
|
||||
validate!(self.apply_ops(module))
|
||||
self.apply_ops(module)
|
||||
}
|
||||
|
||||
fn fold_object_lit(&mut self, node: ObjectLit) -> ObjectLit {
|
||||
fn visit_mut_object_lit(&mut self, node: &mut ObjectLit) {
|
||||
let mut folder = Hygiene {
|
||||
current: Scope::new(ScopeKind::Block, Some(&self.current)),
|
||||
ident_type: IdentType::Ref,
|
||||
};
|
||||
let node = node.fold_children_with(&mut folder);
|
||||
node.visit_mut_children_with(&mut folder);
|
||||
|
||||
folder.apply_ops(node)
|
||||
}
|
||||
|
||||
fn fold_try_stmt(&mut self, node: TryStmt) -> TryStmt {
|
||||
TryStmt {
|
||||
span: node.span,
|
||||
block: node.block.fold_children_with(self),
|
||||
handler: node.handler.fold_with(self),
|
||||
finalizer: node.finalizer.fold_children_with(self),
|
||||
}
|
||||
fn visit_mut_try_stmt(&mut self, node: &mut TryStmt) {
|
||||
node.block.visit_mut_children_with(self);
|
||||
|
||||
node.handler.visit_mut_with(self);
|
||||
node.finalizer.visit_mut_children_with(self);
|
||||
}
|
||||
|
||||
fn fold_var_declarator(&mut self, decl: VarDeclarator) -> VarDeclarator {
|
||||
fn visit_mut_var_declarator(&mut self, decl: &mut VarDeclarator) {
|
||||
let old = self.ident_type;
|
||||
self.ident_type = IdentType::Binding;
|
||||
let name = decl.name.fold_with(self);
|
||||
decl.name.visit_mut_with(self);
|
||||
self.ident_type = old;
|
||||
|
||||
let init = decl.init.fold_with(self);
|
||||
VarDeclarator { name, init, ..decl }
|
||||
decl.init.visit_mut_with(self);
|
||||
}
|
||||
}
|
||||
|
@ -1,25 +1,24 @@
|
||||
use crate::ext::MapWithMut;
|
||||
use fxhash::FxHashMap;
|
||||
use swc_atoms::JsWord;
|
||||
use swc_common::{util::move_map::MoveMap, Spanned, SyntaxContext, DUMMY_SP};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_visit::{noop_fold_type, Fold, FoldWith};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum ScopeOp {
|
||||
Rename {
|
||||
from: (JsWord, SyntaxContext),
|
||||
to: JsWord,
|
||||
},
|
||||
use swc_ecma_utils::{ident::IdentLike, Id};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct Operations {
|
||||
pub rename: FxHashMap<Id, JsWord>,
|
||||
}
|
||||
|
||||
pub(super) struct Operator<'a>(pub &'a [ScopeOp]);
|
||||
pub(super) struct Operator<'a>(pub &'a Operations);
|
||||
|
||||
impl<'a> Fold for Operator<'a> {
|
||||
noop_fold_type!();
|
||||
impl<'a> VisitMut for Operator<'a> {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn fold_module_items(&mut self, items: Vec<ModuleItem>) -> Vec<ModuleItem> {
|
||||
fn visit_mut_module_items(&mut self, items: &mut Vec<ModuleItem>) {
|
||||
let mut stmts = Vec::with_capacity(items.len());
|
||||
|
||||
for item in items {
|
||||
for mut item in items.take() {
|
||||
let span = item.span();
|
||||
|
||||
macro_rules! export {
|
||||
@ -44,15 +43,15 @@ impl<'a> Fold for Operator<'a> {
|
||||
span,
|
||||
decl:
|
||||
Decl::Class(ClassDecl {
|
||||
ident,
|
||||
class,
|
||||
mut ident,
|
||||
mut class,
|
||||
declare,
|
||||
}),
|
||||
})) => {
|
||||
let class = class.fold_with(self);
|
||||
class.visit_mut_with(self);
|
||||
let orig_ident = ident.clone();
|
||||
match self.rename_ident(ident) {
|
||||
Ok(ident) => {
|
||||
match self.rename_ident(&mut ident) {
|
||||
Ok(..) => {
|
||||
stmts.push(ModuleItem::Stmt(Stmt::Decl(Decl::Class(ClassDecl {
|
||||
ident: ident.clone(),
|
||||
class,
|
||||
@ -60,7 +59,7 @@ impl<'a> Fold for Operator<'a> {
|
||||
}))));
|
||||
export!(orig_ident, ident);
|
||||
}
|
||||
Err(ident) => {
|
||||
Err(..) => {
|
||||
stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
span,
|
||||
decl: Decl::Class(ClassDecl {
|
||||
@ -76,15 +75,15 @@ impl<'a> Fold for Operator<'a> {
|
||||
span,
|
||||
decl:
|
||||
Decl::Fn(FnDecl {
|
||||
ident,
|
||||
function,
|
||||
mut ident,
|
||||
mut function,
|
||||
declare,
|
||||
}),
|
||||
})) => {
|
||||
let function = function.fold_with(self);
|
||||
function.visit_mut_with(self);
|
||||
let orig_ident = ident.clone();
|
||||
match self.rename_ident(ident) {
|
||||
Ok(ident) => {
|
||||
match self.rename_ident(&mut ident) {
|
||||
Ok(..) => {
|
||||
stmts.push(ModuleItem::Stmt(Stmt::Decl(Decl::Fn(FnDecl {
|
||||
ident: ident.clone(),
|
||||
function,
|
||||
@ -92,7 +91,7 @@ impl<'a> Fold for Operator<'a> {
|
||||
}))));
|
||||
export!(orig_ident, ident);
|
||||
}
|
||||
Err(ident) => {
|
||||
Err(..) => {
|
||||
stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
span,
|
||||
decl: Decl::Fn(FnDecl {
|
||||
@ -111,13 +110,13 @@ impl<'a> Fold for Operator<'a> {
|
||||
let decls = var.decls;
|
||||
|
||||
let mut renamed: Vec<ExportSpecifier> = vec![];
|
||||
let decls = decls.move_map(|decl| {
|
||||
let name = decl.name.fold_with(&mut VarFolder {
|
||||
let decls = decls.move_map(|mut decl| {
|
||||
decl.name.visit_mut_with(&mut VarFolder {
|
||||
orig: self,
|
||||
renamed: &mut renamed,
|
||||
});
|
||||
let init = decl.init.fold_with(self);
|
||||
VarDeclarator { name, init, ..decl }
|
||||
decl.init.visit_mut_with(self);
|
||||
decl
|
||||
});
|
||||
if renamed.is_empty() {
|
||||
stmts.push(ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(ExportDecl {
|
||||
@ -139,144 +138,132 @@ impl<'a> Fold for Operator<'a> {
|
||||
},
|
||||
)));
|
||||
}
|
||||
_ => stmts.push(item.fold_with(self)),
|
||||
_ => {
|
||||
item.visit_mut_with(self);
|
||||
stmts.push(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
stmts
|
||||
*items = stmts
|
||||
}
|
||||
|
||||
/// Preserve key of properties.
|
||||
fn fold_assign_pat_prop(&mut self, p: AssignPatProp) -> AssignPatProp {
|
||||
match p.value {
|
||||
Some(value) => AssignPatProp {
|
||||
value: Some(value.fold_children_with(self)),
|
||||
..p
|
||||
},
|
||||
None => p,
|
||||
fn visit_mut_assign_pat_prop(&mut self, p: &mut AssignPatProp) {
|
||||
match &mut p.value {
|
||||
Some(value) => {
|
||||
value.visit_mut_children_with(self);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_export_named_specifier(&mut self, s: ExportNamedSpecifier) -> ExportNamedSpecifier {
|
||||
fn visit_mut_export_named_specifier(&mut self, s: &mut ExportNamedSpecifier) {
|
||||
if s.exported.is_some() {
|
||||
return ExportNamedSpecifier {
|
||||
orig: s.orig.fold_with(self),
|
||||
..s
|
||||
};
|
||||
s.orig.visit_mut_with(self);
|
||||
return;
|
||||
}
|
||||
|
||||
let exported = s.orig.clone();
|
||||
|
||||
match self.rename_ident(s.orig) {
|
||||
Ok(orig) => ExportNamedSpecifier {
|
||||
exported: Some(exported),
|
||||
orig,
|
||||
..s
|
||||
},
|
||||
Err(orig) => ExportNamedSpecifier { orig, ..s },
|
||||
match self.rename_ident(&mut s.orig) {
|
||||
Ok(..) => {
|
||||
s.exported = Some(exported);
|
||||
}
|
||||
Err(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_ident(&mut self, ident: Ident) -> Ident {
|
||||
fn visit_mut_ident(&mut self, ident: &mut Ident) {
|
||||
match self.rename_ident(ident) {
|
||||
Ok(i) | Err(i) => i,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_import_named_specifier(&mut self, s: ImportNamedSpecifier) -> ImportNamedSpecifier {
|
||||
fn visit_mut_import_named_specifier(&mut self, s: &mut ImportNamedSpecifier) {
|
||||
if s.imported.is_some() {
|
||||
return ImportNamedSpecifier {
|
||||
local: s.local.fold_with(self),
|
||||
..s
|
||||
};
|
||||
s.local.visit_mut_with(self);
|
||||
return;
|
||||
}
|
||||
|
||||
let imported = s.local.clone();
|
||||
let local = self.rename_ident(s.local);
|
||||
let local = self.rename_ident(&mut s.local);
|
||||
|
||||
match local {
|
||||
Ok(local) => ImportNamedSpecifier {
|
||||
imported: Some(imported),
|
||||
local,
|
||||
..s
|
||||
},
|
||||
Err(local) => ImportNamedSpecifier { local, ..s },
|
||||
Ok(..) => {
|
||||
s.imported = Some(imported);
|
||||
}
|
||||
Err(..) => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Preserve key of properties.
|
||||
fn fold_key_value_pat_prop(&mut self, p: KeyValuePatProp) -> KeyValuePatProp {
|
||||
KeyValuePatProp {
|
||||
key: p.key.fold_with(self),
|
||||
value: p.value.fold_with(self),
|
||||
..p
|
||||
fn visit_mut_key_value_pat_prop(&mut self, p: &mut KeyValuePatProp) {
|
||||
p.key.visit_mut_with(self);
|
||||
p.value.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_key_value_prop(&mut self, p: &mut KeyValueProp) {
|
||||
p.value.visit_mut_with(self);
|
||||
}
|
||||
|
||||
fn visit_mut_member_expr(&mut self, expr: &mut MemberExpr) {
|
||||
expr.span.visit_mut_with(self);
|
||||
expr.obj.visit_mut_with(self);
|
||||
|
||||
if expr.computed {
|
||||
expr.prop.visit_mut_with(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_key_value_prop(&mut self, p: KeyValueProp) -> KeyValueProp {
|
||||
KeyValueProp {
|
||||
value: p.value.fold_with(self),
|
||||
..p
|
||||
}
|
||||
}
|
||||
fn visit_mut_object_pat_prop(&mut self, n: &mut ObjectPatProp) {
|
||||
n.visit_mut_children_with(self);
|
||||
|
||||
fn fold_member_expr(&mut self, expr: MemberExpr) -> MemberExpr {
|
||||
let span = expr.span.fold_with(self);
|
||||
let obj = expr.obj.fold_with(self);
|
||||
|
||||
let prop = if expr.computed {
|
||||
expr.prop.fold_with(self)
|
||||
} else {
|
||||
expr.prop
|
||||
};
|
||||
MemberExpr {
|
||||
span,
|
||||
obj,
|
||||
prop,
|
||||
computed: expr.computed,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_object_pat_prop(&mut self, p: ObjectPatProp) -> ObjectPatProp {
|
||||
let p = p.fold_children_with(self);
|
||||
|
||||
match p {
|
||||
ObjectPatProp::Assign(p) => match self.rename_ident(p.key.clone()) {
|
||||
Ok(renamed) => KeyValuePatProp {
|
||||
key: PropName::Ident(p.key),
|
||||
|
||||
value: Box::new(Pat::Ident(renamed)),
|
||||
}
|
||||
.into(),
|
||||
Err(_) => p.into(),
|
||||
},
|
||||
_ => p,
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_prop(&mut self, prop: Prop) -> Prop {
|
||||
match prop {
|
||||
Prop::Shorthand(i) => {
|
||||
match self.rename_ident(i.clone()) {
|
||||
Ok(renamed) => Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(Ident {
|
||||
// clear mark
|
||||
span: i.span.with_ctxt(SyntaxContext::empty()),
|
||||
..i
|
||||
}),
|
||||
value: Box::new(Expr::Ident(renamed)),
|
||||
}),
|
||||
Err(i) => Prop::Shorthand(i),
|
||||
match n {
|
||||
ObjectPatProp::Assign(p) => {
|
||||
let mut renamed = p.key.clone();
|
||||
match self.rename_ident(&mut renamed) {
|
||||
Ok(..) => {
|
||||
*n = KeyValuePatProp {
|
||||
key: PropName::Ident(p.key.take()),
|
||||
value: Box::new(Pat::Ident(renamed)),
|
||||
}
|
||||
.into();
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
_ => prop.fold_children_with(self),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn fold_prop_name(&mut self, n: PropName) -> PropName {
|
||||
fn visit_mut_prop(&mut self, prop: &mut Prop) {
|
||||
match prop {
|
||||
Prop::Shorthand(i) => {
|
||||
let mut renamed = i.clone();
|
||||
match self.rename_ident(&mut renamed) {
|
||||
Ok(..) => {
|
||||
*prop = Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(Ident {
|
||||
// clear mark
|
||||
span: i.span.with_ctxt(SyntaxContext::empty()),
|
||||
type_ann: None,
|
||||
..i.clone()
|
||||
}),
|
||||
value: Box::new(Expr::Ident(renamed)),
|
||||
})
|
||||
}
|
||||
Err(..) => {}
|
||||
}
|
||||
}
|
||||
_ => prop.visit_mut_children_with(self),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_prop_name(&mut self, n: &mut PropName) {
|
||||
match n {
|
||||
PropName::Computed(c) => PropName::Computed(c.fold_with(self)),
|
||||
_ => n,
|
||||
PropName::Computed(c) => c.visit_mut_with(self),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -286,48 +273,37 @@ struct VarFolder<'a, 'b> {
|
||||
renamed: &'a mut Vec<ExportSpecifier>,
|
||||
}
|
||||
|
||||
impl Fold for VarFolder<'_, '_> {
|
||||
noop_fold_type!();
|
||||
impl VisitMut for VarFolder<'_, '_> {
|
||||
noop_visit_mut_type!();
|
||||
|
||||
fn fold_expr(&mut self, n: Expr) -> Expr {
|
||||
n
|
||||
}
|
||||
#[inline]
|
||||
fn visit_mut_expr(&mut self, _: &mut Expr) {}
|
||||
|
||||
fn fold_ident(&mut self, i: Ident) -> Ident {
|
||||
fn visit_mut_ident(&mut self, i: &mut Ident) {
|
||||
let orig = i.clone();
|
||||
match self.orig.rename_ident(i) {
|
||||
Ok(i) => {
|
||||
Ok(..) => {
|
||||
self.renamed
|
||||
.push(ExportSpecifier::Named(ExportNamedSpecifier {
|
||||
span: i.span,
|
||||
exported: Some(orig),
|
||||
orig: i.clone(),
|
||||
}));
|
||||
i
|
||||
}
|
||||
Err(i) => i,
|
||||
Err(..) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Operator<'a> {
|
||||
/// Returns `Ok(renamed_ident)` if ident should be renamed.
|
||||
fn rename_ident(&mut self, ident: Ident) -> Result<Ident, Ident> {
|
||||
for op in self.0 {
|
||||
match *op {
|
||||
ScopeOp::Rename { ref from, ref to }
|
||||
if *from.0 == ident.sym && from.1 == ident.span.ctxt() =>
|
||||
{
|
||||
return Ok(Ident {
|
||||
// Clear mark
|
||||
span: ident.span.with_ctxt(SyntaxContext::empty()),
|
||||
sym: to.clone(),
|
||||
..ident
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
fn rename_ident(&mut self, ident: &mut Ident) -> Result<(), ()> {
|
||||
if let Some(sym) = self.0.rename.get(&ident.to_id()) {
|
||||
ident.span = ident.span.with_ctxt(SyntaxContext::empty());
|
||||
ident.sym = sym.clone();
|
||||
return Ok(());
|
||||
}
|
||||
Err(ident)
|
||||
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ use crate::tests::HygieneVisualizer;
|
||||
use std::collections::HashMap;
|
||||
use swc_common::{hygiene::*, DUMMY_SP};
|
||||
use swc_ecma_parser::Syntax;
|
||||
use swc_ecma_visit::{Fold, FoldWith};
|
||||
|
||||
struct Marker {
|
||||
map: HashMap<JsWord, Mark>,
|
||||
@ -1183,93 +1184,3 @@ fn exported_class_1() {
|
||||
export { Foo1 as Foo };",
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn issue_598() {
|
||||
// test_module(
|
||||
// |tester| {
|
||||
// let mark1 = Mark::fresh(Mark::root());
|
||||
// let mark2 = Mark::fresh(Mark::root());
|
||||
|
||||
// Ok(tester
|
||||
// .parse_module(
|
||||
// "actual1.js",
|
||||
// "export function foo() {
|
||||
// console.log(i18n(_templateObject()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// }",
|
||||
// )?
|
||||
// .fold_with(&mut OnceMarker::new(&[(
|
||||
// "_templateObject",
|
||||
// &[mark1, mark2],
|
||||
// )])))
|
||||
// },
|
||||
// "export function foo() {
|
||||
// console.log(i18n(_templateObject1()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// }",
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn issue_598_2() {
|
||||
// test_module(
|
||||
// |tester| {
|
||||
// let mark1 = Mark::fresh(Mark::root());
|
||||
// let mark2 = Mark::fresh(Mark::root());
|
||||
// let mark3 = Mark::fresh(Mark::root());
|
||||
|
||||
// Ok(tester
|
||||
// .parse_module(
|
||||
// "actual1.js",
|
||||
// "export function foo() {
|
||||
// console.log(i18n(_templateObject()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// }",
|
||||
// )?
|
||||
// .fold_with(&mut OnceMarker::new(&[(
|
||||
// "_templateObject",
|
||||
// &[mark1, mark2, mark3],
|
||||
// )])))
|
||||
// },
|
||||
// "export function foo() {
|
||||
// console.log(i18n(_templateObject1()));
|
||||
// console.log(i18n(_templateObject2()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// }",
|
||||
// );
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn issue_598_3() {
|
||||
// test_module(
|
||||
// |tester| {
|
||||
// let mark1 = Mark::fresh(Mark::root());
|
||||
// let mark2 = Mark::fresh(Mark::root());
|
||||
// let mark3 = Mark::fresh(Mark::root());
|
||||
// let mark4 = Mark::fresh(Mark::root());
|
||||
|
||||
// Ok(tester
|
||||
// .parse_module(
|
||||
// "actual1.js",
|
||||
// "export function foo() {
|
||||
// console.log(i18n(_templateObject()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// }",
|
||||
// )?
|
||||
// .fold_with(&mut OnceMarker::new(&[(
|
||||
// "_templateObject",
|
||||
// &[mark1, mark2, mark3, mark4],
|
||||
// )])))
|
||||
// },
|
||||
// "export function foo() {
|
||||
// console.log(i18n(_templateObject1()));
|
||||
// console.log(i18n(_templateObject2()));
|
||||
// console.log(i18n(_templateObject3()));
|
||||
// console.log(i18n(_templateObject()));
|
||||
// }",
|
||||
// );
|
||||
// }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1096,7 +1096,8 @@ var instance = new SomeClass({
|
||||
console.log('CORRECT FUNCTION CALLED');
|
||||
}
|
||||
});
|
||||
instance.call();"
|
||||
instance.call();",
|
||||
ok_if_code_eq
|
||||
);
|
||||
|
||||
test!(
|
||||
@ -1220,7 +1221,8 @@ export class HygieneTest {
|
||||
_defineProperty(this, 'duration', DURATION);
|
||||
this.duration = duration ?? DURATION;
|
||||
}
|
||||
}"
|
||||
}",
|
||||
ok_if_code_eq
|
||||
);
|
||||
|
||||
identical_ts!(ts_resolver_001, "type A = B;");
|
||||
|
@ -201,11 +201,11 @@ pub(crate) fn test_transform<F, P>(
|
||||
}
|
||||
|
||||
let actual = actual
|
||||
.fold_with(&mut crate::debug::validator::Validator { name: "actual-1" })
|
||||
.fold_with(&mut crate::hygiene::hygiene())
|
||||
.fold_with(&mut crate::debug::validator::Validator { name: "actual-2" })
|
||||
.fold_with(&mut crate::fixer::fixer(None))
|
||||
.fold_with(&mut crate::debug::validator::Validator { name: "actual-3" });
|
||||
.fold_with(&mut as_folder(DropSpan {
|
||||
preserve_ctxt: false,
|
||||
}));
|
||||
|
||||
if actual == expected {
|
||||
return Ok(());
|
||||
|
@ -101,6 +101,70 @@ impl Strip {
|
||||
}
|
||||
|
||||
impl Strip {
|
||||
/// Returns [Some] if the method should be called again.
|
||||
fn handle_expr<'a>(&mut self, n: &'a mut Expr) -> Vec<&'a mut Expr> {
|
||||
match n {
|
||||
Expr::Bin(BinExpr { left, right, .. }) => return vec![&mut **left, &mut **right],
|
||||
_ => {}
|
||||
}
|
||||
|
||||
n.map_with_mut(|expr| {
|
||||
let mut expr = match expr {
|
||||
Expr::TsAs(TsAsExpr { expr, .. })
|
||||
| Expr::TsNonNull(TsNonNullExpr { expr, .. })
|
||||
| Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
|
||||
| Expr::TsConstAssertion(TsConstAssertion { expr, .. })
|
||||
| Expr::TsTypeCast(TsTypeCastExpr { expr, .. }) => {
|
||||
let mut expr = *expr;
|
||||
expr.visit_mut_with(self);
|
||||
expr
|
||||
}
|
||||
_ => expr,
|
||||
};
|
||||
|
||||
let expr = match expr {
|
||||
Expr::Bin(..) => expr,
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
mut obj,
|
||||
mut prop,
|
||||
computed,
|
||||
}) => {
|
||||
obj.visit_mut_with(self);
|
||||
|
||||
let prop = if computed {
|
||||
prop.visit_mut_with(self);
|
||||
prop
|
||||
} else {
|
||||
match *prop {
|
||||
Expr::Ident(i) => Box::new(Expr::Ident(Ident {
|
||||
optional: false,
|
||||
type_ann: None,
|
||||
..i
|
||||
})),
|
||||
_ => prop,
|
||||
}
|
||||
};
|
||||
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
obj,
|
||||
prop,
|
||||
computed,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
expr.visit_mut_children_with(self);
|
||||
expr
|
||||
}
|
||||
};
|
||||
|
||||
expr
|
||||
});
|
||||
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn handle_enum<T>(&mut self, e: TsEnumDecl, stmts: &mut Vec<T>)
|
||||
where
|
||||
T: StmtLike,
|
||||
@ -577,59 +641,22 @@ impl VisitMut for Strip {
|
||||
};
|
||||
}
|
||||
|
||||
fn visit_mut_expr(&mut self, expr: &mut Expr) {
|
||||
expr.map_with_mut(|expr| {
|
||||
let mut expr = match expr {
|
||||
Expr::TsAs(TsAsExpr { expr, .. })
|
||||
| Expr::TsNonNull(TsNonNullExpr { expr, .. })
|
||||
| Expr::TsTypeAssertion(TsTypeAssertion { expr, .. })
|
||||
| Expr::TsConstAssertion(TsConstAssertion { expr, .. })
|
||||
| Expr::TsTypeCast(TsTypeCastExpr { expr, .. }) => {
|
||||
let mut expr = *expr;
|
||||
expr.visit_mut_with(self);
|
||||
expr
|
||||
}
|
||||
_ => expr,
|
||||
};
|
||||
fn visit_mut_expr(&mut self, n: &mut Expr) {
|
||||
let mut stack = vec![n];
|
||||
loop {
|
||||
let mut new_stack = vec![];
|
||||
for expr in stack {
|
||||
let res = self.handle_expr(expr);
|
||||
|
||||
let expr = match expr {
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
mut obj,
|
||||
mut prop,
|
||||
computed,
|
||||
}) => {
|
||||
obj.visit_mut_with(self);
|
||||
new_stack.extend(res)
|
||||
}
|
||||
|
||||
let prop = if computed {
|
||||
prop.visit_mut_with(self);
|
||||
prop
|
||||
} else {
|
||||
match *prop {
|
||||
Expr::Ident(i) => Box::new(Expr::Ident(Ident {
|
||||
optional: false,
|
||||
type_ann: None,
|
||||
..i
|
||||
})),
|
||||
_ => prop,
|
||||
}
|
||||
};
|
||||
if new_stack.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
Expr::Member(MemberExpr {
|
||||
span,
|
||||
obj,
|
||||
prop,
|
||||
computed,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
expr.visit_mut_children_with(self);
|
||||
expr
|
||||
}
|
||||
};
|
||||
|
||||
expr
|
||||
});
|
||||
stack = new_stack;
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_mut_ident(&mut self, i: &mut Ident) {
|
||||
|
@ -5,46 +5,22 @@ use std::{
|
||||
fmt,
|
||||
fs::{create_dir_all, remove_dir_all, OpenOptions},
|
||||
io::{self, Write},
|
||||
mem::replace,
|
||||
path::Path,
|
||||
process::Command,
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use swc_common::{
|
||||
chain, comments::SingleThreadedComments, errors::Handler, sync::Lrc, FileName, SourceMap,
|
||||
comments::SingleThreadedComments, errors::Handler, sync::Lrc, FileName, SourceMap, DUMMY_SP,
|
||||
};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::Emitter;
|
||||
use swc_ecma_parser::{error::Error, lexer::Lexer, Parser, StringInput, Syntax};
|
||||
use swc_ecma_transforms::helpers::{inject_helpers, HELPERS};
|
||||
use swc_ecma_utils::DropSpan;
|
||||
use swc_ecma_visit::{as_folder, Fold, FoldWith};
|
||||
use swc_ecma_visit::{as_folder, Fold, FoldWith, VisitMut, VisitMutWith};
|
||||
use tempfile::tempdir_in;
|
||||
|
||||
pub fn validating(name: &'static str, tr: impl Fold + 'static) -> Box<dyn Fold> {
|
||||
Box::new(chain!(
|
||||
tr,
|
||||
swc_ecma_transforms::debug::validator::Validator { name },
|
||||
))
|
||||
}
|
||||
|
||||
macro_rules! validating {
|
||||
($folder:expr) => {{
|
||||
common::validating(stringify!($folder), $folder)
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! validate {
|
||||
($e:expr) => {{
|
||||
use swc_ecma_visit::FoldWith;
|
||||
if cfg!(debug_assertions) {
|
||||
$e.fold_with(&mut swc_ecma_transforms::debug::validator::Validator {
|
||||
name: concat!(file!(), ':', line!(), ':', column!()),
|
||||
})
|
||||
} else {
|
||||
$e
|
||||
}
|
||||
}};
|
||||
}
|
||||
use testing::assert_eq;
|
||||
|
||||
pub struct Tester<'a> {
|
||||
pub cm: Lrc<SourceMap>,
|
||||
@ -127,12 +103,12 @@ impl<'a> Tester<'a> {
|
||||
res?
|
||||
};
|
||||
|
||||
let module = validate!(module)
|
||||
.fold_with(&mut tr)
|
||||
.fold_with(&mut as_folder(DropSpan {
|
||||
preserve_ctxt: true,
|
||||
}))
|
||||
.fold_with(&mut Normalizer);
|
||||
let mut module = module.fold_with(&mut tr);
|
||||
|
||||
module.visit_mut_with(&mut DropSpan {
|
||||
preserve_ctxt: true,
|
||||
});
|
||||
module.visit_mut_with(&mut Normalizer);
|
||||
|
||||
Ok(module)
|
||||
}
|
||||
@ -211,11 +187,11 @@ where
|
||||
}
|
||||
|
||||
let actual = actual
|
||||
.fold_with(&mut swc_ecma_transforms::debug::validator::Validator { name: "actual-1" })
|
||||
.fold_with(&mut swc_ecma_transforms::hygiene())
|
||||
.fold_with(&mut swc_ecma_transforms::debug::validator::Validator { name: "actual-2" })
|
||||
.fold_with(&mut swc_ecma_transforms::fixer(None))
|
||||
.fold_with(&mut swc_ecma_transforms::debug::validator::Validator { name: "actual-3" });
|
||||
.fold_with(&mut as_folder(DropSpan {
|
||||
preserve_ctxt: false,
|
||||
}));
|
||||
|
||||
if actual == expected {
|
||||
return Ok(());
|
||||
@ -401,16 +377,19 @@ impl Write for Buf {
|
||||
}
|
||||
|
||||
struct Normalizer;
|
||||
impl Fold for Normalizer {
|
||||
fn fold_pat_or_expr(&mut self, node: PatOrExpr) -> PatOrExpr {
|
||||
let node = node.fold_children_with(self);
|
||||
impl VisitMut for Normalizer {
|
||||
fn visit_mut_pat_or_expr(&mut self, node: &mut PatOrExpr) {
|
||||
node.visit_mut_children_with(self);
|
||||
|
||||
match node {
|
||||
PatOrExpr::Pat(pat) => match *pat {
|
||||
Pat::Expr(expr) => PatOrExpr::Expr(expr),
|
||||
_ => PatOrExpr::Pat(pat),
|
||||
PatOrExpr::Pat(pat) => match &mut **pat {
|
||||
Pat::Expr(e) => {
|
||||
let e = replace(e, Box::new(Expr::Invalid(Invalid { span: DUMMY_SP })));
|
||||
*node = PatOrExpr::Expr(e);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
_ => node,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,19 +17,19 @@ mod common;
|
||||
struct ParenRemover;
|
||||
impl Fold for ParenRemover {
|
||||
fn fold_expr(&mut self, expr: Expr) -> Expr {
|
||||
let expr = validate!(expr);
|
||||
let expr = expr;
|
||||
let span = expr.span();
|
||||
|
||||
let expr = expr.fold_children_with(self);
|
||||
|
||||
validate!(match expr {
|
||||
match expr {
|
||||
Expr::Paren(ParenExpr { expr, .. }) => match *expr {
|
||||
Expr::Member(e) => Expr::Member(MemberExpr { span, ..e }),
|
||||
Expr::New(e) => Expr::New(NewExpr { span, ..e }),
|
||||
_ => *expr,
|
||||
},
|
||||
_ => expr,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,10 +40,10 @@ fn syntax() -> Syntax {
|
||||
fn tr() -> impl Fold {
|
||||
chain!(
|
||||
ParenRemover,
|
||||
validating!(arrow()),
|
||||
validating!(parameters()),
|
||||
validating!(destructuring(destructuring::Config { loose: false })),
|
||||
validating!(function_name()),
|
||||
arrow(),
|
||||
parameters(),
|
||||
destructuring(destructuring::Config { loose: false }),
|
||||
function_name(),
|
||||
async_to_generator(),
|
||||
fixer(None)
|
||||
)
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,17 +1,16 @@
|
||||
[package]
|
||||
name = "swc_ecma_visit"
|
||||
version = "0.17.0"
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
documentation = "https://swc-project.github.io/rustdoc/swc_ecma_visit/"
|
||||
description = "Visitors for swc ecmascript nodes which works on stable rustc"
|
||||
documentation = "https://swc-project.github.io/rustdoc/swc_ecma_visit/"
|
||||
edition = "2018"
|
||||
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "swc_ecma_visit"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.17.1"
|
||||
|
||||
[dependencies]
|
||||
swc_atoms = { version = "0.2", path = "../../atoms" }
|
||||
swc_common = { version = "0.10.0", path = "../../common" }
|
||||
swc_ecma_ast = { version = "0.31.0", path ="../ast" }
|
||||
swc_visit = { version = "0.2.0", path ="../../visit" }
|
||||
num-bigint = { version = "0.2", features = ["serde"] }
|
||||
num-bigint = {version = "0.2", features = ["serde"]}
|
||||
swc_atoms = {version = "0.2", path = "../../atoms"}
|
||||
swc_common = {version = "0.10.0", path = "../../common"}
|
||||
swc_ecma_ast = {version = "0.31.0", path = "../ast"}
|
||||
swc_visit = {version = "0.2.0", path = "../../visit"}
|
||||
|
@ -20,11 +20,13 @@ where
|
||||
|
||||
B: Fold,
|
||||
{
|
||||
#[inline(always)]
|
||||
fn fold_module(&mut self, n: Module) -> Module {
|
||||
let n = self.first.fold_module(n);
|
||||
self.second.fold_module(n)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn fold_script(&mut self, n: Script) -> Script {
|
||||
let n = self.first.fold_script(n);
|
||||
self.second.fold_script(n)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@swc/core",
|
||||
"version": "1.2.23",
|
||||
"version": "1.2.23-alpha.1",
|
||||
"description": "Super-fast alternative for babel",
|
||||
"homepage": "https://swc-project.github.io",
|
||||
"main": "./index.js",
|
||||
@ -73,4 +73,4 @@
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/swc"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +1,19 @@
|
||||
[package]
|
||||
name = "testing"
|
||||
version = "0.10.0"
|
||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||
license = "Apache-2.0/MIT"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
documentation = "https://swc-project.github.io/rustdoc/testing/"
|
||||
description = "Testing utilities for the swc project."
|
||||
documentation = "https://swc-project.github.io/rustdoc/testing/"
|
||||
edition = "2018"
|
||||
license = "Apache-2.0/MIT"
|
||||
name = "testing"
|
||||
repository = "https://github.com/swc-project/swc.git"
|
||||
version = "0.10.1"
|
||||
|
||||
[dependencies]
|
||||
swc_common = { version = "0.10.0", path ="../common", features = ["tty-emitter"] }
|
||||
once_cell = "1"
|
||||
regex = "1"
|
||||
difference = "2"
|
||||
log = "0.4"
|
||||
env_logger = "0.7.1"
|
||||
ansi_term = "0.12.1"
|
||||
difference = "2"
|
||||
env_logger = "0.7.1"
|
||||
log = "0.4"
|
||||
once_cell = "1"
|
||||
pretty_assertions = "0.6.1"
|
||||
regex = "1"
|
||||
swc_common = {version = "0.10.0", path = "../common", features = ["tty-emitter"]}
|
||||
|
@ -3,6 +3,7 @@
|
||||
pub use self::output::{NormalizedOutput, StdErr, StdOut, TestOutput};
|
||||
use difference::Changeset;
|
||||
use once_cell::sync::Lazy;
|
||||
pub use pretty_assertions::{assert_eq, assert_ne};
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
fmt,
|
||||
|
Loading…
Reference in New Issue
Block a user