diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 76323f83e0892..6508ce755d0c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7328,6 +7328,50 @@ namespace ts { } } + function isSuperCallExpression(n: Node): boolean { + return n.kind === SyntaxKind.CallExpression && (n).expression.kind === SyntaxKind.SuperKeyword; + } + + function findFirstSuperCall(n: Node): Node { + if (isSuperCallExpression(n)) { + return n; + } + else if (isFunctionLike(n)) { + return undefined; + } + return forEachChild(n, findFirstSuperCall); + } + + /** + * Return a cached result if super-statement is already found. + * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor + * + * @param constructor constructor-function to look for super statement + */ + function getSuperCallInConstructor(constructor: ConstructorDeclaration): ExpressionStatement { + const links = getNodeLinks(constructor); + + // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result + if (links.hasSuperCall === undefined) { + links.superCall = findFirstSuperCall(constructor.body); + links.hasSuperCall = links.superCall ? true : false; + } + return links.superCall; + } + + /** + * Check if the given class-declaration extends null then return true. + * Otherwise, return false + * @param classDecl a class declaration to check if it extends null + */ + function classDeclarationExtendsNull(classDecl: ClassDeclaration): boolean { + const classSymbol = getSymbolOfNode(classDecl); + const classInstanceType = getDeclaredTypeOfSymbol(classSymbol); + const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType); + + return baseConstructorType === nullType; + } + function checkThisExpression(node: Node): Type { // Stop at the first arrow function so that we can // tell whether 'this' needs to be captured. @@ -7335,10 +7379,25 @@ namespace ts { let needToCaptureLexicalThis = false; if (container.kind === SyntaxKind.Constructor) { - const baseTypeNode = getClassExtendsHeritageClauseElement(container.parent); - if (baseTypeNode && !(getNodeCheckFlags(container) & NodeCheckFlags.HasSeenSuperCall)) { - // In ES6, super inside constructor of class-declaration has to precede "this" accessing - error(node, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class); + const containingClassDecl = container.parent; + const baseTypeNode = getClassExtendsHeritageClauseElement(containingClassDecl); + + // If a containing class does not have extends clause or the class extends null + // skip checking whether super statement is called before "this" accessing. + if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) { + const superCall = getSuperCallInConstructor(container); + + // We should give an error in the following cases: + // - No super-call + // - "this" is accessing before super-call. + // i.e super(this) + // this.x; super(); + // We want to make sure that super-call is done before accessing "this" so that + // "this" is not accessed as a parameter of the super-call. + if (!superCall || superCall.end > node.pos) { + // In ES6, super inside constructor of class-declaration has to precede "this" accessing + error(node, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class); + } } } @@ -10287,14 +10346,11 @@ namespace ts { checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments); const signature = getResolvedSignature(node); - if (node.expression.kind === SyntaxKind.SuperKeyword) { - const containingFunction = getContainingFunction(node.expression); - if (containingFunction && containingFunction.kind === SyntaxKind.Constructor) { - getNodeLinks(containingFunction).flags |= NodeCheckFlags.HasSeenSuperCall; - } + if (node.expression.kind === SyntaxKind.SuperKeyword) { return voidType; } + if (node.kind === SyntaxKind.NewExpression) { const declaration = signature.declaration; @@ -11875,17 +11931,15 @@ namespace ts { } // TS 1.0 spec (April 2014): 8.3.2 - // Constructors of classes with no extends clause may not contain super calls, whereas - // constructors of derived classes must contain at least one super call somewhere in their function body. + // Constructors of classes with no extends clause and constructors of classes that extends null may not contain super calls, + // whereas constructors of derived classes must contain at least one super call somewhere in their function body. const containingClassDecl = node.parent; if (getClassExtendsHeritageClauseElement(containingClassDecl)) { - const containingClassSymbol = getSymbolOfNode(containingClassDecl); - const containingClassInstanceType = getDeclaredTypeOfSymbol(containingClassSymbol); - const baseConstructorType = getBaseConstructorTypeOfClass(containingClassInstanceType); - - if (containsSuperCall(node.body)) { - if (baseConstructorType === nullType) { - error(node, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null); + const classExtendsNull = classDeclarationExtendsNull(containingClassDecl); + const superCall = getSuperCallInConstructor(node); + if (superCall) { + if (classExtendsNull) { + error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null); } // The first statement in the body of a constructor (excluding prologue directives) must be a super call @@ -11902,6 +11956,7 @@ namespace ts { if (superCallShouldBeFirst) { const statements = (node.body).statements; let superCallStatement: ExpressionStatement; + for (const statement of statements) { if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((statement).expression)) { superCallStatement = statement; @@ -11916,7 +11971,7 @@ namespace ts { } } } - else if (baseConstructorType !== nullType) { + else if (!classExtendsNull) { error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 31448c2859c60..78f441c4b8901 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -430,7 +430,6 @@ namespace ts { IntrinsicElement = IntrinsicNamedElement | IntrinsicIndexedElement, } - /* @internal */ export const enum RelationComparisonResult { Succeeded = 1, // Should be truthy @@ -2041,10 +2040,9 @@ namespace ts { LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement - HasSeenSuperCall = 0x00080000, // Set during the binding when encounter 'super' - ClassWithBodyScopedClassBinding = 0x00100000, // Decorated class that contains a binding to itself inside of the class body. - BodyScopedClassBinding = 0x00200000, // Binding to a decorated class inside of the class's body. - NeedsLoopOutParameter = 0x00400000, // Block scoped binding whose value should be explicitly copied outside of the converted loop + ClassWithBodyScopedClassBinding = 0x00080000, // Decorated class that contains a binding to itself inside of the class body. + BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body. + NeedsLoopOutParameter = 0x00200000, // Block scoped binding whose value should be explicitly copied outside of the converted loop } /* @internal */ @@ -2064,6 +2062,8 @@ namespace ts { importOnRightSide?: Symbol; // for import declarations - import that appear on the right side jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element + hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt. + superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing } export const enum TypeFlags { diff --git a/tests/baselines/reference/classExtendsNull.errors.txt b/tests/baselines/reference/classExtendsNull.errors.txt index 7bb44774826a8..905c90c42fde2 100644 --- a/tests/baselines/reference/classExtendsNull.errors.txt +++ b/tests/baselines/reference/classExtendsNull.errors.txt @@ -1,17 +1,14 @@ -tests/cases/compiler/classExtendsNull.ts(2,5): error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' +tests/cases/compiler/classExtendsNull.ts(3,9): error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' ==== tests/cases/compiler/classExtendsNull.ts (1 errors) ==== class C extends null { constructor() { - ~~~~~~~~~~~~~~~ super(); - ~~~~~~~~~~~~~~~~ + ~~~~~~~ +!!! error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' return Object.create(null); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ } - ~~~~~ -!!! error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' } class D extends null { diff --git a/tests/baselines/reference/superCallBeforeThisAccessing1.js b/tests/baselines/reference/superCallBeforeThisAccessing1.js new file mode 100644 index 0000000000000..1fc7ab39c9179 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing1.js @@ -0,0 +1,40 @@ +//// [superCallBeforeThisAccessing1.ts] +declare var Factory: any + +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + super(i); + var s = { + t: this._t + } + var i = Factory.create(s); + } +} + + +//// [superCallBeforeThisAccessing1.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base(c) { + } + return Base; +}()); +var D = (function (_super) { + __extends(D, _super); + function D() { + _super.call(this, i); + var s = { + t: this._t + }; + var i = Factory.create(s); + } + return D; +}(Base)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing1.symbols b/tests/baselines/reference/superCallBeforeThisAccessing1.symbols new file mode 100644 index 0000000000000..5a153728a73cd --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing1.symbols @@ -0,0 +1,38 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing1.ts === +declare var Factory: any +>Factory : Symbol(Factory, Decl(superCallBeforeThisAccessing1.ts, 0, 11)) + +class Base { +>Base : Symbol(Base, Decl(superCallBeforeThisAccessing1.ts, 0, 24)) + + constructor(c) { } +>c : Symbol(c, Decl(superCallBeforeThisAccessing1.ts, 3, 16)) +} +class D extends Base { +>D : Symbol(D, Decl(superCallBeforeThisAccessing1.ts, 4, 1)) +>Base : Symbol(Base, Decl(superCallBeforeThisAccessing1.ts, 0, 24)) + + private _t; +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing1.ts, 5, 22)) + + constructor() { + super(i); +>super : Symbol(Base, Decl(superCallBeforeThisAccessing1.ts, 0, 24)) +>i : Symbol(i, Decl(superCallBeforeThisAccessing1.ts, 12, 11)) + + var s = { +>s : Symbol(s, Decl(superCallBeforeThisAccessing1.ts, 9, 11)) + + t: this._t +>t : Symbol(t, Decl(superCallBeforeThisAccessing1.ts, 9, 17)) +>this._t : Symbol(_t, Decl(superCallBeforeThisAccessing1.ts, 5, 22)) +>this : Symbol(D, Decl(superCallBeforeThisAccessing1.ts, 4, 1)) +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing1.ts, 5, 22)) + } + var i = Factory.create(s); +>i : Symbol(i, Decl(superCallBeforeThisAccessing1.ts, 12, 11)) +>Factory : Symbol(Factory, Decl(superCallBeforeThisAccessing1.ts, 0, 11)) +>s : Symbol(s, Decl(superCallBeforeThisAccessing1.ts, 9, 11)) + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing1.types b/tests/baselines/reference/superCallBeforeThisAccessing1.types new file mode 100644 index 0000000000000..e947653584a21 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing1.types @@ -0,0 +1,43 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing1.ts === +declare var Factory: any +>Factory : any + +class Base { +>Base : Base + + constructor(c) { } +>c : any +} +class D extends Base { +>D : D +>Base : Base + + private _t; +>_t : any + + constructor() { + super(i); +>super(i) : void +>super : typeof Base +>i : any + + var s = { +>s : { t: any; } +>{ t: this._t } : { t: any; } + + t: this._t +>t : any +>this._t : any +>this : this +>_t : any + } + var i = Factory.create(s); +>i : any +>Factory.create(s) : any +>Factory.create : any +>Factory : any +>create : any +>s : { t: any; } + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing2.js b/tests/baselines/reference/superCallBeforeThisAccessing2.js new file mode 100644 index 0000000000000..e5acf06bbfd49 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing2.js @@ -0,0 +1,31 @@ +//// [superCallBeforeThisAccessing2.ts] +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + super(() => { this._t }); // no error. only check when this is directly accessing in constructor + } +} + + +//// [superCallBeforeThisAccessing2.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base(c) { + } + return Base; +}()); +var D = (function (_super) { + __extends(D, _super); + function D() { + var _this = this; + _super.call(this, function () { _this._t; }); // no error. only check when this is directly accessing in constructor + } + return D; +}(Base)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing2.symbols b/tests/baselines/reference/superCallBeforeThisAccessing2.symbols new file mode 100644 index 0000000000000..2df2e62354a69 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing2.symbols @@ -0,0 +1,23 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing2.ts === +class Base { +>Base : Symbol(Base, Decl(superCallBeforeThisAccessing2.ts, 0, 0)) + + constructor(c) { } +>c : Symbol(c, Decl(superCallBeforeThisAccessing2.ts, 1, 16)) +} +class D extends Base { +>D : Symbol(D, Decl(superCallBeforeThisAccessing2.ts, 2, 1)) +>Base : Symbol(Base, Decl(superCallBeforeThisAccessing2.ts, 0, 0)) + + private _t; +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing2.ts, 3, 22)) + + constructor() { + super(() => { this._t }); // no error. only check when this is directly accessing in constructor +>super : Symbol(Base, Decl(superCallBeforeThisAccessing2.ts, 0, 0)) +>this._t : Symbol(_t, Decl(superCallBeforeThisAccessing2.ts, 3, 22)) +>this : Symbol(D, Decl(superCallBeforeThisAccessing2.ts, 2, 1)) +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing2.ts, 3, 22)) + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing2.types b/tests/baselines/reference/superCallBeforeThisAccessing2.types new file mode 100644 index 0000000000000..cf0f54b624642 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing2.types @@ -0,0 +1,25 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing2.ts === +class Base { +>Base : Base + + constructor(c) { } +>c : any +} +class D extends Base { +>D : D +>Base : Base + + private _t; +>_t : any + + constructor() { + super(() => { this._t }); // no error. only check when this is directly accessing in constructor +>super(() => { this._t }) : void +>super : typeof Base +>() => { this._t } : () => void +>this._t : any +>this : this +>_t : any + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing3.errors.txt b/tests/baselines/reference/superCallBeforeThisAccessing3.errors.txt new file mode 100644 index 0000000000000..90b9b034263d4 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing3.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing3.ts(9,9): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. + + +==== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing3.ts (1 errors) ==== + class Base { + constructor(c) { } + } + class D extends Base { + private _t; + constructor() { + let x = () => { this._t }; + x(); // no error; we only check super is called before this when the container is a constructor + this._t; // error + ~~~~ +!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. + super(undefined); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/superCallBeforeThisAccessing3.js b/tests/baselines/reference/superCallBeforeThisAccessing3.js new file mode 100644 index 0000000000000..101d74e4bc724 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing3.js @@ -0,0 +1,37 @@ +//// [superCallBeforeThisAccessing3.ts] +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + let x = () => { this._t }; + x(); // no error; we only check super is called before this when the container is a constructor + this._t; // error + super(undefined); + } +} + + +//// [superCallBeforeThisAccessing3.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base(c) { + } + return Base; +}()); +var D = (function (_super) { + __extends(D, _super); + function D() { + var _this = this; + var x = function () { _this._t; }; + x(); // no error; we only check super is called before this when the container is a constructor + this._t; // error + _super.call(this, undefined); + } + return D; +}(Base)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing4.errors.txt b/tests/baselines/reference/superCallBeforeThisAccessing4.errors.txt new file mode 100644 index 0000000000000..91c3e7763f6f9 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing4.errors.txt @@ -0,0 +1,24 @@ +tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing4.ts(5,9): error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' +tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing4.ts(12,9): error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' + + +==== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing4.ts (2 errors) ==== + class D extends null { + private _t; + constructor() { + this._t; + super(); + ~~~~~~~ +!!! error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' + } + } + + class E extends null { + private _t; + constructor() { + super(); + ~~~~~~~ +!!! error TS17005: A constructor cannot contain a 'super' call when its class extends 'null' + this._t; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/superCallBeforeThisAccessing4.js b/tests/baselines/reference/superCallBeforeThisAccessing4.js new file mode 100644 index 0000000000000..9c3c5ab60c4c9 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing4.js @@ -0,0 +1,39 @@ +//// [superCallBeforeThisAccessing4.ts] +class D extends null { + private _t; + constructor() { + this._t; + super(); + } +} + +class E extends null { + private _t; + constructor() { + super(); + this._t; + } +} + +//// [superCallBeforeThisAccessing4.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var D = (function (_super) { + __extends(D, _super); + function D() { + this._t; + _super.call(this); + } + return D; +}(null)); +var E = (function (_super) { + __extends(E, _super); + function E() { + _super.call(this); + this._t; + } + return E; +}(null)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing5.js b/tests/baselines/reference/superCallBeforeThisAccessing5.js new file mode 100644 index 0000000000000..3281a3f8b55da --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing5.js @@ -0,0 +1,22 @@ +//// [superCallBeforeThisAccessing5.ts] +class D extends null { + private _t; + constructor() { + this._t; // No error + } +} + + +//// [superCallBeforeThisAccessing5.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var D = (function (_super) { + __extends(D, _super); + function D() { + this._t; // No error + } + return D; +}(null)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing5.symbols b/tests/baselines/reference/superCallBeforeThisAccessing5.symbols new file mode 100644 index 0000000000000..a7628e3b8df0f --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing5.symbols @@ -0,0 +1,15 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing5.ts === +class D extends null { +>D : Symbol(D, Decl(superCallBeforeThisAccessing5.ts, 0, 0)) + + private _t; +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing5.ts, 0, 22)) + + constructor() { + this._t; // No error +>this._t : Symbol(_t, Decl(superCallBeforeThisAccessing5.ts, 0, 22)) +>this : Symbol(D, Decl(superCallBeforeThisAccessing5.ts, 0, 0)) +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing5.ts, 0, 22)) + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing5.types b/tests/baselines/reference/superCallBeforeThisAccessing5.types new file mode 100644 index 0000000000000..2c0fc33c4dc1c --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing5.types @@ -0,0 +1,16 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing5.ts === +class D extends null { +>D : D +>null : null + + private _t; +>_t : any + + constructor() { + this._t; // No error +>this._t : any +>this : this +>_t : any + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing6.errors.txt b/tests/baselines/reference/superCallBeforeThisAccessing6.errors.txt new file mode 100644 index 0000000000000..346b2d58bec50 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing6.errors.txt @@ -0,0 +1,16 @@ +tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing6.ts(7,15): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. + + +==== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing6.ts (1 errors) ==== + class Base { + constructor(c) { } + } + class D extends Base { + private _t; + constructor() { + super(this); + ~~~~ +!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/superCallBeforeThisAccessing6.js b/tests/baselines/reference/superCallBeforeThisAccessing6.js new file mode 100644 index 0000000000000..746ce9f2791f1 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing6.js @@ -0,0 +1,30 @@ +//// [superCallBeforeThisAccessing6.ts] +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + super(this); + } +} + + +//// [superCallBeforeThisAccessing6.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base(c) { + } + return Base; +}()); +var D = (function (_super) { + __extends(D, _super); + function D() { + _super.call(this, this); + } + return D; +}(Base)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing7.errors.txt b/tests/baselines/reference/superCallBeforeThisAccessing7.errors.txt new file mode 100644 index 0000000000000..4e374fd96ea15 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing7.errors.txt @@ -0,0 +1,19 @@ +tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing7.ts(8,16): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. + + +==== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing7.ts (1 errors) ==== + class Base { + constructor(c) { } + } + class D extends Base { + private _t; + constructor() { + let x = { + j: this._t, + ~~~~ +!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. + } + super(undefined); + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/superCallBeforeThisAccessing7.js b/tests/baselines/reference/superCallBeforeThisAccessing7.js new file mode 100644 index 0000000000000..c3aa1655cf75c --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing7.js @@ -0,0 +1,36 @@ +//// [superCallBeforeThisAccessing7.ts] +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + let x = { + j: this._t, + } + super(undefined); + } +} + + +//// [superCallBeforeThisAccessing7.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base(c) { + } + return Base; +}()); +var D = (function (_super) { + __extends(D, _super); + function D() { + var x = { + j: this._t + }; + _super.call(this, undefined); + } + return D; +}(Base)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing8.js b/tests/baselines/reference/superCallBeforeThisAccessing8.js new file mode 100644 index 0000000000000..0865c4305a5ff --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing8.js @@ -0,0 +1,36 @@ +//// [superCallBeforeThisAccessing8.ts] +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + let x = { + k: super(undefined), + j: this._t, // no error + } + } +} + + +//// [superCallBeforeThisAccessing8.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base(c) { + } + return Base; +}()); +var D = (function (_super) { + __extends(D, _super); + function D() { + var x = { + k: _super.call(this, undefined), + j: this._t + }; + } + return D; +}(Base)); diff --git a/tests/baselines/reference/superCallBeforeThisAccessing8.symbols b/tests/baselines/reference/superCallBeforeThisAccessing8.symbols new file mode 100644 index 0000000000000..65341ee9fe366 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing8.symbols @@ -0,0 +1,32 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing8.ts === +class Base { +>Base : Symbol(Base, Decl(superCallBeforeThisAccessing8.ts, 0, 0)) + + constructor(c) { } +>c : Symbol(c, Decl(superCallBeforeThisAccessing8.ts, 1, 16)) +} +class D extends Base { +>D : Symbol(D, Decl(superCallBeforeThisAccessing8.ts, 2, 1)) +>Base : Symbol(Base, Decl(superCallBeforeThisAccessing8.ts, 0, 0)) + + private _t; +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing8.ts, 3, 22)) + + constructor() { + let x = { +>x : Symbol(x, Decl(superCallBeforeThisAccessing8.ts, 6, 11)) + + k: super(undefined), +>k : Symbol(k, Decl(superCallBeforeThisAccessing8.ts, 6, 17)) +>super : Symbol(Base, Decl(superCallBeforeThisAccessing8.ts, 0, 0)) +>undefined : Symbol(undefined) + + j: this._t, // no error +>j : Symbol(j, Decl(superCallBeforeThisAccessing8.ts, 7, 32)) +>this._t : Symbol(_t, Decl(superCallBeforeThisAccessing8.ts, 3, 22)) +>this : Symbol(D, Decl(superCallBeforeThisAccessing8.ts, 2, 1)) +>_t : Symbol(_t, Decl(superCallBeforeThisAccessing8.ts, 3, 22)) + } + } +} + diff --git a/tests/baselines/reference/superCallBeforeThisAccessing8.types b/tests/baselines/reference/superCallBeforeThisAccessing8.types new file mode 100644 index 0000000000000..dd92d924cebb7 --- /dev/null +++ b/tests/baselines/reference/superCallBeforeThisAccessing8.types @@ -0,0 +1,34 @@ +=== tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing8.ts === +class Base { +>Base : Base + + constructor(c) { } +>c : any +} +class D extends Base { +>D : D +>Base : Base + + private _t; +>_t : any + + constructor() { + let x = { +>x : { k: void; j: any; } +>{ k: super(undefined), j: this._t, // no error } : { k: void; j: any; } + + k: super(undefined), +>k : void +>super(undefined) : void +>super : typeof Base +>undefined : undefined + + j: this._t, // no error +>j : any +>this._t : any +>this : this +>_t : any + } + } +} + diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing1.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing1.ts new file mode 100644 index 0000000000000..e5d63f2e930f8 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing1.ts @@ -0,0 +1,15 @@ +declare var Factory: any + +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + super(i); + var s = { + t: this._t + } + var i = Factory.create(s); + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing2.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing2.ts new file mode 100644 index 0000000000000..1b0a0d541e9b1 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing2.ts @@ -0,0 +1,9 @@ +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + super(() => { this._t }); // no error. only check when this is directly accessing in constructor + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing3.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing3.ts new file mode 100644 index 0000000000000..1386998aaaec8 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing3.ts @@ -0,0 +1,12 @@ +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + let x = () => { this._t }; + x(); // no error; we only check super is called before this when the container is a constructor + this._t; // error + super(undefined); + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing4.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing4.ts new file mode 100644 index 0000000000000..9135e03b0cece --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing4.ts @@ -0,0 +1,15 @@ +class D extends null { + private _t; + constructor() { + this._t; + super(); + } +} + +class E extends null { + private _t; + constructor() { + super(); + this._t; + } +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing5.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing5.ts new file mode 100644 index 0000000000000..1782027758556 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing5.ts @@ -0,0 +1,6 @@ +class D extends null { + private _t; + constructor() { + this._t; // No error + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing6.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing6.ts new file mode 100644 index 0000000000000..8f36f2eb056bd --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing6.ts @@ -0,0 +1,9 @@ +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + super(this); + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing7.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing7.ts new file mode 100644 index 0000000000000..d40c96a60f313 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing7.ts @@ -0,0 +1,12 @@ +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + let x = { + j: this._t, + } + super(undefined); + } +} diff --git a/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing8.ts b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing8.ts new file mode 100644 index 0000000000000..5742cdab845a9 --- /dev/null +++ b/tests/cases/conformance/es6/classDeclaration/superCallBeforeThisAccessing8.ts @@ -0,0 +1,12 @@ +class Base { + constructor(c) { } +} +class D extends Base { + private _t; + constructor() { + let x = { + k: super(undefined), + j: this._t, // no error + } + } +}