Handle intersection types when looking up base types for visibility (#25418)

* Handle intersection types when looking up base types for visibility

* Extract protected constructor check to function and recur on intersections

* Remove unneeded cast
This commit is contained in:
Wesley Wigham
2018-07-09 16:58:55 -07:00
committed by GitHub
parent 6b4a3b2bbb
commit 3d64b9d7ac
6 changed files with 261 additions and 9 deletions
+35 -9
View File
@@ -19345,6 +19345,38 @@ namespace ts {
return resolveErrorCall(node);
}
function typeHasProtectedAccessibleBase(target: Symbol, type: InterfaceType): boolean {
const baseTypes = getBaseTypes(type);
if (!length(baseTypes)) {
return false;
}
const firstBase = baseTypes[0];
if (firstBase.flags & TypeFlags.Intersection) {
const types = (firstBase as IntersectionType).types;
const mixinCount = countWhere(types, isMixinConstructorType);
let i = 0;
for (const intersectionMember of (firstBase as IntersectionType).types) {
i++;
// We want to ignore mixin ctors
if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(intersectionMember)) {
if (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) {
if (intersectionMember.symbol === target) {
return true;
}
if (typeHasProtectedAccessibleBase(target, intersectionMember as InterfaceType)) {
return true;
}
}
}
}
return false;
}
if (firstBase.symbol === target) {
return true;
}
return typeHasProtectedAccessibleBase(target, firstBase as InterfaceType);
}
function isConstructorAccessible(node: NewExpression, signature: Signature) {
if (!signature || !signature.declaration) {
return true;
@@ -19364,16 +19396,10 @@ namespace ts {
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
const containingClass = getContainingClass(node);
if (containingClass) {
if (containingClass && modifiers & ModifierFlags.Protected) {
const containingType = getTypeOfNode(containingClass);
let baseTypes = getBaseTypes(containingType as InterfaceType);
while (baseTypes.length) {
const baseType = baseTypes[0];
if (modifiers & ModifierFlags.Protected &&
baseType.symbol === declaration.parent.symbol) {
return true;
}
baseTypes = getBaseTypes(baseType as InterfaceType);
if (typeHasProtectedAccessibleBase(declaration.parent.symbol, containingType as InterfaceType)) {
return true;
}
}
if (modifiers & ModifierFlags.Private) {
@@ -0,0 +1,29 @@
tests/cases/compiler/noCrashOnMixin.ts(21,9): error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration.
==== tests/cases/compiler/noCrashOnMixin.ts (1 errors) ====
class Abstract {
protected constructor() {
}
}
class Concrete extends Abstract {
}
type Constructor<T = {}> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor>(Base: TBase) {
return class extends Base {
};
}
class Empty {
}
class CrashTrigger extends Mixin(Empty) {
public trigger() {
new Concrete();
~~~~~~~~~~~~~~
!!! error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration.
}
}
@@ -0,0 +1,75 @@
//// [noCrashOnMixin.ts]
class Abstract {
protected constructor() {
}
}
class Concrete extends Abstract {
}
type Constructor<T = {}> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor>(Base: TBase) {
return class extends Base {
};
}
class Empty {
}
class CrashTrigger extends Mixin(Empty) {
public trigger() {
new Concrete();
}
}
//// [noCrashOnMixin.js]
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
}
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var Abstract = /** @class */ (function () {
function Abstract() {
}
return Abstract;
}());
var Concrete = /** @class */ (function (_super) {
__extends(Concrete, _super);
function Concrete() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Concrete;
}(Abstract));
function Mixin(Base) {
return /** @class */ (function (_super) {
__extends(class_1, _super);
function class_1() {
return _super !== null && _super.apply(this, arguments) || this;
}
return class_1;
}(Base));
}
var Empty = /** @class */ (function () {
function Empty() {
}
return Empty;
}());
var CrashTrigger = /** @class */ (function (_super) {
__extends(CrashTrigger, _super);
function CrashTrigger() {
return _super !== null && _super.apply(this, arguments) || this;
}
CrashTrigger.prototype.trigger = function () {
new Concrete();
};
return CrashTrigger;
}(Mixin(Empty)));
@@ -0,0 +1,48 @@
=== tests/cases/compiler/noCrashOnMixin.ts ===
class Abstract {
>Abstract : Symbol(Abstract, Decl(noCrashOnMixin.ts, 0, 0))
protected constructor() {
}
}
class Concrete extends Abstract {
>Concrete : Symbol(Concrete, Decl(noCrashOnMixin.ts, 3, 1))
>Abstract : Symbol(Abstract, Decl(noCrashOnMixin.ts, 0, 0))
}
type Constructor<T = {}> = new (...args: any[]) => T;
>Constructor : Symbol(Constructor, Decl(noCrashOnMixin.ts, 6, 1))
>T : Symbol(T, Decl(noCrashOnMixin.ts, 8, 17))
>args : Symbol(args, Decl(noCrashOnMixin.ts, 8, 32))
>T : Symbol(T, Decl(noCrashOnMixin.ts, 8, 17))
function Mixin<TBase extends Constructor>(Base: TBase) {
>Mixin : Symbol(Mixin, Decl(noCrashOnMixin.ts, 8, 53))
>TBase : Symbol(TBase, Decl(noCrashOnMixin.ts, 10, 15))
>Constructor : Symbol(Constructor, Decl(noCrashOnMixin.ts, 6, 1))
>Base : Symbol(Base, Decl(noCrashOnMixin.ts, 10, 42))
>TBase : Symbol(TBase, Decl(noCrashOnMixin.ts, 10, 15))
return class extends Base {
>Base : Symbol(Base, Decl(noCrashOnMixin.ts, 10, 42))
};
}
class Empty {
>Empty : Symbol(Empty, Decl(noCrashOnMixin.ts, 13, 1))
}
class CrashTrigger extends Mixin(Empty) {
>CrashTrigger : Symbol(CrashTrigger, Decl(noCrashOnMixin.ts, 16, 1))
>Mixin : Symbol(Mixin, Decl(noCrashOnMixin.ts, 8, 53))
>Empty : Symbol(Empty, Decl(noCrashOnMixin.ts, 13, 1))
public trigger() {
>trigger : Symbol(CrashTrigger.trigger, Decl(noCrashOnMixin.ts, 18, 41))
new Concrete();
>Concrete : Symbol(Concrete, Decl(noCrashOnMixin.ts, 3, 1))
}
}
@@ -0,0 +1,51 @@
=== tests/cases/compiler/noCrashOnMixin.ts ===
class Abstract {
>Abstract : Abstract
protected constructor() {
}
}
class Concrete extends Abstract {
>Concrete : Concrete
>Abstract : Abstract
}
type Constructor<T = {}> = new (...args: any[]) => T;
>Constructor : Constructor<T>
>T : T
>args : any[]
>T : T
function Mixin<TBase extends Constructor>(Base: TBase) {
>Mixin : <TBase extends Constructor<{}>>(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
>TBase : TBase
>Constructor : Constructor<T>
>Base : TBase
>TBase : TBase
return class extends Base {
>class extends Base { } : { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
>Base : {}
};
}
class Empty {
>Empty : Empty
}
class CrashTrigger extends Mixin(Empty) {
>CrashTrigger : CrashTrigger
>Mixin(Empty) : Mixin<typeof Empty>.(Anonymous class) & Empty
>Mixin : <TBase extends Constructor<{}>>(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
>Empty : typeof Empty
public trigger() {
>trigger : () => void
new Concrete();
>new Concrete() : any
>Concrete : typeof Concrete
}
}
+23
View File
@@ -0,0 +1,23 @@
class Abstract {
protected constructor() {
}
}
class Concrete extends Abstract {
}
type Constructor<T = {}> = new (...args: any[]) => T;
function Mixin<TBase extends Constructor>(Base: TBase) {
return class extends Base {
};
}
class Empty {
}
class CrashTrigger extends Mixin(Empty) {
public trigger() {
new Concrete();
}
}