Skip to content

Commit fce728e

Browse files
ajafffrbuckton
andcommitted
fix emit for delete on optional chain (microsoft#35090)
* fix emit for delete on optional chain * Apply suggestions from code review Co-Authored-By: Ron Buckton <[email protected]>
1 parent 8b83703 commit fce728e

File tree

5 files changed

+406
-16
lines changed

5 files changed

+406
-16
lines changed

src/compiler/transformers/esnext.ts

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ namespace ts {
2424
case SyntaxKind.ElementAccessExpression:
2525
case SyntaxKind.CallExpression:
2626
if (node.flags & NodeFlags.OptionalChain) {
27-
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false);
27+
const updated = visitOptionalExpression(node as OptionalChain, /*captureThisArg*/ false, /*isDelete*/ false);
2828
Debug.assertNotNode(updated, isSyntheticReference);
2929
return updated;
3030
}
@@ -34,6 +34,8 @@ namespace ts {
3434
return transformNullishCoalescingExpression(<BinaryExpression>node);
3535
}
3636
return visitEachChild(node, visitor, context);
37+
case SyntaxKind.DeleteExpression:
38+
return visitDeleteExpression(node as DeleteExpression);
3739
default:
3840
return visitEachChild(node, visitor, context);
3941
}
@@ -48,8 +50,8 @@ namespace ts {
4850
return { expression: chain.expression, chain: links };
4951
}
5052

51-
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean): Expression {
52-
const expression = visitNonOptionalExpression(node.expression, captureThisArg);
53+
function visitNonOptionalParenthesizedExpression(node: ParenthesizedExpression, captureThisArg: boolean, isDelete: boolean): Expression {
54+
const expression = visitNonOptionalExpression(node.expression, captureThisArg, isDelete);
5355
if (isSyntheticReference(expression)) {
5456
// `(a.b)` -> { expression `((_a = a).b)`, thisArg: `_a` }
5557
// `(a[b])` -> { expression `((_a = a)[b])`, thisArg: `_a` }
@@ -58,10 +60,10 @@ namespace ts {
5860
return updateParen(node, expression);
5961
}
6062

61-
function visitNonOptionalPropertyOrElementAccessExpression(node: AccessExpression, captureThisArg: boolean): Expression {
63+
function visitNonOptionalPropertyOrElementAccessExpression(node: AccessExpression, captureThisArg: boolean, isDelete: boolean): Expression {
6264
if (isOptionalChain(node)) {
6365
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
64-
return visitOptionalExpression(node, captureThisArg);
66+
return visitOptionalExpression(node, captureThisArg, isDelete);
6567
}
6668

6769
let expression: Expression = visitNode(node.expression, visitor, isExpression);
@@ -87,24 +89,24 @@ namespace ts {
8789
function visitNonOptionalCallExpression(node: CallExpression, captureThisArg: boolean): Expression {
8890
if (isOptionalChain(node)) {
8991
// If `node` is an optional chain, then it is the outermost chain of an optional expression.
90-
return visitOptionalExpression(node, captureThisArg);
92+
return visitOptionalExpression(node, captureThisArg, /*isDelete*/ false);
9193
}
9294
return visitEachChild(node, visitor, context);
9395
}
9496

95-
function visitNonOptionalExpression(node: Expression, captureThisArg: boolean): Expression {
97+
function visitNonOptionalExpression(node: Expression, captureThisArg: boolean, isDelete: boolean): Expression {
9698
switch (node.kind) {
97-
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg);
99+
case SyntaxKind.ParenthesizedExpression: return visitNonOptionalParenthesizedExpression(node as ParenthesizedExpression, captureThisArg, isDelete);
98100
case SyntaxKind.PropertyAccessExpression:
99-
case SyntaxKind.ElementAccessExpression: return visitNonOptionalPropertyOrElementAccessExpression(node as AccessExpression, captureThisArg);
101+
case SyntaxKind.ElementAccessExpression: return visitNonOptionalPropertyOrElementAccessExpression(node as AccessExpression, captureThisArg, isDelete);
100102
case SyntaxKind.CallExpression: return visitNonOptionalCallExpression(node as CallExpression, captureThisArg);
101103
default: return visitNode(node, visitor, isExpression);
102104
}
103105
}
104106

105-
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean): Expression {
107+
function visitOptionalExpression(node: OptionalChain, captureThisArg: boolean, isDelete: boolean): Expression {
106108
const { expression, chain } = flattenChain(node);
107-
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]));
109+
const left = visitNonOptionalExpression(expression, isCallChain(chain[0]), /*isDelete*/ false);
108110
const leftThisArg = isSyntheticReference(left) ? left.thisArg : undefined;
109111
let leftExpression = isSyntheticReference(left) ? left.expression : left;
110112
let capturedLeft: Expression = leftExpression;
@@ -152,11 +154,9 @@ namespace ts {
152154
setOriginalNode(rightExpression, segment);
153155
}
154156

155-
const target = createConditional(
156-
createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true),
157-
createVoidZero(),
158-
rightExpression,
159-
);
157+
const target = isDelete
158+
? createConditional(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), createTrue(), createDelete(rightExpression))
159+
: createConditional(createNotNullCondition(leftExpression, capturedLeft, /*invert*/ true), createVoidZero(), rightExpression);
160160
return thisArg ? createSyntheticReferenceExpression(target, thisArg) : target;
161161
}
162162

@@ -197,5 +197,11 @@ namespace ts {
197197
expression.kind !== SyntaxKind.ThisKeyword &&
198198
expression.kind !== SyntaxKind.SuperKeyword;
199199
}
200+
201+
function visitDeleteExpression(node: DeleteExpression) {
202+
return isOptionalChain(skipParentheses(node.expression))
203+
? setOriginalNode(visitNonOptionalExpression(node.expression, /*captureThisArg*/ false, /*isDelete*/ true), node)
204+
: updateDelete(node, visitNode(node.expression, visitor, isExpression));
205+
}
200206
}
201207
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [deleteChain.ts]
2+
declare const o1: undefined | { b: string };
3+
delete o1?.b;
4+
delete (o1?.b);
5+
6+
declare const o2: undefined | { b: { c: string } };
7+
delete o2?.b.c;
8+
delete (o2?.b.c);
9+
10+
declare const o3: { b: undefined | { c: string } };
11+
delete o3.b?.c;
12+
delete (o3.b?.c);
13+
14+
declare const o4: { b?: { c: { d?: { e: string } } } };
15+
delete o4.b?.c.d?.e;
16+
delete (o4.b?.c.d)?.e;
17+
delete (o4.b?.c.d?.e);
18+
19+
declare const o5: { b?(): { c: { d?: { e: string } } } };
20+
delete o5.b?.().c.d?.e;
21+
delete (o5.b?.().c.d?.e);
22+
23+
declare const o6: { b?: { c: { d?: { e: string } } } };
24+
delete o6.b?.['c'].d?.['e'];
25+
delete (o6.b?.['c'].d?.['e']);
26+
27+
//// [deleteChain.js]
28+
"use strict";
29+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
30+
o1 === null || o1 === void 0 ? true : delete o1.b;
31+
(o1 === null || o1 === void 0 ? true : delete o1.b);
32+
o2 === null || o2 === void 0 ? true : delete o2.b.c;
33+
(o2 === null || o2 === void 0 ? true : delete o2.b.c);
34+
(_a = o3.b) === null || _a === void 0 ? true : delete _a.c;
35+
((_b = o3.b) === null || _b === void 0 ? true : delete _b.c);
36+
(_d = (_c = o4.b) === null || _c === void 0 ? void 0 : _c.c.d) === null || _d === void 0 ? true : delete _d.e;
37+
(_f = ((_e = o4.b) === null || _e === void 0 ? void 0 : _e.c.d)) === null || _f === void 0 ? true : delete _f.e;
38+
((_h = (_g = o4.b) === null || _g === void 0 ? void 0 : _g.c.d) === null || _h === void 0 ? true : delete _h.e);
39+
(_k = (_j = o5.b) === null || _j === void 0 ? void 0 : _j.call(o5).c.d) === null || _k === void 0 ? true : delete _k.e;
40+
((_m = (_l = o5.b) === null || _l === void 0 ? void 0 : _l.call(o5).c.d) === null || _m === void 0 ? true : delete _m.e);
41+
(_p = (_o = o6.b) === null || _o === void 0 ? void 0 : _o['c'].d) === null || _p === void 0 ? true : delete _p['e'];
42+
((_r = (_q = o6.b) === null || _q === void 0 ? void 0 : _q['c'].d) === null || _r === void 0 ? true : delete _r['e']);
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
=== tests/cases/conformance/expressions/optionalChaining/delete/deleteChain.ts ===
2+
declare const o1: undefined | { b: string };
3+
>o1 : Symbol(o1, Decl(deleteChain.ts, 0, 13))
4+
>b : Symbol(b, Decl(deleteChain.ts, 0, 31))
5+
6+
delete o1?.b;
7+
>o1?.b : Symbol(b, Decl(deleteChain.ts, 0, 31))
8+
>o1 : Symbol(o1, Decl(deleteChain.ts, 0, 13))
9+
>b : Symbol(b, Decl(deleteChain.ts, 0, 31))
10+
11+
delete (o1?.b);
12+
>o1?.b : Symbol(b, Decl(deleteChain.ts, 0, 31))
13+
>o1 : Symbol(o1, Decl(deleteChain.ts, 0, 13))
14+
>b : Symbol(b, Decl(deleteChain.ts, 0, 31))
15+
16+
declare const o2: undefined | { b: { c: string } };
17+
>o2 : Symbol(o2, Decl(deleteChain.ts, 4, 13))
18+
>b : Symbol(b, Decl(deleteChain.ts, 4, 31))
19+
>c : Symbol(c, Decl(deleteChain.ts, 4, 36))
20+
21+
delete o2?.b.c;
22+
>o2?.b.c : Symbol(c, Decl(deleteChain.ts, 4, 36))
23+
>o2?.b : Symbol(b, Decl(deleteChain.ts, 4, 31))
24+
>o2 : Symbol(o2, Decl(deleteChain.ts, 4, 13))
25+
>b : Symbol(b, Decl(deleteChain.ts, 4, 31))
26+
>c : Symbol(c, Decl(deleteChain.ts, 4, 36))
27+
28+
delete (o2?.b.c);
29+
>o2?.b.c : Symbol(c, Decl(deleteChain.ts, 4, 36))
30+
>o2?.b : Symbol(b, Decl(deleteChain.ts, 4, 31))
31+
>o2 : Symbol(o2, Decl(deleteChain.ts, 4, 13))
32+
>b : Symbol(b, Decl(deleteChain.ts, 4, 31))
33+
>c : Symbol(c, Decl(deleteChain.ts, 4, 36))
34+
35+
declare const o3: { b: undefined | { c: string } };
36+
>o3 : Symbol(o3, Decl(deleteChain.ts, 8, 13))
37+
>b : Symbol(b, Decl(deleteChain.ts, 8, 19))
38+
>c : Symbol(c, Decl(deleteChain.ts, 8, 36))
39+
40+
delete o3.b?.c;
41+
>o3.b?.c : Symbol(c, Decl(deleteChain.ts, 8, 36))
42+
>o3.b : Symbol(b, Decl(deleteChain.ts, 8, 19))
43+
>o3 : Symbol(o3, Decl(deleteChain.ts, 8, 13))
44+
>b : Symbol(b, Decl(deleteChain.ts, 8, 19))
45+
>c : Symbol(c, Decl(deleteChain.ts, 8, 36))
46+
47+
delete (o3.b?.c);
48+
>o3.b?.c : Symbol(c, Decl(deleteChain.ts, 8, 36))
49+
>o3.b : Symbol(b, Decl(deleteChain.ts, 8, 19))
50+
>o3 : Symbol(o3, Decl(deleteChain.ts, 8, 13))
51+
>b : Symbol(b, Decl(deleteChain.ts, 8, 19))
52+
>c : Symbol(c, Decl(deleteChain.ts, 8, 36))
53+
54+
declare const o4: { b?: { c: { d?: { e: string } } } };
55+
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
56+
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
57+
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
58+
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
59+
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))
60+
61+
delete o4.b?.c.d?.e;
62+
>o4.b?.c.d?.e : Symbol(e, Decl(deleteChain.ts, 12, 36))
63+
>o4.b?.c.d : Symbol(d, Decl(deleteChain.ts, 12, 30))
64+
>o4.b?.c : Symbol(c, Decl(deleteChain.ts, 12, 25))
65+
>o4.b : Symbol(b, Decl(deleteChain.ts, 12, 19))
66+
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
67+
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
68+
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
69+
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
70+
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))
71+
72+
delete (o4.b?.c.d)?.e;
73+
>(o4.b?.c.d)?.e : Symbol(e, Decl(deleteChain.ts, 12, 36))
74+
>o4.b?.c.d : Symbol(d, Decl(deleteChain.ts, 12, 30))
75+
>o4.b?.c : Symbol(c, Decl(deleteChain.ts, 12, 25))
76+
>o4.b : Symbol(b, Decl(deleteChain.ts, 12, 19))
77+
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
78+
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
79+
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
80+
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
81+
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))
82+
83+
delete (o4.b?.c.d?.e);
84+
>o4.b?.c.d?.e : Symbol(e, Decl(deleteChain.ts, 12, 36))
85+
>o4.b?.c.d : Symbol(d, Decl(deleteChain.ts, 12, 30))
86+
>o4.b?.c : Symbol(c, Decl(deleteChain.ts, 12, 25))
87+
>o4.b : Symbol(b, Decl(deleteChain.ts, 12, 19))
88+
>o4 : Symbol(o4, Decl(deleteChain.ts, 12, 13))
89+
>b : Symbol(b, Decl(deleteChain.ts, 12, 19))
90+
>c : Symbol(c, Decl(deleteChain.ts, 12, 25))
91+
>d : Symbol(d, Decl(deleteChain.ts, 12, 30))
92+
>e : Symbol(e, Decl(deleteChain.ts, 12, 36))
93+
94+
declare const o5: { b?(): { c: { d?: { e: string } } } };
95+
>o5 : Symbol(o5, Decl(deleteChain.ts, 17, 13))
96+
>b : Symbol(b, Decl(deleteChain.ts, 17, 19))
97+
>c : Symbol(c, Decl(deleteChain.ts, 17, 27))
98+
>d : Symbol(d, Decl(deleteChain.ts, 17, 32))
99+
>e : Symbol(e, Decl(deleteChain.ts, 17, 38))
100+
101+
delete o5.b?.().c.d?.e;
102+
>o5.b?.().c.d?.e : Symbol(e, Decl(deleteChain.ts, 17, 38))
103+
>o5.b?.().c.d : Symbol(d, Decl(deleteChain.ts, 17, 32))
104+
>o5.b?.().c : Symbol(c, Decl(deleteChain.ts, 17, 27))
105+
>o5.b : Symbol(b, Decl(deleteChain.ts, 17, 19))
106+
>o5 : Symbol(o5, Decl(deleteChain.ts, 17, 13))
107+
>b : Symbol(b, Decl(deleteChain.ts, 17, 19))
108+
>c : Symbol(c, Decl(deleteChain.ts, 17, 27))
109+
>d : Symbol(d, Decl(deleteChain.ts, 17, 32))
110+
>e : Symbol(e, Decl(deleteChain.ts, 17, 38))
111+
112+
delete (o5.b?.().c.d?.e);
113+
>o5.b?.().c.d?.e : Symbol(e, Decl(deleteChain.ts, 17, 38))
114+
>o5.b?.().c.d : Symbol(d, Decl(deleteChain.ts, 17, 32))
115+
>o5.b?.().c : Symbol(c, Decl(deleteChain.ts, 17, 27))
116+
>o5.b : Symbol(b, Decl(deleteChain.ts, 17, 19))
117+
>o5 : Symbol(o5, Decl(deleteChain.ts, 17, 13))
118+
>b : Symbol(b, Decl(deleteChain.ts, 17, 19))
119+
>c : Symbol(c, Decl(deleteChain.ts, 17, 27))
120+
>d : Symbol(d, Decl(deleteChain.ts, 17, 32))
121+
>e : Symbol(e, Decl(deleteChain.ts, 17, 38))
122+
123+
declare const o6: { b?: { c: { d?: { e: string } } } };
124+
>o6 : Symbol(o6, Decl(deleteChain.ts, 21, 13))
125+
>b : Symbol(b, Decl(deleteChain.ts, 21, 19))
126+
>c : Symbol(c, Decl(deleteChain.ts, 21, 25))
127+
>d : Symbol(d, Decl(deleteChain.ts, 21, 30))
128+
>e : Symbol(e, Decl(deleteChain.ts, 21, 36))
129+
130+
delete o6.b?.['c'].d?.['e'];
131+
>o6.b?.['c'].d : Symbol(d, Decl(deleteChain.ts, 21, 30))
132+
>o6.b : Symbol(b, Decl(deleteChain.ts, 21, 19))
133+
>o6 : Symbol(o6, Decl(deleteChain.ts, 21, 13))
134+
>b : Symbol(b, Decl(deleteChain.ts, 21, 19))
135+
>d : Symbol(d, Decl(deleteChain.ts, 21, 30))
136+
137+
delete (o6.b?.['c'].d?.['e']);
138+
>o6.b?.['c'].d : Symbol(d, Decl(deleteChain.ts, 21, 30))
139+
>o6.b : Symbol(b, Decl(deleteChain.ts, 21, 19))
140+
>o6 : Symbol(o6, Decl(deleteChain.ts, 21, 13))
141+
>b : Symbol(b, Decl(deleteChain.ts, 21, 19))
142+
>d : Symbol(d, Decl(deleteChain.ts, 21, 30))
143+

0 commit comments

Comments
 (0)