Skip to content

Commit c135f71

Browse files
authoredNov 4, 2024··
fix(es/typescript): Handle ASI hazards in fast type strip (#9707)
**Related issue:** - Closes: #9681
1 parent 1ccbbf0 commit c135f71

File tree

5 files changed

+215
-15
lines changed

5 files changed

+215
-15
lines changed
 

‎.changeset/neat-cheetahs-suffer.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
swc_fast_ts_strip: patch
3+
swc_core: patch
4+
---
5+
6+
fix(es/typescript): Handle ASI hazards in fast type strip

‎crates/swc_fast_ts_strip/src/lib.rs

+96-15
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,17 @@ impl Visit for TsStrip {
561561
}
562562

563563
'type_params: {
564+
// ```TypeScript
565+
// let f = async <
566+
// T
567+
// >(v: T) => v;
568+
// ```
569+
570+
// ```TypeScript
571+
// let f = async (
572+
//
573+
// v ) => v;
574+
// ```
564575
if let Some(tp) = &n.type_params {
565576
self.add_replacement(tp.span);
566577

@@ -673,7 +684,12 @@ impl Visit for TsStrip {
673684
return;
674685
}
675686

676-
self.strip_class_modifier(n.span.lo, n.key.span_lo());
687+
// TODO(AST): constructor can not be optional
688+
debug_assert!(!n.is_optional);
689+
690+
if n.accessibility.is_some() {
691+
self.strip_class_modifier(n.span.lo, n.key.span_lo());
692+
}
677693

678694
n.visit_children_with(self);
679695
}
@@ -684,20 +700,49 @@ impl Visit for TsStrip {
684700
return;
685701
}
686702

703+
let has_modifier = n.is_override || n.accessibility.is_some();
704+
687705
// @foo public m(): void {}
688706
let start_pos = n
689707
.function
690708
.decorators
691709
.last()
692710
.map_or(n.span.lo, |d| d.span.hi);
693711

694-
self.strip_class_modifier(start_pos, n.key.span_lo());
712+
if has_modifier {
713+
self.strip_class_modifier(start_pos, n.key.span_lo());
714+
}
695715

696716
if n.is_optional {
697717
let mark_index = self.get_next_token_index(n.key.span_hi());
698718
self.strip_optional_mark(mark_index);
699719
}
700720

721+
// It's dangerous to strip TypeScript modifiers if the key is computed, a
722+
// generator, or `in`/`instanceof` keyword. However, it is safe to do so
723+
// if the key is preceded by a `static` keyword or decorators.
724+
//
725+
// `public [foo]()`
726+
// `; [foo]()`
727+
//
728+
// `public *foo()`
729+
// `; *foo()`
730+
//
731+
// `public in()`
732+
// `; in()`
733+
if has_modifier
734+
&& !n.is_static
735+
&& n.function.decorators.is_empty()
736+
&& (n.key.is_computed()
737+
|| n.function.is_generator
738+
|| n.key
739+
.as_ident()
740+
.filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
741+
.is_some())
742+
{
743+
self.add_overwrite(start_pos, b';');
744+
}
745+
701746
n.visit_children_with(self);
702747
}
703748

@@ -707,9 +752,12 @@ impl Visit for TsStrip {
707752
return;
708753
}
709754

755+
let has_modifier = n.readonly || n.is_override || n.accessibility.is_some();
710756
let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
711757

712-
self.strip_class_modifier(start_pos, n.key.span_lo());
758+
if has_modifier {
759+
self.strip_class_modifier(start_pos, n.key.span_lo());
760+
}
713761

714762
if n.is_optional {
715763
let mark_index = self.get_next_token_index(n.key.span_hi());
@@ -720,23 +768,53 @@ impl Visit for TsStrip {
720768
self.strip_definite_mark(mark_index);
721769
}
722770

723-
if n.value.is_none() && n.key.as_ident().filter(|k| k.sym == "static").is_some() {
724-
if let Some(type_ann) = &n.type_ann {
725-
self.add_overwrite(type_ann.span.lo, b';');
771+
// It's dangerous to strip types if the key is `get`, `set`, or `static`.
772+
if n.value.is_none() {
773+
if let Some(key) = n.key.as_ident() {
774+
if matches!(key.sym.as_ref(), "get" | "set" | "static") {
775+
// `get: number`
776+
// `get; `
777+
if let Some(type_ann) = &n.type_ann {
778+
self.add_overwrite(type_ann.span.lo, b';');
779+
}
780+
}
726781
}
727782
}
728783

784+
// `private [foo]`
785+
// `; [foo]`
786+
//
787+
// `private in`
788+
// `; in`
789+
if !n.is_static
790+
&& has_modifier
791+
&& n.decorators.is_empty()
792+
&& (n.key.is_computed()
793+
|| n.key
794+
.as_ident()
795+
.filter(|k| matches!(k.sym.as_ref(), "in" | "instanceof"))
796+
.is_some())
797+
{
798+
self.add_overwrite(start_pos, b';');
799+
}
800+
729801
n.visit_children_with(self);
730802
}
731803

732804
fn visit_private_method(&mut self, n: &PrivateMethod) {
733-
let start_pos = n
734-
.function
735-
.decorators
736-
.last()
737-
.map_or(n.span.lo, |d| d.span.hi);
738-
739-
self.strip_class_modifier(start_pos, n.key.span.lo);
805+
debug_assert!(!n.is_override);
806+
debug_assert!(!n.is_abstract);
807+
808+
// Is `private #foo()` valid?
809+
if n.accessibility.is_some() {
810+
let start_pos = n
811+
.function
812+
.decorators
813+
.last()
814+
.map_or(n.span.lo, |d| d.span.hi);
815+
816+
self.strip_class_modifier(start_pos, n.key.span.lo);
817+
}
740818

741819
if n.is_optional {
742820
let mark_index = self.get_next_token_index(n.key.span.hi);
@@ -747,9 +825,12 @@ impl Visit for TsStrip {
747825
}
748826

749827
fn visit_private_prop(&mut self, n: &PrivateProp) {
750-
let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
828+
debug_assert!(!n.is_override);
751829

752-
self.strip_class_modifier(start_pos, n.key.span.lo);
830+
if n.readonly || n.accessibility.is_some() {
831+
let start_pos = n.decorators.last().map_or(n.span.lo, |d| d.span.hi);
832+
self.strip_class_modifier(start_pos, n.key.span.lo);
833+
}
753834

754835
if n.is_optional {
755836
let mark_index = self.get_next_token_index(n.key.span.hi);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class A {
2+
foo = 1
3+
; ["bar"] = 2;
4+
5+
x = 1
6+
; ["y"](){}
7+
8+
z = 1
9+
static ["w"](){}
10+
}
11+
12+
class Test {
13+
x = 1
14+
; *f() {}
15+
}
16+
17+
class Test2 {
18+
out = 'out'
19+
; in
20+
name
21+
}
22+
23+
class Test3 {
24+
out = 'out'
25+
; in
26+
name;
27+
28+
x = 'x'
29+
; instanceof
30+
String
31+
}
32+
33+
class Test4 {
34+
set;
35+
a(x) {}
36+
get;
37+
b() {}
38+
static;
39+
c(x) {}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
class A {
2+
foo = 1;
3+
["bar"] = 2;
4+
x = 1;
5+
["y"]() {}
6+
z = 1;
7+
static ["w"]() {}
8+
}
9+
class Test {
10+
x = 1;
11+
*f() {}
12+
}
13+
class Test2 {
14+
out = 'out';
15+
in;
16+
name;
17+
}
18+
class Test3 {
19+
out = 'out';
20+
in;
21+
name;
22+
x = 'x';
23+
instanceof;
24+
String;
25+
}
26+
class Test4 {
27+
set;
28+
a(x) {}
29+
get;
30+
b() {}
31+
static;
32+
c(x) {}
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class A {
2+
foo = 1
3+
private ["bar"] = 2;
4+
5+
x = 1
6+
public ["y"](){}
7+
8+
z = 1
9+
public static ["w"](){}
10+
}
11+
12+
class Test {
13+
x = 1
14+
public *f() {}
15+
}
16+
17+
class Test2 {
18+
out = 'out'
19+
public in
20+
public name
21+
}
22+
23+
class Test3 {
24+
out = 'out'
25+
public in: any
26+
name;
27+
28+
x = 'x'
29+
public instanceof: any
30+
String: any
31+
}
32+
33+
class Test4 {
34+
set: any
35+
a(x) {}
36+
get: any
37+
b() {}
38+
static: any
39+
c(x) {}
40+
}

0 commit comments

Comments
 (0)
Please sign in to comment.