Skip to content

Commit 43fc19c

Browse files
authored
Emit statements before super (#36417)
* Emit statements before super When statements come before super, Typescript's emit is incorrect, whether there is an error or not. This change preserves statements that come before super whether there is an error or not. Here is the case with no errors: ```ts class Test extends Array { p: number constructor() { console.log("p is initialised in the constructor below super()") super() this.p = 1 } } ``` Notice that `p` is manually initialised in the constructor after `super()` instead of at the property declaration. * Update baselines Parameter properties in the error case now move below the super call. This is an improvement because it means the code is more likely to execute correctly. * remove outdated comments
1 parent 9811090 commit 43fc19c

22 files changed

+816
-12
lines changed

src/compiler/transformers/utilities.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,10 +294,12 @@ namespace ts {
294294
return index;
295295
}
296296

297-
const statement = statements[index];
298-
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCall((<ExpressionStatement>statement).expression)) {
299-
result.push(visitNode(statement, visitor, isStatement));
300-
return index + 1;
297+
const superIndex = findIndex(statements, s => isExpressionStatement(s) && isSuperCall(s.expression), index);
298+
if (superIndex > -1) {
299+
for (let i = index; i <= superIndex; i++) {
300+
result.push(visitNode(statements[i], visitor, isStatement));
301+
}
302+
return superIndex + 1;
301303
}
302304

303305
return index;

tests/baselines/reference/classUpdateTests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,9 @@ var K = /** @class */ (function (_super) {
214214
__extends(K, _super);
215215
function K(p1) {
216216
var _this = this;
217-
_this.p1 = p1;
218217
var i = 0;
219218
_this = _super.call(this) || this;
219+
_this.p1 = p1;
220220
return _this;
221221
}
222222
return K;
@@ -234,9 +234,9 @@ var M = /** @class */ (function (_super) {
234234
__extends(M, _super);
235235
function M(p1) {
236236
var _this = this;
237-
_this.p1 = p1;
238237
var i = 0;
239238
_this = _super.call(this) || this;
239+
_this.p1 = p1;
240240
return _this;
241241
}
242242
return M;

tests/baselines/reference/derivedClassParameterProperties.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,9 +128,9 @@ var Derived2 = /** @class */ (function (_super) {
128128
__extends(Derived2, _super);
129129
function Derived2(y) {
130130
var _this = this;
131-
_this.y = y;
132131
var a = 1;
133132
_this = _super.call(this) || this; // error
133+
_this.y = y;
134134
return _this;
135135
}
136136
return Derived2;
@@ -149,9 +149,9 @@ var Derived4 = /** @class */ (function (_super) {
149149
__extends(Derived4, _super);
150150
function Derived4(y) {
151151
var _this = this;
152-
_this.a = 1;
153152
var b = 2;
154153
_this = _super.call(this) || this; // error
154+
_this.a = 1;
155155
return _this;
156156
}
157157
return Derived4;
@@ -181,10 +181,10 @@ var Derived7 = /** @class */ (function (_super) {
181181
__extends(Derived7, _super);
182182
function Derived7(y) {
183183
var _this = this;
184-
_this.a = 1;
185184
_this.a = 3;
186185
_this.b = 3;
187186
_this = _super.call(this) || this; // error
187+
_this.a = 1;
188188
return _this;
189189
}
190190
return Derived7;
@@ -210,10 +210,10 @@ var Derived9 = /** @class */ (function (_super) {
210210
__extends(Derived9, _super);
211211
function Derived9(y) {
212212
var _this = this;
213-
_this.a = 1;
214213
_this.a = 3;
215214
_this.b = 3;
216215
_this = _super.call(this) || this; // error
216+
_this.a = 1;
217217
return _this;
218218
}
219219
return Derived9;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//// [emitCodeBeforeSuperCall.ts]
2+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
3+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
4+
// doesn't need to emit any code for initialisation because it's already
5+
// part of the user code
6+
7+
class Base {
8+
}
9+
class Sub extends Base {
10+
// @ts-ignore
11+
constructor(public p: number) {
12+
console.log('hi');
13+
super();
14+
}
15+
field = 0;
16+
}
17+
18+
class Test extends Base {
19+
prop: number;
20+
// @ts-ignore
21+
constructor(public p: number) {
22+
1; // Any statements here break it
23+
super();
24+
this.prop = 1;
25+
}
26+
}
27+
28+
29+
//// [emitCodeBeforeSuperCall.js]
30+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
31+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
32+
// doesn't need to emit any code for initialisation because it's already
33+
// part of the user code
34+
class Base {
35+
}
36+
class Sub extends Base {
37+
// @ts-ignore
38+
constructor(p) {
39+
console.log('hi');
40+
super();
41+
this.p = p;
42+
this.field = 0;
43+
}
44+
}
45+
class Test extends Base {
46+
// @ts-ignore
47+
constructor(p) {
48+
1; // Any statements here break it
49+
super();
50+
this.p = p;
51+
this.prop = 1;
52+
}
53+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
=== tests/cases/conformance/classes/constructorDeclarations/superCalls/emitCodeBeforeSuperCall.ts ===
2+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
3+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
4+
// doesn't need to emit any code for initialisation because it's already
5+
// part of the user code
6+
7+
class Base {
8+
>Base : Symbol(Base, Decl(emitCodeBeforeSuperCall.ts, 0, 0))
9+
}
10+
class Sub extends Base {
11+
>Sub : Symbol(Sub, Decl(emitCodeBeforeSuperCall.ts, 6, 1))
12+
>Base : Symbol(Base, Decl(emitCodeBeforeSuperCall.ts, 0, 0))
13+
14+
// @ts-ignore
15+
constructor(public p: number) {
16+
>p : Symbol(Sub.p, Decl(emitCodeBeforeSuperCall.ts, 9, 16))
17+
18+
console.log('hi');
19+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
20+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
21+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
22+
23+
super();
24+
>super : Symbol(Base, Decl(emitCodeBeforeSuperCall.ts, 0, 0))
25+
}
26+
field = 0;
27+
>field : Symbol(Sub.field, Decl(emitCodeBeforeSuperCall.ts, 12, 5))
28+
}
29+
30+
class Test extends Base {
31+
>Test : Symbol(Test, Decl(emitCodeBeforeSuperCall.ts, 14, 1))
32+
>Base : Symbol(Base, Decl(emitCodeBeforeSuperCall.ts, 0, 0))
33+
34+
prop: number;
35+
>prop : Symbol(Test.prop, Decl(emitCodeBeforeSuperCall.ts, 16, 25))
36+
37+
// @ts-ignore
38+
constructor(public p: number) {
39+
>p : Symbol(Test.p, Decl(emitCodeBeforeSuperCall.ts, 19, 16))
40+
41+
1; // Any statements here break it
42+
super();
43+
>super : Symbol(Base, Decl(emitCodeBeforeSuperCall.ts, 0, 0))
44+
45+
this.prop = 1;
46+
>this.prop : Symbol(Test.prop, Decl(emitCodeBeforeSuperCall.ts, 16, 25))
47+
>this : Symbol(Test, Decl(emitCodeBeforeSuperCall.ts, 14, 1))
48+
>prop : Symbol(Test.prop, Decl(emitCodeBeforeSuperCall.ts, 16, 25))
49+
}
50+
}
51+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
=== tests/cases/conformance/classes/constructorDeclarations/superCalls/emitCodeBeforeSuperCall.ts ===
2+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
3+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
4+
// doesn't need to emit any code for initialisation because it's already
5+
// part of the user code
6+
7+
class Base {
8+
>Base : Base
9+
}
10+
class Sub extends Base {
11+
>Sub : Sub
12+
>Base : Base
13+
14+
// @ts-ignore
15+
constructor(public p: number) {
16+
>p : number
17+
18+
console.log('hi');
19+
>console.log('hi') : void
20+
>console.log : (message?: any, ...optionalParams: any[]) => void
21+
>console : Console
22+
>log : (message?: any, ...optionalParams: any[]) => void
23+
>'hi' : "hi"
24+
25+
super();
26+
>super() : void
27+
>super : typeof Base
28+
}
29+
field = 0;
30+
>field : number
31+
>0 : 0
32+
}
33+
34+
class Test extends Base {
35+
>Test : Test
36+
>Base : Base
37+
38+
prop: number;
39+
>prop : number
40+
41+
// @ts-ignore
42+
constructor(public p: number) {
43+
>p : number
44+
45+
1; // Any statements here break it
46+
>1 : 1
47+
48+
super();
49+
>super() : void
50+
>super : typeof Base
51+
52+
this.prop = 1;
53+
>this.prop = 1 : 1
54+
>this.prop : number
55+
>this : this
56+
>prop : number
57+
>1 : 1
58+
}
59+
}
60+
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//// [emitCodeBeforeSuperCall2.ts]
2+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
3+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
4+
// doesn't need to emit any code for initialisation because it's already
5+
// part of the user code
6+
7+
8+
class BaseA {
9+
public constructor(public x: number) { }
10+
}
11+
class DerivedA extends BaseA {
12+
constructor(public x: number) { super(x); }
13+
}
14+
15+
16+
//// [emitCodeBeforeSuperCall2.js]
17+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
18+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
19+
// doesn't need to emit any code for initialisation because it's already
20+
// part of the user code
21+
var __extends = (this && this.__extends) || (function () {
22+
var extendStatics = function (d, b) {
23+
extendStatics = Object.setPrototypeOf ||
24+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
25+
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
26+
return extendStatics(d, b);
27+
};
28+
return function (d, b) {
29+
extendStatics(d, b);
30+
function __() { this.constructor = d; }
31+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
32+
};
33+
})();
34+
var BaseA = /** @class */ (function () {
35+
function BaseA(x) {
36+
this.x = x;
37+
}
38+
return BaseA;
39+
}());
40+
var DerivedA = /** @class */ (function (_super) {
41+
__extends(DerivedA, _super);
42+
function DerivedA(x) {
43+
var _this = this;
44+
_this.x = x;
45+
_this = _super.call(this, x) || this;
46+
return _this;
47+
}
48+
return DerivedA;
49+
}(BaseA));
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/conformance/classes/constructorDeclarations/superCalls/emitCodeBeforeSuperCall2.ts ===
2+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
3+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
4+
// doesn't need to emit any code for initialisation because it's already
5+
// part of the user code
6+
7+
8+
class BaseA {
9+
>BaseA : Symbol(BaseA, Decl(emitCodeBeforeSuperCall2.ts, 0, 0))
10+
11+
public constructor(public x: number) { }
12+
>x : Symbol(BaseA.x, Decl(emitCodeBeforeSuperCall2.ts, 7, 23))
13+
}
14+
class DerivedA extends BaseA {
15+
>DerivedA : Symbol(DerivedA, Decl(emitCodeBeforeSuperCall2.ts, 8, 1))
16+
>BaseA : Symbol(BaseA, Decl(emitCodeBeforeSuperCall2.ts, 0, 0))
17+
18+
constructor(public x: number) { super(x); }
19+
>x : Symbol(DerivedA.x, Decl(emitCodeBeforeSuperCall2.ts, 10, 16))
20+
>super : Symbol(BaseA, Decl(emitCodeBeforeSuperCall2.ts, 0, 0))
21+
>x : Symbol(x, Decl(emitCodeBeforeSuperCall2.ts, 10, 16))
22+
}
23+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/conformance/classes/constructorDeclarations/superCalls/emitCodeBeforeSuperCall2.ts ===
2+
// TODO: With false, master is correct for `Test` but incorrect for `Sub`.
3+
// `Test` is correct because classic emit doesn't emit for definition and `Test`
4+
// doesn't need to emit any code for initialisation because it's already
5+
// part of the user code
6+
7+
8+
class BaseA {
9+
>BaseA : BaseA
10+
11+
public constructor(public x: number) { }
12+
>x : number
13+
}
14+
class DerivedA extends BaseA {
15+
>DerivedA : DerivedA
16+
>BaseA : BaseA
17+
18+
constructor(public x: number) { super(x); }
19+
>x : number
20+
>super(x) : void
21+
>super : typeof BaseA
22+
>x : number
23+
}
24+

0 commit comments

Comments
 (0)