Skip to content

Commit ed09628

Browse files
author
Kanchalai Tanglertsampan
committed
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
1 parent 76b6bff commit ed09628

File tree

2 files changed

+64
-41
lines changed

2 files changed

+64
-41
lines changed

src/compiler/checker.ts

Lines changed: 61 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7262,17 +7262,65 @@ namespace ts {
72627262
}
72637263
}
72647264

7265+
function isSuperCallExpression(n: Node): boolean {
7266+
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).expression.kind === SyntaxKind.SuperKeyword;
7267+
}
7268+
7269+
/**
7270+
* Return a cached result if super-statement is already found.
7271+
* Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
7272+
*
7273+
* @param constructor constructor-function to look for super statement
7274+
*/
7275+
function getSuperStatementInConstructor(constructor: ConstructorDeclaration): ExpressionStatement {
7276+
function getContainingSuperCall(n: Node): Node {
7277+
if (isSuperCallExpression(n)) {
7278+
return n;
7279+
}
7280+
else if (isFunctionLike(n)) {
7281+
return undefined;
7282+
}
7283+
return forEachChild(n, getContainingSuperCall);
7284+
}
7285+
7286+
const links = getNodeLinks(constructor);
7287+
if (!links.superStatement) {
7288+
links.superStatement = <ExpressionStatement>getContainingSuperCall(constructor.body);
7289+
}
7290+
return links.superStatement;
7291+
}
7292+
7293+
/**
7294+
* Check if the given class-declaration extends null then return true.
7295+
* Otherwise, return false
7296+
* @param classDecl a class declaration to check if it extends null
7297+
*/
7298+
function isClassDeclarationExtendNull(classDecl: ClassDeclaration): boolean {
7299+
const classSymbol = getSymbolOfNode(classDecl);
7300+
const classInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(classSymbol);
7301+
const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType);
7302+
7303+
return baseConstructorType === nullType;
7304+
}
7305+
72657306
function checkThisExpression(node: Node): Type {
72667307
// Stop at the first arrow function so that we can
72677308
// tell whether 'this' needs to be captured.
72687309
let container = getThisContainer(node, /* includeArrowFunctions */ true);
72697310
let needToCaptureLexicalThis = false;
72707311

72717312
if (container.kind === SyntaxKind.Constructor) {
7272-
const baseTypeNode = getClassExtendsHeritageClauseElement(<ClassLikeDeclaration>container.parent);
7273-
if (baseTypeNode && !(getNodeCheckFlags(container) & NodeCheckFlags.HasSeenSuperCall)) {
7274-
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
7275-
error(node, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class);
7313+
const containingClassDecl = <ClassDeclaration>container.parent;
7314+
const baseTypeNode = getClassExtendsHeritageClauseElement(containingClassDecl);
7315+
7316+
// If a containing class does not have extends clause or the class extends null
7317+
// skip checking whether super statement is called before "this" accessing.
7318+
if (baseTypeNode && !isClassDeclarationExtendNull(containingClassDecl)) {
7319+
const superStatement = getSuperStatementInConstructor(<ConstructorDeclaration>container);
7320+
if (!superStatement || (superStatement && superStatement.pos > node.pos || superStatement.end > node.pos)) {
7321+
// In ES6, super inside constructor of class-declaration has to precede "this" accessing
7322+
error(node, Diagnostics.super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class);
7323+
}
72767324
}
72777325
}
72787326

@@ -10219,14 +10267,11 @@ namespace ts {
1021910267
checkGrammarTypeArguments(node, node.typeArguments) || checkGrammarArguments(node, node.arguments);
1022010268

1022110269
const signature = getResolvedSignature(node);
10222-
if (node.expression.kind === SyntaxKind.SuperKeyword) {
10223-
const containgFunction = getContainingFunction(node.expression);
1022410270

10225-
if (containgFunction && containgFunction.kind === SyntaxKind.Constructor) {
10226-
getNodeLinks(containgFunction).flags |= NodeCheckFlags.HasSeenSuperCall;
10227-
}
10271+
if (node.expression.kind === SyntaxKind.SuperKeyword) {
1022810272
return voidType;
1022910273
}
10274+
1023010275
if (node.kind === SyntaxKind.NewExpression) {
1023110276
const declaration = signature.declaration;
1023210277

@@ -11766,27 +11811,6 @@ namespace ts {
1176611811
return;
1176711812
}
1176811813

11769-
function isSuperCallExpression(n: Node): boolean {
11770-
return n.kind === SyntaxKind.CallExpression && (<CallExpression>n).expression.kind === SyntaxKind.SuperKeyword;
11771-
}
11772-
11773-
function containsSuperCallAsComputedPropertyName(n: Declaration): boolean {
11774-
return n.name && containsSuperCall(n.name);
11775-
}
11776-
11777-
function containsSuperCall(n: Node): boolean {
11778-
if (isSuperCallExpression(n)) {
11779-
return true;
11780-
}
11781-
else if (isFunctionLike(n)) {
11782-
return false;
11783-
}
11784-
else if (isClassLike(n)) {
11785-
return forEach((<ClassLikeDeclaration>n).members, containsSuperCallAsComputedPropertyName);
11786-
}
11787-
return forEachChild(n, containsSuperCall);
11788-
}
11789-
1179011814
function markThisReferencesAsErrors(n: Node): void {
1179111815
if (n.kind === SyntaxKind.ThisKeyword) {
1179211816
error(n, Diagnostics.this_cannot_be_referenced_in_current_location);
@@ -11803,16 +11827,14 @@ namespace ts {
1180311827
}
1180411828

1180511829
// TS 1.0 spec (April 2014): 8.3.2
11806-
// Constructors of classes with no extends clause may not contain super calls, whereas
11807-
// constructors of derived classes must contain at least one super call somewhere in their function body.
11830+
// Constructors of classes with no extends clause and constructors of classes that extends null may not contain super calls,
11831+
// whereas constructors of derived classes must contain at least one super call somewhere in their function body.
1180811832
const containingClassDecl = <ClassDeclaration>node.parent;
1180911833
if (getClassExtendsHeritageClauseElement(containingClassDecl)) {
11810-
const containingClassSymbol = getSymbolOfNode(containingClassDecl);
11811-
const containingClassInstanceType = <InterfaceType>getDeclaredTypeOfSymbol(containingClassSymbol);
11812-
const baseConstructorType = getBaseConstructorTypeOfClass(containingClassInstanceType);
11834+
const isClassExtendNull = isClassDeclarationExtendNull(containingClassDecl);
1181311835

11814-
if (containsSuperCall(node.body)) {
11815-
if (baseConstructorType === nullType) {
11836+
if (getSuperStatementInConstructor(node)) {
11837+
if (isClassExtendNull) {
1181611838
error(node, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
1181711839
}
1181811840

@@ -11830,6 +11852,7 @@ namespace ts {
1183011852
if (superCallShouldBeFirst) {
1183111853
const statements = (<Block>node.body).statements;
1183211854
let superCallStatement: ExpressionStatement;
11855+
1183311856
for (const statement of statements) {
1183411857
if (statement.kind === SyntaxKind.ExpressionStatement && isSuperCallExpression((<ExpressionStatement>statement).expression)) {
1183511858
superCallStatement = <ExpressionStatement>statement;
@@ -11844,7 +11867,7 @@ namespace ts {
1184411867
}
1184511868
}
1184611869
}
11847-
else if (baseConstructorType !== nullType) {
11870+
else if (!isClassExtendNull) {
1184811871
error(node, Diagnostics.Constructors_for_derived_classes_must_contain_a_super_call);
1184911872
}
1185011873
}

src/compiler/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2068,9 +2068,8 @@ namespace ts {
20682068
LoopWithCapturedBlockScopedBinding = 0x00010000, // Loop that contains block scoped variable captured in closure
20692069
CapturedBlockScopedBinding = 0x00020000, // Block-scoped binding that is captured in some function
20702070
BlockScopedBindingInLoop = 0x00040000, // Block-scoped binding with declaration nested inside iteration statement
2071-
HasSeenSuperCall = 0x00080000, // Set during the binding when encounter 'super'
2072-
ClassWithBodyScopedClassBinding = 0x00100000, // Decorated class that contains a binding to itself inside of the class body.
2073-
BodyScopedClassBinding = 0x00200000, // Binding to a decorated class inside of the class's body.
2071+
ClassWithBodyScopedClassBinding = 0x0080000, // Decorated class that contains a binding to itself inside of the class body.
2072+
BodyScopedClassBinding = 0x00100000, // Binding to a decorated class inside of the class's body.
20742073
}
20752074

20762075
/* @internal */
@@ -2089,6 +2088,7 @@ namespace ts {
20892088
importOnRightSide?: Symbol; // for import declarations - import that appear on the right side
20902089
jsxFlags?: JsxFlags; // flags for knowning what kind of element/attributes we're dealing with
20912090
resolvedJsxType?: Type; // resolved element attributes type of a JSX openinglike element
2091+
superStatement?: ExpressionStatement; // Cached super-statement found in the constructor. Used in checking whether super is called before this-accessing
20922092
}
20932093

20942094
export const enum TypeFlags {

0 commit comments

Comments
 (0)