Skip to content

Commit

Permalink
always set NodeCheckFlags when checking super expression
Browse files Browse the repository at this point in the history
  • Loading branch information
vladima committed Aug 2, 2015
1 parent a4a1a51 commit 24c8a8e
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 80 deletions.
144 changes: 77 additions & 67 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6436,98 +6436,108 @@ namespace ts {
let classType = classDeclaration && <InterfaceType>getDeclaredTypeOfSymbol(getSymbolOfNode(classDeclaration));
let baseClassType = classType && getBaseTypes(classType)[0];

let container = getSuperContainer(node, /*includeFunctions*/ true);
let needToCaptureLexicalThis = false;

if (!isCallExpression) {
// adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting
while (container && container.kind === SyntaxKind.ArrowFunction) {
container = getSuperContainer(container, /*includeFunctions*/ true);
needToCaptureLexicalThis = languageVersion < ScriptTarget.ES6;
}
}

let canUseSuperExpression = isLegalUsageOfSuperExpression(container);
let nodeCheckFlag: NodeCheckFlags = 0;

// always set NodeCheckFlags for 'super' expression node
if (canUseSuperExpression) {
if ((container.flags & NodeFlags.Static) || isCallExpression) {
nodeCheckFlag = NodeCheckFlags.SuperStatic;
}
else {
nodeCheckFlag = NodeCheckFlags.SuperInstance;
}

getNodeLinks(node).flags |= nodeCheckFlag;

if (needToCaptureLexicalThis) {
// call expressions are allowed only in constructors so they should always capture correct 'this'
// super property access expressions can also appear in arrow functions -
// in this case they should also use correct lexical this
captureLexicalThis(node.parent, container);
}
}

if (!baseClassType) {
if (!classDeclaration || !getClassExtendsHeritageClauseElement(classDeclaration)) {
error(node, Diagnostics.super_can_only_be_referenced_in_a_derived_class);
}
return unknownType;
}

if (!canUseSuperExpression) {
if (container && container.kind === SyntaxKind.ComputedPropertyName) {
error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name);
}
else if (isCallExpression) {
error(node, Diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors);
}
else {
error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class);
}

return unknownType;
}

if (container.kind === SyntaxKind.Constructor && isInConstructorArgumentInitializer(node, container)) {
// issue custom error message for super property access in constructor arguments (to be aligned with old compiler)
error(node, Diagnostics.super_cannot_be_referenced_in_constructor_arguments);
return unknownType;
}

return nodeCheckFlag === NodeCheckFlags.SuperStatic
? getBaseConstructorTypeOfClass(classType)
: baseClassType;

function isLegalUsageOfSuperExpression(container: Node): boolean {
if (!container) {
return false;
}

let container = getSuperContainer(node, /*includeFunctions*/ true);

if (container) {
let canUseSuperExpression = false;
let needToCaptureLexicalThis: boolean;
if (isCallExpression) {
// TS 1.0 SPEC (April 2014): 4.8.1
// Super calls are only permitted in constructors of derived classes
canUseSuperExpression = container.kind === SyntaxKind.Constructor;
return container.kind === SyntaxKind.Constructor;
}
else {
// TS 1.0 SPEC (April 2014)
// 'super' property access is allowed
// - In a constructor, instance member function, instance member accessor, or instance member variable initializer where this references a derived class instance
// - In a static member function or static member accessor

// super property access might appear in arrow functions with arbitrary deep nesting
needToCaptureLexicalThis = false;
while (container && container.kind === SyntaxKind.ArrowFunction) {
container = getSuperContainer(container, /*includeFunctions*/ true);
needToCaptureLexicalThis = languageVersion < ScriptTarget.ES6;
}

// topmost container must be something that is directly nested in the class declaration
if (container && isClassLike(container.parent)) {
if (container.flags & NodeFlags.Static) {
canUseSuperExpression =
container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor;
return container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor;
}
else {
canUseSuperExpression =
container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor ||
container.kind === SyntaxKind.PropertyDeclaration ||
container.kind === SyntaxKind.PropertySignature ||
container.kind === SyntaxKind.Constructor;
return container.kind === SyntaxKind.MethodDeclaration ||
container.kind === SyntaxKind.MethodSignature ||
container.kind === SyntaxKind.GetAccessor ||
container.kind === SyntaxKind.SetAccessor ||
container.kind === SyntaxKind.PropertyDeclaration ||
container.kind === SyntaxKind.PropertySignature ||
container.kind === SyntaxKind.Constructor;
}
}
}

if (canUseSuperExpression) {
let returnType: Type;

if ((container.flags & NodeFlags.Static) || isCallExpression) {
getNodeLinks(node).flags |= NodeCheckFlags.SuperStatic;
returnType = getBaseConstructorTypeOfClass(classType);
}
else {
getNodeLinks(node).flags |= NodeCheckFlags.SuperInstance;
returnType = baseClassType;
}

if (container.kind === SyntaxKind.Constructor && isInConstructorArgumentInitializer(node, container)) {
// issue custom error message for super property access in constructor arguments (to be aligned with old compiler)
error(node, Diagnostics.super_cannot_be_referenced_in_constructor_arguments);
returnType = unknownType;
}

if (!isCallExpression && needToCaptureLexicalThis) {
// call expressions are allowed only in constructors so they should always capture correct 'this'
// super property access expressions can also appear in arrow functions -
// in this case they should also use correct lexical this
captureLexicalThis(node.parent, container);
}

return returnType;
}
}

if (container && container.kind === SyntaxKind.ComputedPropertyName) {
error(node, Diagnostics.super_cannot_be_referenced_in_a_computed_property_name);
}
else if (isCallExpression) {
error(node, Diagnostics.Super_calls_are_not_permitted_outside_constructors_or_in_nested_functions_inside_constructors);
}
else {
error(node, Diagnostics.super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class);
}

return unknownType;

return false;
}
}

// Return contextual type of parameter or undefined if no contextual type is available
Expand Down
12 changes: 6 additions & 6 deletions tests/baselines/reference/errorSuperPropertyAccess.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,14 @@ var __extends = (this && this.__extends) || function (d, b) {
//super property access in instance member accessor(get and set) of class with no base type
var NoBase = (function () {
function NoBase() {
this.m = _super.prototype;
this.n = _super.hasOwnProperty.call(this, '');
var a = _super.prototype;
var b = _super.hasOwnProperty.call(this, '');
this.m = _super.prototype.prototype;
this.n = _super.prototype.hasOwnProperty.call(this, '');
var a = _super.prototype.prototype;
var b = _super.prototype.hasOwnProperty.call(this, '');
}
NoBase.prototype.fn = function () {
var a = _super.prototype;
var b = _super.hasOwnProperty.call(this, '');
var a = _super.prototype.prototype;
var b = _super.prototype.hasOwnProperty.call(this, '');
};
//super static property access in static member function of class with no base type
//super static property access in static member accessor(get and set) of class with no base type
Expand Down
4 changes: 2 additions & 2 deletions tests/baselines/reference/parserSuperExpression1.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var C = (function () {
function C() {
}
C.prototype.foo = function () {
_super.foo.call(this);
_super.prototype.foo.call(this);
};
return C;
})();
Expand All @@ -30,7 +30,7 @@ var M1;
function C() {
}
C.prototype.foo = function () {
_super.foo.call(this);
_super.prototype.foo.call(this);
};
return C;
})();
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/parserSuperExpression2.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var C = (function () {
function C() {
}
C.prototype.M = function () {
_super..call(this, 0);
_super.prototype..call(this, 0);
};
return C;
})();
4 changes: 2 additions & 2 deletions tests/baselines/reference/parserSuperExpression4.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ var C = (function () {
function C() {
}
C.prototype.foo = function () {
_super.foo = 1;
_super.prototype.foo = 1;
};
return C;
})();
Expand All @@ -30,7 +30,7 @@ var M1;
function C() {
}
C.prototype.foo = function () {
_super.foo = 1;
_super.prototype.foo = 1;
};
return C;
})();
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/super.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ var Base2 = (function () {
function Base2() {
}
Base2.prototype.foo = function () {
_super.foo.call(this);
_super.prototype.foo.call(this);
};
return Base2;
})();
Expand Down
2 changes: 1 addition & 1 deletion tests/baselines/reference/super1.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ var Base4;
function Sub4E() {
}
Sub4E.prototype.x = function () {
return _super.x.call(this);
return _super.prototype.x.call(this);
};
return Sub4E;
})();
Expand Down
15 changes: 15 additions & 0 deletions tests/baselines/reference/superCallWithMissingBaseClass.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
tests/cases/compiler/superCallWithMissingBaseClass.ts(1,19): error TS2304: Cannot find name 'Bar'.


==== tests/cases/compiler/superCallWithMissingBaseClass.ts (1 errors) ====
class Foo extends Bar {
~~~
!!! error TS2304: Cannot find name 'Bar'.
m1() {
return super.m1();
}

static m2() {
return super.m2();
}
}
30 changes: 30 additions & 0 deletions tests/baselines/reference/superCallWithMissingBaseClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//// [superCallWithMissingBaseClass.ts]
class Foo extends Bar {
m1() {
return super.m1();
}

static m2() {
return super.m2();
}
}

//// [superCallWithMissingBaseClass.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 Foo = (function (_super) {
__extends(Foo, _super);
function Foo() {
_super.apply(this, arguments);
}
Foo.prototype.m1 = function () {
return _super.prototype.m1.call(this);
};
Foo.m2 = function () {
return _super.m2.call(this);
};
return Foo;
})(Bar);
9 changes: 9 additions & 0 deletions tests/cases/compiler/superCallWithMissingBaseClass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class Foo extends Bar {
m1() {
return super.m1();
}

static m2() {
return super.m2();
}
}

0 comments on commit 24c8a8e

Please sign in to comment.