From 4b820cba83d2d33c1798eaa0916be5d6ee5ec6c8 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Sat, 15 Jul 2023 11:41:22 +0800 Subject: [PATCH] fix: class field is not accessible via super --- src/compiler/checker.ts | 12 +++++ src/compiler/diagnosticMessages.json | 4 ++ src/compiler/utilitiesPublic.ts | 5 ++ .../checkSuperCallBeforeThisAccess.errors.txt | 17 +++++- .../reference/classFieldSuperAccessible.js | 37 +++++++++++++ .../classFieldSuperAccessible.symbols | 48 +++++++++++++++++ .../reference/classFieldSuperAccessible.types | 52 +++++++++++++++++++ .../classFieldSuperNotAccessible.errors.txt | 17 ++++++ .../reference/classFieldSuperNotAccessible.js | 25 +++++++++ .../classFieldSuperNotAccessible.symbols | 28 ++++++++++ .../classFieldSuperNotAccessible.types | 32 ++++++++++++ .../compiler/classFieldSuperAccessible.ts | 16 ++++++ .../compiler/classFieldSuperNotAccessible.ts | 11 ++++ 13 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/classFieldSuperAccessible.js create mode 100644 tests/baselines/reference/classFieldSuperAccessible.symbols create mode 100644 tests/baselines/reference/classFieldSuperAccessible.types create mode 100644 tests/baselines/reference/classFieldSuperNotAccessible.errors.txt create mode 100644 tests/baselines/reference/classFieldSuperNotAccessible.js create mode 100644 tests/baselines/reference/classFieldSuperNotAccessible.symbols create mode 100644 tests/baselines/reference/classFieldSuperNotAccessible.types create mode 100644 tests/cases/compiler/classFieldSuperAccessible.ts create mode 100644 tests/cases/compiler/classFieldSuperNotAccessible.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f1324425367bb..9bceff73422a1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -475,6 +475,7 @@ import { isClassDeclaration, isClassElement, isClassExpression, + isClassFieldAndNotAutoAccessor, isClassLike, isClassStaticBlockDeclaration, isCommaSequence, @@ -31182,6 +31183,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } return false; } + // A class field cannot be accessed via super.* from a derived class. + // This is true for both [[Set]] (old) and [[Define]] (ES spec) semantics. + if (!(flags & ModifierFlags.Static) && prop.declarations?.some(isClassFieldAndNotAutoAccessor)) { + if (errorNode) { + error(errorNode, + Diagnostics.Class_field_0_defined_by_the_parent_class_is_not_accessible_in_the_child_class_via_super, + symbolToString(prop) + ); + } + return false; + } } // Referencing abstract properties within their own constructors is not allowed diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index e15199127169c..181c33d5e9028 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3659,6 +3659,10 @@ "category": "Error", "code": 2854 }, + "Class field '{0}' defined by the parent class is not accessible in the child class via super.": { + "category": "Error", + "code": 2855 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 62be656474719..be0aedfe5d273 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -1704,6 +1704,11 @@ export function isAutoAccessorPropertyDeclaration(node: Node): node is AutoAcces return isPropertyDeclaration(node) && hasAccessorModifier(node); } +/** @internal */ +export function isClassFieldAndNotAutoAccessor(node: Node): boolean { + return node.parent && isClassLike(node.parent) && isPropertyDeclaration(node) && !hasAccessorModifier(node); +} + /** @internal */ export function isMethodOrAccessor(node: Node): node is MethodDeclaration | AccessorDeclaration { switch (node.kind) { diff --git a/tests/baselines/reference/checkSuperCallBeforeThisAccess.errors.txt b/tests/baselines/reference/checkSuperCallBeforeThisAccess.errors.txt index f994ef53f98d1..78a512d71317e 100644 --- a/tests/baselines/reference/checkSuperCallBeforeThisAccess.errors.txt +++ b/tests/baselines/reference/checkSuperCallBeforeThisAccess.errors.txt @@ -1,19 +1,24 @@ checkSuperCallBeforeThisAccess.ts(7,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(8,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(9,18): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class. +checkSuperCallBeforeThisAccess.ts(9,24): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. +checkSuperCallBeforeThisAccess.ts(12,30): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. +checkSuperCallBeforeThisAccess.ts(17,28): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. checkSuperCallBeforeThisAccess.ts(20,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(21,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(22,22): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class. +checkSuperCallBeforeThisAccess.ts(22,28): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. checkSuperCallBeforeThisAccess.ts(30,30): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(39,22): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(43,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(44,18): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(45,18): error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class. +checkSuperCallBeforeThisAccess.ts(45,24): error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. checkSuperCallBeforeThisAccess.ts(59,27): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called before accessing 'this' in the constructor of a derived class. -==== checkSuperCallBeforeThisAccess.ts (13 errors) ==== +==== checkSuperCallBeforeThisAccess.ts (18 errors) ==== class A { x = 1; } @@ -29,14 +34,20 @@ checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called let a3 = super.x; // Error ~~~~~ !!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class. + ~ +!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. let a4 = () => this; let a5 = () => this.x; let a6 = () => super.x; + ~ +!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. if (!!true) { super(); let b1 = this; let b2 = this.x; let b3 = super.x; + ~ +!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. } else { let c1 = this; // Error @@ -48,6 +59,8 @@ checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called let c3 = super.x; // Error ~~~~~ !!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class. + ~ +!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. } if (!!true) { switch (n) { @@ -81,6 +94,8 @@ checkSuperCallBeforeThisAccess.ts(75,27): error TS17009: 'super' must be called let f3 = super.x; // Error ~~~~~ !!! error TS17011: 'super' must be called before accessing a property of 'super' in the constructor of a derived class. + ~ +!!! error TS2855: Class field 'x' defined by the parent class is not accessible in the child class via super. } } diff --git a/tests/baselines/reference/classFieldSuperAccessible.js b/tests/baselines/reference/classFieldSuperAccessible.js new file mode 100644 index 0000000000000..6412b47de38d1 --- /dev/null +++ b/tests/baselines/reference/classFieldSuperAccessible.js @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/classFieldSuperAccessible.ts] //// + +//// [classFieldSuperAccessible.ts] +class A extends class Expr {} { + static { + console.log(super.name); + } +} +class B extends Number { + static { + console.log(super.EPSILON); + } +} +class C extends Array { + foo() { + console.log(super.length); + } +} + + +//// [classFieldSuperAccessible.js] +class A extends class Expr { +} { + static { + console.log(super.name); + } +} +class B extends Number { + static { + console.log(super.EPSILON); + } +} +class C extends Array { + foo() { + console.log(super.length); + } +} diff --git a/tests/baselines/reference/classFieldSuperAccessible.symbols b/tests/baselines/reference/classFieldSuperAccessible.symbols new file mode 100644 index 0000000000000..5558ce70f4d7a --- /dev/null +++ b/tests/baselines/reference/classFieldSuperAccessible.symbols @@ -0,0 +1,48 @@ +//// [tests/cases/compiler/classFieldSuperAccessible.ts] //// + +=== classFieldSuperAccessible.ts === +class A extends class Expr {} { +>A : Symbol(A, Decl(classFieldSuperAccessible.ts, 0, 0)) +>Expr : Symbol(Expr, Decl(classFieldSuperAccessible.ts, 0, 15)) + + static { + console.log(super.name); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>super.name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --)) +>super : Symbol(Expr, Decl(classFieldSuperAccessible.ts, 0, 15)) +>name : Symbol(Function.name, Decl(lib.es2015.core.d.ts, --, --)) + } +} +class B extends Number { +>B : Symbol(B, Decl(classFieldSuperAccessible.ts, 4, 1)) +>Number : Symbol(Number, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2020.number.d.ts, --, --)) + + static { + console.log(super.EPSILON); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>super.EPSILON : Symbol(NumberConstructor.EPSILON, Decl(lib.es2015.core.d.ts, --, --)) +>super : Symbol(NumberConstructor, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --)) +>EPSILON : Symbol(NumberConstructor.EPSILON, Decl(lib.es2015.core.d.ts, --, --)) + } +} +class C extends Array { +>C : Symbol(C, Decl(classFieldSuperAccessible.ts, 9, 1)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) + + foo() { +>foo : Symbol(C.foo, Decl(classFieldSuperAccessible.ts, 10, 23)) + + console.log(super.length); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>super.length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) +>super : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>length : Symbol(Array.length, Decl(lib.es5.d.ts, --, --)) + } +} + diff --git a/tests/baselines/reference/classFieldSuperAccessible.types b/tests/baselines/reference/classFieldSuperAccessible.types new file mode 100644 index 0000000000000..8ec922a9f52cb --- /dev/null +++ b/tests/baselines/reference/classFieldSuperAccessible.types @@ -0,0 +1,52 @@ +//// [tests/cases/compiler/classFieldSuperAccessible.ts] //// + +=== classFieldSuperAccessible.ts === +class A extends class Expr {} { +>A : A +>class Expr {} : Expr +>Expr : typeof Expr + + static { + console.log(super.name); +>console.log(super.name) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>super.name : string +>super : typeof Expr +>name : string + } +} +class B extends Number { +>B : B +>Number : Number + + static { + console.log(super.EPSILON); +>console.log(super.EPSILON) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>super.EPSILON : number +>super : NumberConstructor +>EPSILON : number + } +} +class C extends Array { +>C : C +>Array : any[] + + foo() { +>foo : () => void + + console.log(super.length); +>console.log(super.length) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>super.length : number +>super : any[] +>length : number + } +} + diff --git a/tests/baselines/reference/classFieldSuperNotAccessible.errors.txt b/tests/baselines/reference/classFieldSuperNotAccessible.errors.txt new file mode 100644 index 0000000000000..6e8cdf21e69a3 --- /dev/null +++ b/tests/baselines/reference/classFieldSuperNotAccessible.errors.txt @@ -0,0 +1,17 @@ +classFieldSuperNotAccessible.ts(6,15): error TS2855: Class field 'field' defined by the parent class is not accessible in the child class via super. + + +==== classFieldSuperNotAccessible.ts (1 errors) ==== + class T { + field = () => {} + } + class T2 extends T { + f() { + super.field() // error + ~~~~~ +!!! error TS2855: Class field 'field' defined by the parent class is not accessible in the child class via super. + } + } + + new T2().f() + \ No newline at end of file diff --git a/tests/baselines/reference/classFieldSuperNotAccessible.js b/tests/baselines/reference/classFieldSuperNotAccessible.js new file mode 100644 index 0000000000000..c8949774834e8 --- /dev/null +++ b/tests/baselines/reference/classFieldSuperNotAccessible.js @@ -0,0 +1,25 @@ +//// [tests/cases/compiler/classFieldSuperNotAccessible.ts] //// + +//// [classFieldSuperNotAccessible.ts] +class T { + field = () => {} +} +class T2 extends T { + f() { + super.field() // error + } +} + +new T2().f() + + +//// [classFieldSuperNotAccessible.js] +class T { + field = () => { }; +} +class T2 extends T { + f() { + super.field(); // error + } +} +new T2().f(); diff --git a/tests/baselines/reference/classFieldSuperNotAccessible.symbols b/tests/baselines/reference/classFieldSuperNotAccessible.symbols new file mode 100644 index 0000000000000..4df5ab1d536e2 --- /dev/null +++ b/tests/baselines/reference/classFieldSuperNotAccessible.symbols @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/classFieldSuperNotAccessible.ts] //// + +=== classFieldSuperNotAccessible.ts === +class T { +>T : Symbol(T, Decl(classFieldSuperNotAccessible.ts, 0, 0)) + + field = () => {} +>field : Symbol(T.field, Decl(classFieldSuperNotAccessible.ts, 0, 9)) +} +class T2 extends T { +>T2 : Symbol(T2, Decl(classFieldSuperNotAccessible.ts, 2, 1)) +>T : Symbol(T, Decl(classFieldSuperNotAccessible.ts, 0, 0)) + + f() { +>f : Symbol(T2.f, Decl(classFieldSuperNotAccessible.ts, 3, 20)) + + super.field() // error +>super.field : Symbol(T.field, Decl(classFieldSuperNotAccessible.ts, 0, 9)) +>super : Symbol(T, Decl(classFieldSuperNotAccessible.ts, 0, 0)) +>field : Symbol(T.field, Decl(classFieldSuperNotAccessible.ts, 0, 9)) + } +} + +new T2().f() +>new T2().f : Symbol(T2.f, Decl(classFieldSuperNotAccessible.ts, 3, 20)) +>T2 : Symbol(T2, Decl(classFieldSuperNotAccessible.ts, 2, 1)) +>f : Symbol(T2.f, Decl(classFieldSuperNotAccessible.ts, 3, 20)) + diff --git a/tests/baselines/reference/classFieldSuperNotAccessible.types b/tests/baselines/reference/classFieldSuperNotAccessible.types new file mode 100644 index 0000000000000..adce54830e7e7 --- /dev/null +++ b/tests/baselines/reference/classFieldSuperNotAccessible.types @@ -0,0 +1,32 @@ +//// [tests/cases/compiler/classFieldSuperNotAccessible.ts] //// + +=== classFieldSuperNotAccessible.ts === +class T { +>T : T + + field = () => {} +>field : () => void +>() => {} : () => void +} +class T2 extends T { +>T2 : T2 +>T : T + + f() { +>f : () => void + + super.field() // error +>super.field() : void +>super.field : () => void +>super : T +>field : () => void + } +} + +new T2().f() +>new T2().f() : void +>new T2().f : () => void +>new T2() : T2 +>T2 : typeof T2 +>f : () => void + diff --git a/tests/cases/compiler/classFieldSuperAccessible.ts b/tests/cases/compiler/classFieldSuperAccessible.ts new file mode 100644 index 0000000000000..92c22f30c1698 --- /dev/null +++ b/tests/cases/compiler/classFieldSuperAccessible.ts @@ -0,0 +1,16 @@ +// @target: esnext +class A extends class Expr {} { + static { + console.log(super.name); + } +} +class B extends Number { + static { + console.log(super.EPSILON); + } +} +class C extends Array { + foo() { + console.log(super.length); + } +} diff --git a/tests/cases/compiler/classFieldSuperNotAccessible.ts b/tests/cases/compiler/classFieldSuperNotAccessible.ts new file mode 100644 index 0000000000000..7b96ef761a400 --- /dev/null +++ b/tests/cases/compiler/classFieldSuperNotAccessible.ts @@ -0,0 +1,11 @@ +// @target: esnext +class T { + field = () => {} +} +class T2 extends T { + f() { + super.field() // error + } +} + +new T2().f()