Skip to content

Commit e954929

Browse files
committed
Merge pull request #5833 from Microsoft/fix4211
Error when accessing this before super
2 parents 78b5253 + bf854a7 commit e954929

File tree

52 files changed

+850
-55
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+850
-55
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7267,6 +7267,14 @@ namespace ts {
72677267
let container = getThisContainer(node, /* includeArrowFunctions */ true);
72687268
let needToCaptureLexicalThis = false;
72697269

7270+
if (container.kind === SyntaxKind.Constructor) {
7271+
const baseTypeNode = getClassExtendsHeritageClauseElement(<ClassLikeDeclaration>container.parent);
7272+
if (baseTypeNode && !(getNodeCheckFlags(container) & NodeCheckFlags.HasSeenSuperCall)) {
7273+
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
7274+
error(node, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class);
7275+
}
7276+
}
7277+
72707278
// Now skip arrow functions to get the "real" owner of 'this'.
72717279
if (container.kind === SyntaxKind.ArrowFunction) {
72727280
container = getThisContainer(container, /* includeArrowFunctions */ false);
@@ -10213,6 +10221,11 @@ namespace ts {
1021310221

1021410222
const signature = getResolvedSignature(node);
1021510223
if (node.expression.kind === SyntaxKind.SuperKeyword) {
10224+
const containgFunction = getContainingFunction(node.expression);
10225+
10226+
if (containgFunction && containgFunction.kind === SyntaxKind.Constructor) {
10227+
getNodeLinks(containgFunction).flags |= NodeCheckFlags.HasSeenSuperCall;
10228+
}
1021610229
return voidType;
1021710230
}
1021810231
if (node.kind === SyntaxKind.NewExpression) {
@@ -11830,10 +11843,6 @@ namespace ts {
1183011843
if (!superCallStatement) {
1183111844
error(node, Diagnostics.A_super_call_must_be_the_first_statement_in_the_constructor_when_a_class_contains_initialized_properties_or_has_parameter_properties);
1183211845
}
11833-
else {
11834-
// In such a required super call, it is a compile-time error for argument expressions to reference this.
11835-
markThisReferencesAsErrors(superCallStatement.expression);
11836-
}
1183711846
}
1183811847
}
1183911848
else if (baseConstructorType !== nullType) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2771,5 +2771,9 @@
27712771
"JSX element '{0}' has no corresponding closing tag.": {
27722772
"category": "Error",
27732773
"code": 17008
2774+
},
2775+
"'super' must be called before accessing 'this' in the constructor of a derived class.": {
2776+
"category": "Error",
2777+
"code": 17009
27742778
}
27752779
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2070,6 +2070,7 @@ namespace ts {
20702070
LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure
20712071
CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function
20722072
BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement
2073+
HasSeenSuperCall = 0x00080000, // Set during the binding when encounter 'super'
20732074
}
20742075

20752076
/* @internal */

tests/baselines/reference/baseCheck.errors.txt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
tests/cases/compiler/baseCheck.ts(9,18): error TS2304: Cannot find name 'loc'.
22
tests/cases/compiler/baseCheck.ts(17,53): error TS2346: Supplied parameters do not match any signature of call target.
3-
tests/cases/compiler/baseCheck.ts(17,59): error TS2332: 'this' cannot be referenced in current location.
4-
tests/cases/compiler/baseCheck.ts(18,62): error TS2332: 'this' cannot be referenced in current location.
3+
tests/cases/compiler/baseCheck.ts(17,59): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
4+
tests/cases/compiler/baseCheck.ts(18,62): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
55
tests/cases/compiler/baseCheck.ts(19,59): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
6-
tests/cases/compiler/baseCheck.ts(19,68): error TS2332: 'this' cannot be referenced in current location.
6+
tests/cases/compiler/baseCheck.ts(19,68): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
77
tests/cases/compiler/baseCheck.ts(22,9): error TS2304: Cannot find name 'x'.
88
tests/cases/compiler/baseCheck.ts(23,7): error TS2304: Cannot find name 'x'.
99
tests/cases/compiler/baseCheck.ts(26,9): error TS2304: Cannot find name 'x'.
@@ -32,15 +32,15 @@ tests/cases/compiler/baseCheck.ts(26,9): error TS2304: Cannot find name 'x'.
3232
~~~~~~~~~~~~~
3333
!!! error TS2346: Supplied parameters do not match any signature of call target.
3434
~~~~
35-
!!! error TS2332: 'this' cannot be referenced in current location.
35+
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
3636
class E extends C { constructor(public z: number) { super(0, this.z) } }
3737
~~~~
38-
!!! error TS2332: 'this' cannot be referenced in current location.
38+
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
3939
class F extends C { constructor(public z: number) { super("hello", this.z) } } // first param type
4040
~~~~~~~
4141
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
4242
~~~~
43-
!!! error TS2332: 'this' cannot be referenced in current location.
43+
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
4444

4545
function f() {
4646
if (x<10) {

tests/baselines/reference/bases.errors.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ tests/cases/compiler/bases.ts(7,17): error TS2304: Cannot find name 'any'.
44
tests/cases/compiler/bases.ts(11,7): error TS2420: Class 'C' incorrectly implements interface 'I'.
55
Property 'x' is missing in type 'C'.
66
tests/cases/compiler/bases.ts(12,5): error TS2377: Constructors for derived classes must contain a 'super' call.
7+
tests/cases/compiler/bases.ts(13,9): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
78
tests/cases/compiler/bases.ts(13,14): error TS2339: Property 'x' does not exist on type 'C'.
89
tests/cases/compiler/bases.ts(13,15): error TS1005: ';' expected.
910
tests/cases/compiler/bases.ts(13,17): error TS2304: Cannot find name 'any'.
1011
tests/cases/compiler/bases.ts(17,9): error TS2339: Property 'x' does not exist on type 'C'.
1112
tests/cases/compiler/bases.ts(18,9): error TS2339: Property 'y' does not exist on type 'C'.
1213

1314

14-
==== tests/cases/compiler/bases.ts (10 errors) ====
15+
==== tests/cases/compiler/bases.ts (11 errors) ====
1516
interface I {
1617
x;
1718
}
@@ -36,6 +37,8 @@ tests/cases/compiler/bases.ts(18,9): error TS2339: Property 'y' does not exist o
3637
~~~~~~~~~~~~~~~
3738
this.x: any;
3839
~~~~~~~~~~~~~~~~~~~~
40+
~~~~
41+
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
3942
~
4043
!!! error TS2339: Property 'x' does not exist on type 'C'.
4144
~
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [checkSuperCallBeforeThisAccessing1.ts]
2+
class Based { }
3+
class Derived extends Based {
4+
public x: number;
5+
constructor() {
6+
super();
7+
this;
8+
this.x = 10;
9+
var that = this;
10+
}
11+
}
12+
13+
//// [checkSuperCallBeforeThisAccessing1.js]
14+
var __extends = (this && this.__extends) || function (d, b) {
15+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
16+
function __() { this.constructor = d; }
17+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
18+
};
19+
var Based = (function () {
20+
function Based() {
21+
}
22+
return Based;
23+
}());
24+
var Derived = (function (_super) {
25+
__extends(Derived, _super);
26+
function Derived() {
27+
_super.call(this);
28+
this;
29+
this.x = 10;
30+
var that = this;
31+
}
32+
return Derived;
33+
}(Based));
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=== tests/cases/compiler/checkSuperCallBeforeThisAccessing1.ts ===
2+
class Based { }
3+
>Based : Symbol(Based, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 0))
4+
5+
class Derived extends Based {
6+
>Derived : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 15))
7+
>Based : Symbol(Based, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 0))
8+
9+
public x: number;
10+
>x : Symbol(x, Decl(checkSuperCallBeforeThisAccessing1.ts, 1, 29))
11+
12+
constructor() {
13+
super();
14+
>super : Symbol(Based, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 0))
15+
16+
this;
17+
>this : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 15))
18+
19+
this.x = 10;
20+
>this.x : Symbol(x, Decl(checkSuperCallBeforeThisAccessing1.ts, 1, 29))
21+
>this : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 15))
22+
>x : Symbol(x, Decl(checkSuperCallBeforeThisAccessing1.ts, 1, 29))
23+
24+
var that = this;
25+
>that : Symbol(that, Decl(checkSuperCallBeforeThisAccessing1.ts, 7, 11))
26+
>this : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing1.ts, 0, 15))
27+
}
28+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/compiler/checkSuperCallBeforeThisAccessing1.ts ===
2+
class Based { }
3+
>Based : Based
4+
5+
class Derived extends Based {
6+
>Derived : Derived
7+
>Based : Based
8+
9+
public x: number;
10+
>x : number
11+
12+
constructor() {
13+
super();
14+
>super() : void
15+
>super : typeof Based
16+
17+
this;
18+
>this : this
19+
20+
this.x = 10;
21+
>this.x = 10 : number
22+
>this.x : number
23+
>this : this
24+
>x : number
25+
>10 : number
26+
27+
var that = this;
28+
>that : this
29+
>this : this
30+
}
31+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tests/cases/compiler/checkSuperCallBeforeThisAccessing2.ts(5,9): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
2+
3+
4+
==== tests/cases/compiler/checkSuperCallBeforeThisAccessing2.ts (1 errors) ====
5+
class Based { }
6+
class Derived extends Based {
7+
public x: number;
8+
constructor() {
9+
this.x = 100;
10+
~~~~
11+
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
12+
super();
13+
this.x = 10;
14+
var that = this;
15+
}
16+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//// [checkSuperCallBeforeThisAccessing2.ts]
2+
class Based { }
3+
class Derived extends Based {
4+
public x: number;
5+
constructor() {
6+
this.x = 100;
7+
super();
8+
this.x = 10;
9+
var that = this;
10+
}
11+
}
12+
13+
//// [checkSuperCallBeforeThisAccessing2.js]
14+
var __extends = (this && this.__extends) || function (d, b) {
15+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
16+
function __() { this.constructor = d; }
17+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
18+
};
19+
var Based = (function () {
20+
function Based() {
21+
}
22+
return Based;
23+
}());
24+
var Derived = (function (_super) {
25+
__extends(Derived, _super);
26+
function Derived() {
27+
this.x = 100;
28+
_super.call(this);
29+
this.x = 10;
30+
var that = this;
31+
}
32+
return Derived;
33+
}(Based));
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [checkSuperCallBeforeThisAccessing3.ts]
2+
class Based { }
3+
class Derived extends Based {
4+
public x: number;
5+
constructor() {
6+
class innver {
7+
public y: boolean;
8+
constructor() {
9+
this.y = true;
10+
}
11+
}
12+
super();
13+
this.x = 10;
14+
var that = this;
15+
}
16+
}
17+
18+
//// [checkSuperCallBeforeThisAccessing3.js]
19+
var __extends = (this && this.__extends) || function (d, b) {
20+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
21+
function __() { this.constructor = d; }
22+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
23+
};
24+
var Based = (function () {
25+
function Based() {
26+
}
27+
return Based;
28+
}());
29+
var Derived = (function (_super) {
30+
__extends(Derived, _super);
31+
function Derived() {
32+
var innver = (function () {
33+
function innver() {
34+
this.y = true;
35+
}
36+
return innver;
37+
}());
38+
_super.call(this);
39+
this.x = 10;
40+
var that = this;
41+
}
42+
return Derived;
43+
}(Based));
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
=== tests/cases/compiler/checkSuperCallBeforeThisAccessing3.ts ===
2+
class Based { }
3+
>Based : Symbol(Based, Decl(checkSuperCallBeforeThisAccessing3.ts, 0, 0))
4+
5+
class Derived extends Based {
6+
>Derived : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing3.ts, 0, 15))
7+
>Based : Symbol(Based, Decl(checkSuperCallBeforeThisAccessing3.ts, 0, 0))
8+
9+
public x: number;
10+
>x : Symbol(x, Decl(checkSuperCallBeforeThisAccessing3.ts, 1, 29))
11+
12+
constructor() {
13+
class innver {
14+
>innver : Symbol(innver, Decl(checkSuperCallBeforeThisAccessing3.ts, 3, 19))
15+
16+
public y: boolean;
17+
>y : Symbol(y, Decl(checkSuperCallBeforeThisAccessing3.ts, 4, 22))
18+
19+
constructor() {
20+
this.y = true;
21+
>this.y : Symbol(y, Decl(checkSuperCallBeforeThisAccessing3.ts, 4, 22))
22+
>this : Symbol(innver, Decl(checkSuperCallBeforeThisAccessing3.ts, 3, 19))
23+
>y : Symbol(y, Decl(checkSuperCallBeforeThisAccessing3.ts, 4, 22))
24+
}
25+
}
26+
super();
27+
>super : Symbol(Based, Decl(checkSuperCallBeforeThisAccessing3.ts, 0, 0))
28+
29+
this.x = 10;
30+
>this.x : Symbol(x, Decl(checkSuperCallBeforeThisAccessing3.ts, 1, 29))
31+
>this : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing3.ts, 0, 15))
32+
>x : Symbol(x, Decl(checkSuperCallBeforeThisAccessing3.ts, 1, 29))
33+
34+
var that = this;
35+
>that : Symbol(that, Decl(checkSuperCallBeforeThisAccessing3.ts, 12, 11))
36+
>this : Symbol(Derived, Decl(checkSuperCallBeforeThisAccessing3.ts, 0, 15))
37+
}
38+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
=== tests/cases/compiler/checkSuperCallBeforeThisAccessing3.ts ===
2+
class Based { }
3+
>Based : Based
4+
5+
class Derived extends Based {
6+
>Derived : Derived
7+
>Based : Based
8+
9+
public x: number;
10+
>x : number
11+
12+
constructor() {
13+
class innver {
14+
>innver : innver
15+
16+
public y: boolean;
17+
>y : boolean
18+
19+
constructor() {
20+
this.y = true;
21+
>this.y = true : boolean
22+
>this.y : boolean
23+
>this : this
24+
>y : boolean
25+
>true : boolean
26+
}
27+
}
28+
super();
29+
>super() : void
30+
>super : typeof Based
31+
32+
this.x = 10;
33+
>this.x = 10 : number
34+
>this.x : number
35+
>this : this
36+
>x : number
37+
>10 : number
38+
39+
var that = this;
40+
>that : this
41+
>this : this
42+
}
43+
}

0 commit comments

Comments
 (0)