Skip to content

Commit 1a87e76

Browse files
authored
fix(es/parser, es/codegen): Handle trailing empty slots in array patterns (#9992)
**Related issue:** - Closes #9921
1 parent afe21b5 commit 1a87e76

26 files changed

+162
-37
lines changed

Diff for: .changeset/violet-apes-enjoy.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
swc_core: patch
3+
swc_ecma_codegen: patch
4+
swc_ecma_minifier: patch
5+
swc_ecma_parser: patch
6+
---
7+
8+
fix(es/parser, es/codegen): Handle trailing empty slots in array patterns
+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
{
2+
"jsc": {
3+
"parser": {
4+
"syntax": "ecmascript",
5+
"jsx": false
6+
},
7+
"target": "es2022",
8+
"loose": false,
9+
"minify": {
10+
"compress": {
11+
"arguments": false,
12+
"arrows": true,
13+
"booleans": true,
14+
"booleans_as_integers": false,
15+
"collapse_vars": true,
16+
"comparisons": true,
17+
"computed_props": true,
18+
"conditionals": true,
19+
"dead_code": true,
20+
"directives": true,
21+
"drop_console": false,
22+
"drop_debugger": true,
23+
"evaluate": true,
24+
"expression": false,
25+
"hoist_funs": false,
26+
"hoist_props": true,
27+
"hoist_vars": false,
28+
"if_return": true,
29+
"join_vars": true,
30+
"keep_classnames": false,
31+
"keep_fargs": true,
32+
"keep_fnames": false,
33+
"keep_infinity": false,
34+
"loops": true,
35+
"negate_iife": true,
36+
"properties": true,
37+
"reduce_funcs": false,
38+
"reduce_vars": false,
39+
"side_effects": true,
40+
"switches": true,
41+
"typeofs": true,
42+
"unsafe": false,
43+
"unsafe_arrows": false,
44+
"unsafe_comps": false,
45+
"unsafe_Function": false,
46+
"unsafe_math": false,
47+
"unsafe_symbols": false,
48+
"unsafe_methods": false,
49+
"unsafe_proto": false,
50+
"unsafe_regexp": false,
51+
"unsafe_undefined": false,
52+
"unused": true,
53+
"const_to_let": true,
54+
"pristine_globals": true
55+
}
56+
}
57+
},
58+
"module": {
59+
"type": "es6"
60+
},
61+
"minify": false,
62+
"isModule": false
63+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let [x, , ] = {
2+
[Symbol.iterator]: function*() {
3+
console.log(1);
4+
yield 1;
5+
console.log(2);
6+
}};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let [x, , ] = {
2+
[Symbol.iterator]: function*() {
3+
console.log(1), yield 1, console.log(2);
4+
}
5+
};
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"jsc": {
3+
"parser": {
4+
"syntax": "ecmascript",
5+
"jsx": false
6+
},
7+
"target": "es2022",
8+
"experimental": {
9+
"disableBuiltinTransformsForInternalTesting": true,
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
let [x, , ] = {
2+
[Symbol.iterator]: function*() {
3+
console.log(1);
4+
yield 1;
5+
console.log(2);
6+
}};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
let [x, , ] = {
2+
[Symbol.iterator]: function*() {
3+
console.log(1);
4+
yield 1;
5+
console.log(2);
6+
}
7+
};

Diff for: crates/swc/tests/tsc-references/destructuringVariableDeclaration1ES5.1.normal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ var _ref4 = {
8484
f5: 0
8585
}
8686
]
87-
}, _ref_f = _sliced_to_array(_ref4.f, 3), f1 = _ref_f[0], f2 = _ref_f[1], _ref_f_ = _ref_f[2], f4 = _ref_f_.f3, f5 = _ref_f_.f5;
87+
}, _ref_f = _sliced_to_array(_ref4.f, 4), f1 = _ref_f[0], f2 = _ref_f[1], _ref_f_ = _ref_f[2], f4 = _ref_f_.f3, f5 = _ref_f_.f5;
8888
// When a destructuring variable declaration, binding property, or binding element specifies
8989
// an initializer expression, the type of the initializer expression is required to be assignable
9090
// to the widened form of the type associated with the destructuring variable declaration, binding property, or binding element.

Diff for: crates/swc/tests/tsc-references/destructuringVariableDeclaration1ES5.2.minified.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ var _ref_e = _sliced_to_array([
3434
f3: 4,
3535
f5: 0
3636
}
37-
], 3)), _ref_f_ = (_ref_f[0], _ref_f[1], _ref_f[2]);
37+
], 4)), _ref_f_ = (_ref_f[0], _ref_f[1], _ref_f[2]);
3838
_ref_f_.f3, _ref_f_.f5;

Diff for: crates/swc/tests/tsc-references/destructuringVariableDeclaration1ES5iterable.1.normal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ var _ref4 = {
8484
f5: 0
8585
}
8686
]
87-
}, _ref_f = _sliced_to_array(_ref4.f, 3), f1 = _ref_f[0], f2 = _ref_f[1], _ref_f_ = _ref_f[2], f4 = _ref_f_.f3, f5 = _ref_f_.f5;
87+
}, _ref_f = _sliced_to_array(_ref4.f, 4), f1 = _ref_f[0], f2 = _ref_f[1], _ref_f_ = _ref_f[2], f4 = _ref_f_.f3, f5 = _ref_f_.f5;
8888
// When a destructuring variable declaration, binding property, or binding element specifies
8989
// an initializer expression, the type of the initializer expression is required to be assignable
9090
// to the widened form of the type associated with the destructuring variable declaration, binding property, or binding element.

Diff for: crates/swc/tests/tsc-references/destructuringVariableDeclaration1ES5iterable.2.minified.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,5 @@ var _ref_e = _sliced_to_array([
3434
f3: 4,
3535
f5: 0
3636
}
37-
], 3)), _ref_f_ = (_ref_f[0], _ref_f[1], _ref_f[2]);
37+
], 4)), _ref_f_ = (_ref_f[0], _ref_f[1], _ref_f[2]);
3838
_ref_f_.f3, _ref_f_.f5;

Diff for: crates/swc/tests/tsc-references/destructuringVariableDeclaration1ES6.1.normal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ var { e: [e1, e2, e3 = {
8989
}
9090
]
9191
};
92-
var { f: [f1, f2, { f3: f4, f5 }] } = {
92+
var { f: [f1, f2, { f3: f4, f5 }, , ] } = {
9393
f: [
9494
1,
9595
2,

Diff for: crates/swc/tests/tsc-references/destructuringVariableDeclaration1ES6.2.minified.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ var { a1, a2 } = {
6060
b4: 0
6161
}
6262
]
63-
}, { f: [f1, f2, { f3: f4, f5 }] } = {
63+
}, { f: [f1, f2, { f3: f4, f5 }, , ] } = {
6464
f: [
6565
1,
6666
2,

Diff for: crates/swc_ecma_codegen/src/lib.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -2910,11 +2910,14 @@ where
29102910
srcmap!(node, true);
29112911

29122912
punct!("[");
2913-
self.emit_list(
2914-
node.span(),
2915-
Some(&node.elems),
2916-
ListFormat::ArrayBindingPatternElements,
2917-
)?;
2913+
2914+
let mut format = ListFormat::ArrayBindingPatternElements;
2915+
2916+
if let Some(None) = node.elems.last() {
2917+
format |= ListFormat::ForceTrailingComma;
2918+
}
2919+
2920+
self.emit_list(node.span(), Some(&node.elems), format)?;
29182921
punct!("]");
29192922
if node.optional {
29202923
punct!("?");

Diff for: crates/swc_ecma_codegen/tests/sourcemap.rs

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ static IGNORED_PASS_TESTS: &[&str] = &[
8484
"d9eb39b11bc766f4.js",
8585
"f9888fa1a1e366e7.js",
8686
"78cf02220fb0937c.js",
87+
"5e7ca8611aaa4d53.js",
8788
// TODO(kdy1): Non-ascii char count
8889
"58cb05d17f7ec010.js",
8990
"4d2c7020de650d40.js",
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
let[a]=1;
1+
let[a,,]=1;
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
let [a] = 1;
1+
let [a, , ] = 1;

Diff for: crates/swc_ecma_minifier/tests/fixture/issues/8670/output.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const [, , c, , ] = [
1+
const [, , c, , , ] = [
22
1,
33
2,
44
3,

Diff for: crates/swc_ecma_minifier/tests/terser/compress/destructuring/destructuring_arrays/output.mangleOnly.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@
88
let [d, [x, a]] = dd;
99
}var [d, x] = a;
1010
var [d, [x, a]] = dd;
11-
var [, [], , , r] = xx;
12-
var [, , t] = xxx;
11+
var [, [, , , , , ], , , r] = xx;
12+
var [, , t, , ] = xxx;
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
var [] = a;
1+
var [, , , , ] = a;
22
var [, , r] = c;
3-
var [v, ] = e;
3+
var [v, , ] = e;

Diff for: crates/swc_ecma_minifier/tests/terser/compress/parameters/destructuring_arguments_2/output.mangleOnly.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
(function([]) {});
22
(function({}) {});
3-
(function([]) {});
4-
(function([n, { b: o }]) {});
3+
(function([, , , , , ]) {});
4+
(function([n, { b: o }]) {});
55
(function([...n]) {});
6-
(function({ x: n }) {});
6+
(function({ x: n }) {});
77
class n {
8-
*method({ [thrower()]: n } = {}) {}
8+
*method({ [thrower()]: n } = {}) {}
99
}
10-
(function(n, o, t, c, [{ e: [...f] , }, ]) {})(1, 2, 3, 4, [
10+
(function(n, o, t, c, [{ e: [...f] }]) {})(1, 2, 3, 4, [
1111
{
1212
e: [
1313
1,

Diff for: crates/swc_ecma_parser/src/parser/pat.rs

+19-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//! 13.3.3 Destructuring Binding Patterns
2-
use std::iter;
32
43
use swc_common::Spanned;
54

@@ -102,22 +101,19 @@ impl<I: Tokens> Parser<I> {
102101
assert_and_bump!(self, '[');
103102

104103
let mut elems = Vec::new();
105-
let mut comma = 0;
104+
106105
let mut rest_span = Span::default();
107106

108107
while !eof!(self) && !is!(self, ']') {
109-
if !rest_span.is_dummy() {
110-
self.emit_err(rest_span, SyntaxError::NonLastRestParam);
111-
}
112-
113108
if eat!(self, ',') {
114-
comma += 1;
109+
elems.push(None);
115110
continue;
116111
}
117-
if comma > 0 {
118-
elems.extend(iter::repeat(None).take(comma));
119-
comma = 0;
112+
113+
if !rest_span.is_dummy() {
114+
self.emit_err(rest_span, SyntaxError::NonLastRestParam);
120115
}
116+
121117
let start = cur_pos!(self);
122118

123119
let mut is_rest = false;
@@ -1057,6 +1053,19 @@ mod tests {
10571053
);
10581054
}
10591055

1056+
#[test]
1057+
fn array_pat_empty_end() {
1058+
assert_eq_ignore_span!(
1059+
array_pat("[a, ,]"),
1060+
Pat::Array(ArrayPat {
1061+
span,
1062+
optional: false,
1063+
elems: vec![Some(Pat::Ident(ident("a").into())), None,],
1064+
type_ann: None
1065+
})
1066+
);
1067+
}
1068+
10601069
#[test]
10611070
fn array_binding_pattern_tail() {
10621071
assert_eq_ignore_span!(

Diff for: crates/swc_ecma_parser/tests/tsc/destructuringVariableDeclaration1ES5.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,8 @@
18811881
],
18821882
"optional": false,
18831883
"typeAnnotation": null
1884-
}
1884+
},
1885+
null
18851886
],
18861887
"optional": false,
18871888
"typeAnnotation": null

Diff for: crates/swc_ecma_parser/tests/tsc/destructuringVariableDeclaration1ES5iterable.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,8 @@
18811881
],
18821882
"optional": false,
18831883
"typeAnnotation": null
1884-
}
1884+
},
1885+
null
18851886
],
18861887
"optional": false,
18871888
"typeAnnotation": null

Diff for: crates/swc_ecma_parser/tests/tsc/destructuringVariableDeclaration1ES6.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -1881,7 +1881,8 @@
18811881
],
18821882
"optional": false,
18831883
"typeAnnotation": null
1884-
}
1884+
},
1885+
null
18851886
],
18861887
"optional": false,
18871888
"typeAnnotation": null

Diff for: crates/swc_ecma_parser/tests/tsc/emptyAssignmentPatterns01_ES5.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@
157157
"start": 78,
158158
"end": 81
159159
},
160-
"elements": [],
160+
"elements": [
161+
null
162+
],
161163
"optional": false,
162164
"typeAnnotation": null
163165
},

0 commit comments

Comments
 (0)