Check using "super" before "this" lexically instead of using the

NodeCheckFlags

Remove "type-checking" way of checking if super is used before this.
Instead check using whether super occurs before this syntactically

Refactor the code

Dive down to get super call
This commit is contained in:
Kanchalai Tanglertsampan
2016-02-02 16:53:44 -08:00
parent 76b6bff3d7
commit ed0962883b
2 changed files with 64 additions and 41 deletions
+61 -38
View File
@@ -7262,6 +7262,47 @@ namespace ts {
}
}
function isSuperCallExpression(n: Node): boolean {
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).expression.kind === SyntaxKind.SuperKeyword;
}
/**
* 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 getSuperStatementInConstructor(constructor: ConstructorDeclaration): ExpressionStatement {
function getContainingSuperCall(n: Node): Node {
if (isSuperCallExpression(n)) {
return n;
}
else if (isFunctionLike(n)) {
return undefined;
}
return forEachChild(n, getContainingSuperCall);
}
const links = getNodeLinks(constructor);
if (!links.superStatement) {
links.superStatement = <ExpressionStatement>getContainingSuperCall(constructor.body);
}
return links.superStatement;
}
/**
* 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 isClassDeclarationExtendNull(classDecl: ClassDeclaration): boolean {
const classSymbol = getSymbolOfNode(classDecl);
const classInstanceType = <InterfaceType>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.
@@ -7269,10 +7310,17 @@ namespace ts {
let needToCaptureLexicalThis = false;
if (container.kind === SyntaxKind.Constructor) {
const baseTypeNode = getClassExtendsHeritageClauseElement(<ClassLikeDeclaration>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 = <ClassDeclaration>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 && !isClassDeclarationExtendNull(containingClassDecl)) {
const superStatement = getSuperStatementInConstructor(<ConstructorDeclaration>container);
if (!superStatement || (superStatement && superStatement.pos > node.pos || superStatement.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);
}
}
}
@@ -10219,14 +10267,11 @@ namespace ts {
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
const signature = getResolvedSignature(node);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
const containgFunction = getContainingFunction(node.expression);
if (containgFunction && containgFunction.kind === SyntaxKind.Constructor) {
getNodeLinks(containgFunction).flags |= NodeCheckFlags.HasSeenSuperCall;
}
if (node.expression.kind === SyntaxKind.SuperKeyword) {
return voidType;
}
if (node.kind === SyntaxKind.NewExpression) {
const declaration = signature.declaration;
@@ -11766,27 +11811,6 @@ namespace ts {
return;
}
function isSuperCallExpression(n: Node): boolean {
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).expression.kind === SyntaxKind.SuperKeyword;
}
function containsSuperCallAsComputedPropertyName(n: Declaration): boolean {
return n.name && containsSuperCall(n.name);
}
function containsSuperCall(n: Node): boolean {
if (isSuperCallExpression(n)) {
return true;
}
else if (isFunctionLike(n)) {
return false;
}
else if (isClassLike(n)) {
return forEach((<ClassLikeDeclaration>n).members, containsSuperCallAsComputedPropertyName);
}
return forEachChild(n, containsSuperCall);
}
function markThisReferencesAsErrors(n: Node): void {
if (n.kind === SyntaxKind.ThisKeyword) {
error(n, Diagnostics.this_cannot_be_referenced_in_current_location);
@@ -11803,16 +11827,14 @@ 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 = <ClassDeclaration>node.parent;
if (getClassExtendsHeritageClauseElement(containingClassDecl)) {
const containingClassSymbol = getSymbolOfNode(containingClassDecl);
const containingClassInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(containingClassSymbol);
const baseConstructorType = getBaseConstructorTypeOfClass(containingClassInstanceType);
const isClassExtendNull = isClassDeclarationExtendNull(containingClassDecl);
if (containsSuperCall(node.body)) {
if (baseConstructorType === nullType) {
if (getSuperStatementInConstructor(node)) {
if (isClassExtendNull) {
error(node, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
}
@@ -11830,6 +11852,7 @@ namespace ts {
if (superCallShouldBeFirst) {
const statements = (<Block>node.body).statements;
let superCallStatement: ExpressionStatement;
for (const statement of statements) {
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((<ExpressionStatement>statement).expression)) {
superCallStatement = <ExpressionStatement>statement;
@@ -11844,7 +11867,7 @@ namespace ts {
}
}
}
else if (baseConstructorType !== nullType) {
else if (!isClassExtendNull) {
error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call);
}
}
+3 -3
View File
@@ -2068,9 +2068,8 @@ 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.
ClassWithBodyScopedClassBinding = 0x0080000, // 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.
}
/* @internal */
@@ -2089,6 +2088,7 @@ namespace ts {
importOnRightSide?: Symbol; // for import declarations - import that appear on the right side
jsxFlags?: JsxFlags; // flags for knowning what kind of element/attributes we're dealing with
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
superStatement?: ExpressionStatement; // Cached super-statement found in the constructor. Used in checking whether super is called before this-accessing
}
export const enum TypeFlags {