Compare commits

...

4 commits

Author SHA1 Message Date
Anders Hejlsberg 41baf41700 Add tests 2020-05-16 10:50:52 -07:00
Anders Hejlsberg 76b9a63359 Accept new baselines 2020-05-15 19:03:10 -07:00
Anders Hejlsberg 056930f1c8 Accept cleaned-up API baselines 2020-05-15 19:02:43 -07:00
Anders Hejlsberg d7c8d3bb97 Use CFA graph to check this/super accesses are preceded by super() call 2020-05-15 19:01:45 -07:00
13 changed files with 948 additions and 104 deletions

View file

@ -985,7 +985,7 @@ namespace ts {
return initFlowNode({ flags: FlowFlags.SwitchClause, antecedent, switchStatement, clauseStart, clauseEnd });
}
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Node): FlowNode {
function createFlowMutation(flags: FlowFlags, antecedent: FlowNode, node: Expression | VariableDeclaration | ArrayBindingElement): FlowNode {
setFlowNodeReferenced(antecedent);
const result = initFlowNode({ flags, antecedent, node });
if (currentExceptionTarget) {
@ -1341,7 +1341,7 @@ namespace ts {
// is potentially an assertion and is therefore included in the control flow.
if (node.expression.kind === SyntaxKind.CallExpression) {
const call = <CallExpression>node.expression;
if (isDottedName(call.expression)) {
if (isDottedName(call.expression) && call.expression.kind !== SyntaxKind.SuperKeyword) {
currentFlow = createFlowCall(currentFlow, call);
}
}
@ -1747,6 +1747,9 @@ namespace ts {
}
else {
bindEachChild(node);
if (node.expression.kind === SyntaxKind.SuperKeyword) {
currentFlow = createFlowCall(currentFlow, node);
}
}
}
if (node.expression.kind === SyntaxKind.PropertyAccessExpression) {
@ -2464,6 +2467,9 @@ namespace ts {
node.flowNode = currentFlow;
}
return checkStrictModeIdentifier(<Identifier>node);
case SyntaxKind.SuperKeyword:
node.flowNode = currentFlow;
break;
case SyntaxKind.PrivateIdentifier:
return checkPrivateIdentifier(node as PrivateIdentifier);
case SyntaxKind.PropertyAccessExpression:

View file

@ -911,6 +911,7 @@ namespace ts {
const sharedFlowNodes: FlowNode[] = [];
const sharedFlowTypes: FlowType[] = [];
const flowNodeReachable: (boolean | undefined)[] = [];
const flowNodePostSuper: (boolean | undefined)[] = [];
const potentialThisCollisions: Node[] = [];
const potentialNewTargetCollisions: Node[] = [];
const potentialWeakMapCollisions: Node[] = [];
@ -20134,7 +20135,7 @@ namespace ts {
noCacheCheck = false;
}
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation)) {
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | PreFinallyFlow>flow).antecedent;
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation>flow).antecedent;
}
else if (flags & FlowFlags.Call) {
const signature = getEffectsSignature((<FlowCall>flow).node);
@ -20184,6 +20185,51 @@ namespace ts {
}
}
// Return true if the given flow node is preceded by a 'super(...)' call in every possible code path
// leading to the node.
function isPostSuperFlowNode(flow: FlowNode, noCacheCheck: boolean): boolean {
while (true) {
const flags = flow.flags;
if (flags & FlowFlags.Shared) {
if (!noCacheCheck) {
const id = getFlowNodeId(flow);
const postSuper = flowNodePostSuper[id];
return postSuper !== undefined ? postSuper : (flowNodePostSuper[id] = isPostSuperFlowNode(flow, /*noCacheCheck*/ true));
}
noCacheCheck = false;
}
if (flags & (FlowFlags.Assignment | FlowFlags.Condition | FlowFlags.ArrayMutation | FlowFlags.SwitchClause)) {
flow = (<FlowAssignment | FlowCondition | FlowArrayMutation | FlowSwitchClause>flow).antecedent;
}
else if (flags & FlowFlags.Call) {
if ((<FlowCall>flow).node.expression.kind === SyntaxKind.SuperKeyword) {
return true;
}
flow = (<FlowCall>flow).antecedent;
}
else if (flags & FlowFlags.BranchLabel) {
// A branching point is post-super if every branch is post-super.
return every((<FlowLabel>flow).antecedents, f => isPostSuperFlowNode(f, /*noCacheCheck*/ false));
}
else if (flags & FlowFlags.LoopLabel) {
// A loop is post-super if the control flow path that leads to the top is post-super.
flow = (<FlowLabel>flow).antecedents![0];
}
else if (flags & FlowFlags.ReduceLabel) {
const target = (<FlowReduceLabel>flow).target;
const saveAntecedents = target.antecedents;
target.antecedents = (<FlowReduceLabel>flow).antecedents;
const result = isPostSuperFlowNode((<FlowReduceLabel>flow).antecedent, /*noCacheCheck*/ false);
target.antecedents = saveAntecedents;
return result;
}
else {
// Unreachable nodes are considered post-super to silence errors
return !!(flags & FlowFlags.Unreachable);
}
}
}
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
let key: string | undefined;
let keySet = false;
@ -21583,31 +21629,10 @@ namespace ts {
}
}
function findFirstSuperCall(n: Node): SuperCall | undefined {
if (isSuperCall(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): SuperCall | undefined {
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!;
function findFirstSuperCall(node: Node): SuperCall | undefined {
return isSuperCall(node) ? node :
isFunctionLike(node) ? undefined :
forEachChild(node, findFirstSuperCall);
}
/**
@ -21630,17 +21655,7 @@ namespace ts {
// 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(<ConstructorDeclaration>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
if (node.flowNode && !isPostSuperFlowNode(node.flowNode, /*noCacheCheck*/ false)) {
error(node, diagnosticMessage);
}
}
@ -21865,7 +21880,8 @@ namespace ts {
function checkSuperExpression(node: Node): Type {
const isCallExpression = node.parent.kind === SyntaxKind.CallExpression && (<CallExpression>node.parent).expression === node;
let container = getSuperContainer(node, /*stopOnFunctions*/ true);
const immediateContainer = getSuperContainer(node, /*stopOnFunctions*/ true);
let container = immediateContainer;
let needToCaptureLexicalThis = false;
// adjust the container reference in case if super is used inside arrow functions with arbitrarily deep nesting
@ -21901,7 +21917,7 @@ namespace ts {
return errorType;
}
if (!isCallExpression && container.kind === SyntaxKind.Constructor) {
if (!isCallExpression && immediateContainer.kind === SyntaxKind.Constructor) {
checkThisBeforeSuper(node, container, Diagnostics.super_must_be_called_before_accessing_a_property_of_super_in_the_constructor_of_a_derived_class);
}
@ -29898,7 +29914,7 @@ namespace ts {
if (getClassExtendsHeritageElement(containingClassDecl)) {
captureLexicalThis(node.parent, containingClassDecl);
const classExtendsNull = classDeclarationExtendsNull(containingClassDecl);
const superCall = getSuperCallInConstructor(node);
const superCall = findFirstSuperCall(node.body!);
if (superCall) {
if (classExtendsNull) {
error(superCall, Diagnostics.A_constructor_cannot_contain_a_super_call_when_its_class_extends_null);

View file

@ -150,7 +150,7 @@ namespace ts {
* returns a falsey value, then returns false.
* If no such value is found, the callback is applied to each element of array and `true` is returned.
*/
export function every<T>(array: readonly T[], callback: (element: T, index: number) => boolean): boolean {
export function every<T>(array: readonly T[] | undefined, callback: (element: T, index: number) => boolean): boolean {
if (array) {
for (let i = 0; i < array.length; i++) {
if (!callback(array[i], i)) {

View file

@ -2791,34 +2791,21 @@ namespace ts {
}
export type FlowNode =
| AfterFinallyFlow
| PreFinallyFlow
| FlowStart
| FlowLabel
| FlowAssignment
| FlowCall
| FlowCondition
| FlowSwitchClause
| FlowArrayMutation;
| FlowArrayMutation
| FlowCall
| FlowReduceLabel;
export interface FlowNodeBase {
flags: FlowFlags;
id?: number; // Node id used by flow type cache in checker
}
export interface FlowLock {
locked?: boolean;
}
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
antecedent: FlowNode;
}
export interface PreFinallyFlow extends FlowNodeBase {
antecedent: FlowNode;
lock: FlowLock;
}
// FlowStart represents the start of a control flow. For a function expression or arrow
// function, the node property references the function (which in turn has a flowNode
// property for the containing control flow).
@ -4316,8 +4303,6 @@ namespace ts {
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element
resolvedJSDocType?: Type; // Resolved type of a JSDoc type reference
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?: SuperCall; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
switchTypes?: Type[]; // Cached array of switch case expression types
jsxNamespace?: Symbol | false; // Resolved jsx namespace symbol for this node
contextFreeType?: Type; // Cached context-free type used by the first pass of inference; used when a function's return is partially contextually sensitive

View file

@ -1743,21 +1743,11 @@ declare namespace ts {
Label = 12,
Condition = 96
}
export type FlowNode = AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation;
export type FlowNode = FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation | FlowCall | FlowReduceLabel;
export interface FlowNodeBase {
flags: FlowFlags;
id?: number;
}
export interface FlowLock {
locked?: boolean;
}
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
antecedent: FlowNode;
}
export interface PreFinallyFlow extends FlowNodeBase {
antecedent: FlowNode;
lock: FlowLock;
}
export interface FlowStart extends FlowNodeBase {
node?: FunctionExpression | ArrowFunction | MethodDeclaration;
}

View file

@ -1743,21 +1743,11 @@ declare namespace ts {
Label = 12,
Condition = 96
}
export type FlowNode = AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation;
export type FlowNode = FlowStart | FlowLabel | FlowAssignment | FlowCall | FlowCondition | FlowSwitchClause | FlowArrayMutation | FlowCall | FlowReduceLabel;
export interface FlowNodeBase {
flags: FlowFlags;
id?: number;
}
export interface FlowLock {
locked?: boolean;
}
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
antecedent: FlowNode;
}
export interface PreFinallyFlow extends FlowNodeBase {
antecedent: FlowNode;
lock: FlowLock;
}
export interface FlowStart extends FlowNodeBase {
node?: FunctionExpression | ArrowFunction | MethodDeclaration;
}

View file

@ -1,17 +0,0 @@
tests/cases/compiler/captureSuperPropertyAccessInSuperCall01.ts(9,24): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
==== tests/cases/compiler/captureSuperPropertyAccessInSuperCall01.ts (1 errors) ====
class A {
constructor(f: () => string) {
}
public blah(): string { return ""; }
}
class B extends A {
constructor() {
super(() => { return super.blah(); })
~~~~~
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
}
}

View file

@ -0,0 +1,123 @@
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(7,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(8,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(9,18): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(20,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(21,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(22,22): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(30,30): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(39,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(43,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(44,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(45,18): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(59,27): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
tests/cases/compiler/checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
==== tests/cases/compiler/checkSuperCallBeforeThisAccess.ts (13 errors) ====
class A {
x = 1;
}
class C1 extends A {
constructor(n: number) {
let a1 = this; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let a2 = this.x; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let a3 = super.x; // Error
~~~~~
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
let a4 = () => this;
let a5 = () => this.x;
let a6 = () => super.x;
if (!!true) {
super();
let b1 = this;
let b2 = this.x;
let b3 = super.x;
}
else {
let c1 = this; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let c2 = this.x; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let c3 = super.x; // Error
~~~~~
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
}
if (!!true) {
switch (n) {
case 1:
super();
let d1 = this.x;
case 2:
let d2 = this.x; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
default:
super();
let d3 = this.x;
}
let d4 = this.x;
}
if (!!true) {
let e1 = { w: !!true ? super() : 0 };
let e2 = this.x; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let e3 = { w: !!true ? super() : super() };
let e4 = this.x;
}
let f1 = this; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let f2 = this.x; // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
let f3 = super.x; // Error
~~~~~
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
}
}
// Repro from #38512
export class Foo {
constructor(value: number) {
}
}
export class BarCorrectlyFails extends Foo {
constructor(something: boolean) {
if (!something) {
const value = this.bar(); // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
super(value);
}
else {
super(1337);
}
}
bar(): number { return 4; }
}
export class BarIncorrectlyWorks extends Foo {
constructor(something: boolean) {
if (something) {
super(1337);
}
else {
const value = this.bar(); // Error
~~~~
!!! error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class.
super(value);
}
}
bar(): number { return 4; }
}

View file

@ -0,0 +1,161 @@
//// [checkSuperCallBeforeThisAccess.ts]
class A {
x = 1;
}
class C1 extends A {
constructor(n: number) {
let a1 = this; // Error
let a2 = this.x; // Error
let a3 = super.x; // Error
let a4 = () => this;
let a5 = () => this.x;
let a6 = () => super.x;
if (!!true) {
super();
let b1 = this;
let b2 = this.x;
let b3 = super.x;
}
else {
let c1 = this; // Error
let c2 = this.x; // Error
let c3 = super.x; // Error
}
if (!!true) {
switch (n) {
case 1:
super();
let d1 = this.x;
case 2:
let d2 = this.x; // Error
default:
super();
let d3 = this.x;
}
let d4 = this.x;
}
if (!!true) {
let e1 = { w: !!true ? super() : 0 };
let e2 = this.x; // Error
let e3 = { w: !!true ? super() : super() };
let e4 = this.x;
}
let f1 = this; // Error
let f2 = this.x; // Error
let f3 = super.x; // Error
}
}
// Repro from #38512
export class Foo {
constructor(value: number) {
}
}
export class BarCorrectlyFails extends Foo {
constructor(something: boolean) {
if (!something) {
const value = this.bar(); // Error
super(value);
}
else {
super(1337);
}
}
bar(): number { return 4; }
}
export class BarIncorrectlyWorks extends Foo {
constructor(something: boolean) {
if (something) {
super(1337);
}
else {
const value = this.bar(); // Error
super(value);
}
}
bar(): number { return 4; }
}
//// [checkSuperCallBeforeThisAccess.js]
class A {
constructor() {
this.x = 1;
}
}
class C1 extends A {
constructor(n) {
let a1 = this; // Error
let a2 = this.x; // Error
let a3 = super.x; // Error
let a4 = () => this;
let a5 = () => this.x;
let a6 = () => super.x;
if (!!true) {
super();
let b1 = this;
let b2 = this.x;
let b3 = super.x;
}
else {
let c1 = this; // Error
let c2 = this.x; // Error
let c3 = super.x; // Error
}
if (!!true) {
switch (n) {
case 1:
super();
let d1 = this.x;
case 2:
let d2 = this.x; // Error
default:
super();
let d3 = this.x;
}
let d4 = this.x;
}
if (!!true) {
let e1 = { w: !!true ? super() : 0 };
let e2 = this.x; // Error
let e3 = { w: !!true ? super() : super() };
let e4 = this.x;
}
let f1 = this; // Error
let f2 = this.x; // Error
let f3 = super.x; // Error
}
}
// Repro from #38512
export class Foo {
constructor(value) {
}
}
export class BarCorrectlyFails extends Foo {
constructor(something) {
if (!something) {
const value = this.bar(); // Error
super(value);
}
else {
super(1337);
}
}
bar() { return 4; }
}
export class BarIncorrectlyWorks extends Foo {
constructor(something) {
if (something) {
super(1337);
}
else {
const value = this.bar(); // Error
super(value);
}
}
bar() { return 4; }
}

View file

@ -0,0 +1,231 @@
=== tests/cases/compiler/checkSuperCallBeforeThisAccess.ts ===
class A {
>A : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
x = 1;
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
class C1 extends A {
>C1 : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>A : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
constructor(n: number) {
>n : Symbol(n, Decl(checkSuperCallBeforeThisAccess.ts, 5, 16))
let a1 = this; // Error
>a1 : Symbol(a1, Decl(checkSuperCallBeforeThisAccess.ts, 6, 11))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
let a2 = this.x; // Error
>a2 : Symbol(a2, Decl(checkSuperCallBeforeThisAccess.ts, 7, 11))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let a3 = super.x; // Error
>a3 : Symbol(a3, Decl(checkSuperCallBeforeThisAccess.ts, 8, 11))
>super.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let a4 = () => this;
>a4 : Symbol(a4, Decl(checkSuperCallBeforeThisAccess.ts, 9, 11))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
let a5 = () => this.x;
>a5 : Symbol(a5, Decl(checkSuperCallBeforeThisAccess.ts, 10, 11))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let a6 = () => super.x;
>a6 : Symbol(a6, Decl(checkSuperCallBeforeThisAccess.ts, 11, 11))
>super.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
if (!!true) {
super();
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
let b1 = this;
>b1 : Symbol(b1, Decl(checkSuperCallBeforeThisAccess.ts, 14, 15))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
let b2 = this.x;
>b2 : Symbol(b2, Decl(checkSuperCallBeforeThisAccess.ts, 15, 15))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let b3 = super.x;
>b3 : Symbol(b3, Decl(checkSuperCallBeforeThisAccess.ts, 16, 15))
>super.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
else {
let c1 = this; // Error
>c1 : Symbol(c1, Decl(checkSuperCallBeforeThisAccess.ts, 19, 15))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
let c2 = this.x; // Error
>c2 : Symbol(c2, Decl(checkSuperCallBeforeThisAccess.ts, 20, 15))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let c3 = super.x; // Error
>c3 : Symbol(c3, Decl(checkSuperCallBeforeThisAccess.ts, 21, 15))
>super.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
if (!!true) {
switch (n) {
>n : Symbol(n, Decl(checkSuperCallBeforeThisAccess.ts, 5, 16))
case 1:
super();
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
let d1 = this.x;
>d1 : Symbol(d1, Decl(checkSuperCallBeforeThisAccess.ts, 27, 23))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
case 2:
let d2 = this.x; // Error
>d2 : Symbol(d2, Decl(checkSuperCallBeforeThisAccess.ts, 29, 23))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
default:
super();
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
let d3 = this.x;
>d3 : Symbol(d3, Decl(checkSuperCallBeforeThisAccess.ts, 32, 23))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
let d4 = this.x;
>d4 : Symbol(d4, Decl(checkSuperCallBeforeThisAccess.ts, 34, 15))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
if (!!true) {
let e1 = { w: !!true ? super() : 0 };
>e1 : Symbol(e1, Decl(checkSuperCallBeforeThisAccess.ts, 37, 15))
>w : Symbol(w, Decl(checkSuperCallBeforeThisAccess.ts, 37, 22))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
let e2 = this.x; // Error
>e2 : Symbol(e2, Decl(checkSuperCallBeforeThisAccess.ts, 38, 15))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let e3 = { w: !!true ? super() : super() };
>e3 : Symbol(e3, Decl(checkSuperCallBeforeThisAccess.ts, 39, 15))
>w : Symbol(w, Decl(checkSuperCallBeforeThisAccess.ts, 39, 22))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
let e4 = this.x;
>e4 : Symbol(e4, Decl(checkSuperCallBeforeThisAccess.ts, 40, 15))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
let f1 = this; // Error
>f1 : Symbol(f1, Decl(checkSuperCallBeforeThisAccess.ts, 42, 11))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
let f2 = this.x; // Error
>f2 : Symbol(f2, Decl(checkSuperCallBeforeThisAccess.ts, 43, 11))
>this.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>this : Symbol(C1, Decl(checkSuperCallBeforeThisAccess.ts, 2, 1))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
let f3 = super.x; // Error
>f3 : Symbol(f3, Decl(checkSuperCallBeforeThisAccess.ts, 44, 11))
>super.x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
>super : Symbol(A, Decl(checkSuperCallBeforeThisAccess.ts, 0, 0))
>x : Symbol(A.x, Decl(checkSuperCallBeforeThisAccess.ts, 0, 9))
}
}
// Repro from #38512
export class Foo {
>Foo : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
constructor(value: number) {
>value : Symbol(value, Decl(checkSuperCallBeforeThisAccess.ts, 51, 16))
}
}
export class BarCorrectlyFails extends Foo {
>BarCorrectlyFails : Symbol(BarCorrectlyFails, Decl(checkSuperCallBeforeThisAccess.ts, 53, 1))
>Foo : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
constructor(something: boolean) {
>something : Symbol(something, Decl(checkSuperCallBeforeThisAccess.ts, 56, 16))
if (!something) {
>something : Symbol(something, Decl(checkSuperCallBeforeThisAccess.ts, 56, 16))
const value = this.bar(); // Error
>value : Symbol(value, Decl(checkSuperCallBeforeThisAccess.ts, 58, 17))
>this.bar : Symbol(BarCorrectlyFails.bar, Decl(checkSuperCallBeforeThisAccess.ts, 64, 5))
>this : Symbol(BarCorrectlyFails, Decl(checkSuperCallBeforeThisAccess.ts, 53, 1))
>bar : Symbol(BarCorrectlyFails.bar, Decl(checkSuperCallBeforeThisAccess.ts, 64, 5))
super(value);
>super : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
>value : Symbol(value, Decl(checkSuperCallBeforeThisAccess.ts, 58, 17))
}
else {
super(1337);
>super : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
}
}
bar(): number { return 4; }
>bar : Symbol(BarCorrectlyFails.bar, Decl(checkSuperCallBeforeThisAccess.ts, 64, 5))
}
export class BarIncorrectlyWorks extends Foo {
>BarIncorrectlyWorks : Symbol(BarIncorrectlyWorks, Decl(checkSuperCallBeforeThisAccess.ts, 66, 1))
>Foo : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
constructor(something: boolean) {
>something : Symbol(something, Decl(checkSuperCallBeforeThisAccess.ts, 69, 16))
if (something) {
>something : Symbol(something, Decl(checkSuperCallBeforeThisAccess.ts, 69, 16))
super(1337);
>super : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
}
else {
const value = this.bar(); // Error
>value : Symbol(value, Decl(checkSuperCallBeforeThisAccess.ts, 74, 17))
>this.bar : Symbol(BarIncorrectlyWorks.bar, Decl(checkSuperCallBeforeThisAccess.ts, 77, 5))
>this : Symbol(BarIncorrectlyWorks, Decl(checkSuperCallBeforeThisAccess.ts, 66, 1))
>bar : Symbol(BarIncorrectlyWorks.bar, Decl(checkSuperCallBeforeThisAccess.ts, 77, 5))
super(value);
>super : Symbol(Foo, Decl(checkSuperCallBeforeThisAccess.ts, 46, 1))
>value : Symbol(value, Decl(checkSuperCallBeforeThisAccess.ts, 74, 17))
}
}
bar(): number { return 4; }
>bar : Symbol(BarIncorrectlyWorks.bar, Decl(checkSuperCallBeforeThisAccess.ts, 77, 5))
}

View file

@ -0,0 +1,279 @@
=== tests/cases/compiler/checkSuperCallBeforeThisAccess.ts ===
class A {
>A : A
x = 1;
>x : number
>1 : 1
}
class C1 extends A {
>C1 : C1
>A : A
constructor(n: number) {
>n : number
let a1 = this; // Error
>a1 : this
>this : this
let a2 = this.x; // Error
>a2 : number
>this.x : number
>this : this
>x : number
let a3 = super.x; // Error
>a3 : number
>super.x : number
>super : A
>x : number
let a4 = () => this;
>a4 : () => this
>() => this : () => this
>this : this
let a5 = () => this.x;
>a5 : () => number
>() => this.x : () => number
>this.x : number
>this : this
>x : number
let a6 = () => super.x;
>a6 : () => number
>() => super.x : () => number
>super.x : number
>super : A
>x : number
if (!!true) {
>!!true : true
>!true : false
>true : true
super();
>super() : void
>super : typeof A
let b1 = this;
>b1 : this
>this : this
let b2 = this.x;
>b2 : number
>this.x : number
>this : this
>x : number
let b3 = super.x;
>b3 : number
>super.x : number
>super : A
>x : number
}
else {
let c1 = this; // Error
>c1 : this
>this : this
let c2 = this.x; // Error
>c2 : number
>this.x : number
>this : this
>x : number
let c3 = super.x; // Error
>c3 : number
>super.x : number
>super : A
>x : number
}
if (!!true) {
>!!true : true
>!true : false
>true : true
switch (n) {
>n : number
case 1:
>1 : 1
super();
>super() : void
>super : typeof A
let d1 = this.x;
>d1 : number
>this.x : number
>this : this
>x : number
case 2:
>2 : 2
let d2 = this.x; // Error
>d2 : number
>this.x : number
>this : this
>x : number
default:
super();
>super() : void
>super : typeof A
let d3 = this.x;
>d3 : number
>this.x : number
>this : this
>x : number
}
let d4 = this.x;
>d4 : number
>this.x : number
>this : this
>x : number
}
if (!!true) {
>!!true : true
>!true : false
>true : true
let e1 = { w: !!true ? super() : 0 };
>e1 : { w: number | void; }
>{ w: !!true ? super() : 0 } : { w: number | void; }
>w : number | void
>!!true ? super() : 0 : void | 0
>!!true : true
>!true : false
>true : true
>super() : void
>super : typeof A
>0 : 0
let e2 = this.x; // Error
>e2 : number
>this.x : number
>this : this
>x : number
let e3 = { w: !!true ? super() : super() };
>e3 : { w: void; }
>{ w: !!true ? super() : super() } : { w: void; }
>w : void
>!!true ? super() : super() : void
>!!true : true
>!true : false
>true : true
>super() : void
>super : typeof A
>super() : void
>super : typeof A
let e4 = this.x;
>e4 : number
>this.x : number
>this : this
>x : number
}
let f1 = this; // Error
>f1 : this
>this : this
let f2 = this.x; // Error
>f2 : number
>this.x : number
>this : this
>x : number
let f3 = super.x; // Error
>f3 : number
>super.x : number
>super : A
>x : number
}
}
// Repro from #38512
export class Foo {
>Foo : Foo
constructor(value: number) {
>value : number
}
}
export class BarCorrectlyFails extends Foo {
>BarCorrectlyFails : BarCorrectlyFails
>Foo : Foo
constructor(something: boolean) {
>something : boolean
if (!something) {
>!something : boolean
>something : boolean
const value = this.bar(); // Error
>value : number
>this.bar() : number
>this.bar : () => number
>this : this
>bar : () => number
super(value);
>super(value) : void
>super : typeof Foo
>value : number
}
else {
super(1337);
>super(1337) : void
>super : typeof Foo
>1337 : 1337
}
}
bar(): number { return 4; }
>bar : () => number
>4 : 4
}
export class BarIncorrectlyWorks extends Foo {
>BarIncorrectlyWorks : BarIncorrectlyWorks
>Foo : Foo
constructor(something: boolean) {
>something : boolean
if (something) {
>something : boolean
super(1337);
>super(1337) : void
>super : typeof Foo
>1337 : 1337
}
else {
const value = this.bar(); // Error
>value : number
>this.bar() : number
>this.bar : () => number
>this : this
>bar : () => number
super(value);
>super(value) : void
>super : typeof Foo
>value : number
}
}
bar(): number { return 4; }
>bar : () => number
>4 : 4
}

View file

@ -7,7 +7,6 @@ tests/cases/compiler/superAccess2.ts(11,33): error TS1034: 'super' must be follo
tests/cases/compiler/superAccess2.ts(11,40): error TS2336: 'super' cannot be referenced in constructor arguments.
tests/cases/compiler/superAccess2.ts(11,40): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
tests/cases/compiler/superAccess2.ts(11,45): error TS1034: 'super' must be followed by an argument list or member access.
tests/cases/compiler/superAccess2.ts(11,59): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
tests/cases/compiler/superAccess2.ts(11,64): error TS1034: 'super' must be followed by an argument list or member access.
tests/cases/compiler/superAccess2.ts(15,19): error TS1034: 'super' must be followed by an argument list or member access.
tests/cases/compiler/superAccess2.ts(17,15): error TS2576: Property 'y' is a static member of type 'P'
@ -15,7 +14,7 @@ tests/cases/compiler/superAccess2.ts(20,26): error TS1034: 'super' must be follo
tests/cases/compiler/superAccess2.ts(21,15): error TS2339: Property 'x' does not exist on type 'typeof P'.
==== tests/cases/compiler/superAccess2.ts (15 errors) ====
==== tests/cases/compiler/superAccess2.ts (14 errors) ====
class P {
x() { }
static y() { }
@ -45,8 +44,6 @@ tests/cases/compiler/superAccess2.ts(21,15): error TS2339: Property 'x' does not
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
~
!!! error TS1034: 'super' must be followed by an argument list or member access.
~~~~~
!!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class.
~
!!! error TS1034: 'super' must be followed by an argument list or member access.
super();

View file

@ -0,0 +1,83 @@
// @strict: true
// @target: esnext
class A {
x = 1;
}
class C1 extends A {
constructor(n: number) {
let a1 = this; // Error
let a2 = this.x; // Error
let a3 = super.x; // Error
let a4 = () => this;
let a5 = () => this.x;
let a6 = () => super.x;
if (!!true) {
super();
let b1 = this;
let b2 = this.x;
let b3 = super.x;
}
else {
let c1 = this; // Error
let c2 = this.x; // Error
let c3 = super.x; // Error
}
if (!!true) {
switch (n) {
case 1:
super();
let d1 = this.x;
case 2:
let d2 = this.x; // Error
default:
super();
let d3 = this.x;
}
let d4 = this.x;
}
if (!!true) {
let e1 = { w: !!true ? super() : 0 };
let e2 = this.x; // Error
let e3 = { w: !!true ? super() : super() };
let e4 = this.x;
}
let f1 = this; // Error
let f2 = this.x; // Error
let f3 = super.x; // Error
}
}
// Repro from #38512
export class Foo {
constructor(value: number) {
}
}
export class BarCorrectlyFails extends Foo {
constructor(something: boolean) {
if (!something) {
const value = this.bar(); // Error
super(value);
}
else {
super(1337);
}
}
bar(): number { return 4; }
}
export class BarIncorrectlyWorks extends Foo {
constructor(something: boolean) {
if (something) {
super(1337);
}
else {
const value = this.bar(); // Error
super(value);
}
}
bar(): number { return 4; }
}