@@ -911,6 +911,7 @@ namespace ts {
911
911
const sharedFlowNodes: FlowNode[] = [];
912
912
const sharedFlowTypes: FlowType[] = [];
913
913
const flowNodeReachable: (boolean | undefined)[] = [];
914
+ const flowNodePostSuper: (boolean | undefined)[] = [];
914
915
const potentialThisCollisions: Node[] = [];
915
916
const potentialNewTargetCollisions: Node[] = [];
916
917
const potentialWeakMapCollisions: Node[] = [];
@@ -20148,7 +20149,7 @@ namespace ts {
20148
20149
noCacheCheck = false;
20149
20150
}
20150
20151
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
20151
- flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow >flow).antecedent;
20152
+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
20152
20153
}
20153
20154
else if (flags & FlowFlags.Call) {
20154
20155
const signature = getEffectsSignature((<FlowCall>flow).node);
@@ -20198,6 +20199,51 @@ namespace ts {
20198
20199
}
20199
20200
}
20200
20201
20202
+ // Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
20203
+ // leading to the node.
20204
+ function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean {
20205
+ while (true) {
20206
+ const flags = flow.flags;
20207
+ if (flags & FlowFlags.Shared) {
20208
+ if (!noCacheCheck) {
20209
+ const id = getFlowNodeId(flow);
20210
+ const postSuper = flowNodePostSuper[id];
20211
+ return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true));
20212
+ }
20213
+ noCacheCheck = false;
20214
+ }
20215
+ if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) {
20216
+ flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause>flow).antecedent;
20217
+ }
20218
+ else if (flags & FlowFlags.Call) {
20219
+ if ((<FlowCall>flow).node.expression.kind === SyntaxKind.SuperKeyword) {
20220
+ return true;
20221
+ }
20222
+ flow = (<FlowCall>flow).antecedent;
20223
+ }
20224
+ else if (flags & FlowFlags.BranchLabel) {
20225
+ // A branching point is post-super if every branch is post-super.
20226
+ return every((<FlowLabel>flow).antecedents, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false));
20227
+ }
20228
+ else if (flags & FlowFlags.LoopLabel) {
20229
+ // A loop is post-super if the control flow path that leads to the top is post-super.
20230
+ flow = (<FlowLabel>flow).antecedents![0];
20231
+ }
20232
+ else if (flags & FlowFlags.ReduceLabel) {
20233
+ const target = (<FlowReduceLabel>flow).target;
20234
+ const saveAntecedents = target.antecedents;
20235
+ target.antecedents = (<FlowReduceLabel>flow).antecedents;
20236
+ const result = isPostSuperFlowNode((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
20237
+ target.antecedents = saveAntecedents;
20238
+ return result;
20239
+ }
20240
+ else {
20241
+ // Unreachable nodes are considered post-super to silence errors
20242
+ return !!(flags & FlowFlags.Unreachable);
20243
+ }
20244
+ }
20245
+ }
20246
+
20201
20247
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
20202
20248
let key: string | undefined;
20203
20249
let keySet = false;
@@ -21597,31 +21643,10 @@ namespace ts {
21597
21643
}
21598
21644
}
21599
21645
21600
- function findFirstSuperCall(n: Node): SuperCall | undefined {
21601
- if (isSuperCall(n)) {
21602
- return n;
21603
- }
21604
- else if (isFunctionLike(n)) {
21605
- return undefined;
21606
- }
21607
- return forEachChild(n, findFirstSuperCall);
21608
- }
21609
-
21610
- /**
21611
- * Return a cached result if super-statement is already found.
21612
- * Otherwise, find a super statement in a given constructor function and cache the result in the node-links of the constructor
21613
- *
21614
- * @param constructor constructor-function to look for super statement
21615
- */
21616
- function getSuperCallInConstructor(constructor: ConstructorDeclaration): SuperCall | undefined {
21617
- const links = getNodeLinks(constructor);
21618
-
21619
- // Only trying to find super-call if we haven't yet tried to find one. Once we try, we will record the result
21620
- if (links.hasSuperCall === undefined) {
21621
- links.superCall = findFirstSuperCall(constructor.body!);
21622
- links.hasSuperCall = links.superCall ? true : false;
21623
- }
21624
- return links.superCall!;
21646
+ function findFirstSuperCall(node: Node): SuperCall | undefined {
21647
+ return isSuperCall(node) ? node :
21648
+ isFunctionLike(node) ? undefined :
21649
+ forEachChild(node, findFirstSuperCall);
21625
21650
}
21626
21651
21627
21652
/**
@@ -21644,17 +21669,7 @@ namespace ts {
21644
21669
// If a containing class does not have extends clause or the class extends null
21645
21670
// skip checking whether super statement is called before "this" accessing.
21646
21671
if (baseTypeNode && !classDeclarationExtendsNull(containingClassDecl)) {
21647
- const superCall = getSuperCallInConstructor(<ConstructorDeclaration>container);
21648
-
21649
- // We should give an error in the following cases:
21650
- // - No super-call
21651
- // - "this" is accessing before super-call.
21652
- // i.e super(this)
21653
- // this.x; super();
21654
- // We want to make sure that super-call is done before accessing "this" so that
21655
- // "this" is not accessed as a parameter of the super-call.
21656
- if (!superCall || superCall.end > node.pos) {
21657
- // In ES6, super inside constructor of class-declaration has to precede "this" accessing
21672
+ if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
21658
21673
error(node, diagnosticMessage);
21659
21674
}
21660
21675
}
@@ -21879,7 +21894,8 @@ namespace ts {
21879
21894
function checkSuperExpression(node: Node): Type {
21880
21895
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
21881
21896
21882
- let container = getSuperContainer(node, /*stopOnFunctions*/ true);
21897
+ const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
21898
+ let container = immediateContainer;
21883
21899
let needToCaptureLexicalThis = false;
21884
21900
21885
21901
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@@ -21915,7 +21931,7 @@ namespace ts {
21915
21931
return errorType;
21916
21932
}
21917
21933
21918
- if (!isCallExpression && container .kind === SyntaxKind.Constructor) {
21934
+ if (!isCallExpression && immediateContainer .kind === SyntaxKind.Constructor) {
21919
21935
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
21920
21936
}
21921
21937
@@ -29912,7 +29928,7 @@ namespace ts {
29912
29928
if (getClassExtendsHeritageElement(containingClassDecl)) {
29913
29929
captureLexicalThis(node.parent, containingClassDecl);
29914
29930
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
29915
- const superCall = getSuperCallInConstructor (node);
29931
+ const superCall = findFirstSuperCall (node.body! );
29916
29932
if (superCall) {
29917
29933
if (classExtendsNull) {
29918
29934
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);
0 commit comments