Skip to content

Commit 3e18aba

Browse files
authored
rewrite void-returning statements in constructors that capture result of super call (microsoft#11868)
* rewrite void-returning statements in constructors that capture result of super call * linter
1 parent 8ad68ad commit 3e18aba

File tree

5 files changed

+410
-4
lines changed

5 files changed

+410
-4
lines changed

src/compiler/transformers/es2015.ts

+26-4
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ namespace ts {
186186
let enclosingFunction: FunctionLikeDeclaration;
187187
let enclosingNonArrowFunction: FunctionLikeDeclaration;
188188
let enclosingNonAsyncFunctionBody: FunctionLikeDeclaration | ClassElement;
189+
let isInConstructorWithCapturedSuper: boolean;
189190

190191
/**
191192
* Used to track if we are emitting body of the converted loop
@@ -231,14 +232,17 @@ namespace ts {
231232
const savedCurrentParent = currentParent;
232233
const savedCurrentNode = currentNode;
233234
const savedConvertedLoopState = convertedLoopState;
235+
const savedIsInConstructorWithCapturedSuper = isInConstructorWithCapturedSuper;
234236
if (nodeStartsNewLexicalEnvironment(node)) {
235-
// don't treat content of nodes that start new lexical environment as part of converted loop copy
237+
// don't treat content of nodes that start new lexical environment as part of converted loop copy or constructor body
238+
isInConstructorWithCapturedSuper = false;
236239
convertedLoopState = undefined;
237240
}
238241

239242
onBeforeVisitNode(node);
240243
const visited = f(node);
241244

245+
isInConstructorWithCapturedSuper = savedIsInConstructorWithCapturedSuper;
242246
convertedLoopState = savedConvertedLoopState;
243247
enclosingFunction = savedEnclosingFunction;
244248
enclosingNonArrowFunction = savedEnclosingNonArrowFunction;
@@ -251,17 +255,31 @@ namespace ts {
251255
return visited;
252256
}
253257

258+
function returnCapturedThis(node: Node): Node {
259+
return setOriginalNode(createReturn(createIdentifier("_this")), node);
260+
}
261+
262+
function isReturnVoidStatementInConstructorWithCapturedSuper(node: Node): boolean {
263+
return isInConstructorWithCapturedSuper && node.kind === SyntaxKind.ReturnStatement && !(<ReturnStatement>node).expression;
264+
}
265+
254266
function shouldCheckNode(node: Node): boolean {
255267
return (node.transformFlags & TransformFlags.ES2015) !== 0 ||
256268
node.kind === SyntaxKind.LabeledStatement ||
257269
(isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node));
258270
}
259271

260272
function visitorWorker(node: Node): VisitResult<Node> {
261-
if (shouldCheckNode(node)) {
273+
if (isReturnVoidStatementInConstructorWithCapturedSuper(node)) {
274+
return returnCapturedThis(<ReturnStatement>node);
275+
}
276+
else if (shouldCheckNode(node)) {
262277
return visitJavaScript(node);
263278
}
264-
else if (node.transformFlags & TransformFlags.ContainsES2015) {
279+
else if (node.transformFlags & TransformFlags.ContainsES2015 || (isInConstructorWithCapturedSuper && !isExpression(node))) {
280+
// we want to dive in this branch either if node has children with ES2015 specific syntax
281+
// or we are inside constructor that captures result of the super call so all returns without expression should be
282+
// rewritten. Note: we skip expressions since returns should never appear there
265283
return visitEachChild(node, visitor, context);
266284
}
267285
else {
@@ -283,6 +301,7 @@ namespace ts {
283301
function visitNodesInConvertedLoop(node: Node): VisitResult<Node> {
284302
switch (node.kind) {
285303
case SyntaxKind.ReturnStatement:
304+
node = isReturnVoidStatementInConstructorWithCapturedSuper(node) ? returnCapturedThis(node) : node;
286305
return visitReturnStatement(<ReturnStatement>node);
287306

288307
case SyntaxKind.VariableStatement:
@@ -864,7 +883,10 @@ namespace ts {
864883
}
865884

866885
if (constructor) {
867-
const body = saveStateAndInvoke(constructor, constructor => visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset));
886+
const body = saveStateAndInvoke(constructor, constructor => {
887+
isInConstructorWithCapturedSuper = superCaptureStatus === SuperCaptureResult.ReplaceSuperCapture;
888+
return visitNodes(constructor.body.statements, visitor, isStatement, /*start*/ statementOffset);
889+
});
868890
addRange(statements, body);
869891
}
870892

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//// [constructorWithCapturedSuper.ts]
2+
let oneA: A;
3+
4+
class A {
5+
constructor() {
6+
return oneA;
7+
}
8+
}
9+
10+
class B extends A {
11+
constructor(x: number) {
12+
super();
13+
if (x === 1) {
14+
return;
15+
}
16+
while (x < 2) {
17+
return;
18+
}
19+
try {
20+
return
21+
}
22+
catch (e) {
23+
return;
24+
}
25+
finally {
26+
return;
27+
}
28+
}
29+
}
30+
31+
class C extends A {
32+
constructor(x: number) {
33+
super();
34+
for (let i = 0; i < 10; ++i) {
35+
() => i + x;
36+
if (x === 1) {
37+
return;
38+
}
39+
}
40+
}
41+
}
42+
43+
class D extends A {
44+
constructor(x: number) {
45+
super();
46+
() => {
47+
return;
48+
}
49+
function foo() {
50+
return;
51+
}
52+
}
53+
}
54+
55+
//// [constructorWithCapturedSuper.js]
56+
var __extends = (this && this.__extends) || function (d, b) {
57+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
58+
function __() { this.constructor = d; }
59+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
60+
};
61+
var oneA;
62+
var A = (function () {
63+
function A() {
64+
return oneA;
65+
}
66+
return A;
67+
}());
68+
var B = (function (_super) {
69+
__extends(B, _super);
70+
function B(x) {
71+
var _this = _super.call(this) || this;
72+
if (x === 1) {
73+
return _this;
74+
}
75+
while (x < 2) {
76+
return _this;
77+
}
78+
try {
79+
return _this;
80+
}
81+
catch (e) {
82+
return _this;
83+
}
84+
finally {
85+
return _this;
86+
}
87+
return _this;
88+
}
89+
return B;
90+
}(A));
91+
var C = (function (_super) {
92+
__extends(C, _super);
93+
function C(x) {
94+
var _this = _super.call(this) || this;
95+
var _loop_1 = function (i) {
96+
(function () { return i + x; });
97+
if (x === 1) {
98+
return { value: _this };
99+
}
100+
};
101+
for (var i = 0; i < 10; ++i) {
102+
var state_1 = _loop_1(i);
103+
if (typeof state_1 === "object")
104+
return state_1.value;
105+
}
106+
return _this;
107+
}
108+
return C;
109+
}(A));
110+
var D = (function (_super) {
111+
__extends(D, _super);
112+
function D(x) {
113+
var _this = _super.call(this) || this;
114+
(function () {
115+
return;
116+
});
117+
function foo() {
118+
return;
119+
}
120+
return _this;
121+
}
122+
return D;
123+
}(A));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
=== tests/cases/compiler/constructorWithCapturedSuper.ts ===
2+
let oneA: A;
3+
>oneA : Symbol(oneA, Decl(constructorWithCapturedSuper.ts, 0, 3))
4+
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
5+
6+
class A {
7+
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
8+
9+
constructor() {
10+
return oneA;
11+
>oneA : Symbol(oneA, Decl(constructorWithCapturedSuper.ts, 0, 3))
12+
}
13+
}
14+
15+
class B extends A {
16+
>B : Symbol(B, Decl(constructorWithCapturedSuper.ts, 6, 1))
17+
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
18+
19+
constructor(x: number) {
20+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))
21+
22+
super();
23+
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
24+
25+
if (x === 1) {
26+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))
27+
28+
return;
29+
}
30+
while (x < 2) {
31+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 9, 16))
32+
33+
return;
34+
}
35+
try {
36+
return
37+
}
38+
catch (e) {
39+
>e : Symbol(e, Decl(constructorWithCapturedSuper.ts, 20, 15))
40+
41+
return;
42+
}
43+
finally {
44+
return;
45+
}
46+
}
47+
}
48+
49+
class C extends A {
50+
>C : Symbol(C, Decl(constructorWithCapturedSuper.ts, 27, 1))
51+
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
52+
53+
constructor(x: number) {
54+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))
55+
56+
super();
57+
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
58+
59+
for (let i = 0; i < 10; ++i) {
60+
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
61+
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
62+
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
63+
64+
() => i + x;
65+
>i : Symbol(i, Decl(constructorWithCapturedSuper.ts, 32, 16))
66+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))
67+
68+
if (x === 1) {
69+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 30, 16))
70+
71+
return;
72+
}
73+
}
74+
}
75+
}
76+
77+
class D extends A {
78+
>D : Symbol(D, Decl(constructorWithCapturedSuper.ts, 39, 1))
79+
>A : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
80+
81+
constructor(x: number) {
82+
>x : Symbol(x, Decl(constructorWithCapturedSuper.ts, 42, 16))
83+
84+
super();
85+
>super : Symbol(A, Decl(constructorWithCapturedSuper.ts, 0, 12))
86+
87+
() => {
88+
return;
89+
}
90+
function foo() {
91+
>foo : Symbol(foo, Decl(constructorWithCapturedSuper.ts, 46, 9))
92+
93+
return;
94+
}
95+
}
96+
}

0 commit comments

Comments
 (0)