From 25525607d5332dcf612ea481c9f1d96d6d2be81e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 22 Jul 2016 13:38:45 -0700 Subject: [PATCH 1/3] Test that protected constructors are accessible in static methods of subclasses --- .../classConstructorAccessibility2.errors.txt | 42 +++++++++------- .../classConstructorAccessibility2.js | 49 ++++++++++++------- .../classConstructorAccessibility5.errors.txt | 17 +++++++ .../classConstructorAccessibility5.js | 38 ++++++++++++++ .../classConstructorAccessibility2.ts | 24 +++++---- .../classConstructorAccessibility5.ts | 10 ++++ 6 files changed, 135 insertions(+), 45 deletions(-) create mode 100644 tests/baselines/reference/classConstructorAccessibility5.errors.txt create mode 100644 tests/baselines/reference/classConstructorAccessibility5.js create mode 100644 tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts diff --git a/tests/baselines/reference/classConstructorAccessibility2.errors.txt b/tests/baselines/reference/classConstructorAccessibility2.errors.txt index 7bd784ac268c2..01f2e2ea869bb 100644 --- a/tests/baselines/reference/classConstructorAccessibility2.errors.txt +++ b/tests/baselines/reference/classConstructorAccessibility2.errors.txt @@ -1,11 +1,12 @@ -tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(26,28): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. -tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(29,24): error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private. -tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(32,28): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. -tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(36,10): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. -tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(37,10): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(28,28): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(32,24): error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private. +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(35,28): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(36,35): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(40,10): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(41,10): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. -==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts (5 errors) ==== +==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts (6 errors) ==== class BaseA { public constructor(public x: number) { } @@ -14,35 +15,41 @@ tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessib class BaseB { protected constructor(public x: number) { } - createInstance() { new BaseB(1); } + createInstance() { new BaseB(2); } } class BaseC { - private constructor(public x: number) { } - createInstance() { new BaseC(1); } + private constructor(public x: number) { } + createInstance() { new BaseC(3); } + static staticInstance() { new BaseC(4); } } class DerivedA extends BaseA { constructor(public x: number) { super(x); } - createInstance() { new DerivedA(1); } - createBaseInstance() { new BaseA(1); } + createInstance() { new DerivedA(5); } + createBaseInstance() { new BaseA(6); } + static staticBaseInstance() { new BaseA(7); } } class DerivedB extends BaseB { constructor(public x: number) { super(x); } - createInstance() { new DerivedB(1); } - createBaseInstance() { new BaseB(1); } // error + createInstance() { new DerivedB(7); } + createBaseInstance() { new BaseB(8); } // error ~~~~~~~~~~~~ !!! error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. + static staticBaseInstance() { new BaseB(9); } // ok } class DerivedC extends BaseC { // error ~~~~~ !!! error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private. constructor(public x: number) { super(x); } - createInstance() { new DerivedC(1); } - createBaseInstance() { new BaseC(1); } // error - ~~~~~~~~~~~~ + createInstance() { new DerivedC(9); } + createBaseInstance() { new BaseC(10); } // error + ~~~~~~~~~~~~~ +!!! error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. + static staticBaseInstance() { new BaseC(11); } // error + ~~~~~~~~~~~~~ !!! error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. } @@ -56,4 +63,5 @@ tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessib var da = new DerivedA(1); var db = new DerivedB(1); - var dc = new DerivedC(1); \ No newline at end of file + var dc = new DerivedC(1); + \ No newline at end of file diff --git a/tests/baselines/reference/classConstructorAccessibility2.js b/tests/baselines/reference/classConstructorAccessibility2.js index e19589ece4521..97d0dd94538cc 100644 --- a/tests/baselines/reference/classConstructorAccessibility2.js +++ b/tests/baselines/reference/classConstructorAccessibility2.js @@ -7,30 +7,34 @@ class BaseA { class BaseB { protected constructor(public x: number) { } - createInstance() { new BaseB(1); } + createInstance() { new BaseB(2); } } class BaseC { - private constructor(public x: number) { } - createInstance() { new BaseC(1); } + private constructor(public x: number) { } + createInstance() { new BaseC(3); } + static staticInstance() { new BaseC(4); } } class DerivedA extends BaseA { constructor(public x: number) { super(x); } - createInstance() { new DerivedA(1); } - createBaseInstance() { new BaseA(1); } + createInstance() { new DerivedA(5); } + createBaseInstance() { new BaseA(6); } + static staticBaseInstance() { new BaseA(7); } } class DerivedB extends BaseB { constructor(public x: number) { super(x); } - createInstance() { new DerivedB(1); } - createBaseInstance() { new BaseB(1); } // error + createInstance() { new DerivedB(7); } + createBaseInstance() { new BaseB(8); } // error + static staticBaseInstance() { new BaseB(9); } // ok } class DerivedC extends BaseC { // error constructor(public x: number) { super(x); } - createInstance() { new DerivedC(1); } - createBaseInstance() { new BaseC(1); } // error + createInstance() { new DerivedC(9); } + createBaseInstance() { new BaseC(10); } // error + static staticBaseInstance() { new BaseC(11); } // error } var ba = new BaseA(1); @@ -39,7 +43,8 @@ var bc = new BaseC(1); // error var da = new DerivedA(1); var db = new DerivedB(1); -var dc = new DerivedC(1); +var dc = new DerivedC(1); + //// [classConstructorAccessibility2.js] var __extends = (this && this.__extends) || function (d, b) { @@ -58,14 +63,15 @@ var BaseB = (function () { function BaseB(x) { this.x = x; } - BaseB.prototype.createInstance = function () { new BaseB(1); }; + BaseB.prototype.createInstance = function () { new BaseB(2); }; return BaseB; }()); var BaseC = (function () { function BaseC(x) { this.x = x; } - BaseC.prototype.createInstance = function () { new BaseC(1); }; + BaseC.prototype.createInstance = function () { new BaseC(3); }; + BaseC.staticInstance = function () { new BaseC(4); }; return BaseC; }()); var DerivedA = (function (_super) { @@ -74,8 +80,9 @@ var DerivedA = (function (_super) { _super.call(this, x); this.x = x; } - DerivedA.prototype.createInstance = function () { new DerivedA(1); }; - DerivedA.prototype.createBaseInstance = function () { new BaseA(1); }; + DerivedA.prototype.createInstance = function () { new DerivedA(5); }; + DerivedA.prototype.createBaseInstance = function () { new BaseA(6); }; + DerivedA.staticBaseInstance = function () { new BaseA(7); }; return DerivedA; }(BaseA)); var DerivedB = (function (_super) { @@ -84,8 +91,9 @@ var DerivedB = (function (_super) { _super.call(this, x); this.x = x; } - DerivedB.prototype.createInstance = function () { new DerivedB(1); }; - DerivedB.prototype.createBaseInstance = function () { new BaseB(1); }; // error + DerivedB.prototype.createInstance = function () { new DerivedB(7); }; + DerivedB.prototype.createBaseInstance = function () { new BaseB(8); }; // error + DerivedB.staticBaseInstance = function () { new BaseB(9); }; // ok return DerivedB; }(BaseB)); var DerivedC = (function (_super) { @@ -94,8 +102,9 @@ var DerivedC = (function (_super) { _super.call(this, x); this.x = x; } - DerivedC.prototype.createInstance = function () { new DerivedC(1); }; - DerivedC.prototype.createBaseInstance = function () { new BaseC(1); }; // error + DerivedC.prototype.createInstance = function () { new DerivedC(9); }; + DerivedC.prototype.createBaseInstance = function () { new BaseC(10); }; // error + DerivedC.staticBaseInstance = function () { new BaseC(11); }; // error return DerivedC; }(BaseC)); var ba = new BaseA(1); @@ -121,24 +130,28 @@ declare class BaseC { x: number; private constructor(x); createInstance(): void; + static staticInstance(): void; } declare class DerivedA extends BaseA { x: number; constructor(x: number); createInstance(): void; createBaseInstance(): void; + static staticBaseInstance(): void; } declare class DerivedB extends BaseB { x: number; constructor(x: number); createInstance(): void; createBaseInstance(): void; + static staticBaseInstance(): void; } declare class DerivedC extends BaseC { x: number; constructor(x: number); createInstance(): void; createBaseInstance(): void; + static staticBaseInstance(): void; } declare var ba: BaseA; declare var bb: any; diff --git a/tests/baselines/reference/classConstructorAccessibility5.errors.txt b/tests/baselines/reference/classConstructorAccessibility5.errors.txt new file mode 100644 index 0000000000000..f372bd5c10a1b --- /dev/null +++ b/tests/baselines/reference/classConstructorAccessibility5.errors.txt @@ -0,0 +1,17 @@ +tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts(9,21): error TS2674: Constructor of class 'Base' is protected and only accessible within the class declaration. + + +==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts (1 errors) ==== + class Base { + protected constructor() { } + } + class Derived extends Base { + static make() { new Base() } // ok + } + + class Unrelated { + static fake() { new Base() } // error + ~~~~~~~~~~ +!!! error TS2674: Constructor of class 'Base' is protected and only accessible within the class declaration. + } + \ No newline at end of file diff --git a/tests/baselines/reference/classConstructorAccessibility5.js b/tests/baselines/reference/classConstructorAccessibility5.js new file mode 100644 index 0000000000000..ec9e9f83a8e41 --- /dev/null +++ b/tests/baselines/reference/classConstructorAccessibility5.js @@ -0,0 +1,38 @@ +//// [classConstructorAccessibility5.ts] +class Base { + protected constructor() { } +} +class Derived extends Base { + static make() { new Base() } // ok +} + +class Unrelated { + static fake() { new Base() } // error +} + + +//// [classConstructorAccessibility5.js] +var __extends = (this && this.__extends) || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); +}; +var Base = (function () { + function Base() { + } + return Base; +}()); +var Derived = (function (_super) { + __extends(Derived, _super); + function Derived() { + _super.apply(this, arguments); + } + Derived.make = function () { new Base(); }; // ok + return Derived; +}(Base)); +var Unrelated = (function () { + function Unrelated() { + } + Unrelated.fake = function () { new Base(); }; // error + return Unrelated; +}()); diff --git a/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts b/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts index 2a961e2c0679b..c5d2e6da43392 100644 --- a/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts +++ b/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts @@ -7,30 +7,34 @@ class BaseA { class BaseB { protected constructor(public x: number) { } - createInstance() { new BaseB(1); } + createInstance() { new BaseB(2); } } class BaseC { - private constructor(public x: number) { } - createInstance() { new BaseC(1); } + private constructor(public x: number) { } + createInstance() { new BaseC(3); } + static staticInstance() { new BaseC(4); } } class DerivedA extends BaseA { constructor(public x: number) { super(x); } - createInstance() { new DerivedA(1); } - createBaseInstance() { new BaseA(1); } + createInstance() { new DerivedA(5); } + createBaseInstance() { new BaseA(6); } + static staticBaseInstance() { new BaseA(7); } } class DerivedB extends BaseB { constructor(public x: number) { super(x); } - createInstance() { new DerivedB(1); } - createBaseInstance() { new BaseB(1); } // error + createInstance() { new DerivedB(7); } + createBaseInstance() { new BaseB(8); } // error + static staticBaseInstance() { new BaseB(9); } // ok } class DerivedC extends BaseC { // error constructor(public x: number) { super(x); } - createInstance() { new DerivedC(1); } - createBaseInstance() { new BaseC(1); } // error + createInstance() { new DerivedC(9); } + createBaseInstance() { new BaseC(10); } // error + static staticBaseInstance() { new BaseC(11); } // error } var ba = new BaseA(1); @@ -39,4 +43,4 @@ var bc = new BaseC(1); // error var da = new DerivedA(1); var db = new DerivedB(1); -var dc = new DerivedC(1); \ No newline at end of file +var dc = new DerivedC(1); diff --git a/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts b/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts new file mode 100644 index 0000000000000..ada5108a23af5 --- /dev/null +++ b/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility5.ts @@ -0,0 +1,10 @@ +class Base { + protected constructor() { } +} +class Derived extends Base { + static make() { new Base() } // ok +} + +class Unrelated { + static fake() { new Base() } // error +} From 97ef839a03fc16ea1aa5a7242ed68ad26814443f Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 22 Jul 2016 13:48:43 -0700 Subject: [PATCH 2/3] Protected ctors are accessible in subclass static methods Previously, it was an error to refer to a protected constructor from a base class, even in a static method where the semantics work. Now it is not an error in static methods. --- src/compiler/checker.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1cebd4693885b..ff248d7594142 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11544,8 +11544,23 @@ namespace ts { const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(declaration.parent.symbol); const declaringClass = getDeclaredTypeOfSymbol(declaration.parent.symbol); - // A private or protected constructor can only be instantiated within it's own class + // A private or protected constructor can only be instantiated within its own class or a static method of a subclass if (!isNodeWithinClass(node, declaringClassDeclaration)) { + const containingFunction = getContainingFunction(node); + const containingClass = getContainingClass(node); + if (containingClass) { + const containingType = getTypeOfNode(containingClass); + const baseTypes = getBaseTypes(containingType); + if (baseTypes.length) { + const baseType = baseTypes[0]; + if (containingFunction && + containingFunction.flags & NodeFlags.Static && + flags & NodeFlags.Protected && + baseType.symbol === declaration.parent.symbol) { + return true; + } + } + } if (flags & NodeFlags.Private) { error(node, Diagnostics.Constructor_of_class_0_is_private_and_only_accessible_within_the_class_declaration, typeToString(declaringClass)); } From 2169928f2b651d02f5eadada496c823e6bd9a310 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 22 Jul 2016 17:38:25 -0700 Subject: [PATCH 3/3] Protected constructors now accessible everywhere in subclasses --- src/compiler/checker.ts | 7 ++----- .../reference/classConstructorAccessibility2.errors.txt | 7 ++----- .../baselines/reference/classConstructorAccessibility2.js | 4 ++-- .../classConstructorAccessibility2.ts | 2 +- 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ff248d7594142..6da456afcbc4d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11544,18 +11544,15 @@ namespace ts { const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(declaration.parent.symbol); const declaringClass = getDeclaredTypeOfSymbol(declaration.parent.symbol); - // A private or protected constructor can only be instantiated within its own class or a static method of a subclass + // A private or protected constructor can only be instantiated within its own class (or a subclass, for protected) if (!isNodeWithinClass(node, declaringClassDeclaration)) { - const containingFunction = getContainingFunction(node); const containingClass = getContainingClass(node); if (containingClass) { const containingType = getTypeOfNode(containingClass); const baseTypes = getBaseTypes(containingType); if (baseTypes.length) { const baseType = baseTypes[0]; - if (containingFunction && - containingFunction.flags & NodeFlags.Static && - flags & NodeFlags.Protected && + if (flags & NodeFlags.Protected && baseType.symbol === declaration.parent.symbol) { return true; } diff --git a/tests/baselines/reference/classConstructorAccessibility2.errors.txt b/tests/baselines/reference/classConstructorAccessibility2.errors.txt index 01f2e2ea869bb..337e9787e5e3f 100644 --- a/tests/baselines/reference/classConstructorAccessibility2.errors.txt +++ b/tests/baselines/reference/classConstructorAccessibility2.errors.txt @@ -1,4 +1,3 @@ -tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(28,28): error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(32,24): error TS2675: Cannot extend a class 'BaseC'. Class constructor is marked as private. tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(35,28): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(36,35): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. @@ -6,7 +5,7 @@ tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessib tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts(41,10): error TS2673: Constructor of class 'BaseC' is private and only accessible within the class declaration. -==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts (6 errors) ==== +==== tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts (5 errors) ==== class BaseA { public constructor(public x: number) { } @@ -34,9 +33,7 @@ tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessib class DerivedB extends BaseB { constructor(public x: number) { super(x); } createInstance() { new DerivedB(7); } - createBaseInstance() { new BaseB(8); } // error - ~~~~~~~~~~~~ -!!! error TS2674: Constructor of class 'BaseB' is protected and only accessible within the class declaration. + createBaseInstance() { new BaseB(8); } // ok static staticBaseInstance() { new BaseB(9); } // ok } diff --git a/tests/baselines/reference/classConstructorAccessibility2.js b/tests/baselines/reference/classConstructorAccessibility2.js index 97d0dd94538cc..03b7af3e716c1 100644 --- a/tests/baselines/reference/classConstructorAccessibility2.js +++ b/tests/baselines/reference/classConstructorAccessibility2.js @@ -26,7 +26,7 @@ class DerivedA extends BaseA { class DerivedB extends BaseB { constructor(public x: number) { super(x); } createInstance() { new DerivedB(7); } - createBaseInstance() { new BaseB(8); } // error + createBaseInstance() { new BaseB(8); } // ok static staticBaseInstance() { new BaseB(9); } // ok } @@ -92,7 +92,7 @@ var DerivedB = (function (_super) { this.x = x; } DerivedB.prototype.createInstance = function () { new DerivedB(7); }; - DerivedB.prototype.createBaseInstance = function () { new BaseB(8); }; // error + DerivedB.prototype.createBaseInstance = function () { new BaseB(8); }; // ok DerivedB.staticBaseInstance = function () { new BaseB(9); }; // ok return DerivedB; }(BaseB)); diff --git a/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts b/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts index c5d2e6da43392..5e0ed2454df62 100644 --- a/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts +++ b/tests/cases/conformance/classes/constructorDeclarations/classConstructorAccessibility2.ts @@ -26,7 +26,7 @@ class DerivedA extends BaseA { class DerivedB extends BaseB { constructor(public x: number) { super(x); } createInstance() { new DerivedB(7); } - createBaseInstance() { new BaseB(8); } // error + createBaseInstance() { new BaseB(8); } // ok static staticBaseInstance() { new BaseB(9); } // ok }