Fix class hygiene issue related to class properties (#892)

swc_ecma_transforms:
  - fixed hygiene bug of class_properties pass
This commit is contained in:
강동윤 2020-07-24 22:02:29 +09:00 committed by GitHub
parent 5fe625ea7a
commit c7a5d5fef9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 105 additions and 4 deletions

View File

@ -1354,6 +1354,9 @@ impl<'a> Emitter<'a> {
} else { } else {
// TODO: span // TODO: span
self.wr.write_symbol(ident.span, &ident.sym)?; self.wr.write_symbol(ident.span, &ident.sym)?;
if ident.optional {
punct!("?");
}
if let Some(ty) = &ident.type_ann { if let Some(ty) = &ident.type_ann {
punct!(":"); punct!(":");

View File

@ -17,6 +17,7 @@ use test::{
}; };
use testing::{NormalizedOutput, StdErr}; use testing::{NormalizedOutput, StdErr};
#[path = "common/mod.rs"]
mod common; mod common;
const IGNORED_PASS_TESTS: &[&str] = &[ const IGNORED_PASS_TESTS: &[&str] = &[

View File

@ -21,6 +21,7 @@ use test::{
use testing::StdErr; use testing::StdErr;
use walkdir::WalkDir; use walkdir::WalkDir;
#[path = "common/mod.rs"]
mod common; mod common;
fn add_test<F: FnOnce() + Send + 'static>( fn add_test<F: FnOnce() + Send + 'static>(

View File

@ -539,8 +539,21 @@ impl ClassProperties {
used_names, used_names,
}; };
// Handle collisions // Handle collisions like
//
// var foo = "bar";
//
// class Foo {
// bar = foo;
// static bar = baz;
//
// constructor() {
// var foo = "foo";
// var baz = "baz";
// }
// }
let body = c.body.fold_with(&mut folder); let body = c.body.fold_with(&mut folder);
let params = c.params.fold_with(&mut folder); let params = c.params.fold_with(&mut folder);
Constructor { body, params, ..c } Constructor { body, params, ..c }
}) })

View File

@ -1,8 +1,9 @@
use swc_atoms::JsWord; use swc_atoms::JsWord;
use swc_common::Mark; use swc_common::Mark;
use swc_ecma_ast::*; use swc_ecma_ast::*;
use swc_ecma_visit::{Fold, Node, Visit, VisitWith}; use swc_ecma_visit::{Fold, FoldWith, Node, Visit, VisitWith};
/// Used to rename **binding** identifiers in constructor.
pub(super) struct UsedNameRenamer<'a> { pub(super) struct UsedNameRenamer<'a> {
pub mark: Mark, pub mark: Mark,
pub used_names: &'a [JsWord], pub used_names: &'a [JsWord],
@ -11,6 +12,13 @@ pub(super) struct UsedNameRenamer<'a> {
noop_fold_type!(UsedNameRenamer<'_>); noop_fold_type!(UsedNameRenamer<'_>);
impl<'a> Fold for UsedNameRenamer<'a> { impl<'a> Fold for UsedNameRenamer<'a> {
fn fold_expr(&mut self, e: Expr) -> Expr {
match e {
Expr::Ident(..) => e,
_ => e.fold_children_with(self),
}
}
fn fold_ident(&mut self, ident: Ident) -> Ident { fn fold_ident(&mut self, ident: Ident) -> Ident {
if self.used_names.contains(&ident.sym) { if self.used_names.contains(&ident.sym) {
return Ident { return Ident {
@ -20,6 +28,21 @@ impl<'a> Fold for UsedNameRenamer<'a> {
} }
ident ident
} }
fn fold_member_expr(&mut self, e: MemberExpr) -> MemberExpr {
if e.computed {
MemberExpr {
obj: e.obj.fold_with(self),
prop: e.prop.fold_with(self),
..e
}
} else {
MemberExpr {
obj: e.obj.fold_with(self),
..e
}
}
}
} }
pub(super) struct UsedNameCollector<'a> { pub(super) struct UsedNameCollector<'a> {

View File

@ -289,6 +289,27 @@ impl<'a> Fold for Resolver<'a> {
ClassMethod { key, function, ..m } ClassMethod { key, function, ..m }
} }
fn fold_class_prop(&mut self, p: ClassProp) -> ClassProp {
let decorators = p.decorators.fold_with(self);
let old = self.ident_type;
self.ident_type = IdentType::Binding;
let key = p.key.fold_with(self);
self.ident_type = old;
let old = self.ident_type;
self.ident_type = IdentType::Ref;
let value = p.value.fold_with(self);
self.ident_type = old;
ClassProp {
decorators,
key,
value,
..p
}
}
fn fold_constructor(&mut self, c: Constructor) -> Constructor { fn fold_constructor(&mut self, c: Constructor) -> Constructor {
let old = self.ident_type; let old = self.ident_type;
self.ident_type = IdentType::Binding; self.ident_type = IdentType::Binding;

View File

@ -1,10 +1,13 @@
use super::*; use super::*;
use crate::{ use crate::{
compat::es2015::{block_scoping, destructuring, Classes}, compat::{
es2015::{block_scoping, destructuring, Classes},
es2020::class_properties,
},
modules::common_js::common_js, modules::common_js::common_js,
}; };
use swc_common::chain; use swc_common::chain;
use swc_ecma_parser::{EsConfig, Syntax}; use swc_ecma_parser::{EsConfig, Syntax, TsConfig};
fn tr() -> impl Fold { fn tr() -> impl Fold {
chain!(resolver(), block_scoping()) chain!(resolver(), block_scoping())
@ -17,6 +20,13 @@ fn syntax() -> Syntax {
}) })
} }
fn ts() -> Syntax {
Syntax::Typescript(TsConfig {
decorators: true,
..Default::default()
})
}
macro_rules! identical { macro_rules! identical {
($name:ident, $src:literal) => { ($name:ident, $src:literal) => {
test!(syntax(), |_| tr(), $name, $src, $src); test!(syntax(), |_| tr(), $name, $src, $src);
@ -1127,3 +1137,32 @@ identical!(
} }
});" });"
); );
test!(
ts(),
|_| chain!(resolver(), class_properties()),
issue_890_1,
"const DURATION = 1000
export class HygieneTest {
private readonly duration: number = DURATION
constructor(duration?: number) {
this.duration = duration ?? DURATION
}
getDuration() {
return this.duration
}
}",
"const DURATION = 1000;
export class HygieneTest {
getDuration() {
return this.duration;
}
constructor(duration?: number){
_defineProperty(this, 'duration', DURATION);
this.duration = duration ?? DURATION;
}
}"
);