fix(swc): Fix bugs (#2067)

swc_ecma_transforms_compat:
 - Fix optional chaining. (#2063)

node/swc:
 - Fix definition of `ImportDeclaration`. (#2059)

testing:
 - Allow using `testing` with stable `rustc`.

testing_macros:
 - Add `#[inline(never)]`.
This commit is contained in:
강동윤 2021-08-13 19:57:25 +09:00 committed by GitHub
parent 883c1ac4e4
commit 1b9584cfc0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 160 additions and 37 deletions

9
Cargo.lock generated
View File

@ -2488,7 +2488,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_compat"
version = "0.29.2"
version = "0.29.3"
dependencies = [
"arrayvec",
"fxhash",
@ -2622,9 +2622,10 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_testing"
version = "0.26.1"
version = "0.26.2"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
"serde",
"serde_json",
"swc_common",
@ -2818,7 +2819,7 @@ dependencies = [
[[package]]
name = "testing"
version = "0.12.0"
version = "0.12.1"
dependencies = [
"ansi_term 0.12.1",
"difference",
@ -2833,7 +2834,7 @@ dependencies = [
[[package]]
name = "testing_macros"
version = "0.2.1"
version = "0.2.2"
dependencies = [
"anyhow",
"glob",

View File

@ -73,7 +73,7 @@ pub struct ImportDecl {
#[serde(rename = "source")]
pub src: Str,
#[serde(rename = "typeOnly")]
#[serde(default, rename = "typeOnly")]
pub type_only: bool,
#[serde(default)]

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms_compat"
repository = "https://github.com/swc-project/swc.git"
version = "0.29.2"
version = "0.29.3"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -454,39 +454,71 @@ impl OptChaining {
},
"_obj",
);
let obj = if !is_super_access && aliased {
self.vars_with_init.push(VarDeclarator {
span: obj_span,
definite: false,
name: Pat::Ident(this_obj.clone().into()),
init: Some(obj),
});
Box::new(Expr::Ident(this_obj.clone()))
} else {
obj
};
let i = private_ident!(obj_span, "ref");
let obj_expr = if !is_super_access && aliased {
self.vars_without_init.push(VarDeclarator {
span: obj_span,
definite: false,
name: Pat::Ident(i.clone().into()),
name: Pat::Ident(this_obj.clone().into()),
init: None,
});
match *obj {
Expr::Member(
obj
@
MemberExpr {
obj: ExprOrSuper::Expr(..),
..
},
) => Box::new(Expr::Member(MemberExpr {
span: obj.span,
obj: Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(Box::new(Pat::Ident(
this_obj.clone().into(),
))),
right: obj.obj.expect_expr(),
})
.as_obj(),
prop: obj.prop,
computed: obj.computed,
})),
_ => Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
op: op!("="),
left: PatOrExpr::Pat(Box::new(Pat::Ident(
this_obj.clone().into(),
))),
right: obj,
})),
}
} else {
obj
};
let tmp = private_ident!(obj_span, "ref");
self.vars_without_init.push(VarDeclarator {
span: obj_span,
definite: false,
name: Pat::Ident(tmp.clone().into()),
init: None,
});
(
Box::new(Expr::Assign(AssignExpr {
span: DUMMY_SP,
left: PatOrExpr::Pat(Box::new(Pat::Ident(i.clone().into()))),
left: PatOrExpr::Pat(Box::new(Pat::Ident(tmp.clone().into()))),
op: op!("="),
right: obj,
right: obj_expr,
})),
Box::new(Expr::Ident(i.clone())),
Box::new(Expr::Ident(tmp.clone())),
Box::new(Expr::Call(CallExpr {
span,
callee: ExprOrSuper::Expr(Box::new(Expr::Member(MemberExpr {
span: DUMMY_SP,
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(i.clone()))),
obj: ExprOrSuper::Expr(Box::new(Expr::Ident(tmp.clone()))),
prop: Box::new(Expr::Ident(Ident::new("call".into(), span))),
computed: false,
}))),

View File

@ -1,6 +1,7 @@
use std::{fs::read_to_string, path::PathBuf};
use swc_ecma_parser::{Syntax, TsConfig};
use swc_ecma_transforms_compat::es2020::optional_chaining;
use swc_ecma_transforms_testing::{test, test_exec};
use swc_ecma_transforms_testing::{compare_stdout, test, test_exec};
use swc_ecma_visit::Fold;
fn tr(_: ()) -> impl Fold {
@ -298,7 +299,7 @@ foo?.bar()?.()
"#,
r#"
var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9;
var ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, _obj, ref9;
foo === null || foo === void 0 ? void 0 : foo(foo);
foo === null || foo === void 0 ? void 0 : foo.bar();
(ref = foo.bar) === null || ref === void 0 ? void 0 : ref.call(foo, foo.bar, false);
@ -309,8 +310,7 @@ foo === null || foo === void 0 ? void 0 : (ref2 = foo()) === null || ref2 === vo
(ref4 = foo.bar) === null || ref4 === void 0 ? void 0 : (ref5 = ref4.call(foo)) === null || ref5 === void 0 ? void 0 : ref5.baz;
foo === null || foo === void 0 ? void 0 : (ref6 = foo.bar) === null || ref6 === void 0 ? void 0 : ref6.call(foo).baz;
foo === null || foo === void 0 ? void 0 : (ref7 = foo.bar) === null || ref7 === void 0 ? void 0 : (ref8 = ref7.call(foo)) === null || ref8 === void 0 ? void 0 : ref8.baz;
var _obj = foo === null || foo === void 0 ? void 0 : foo.bar();
(ref9 = _obj) === null || ref9 === void 0 ? void 0 : ref9.call(_obj);"#
(ref9 = _obj = foo === null || foo === void 0 ? void 0 : foo.bar()) === null || ref9 === void 0 ? void 0 : ref9.call(_obj);"#
);
// general_unary_exec
@ -696,11 +696,11 @@ test!(
const patch = PATCHES.get(ident)?.();
",
"
var ref;
var _obj, ref;
const PATCHES = new Map();
const ident = \"foo\";
var _obj = PATCHES.get(ident);
const patch = (ref = _obj) === null || ref === void 0 ? void 0 : ref.call(_obj);
const patch = (ref = _obj = PATCHES.get(ident)) === null || ref === void 0 ? void 0 : \
ref.call(_obj);
"
);
@ -718,11 +718,18 @@ test!(
"
function bug() {
const arrowFn = (arg)=>{
var ref;
var _object = this.object[arg];
return (ref = _object) === null || ref === void 0 ? void 0 : ref.call(_object);
var _object, ref;
return (ref = (_object = this.object)[arg]) === null || ref === void 0 ? void 0 : \
ref.call(_object);
};
}
bug();
"
);
#[testing::fixture("tests/fixture/opt-chain/**/exec.js")]
fn exec(input: PathBuf) {
let src = read_to_string(&input).unwrap();
compare_stdout(Default::default(), |_| optional_chaining(), &src);
}

View File

@ -0,0 +1,7 @@
const myVar = {
target: {
value: "ABC"
}
}
console.log(myVar.target.value.toLowerCase?.())

View File

@ -0,0 +1,5 @@
const myVar = {
value: "ABC"
}
console.log(myVar.value.toLowerCase?.())

View File

@ -6,12 +6,13 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms_testing"
repository = "https://github.com/swc-project/swc.git"
version = "0.26.1"
version = "0.26.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
ansi_term = "0.12.1"
anyhow = "1"
serde = "1"
serde_json = "1"
swc_common = {version = "0.11.0", path = "../../../common"}

View File

@ -1,4 +1,5 @@
use ansi_term::Color;
use anyhow::{bail, Context, Error};
use serde::de::DeserializeOwned;
use std::{
env,
@ -16,7 +17,7 @@ use swc_common::{
};
use swc_ecma_ast::{Pat, *};
use swc_ecma_codegen::Emitter;
use swc_ecma_parser::{error::Error, lexer::Lexer, Parser, StringInput, Syntax};
use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax};
use swc_ecma_transforms_base::{
fixer,
helpers::{inject_helpers, HELPERS},
@ -93,7 +94,7 @@ impl<'a> Tester<'a> {
op: F,
) -> Result<T, ()>
where
F: FnOnce(&mut Parser<Lexer<StringInput>>) -> Result<T, Error>,
F: FnOnce(&mut Parser<Lexer<StringInput>>) -> Result<T, swc_ecma_parser::error::Error>,
{
let fm = self
.cm
@ -234,7 +235,7 @@ impl VisitMut for RegeneratorHandler {
}
}
fn make_tr<F, P>(_: &'static str, op: F, tester: &mut Tester<'_>) -> impl Fold
fn make_tr<F, P>(op: F, tester: &mut Tester<'_>) -> impl Fold
where
F: FnOnce(&mut Tester<'_>) -> P,
P: Fold,
@ -266,7 +267,7 @@ pub fn test_transform<F, P>(
println!("----- Actual -----");
let tr = make_tr("actual", tr, tester);
let tr = make_tr(tr, tester);
let actual = tester.apply_transform(tr, "input.js", syntax, input)?;
match ::std::env::var("PRINT_HYGIENE") {
@ -348,13 +349,49 @@ macro_rules! test {
};
}
pub fn exec_tr<F, P>(test_name: &'static str, syntax: Syntax, tr: F, input: &str)
/// Execute `node` for `input` and ensure that it prints same output after
/// transformation.
pub fn compare_stdout<F, P>(syntax: Syntax, tr: F, input: &str)
where
F: FnOnce(&mut Tester<'_>) -> P,
P: Fold,
{
Tester::run(|tester| {
let tr = make_tr(test_name, tr, tester);
let tr = make_tr(tr, tester);
let module = tester.apply_transform(tr, "input.js", syntax, input)?;
let mut module = module
.fold_with(&mut hygiene::hygiene())
.fold_with(&mut fixer::fixer(Some(&tester.comments)));
let src_without_helpers = tester.print(&module, &tester.comments.clone());
module = module.fold_with(&mut inject_helpers());
let transfomred_src = tester.print(&module, &tester.comments.clone());
println!(
"\t>>>>> Orig <<<<<\n{}\n\t>>>>> Code <<<<<\n{}",
input, src_without_helpers
);
let expected = stdout_of(&input).unwrap();
let actual = stdout_of(&transfomred_src).unwrap();
assert_eq!(expected, actual);
Ok(())
})
}
/// Execute `jest` after transpiling `input` using `tr`.
pub fn exec_tr<F, P>(test_name: &str, syntax: Syntax, tr: F, input: &str)
where
F: FnOnce(&mut Tester<'_>) -> P,
P: Fold,
{
Tester::run(|tester| {
let tr = make_tr(tr, tester);
let module = tester.apply_transform(
tr,
@ -442,6 +479,24 @@ where
})
}
fn stdout_of(code: &str) -> Result<String, Error> {
let actual_output = Command::new("node")
.arg("-e")
.arg(&code)
.output()
.context("failed to execute output of minifier")?;
if !actual_output.status.success() {
bail!(
"failed to execute:\n{}\n{}",
String::from_utf8_lossy(&actual_output.stdout),
String::from_utf8_lossy(&actual_output.stderr)
)
}
Ok(String::from_utf8_lossy(&actual_output.stdout).to_string())
}
/// Test transformation.
#[macro_export]
macro_rules! test_exec {

View File

@ -1449,6 +1449,8 @@ export interface ExportDeclaration extends Node, HasSpan {
export interface ImportDeclaration extends Node, HasSpan {
type: "ImportDeclaration";
typeOnly?: boolean;
specifiers: ImportSpecifier[];
source: StringLiteral;

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "testing"
repository = "https://github.com/swc-project/swc.git"
version = "0.12.0"
version = "0.12.1"
[dependencies]
ansi_term = "0.12.1"

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "testing_macros"
repository = "https://github.com/swc-project/swc.git"
version = "0.2.1"
version = "0.2.2"
[lib]
proc-macro = true

View File

@ -159,6 +159,7 @@ pub fn expand(callee: &Ident, attr: Config) -> Result<Vec<ItemFn>, Error> {
},
{
#[test]
#[inline(never)]
#[ignore]
fn test_ident() {
callee(::std::path::PathBuf::from(path_str));

View File

@ -1,5 +1,3 @@
#![feature(test)]
pub use self::output::{NormalizedOutput, StdErr, StdOut, TestOutput};
use difference::Changeset;
use once_cell::sync::Lazy;

View File

@ -0,0 +1,7 @@
const myVar = {
target: {
value: "ABC"
}
}
console.log(myVar.target.value.toLowerCase?.());

View File

@ -0,0 +1,7 @@
var _value, ref;
var myVar = {
target: {
value: "ABC"
}
};
console.log((ref = (_value = myVar.target.value).toLowerCase) === null || ref === void 0 ? void 0 : ref.call(_value));