Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly handle private and protected properties in intersections #13990

Merged
merged 4 commits into from
Feb 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 166 additions & 115 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1787,6 +1787,10 @@
"category": "Error",
"code": 2545
},
"Property '{0}' has conflicting declarations and is inaccessible in type '{1}'.": {
"category": "Error",
"code": 2546
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
Expand Down
80 changes: 44 additions & 36 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2600,37 +2600,34 @@

export const enum SymbolFlags {
None = 0,
FunctionScopedVariable = 0x00000001, // Variable (var) or parameter
BlockScopedVariable = 0x00000002, // A block-scoped variable (let or const)
Property = 0x00000004, // Property or enum member
EnumMember = 0x00000008, // Enum member
Function = 0x00000010, // Function
Class = 0x00000020, // Class
Interface = 0x00000040, // Interface
ConstEnum = 0x00000080, // Const enum
RegularEnum = 0x00000100, // Enum
ValueModule = 0x00000200, // Instantiated module
NamespaceModule = 0x00000400, // Uninstantiated module
TypeLiteral = 0x00000800, // Type Literal or mapped type
ObjectLiteral = 0x00001000, // Object Literal
Method = 0x00002000, // Method
Constructor = 0x00004000, // Constructor
GetAccessor = 0x00008000, // Get accessor
SetAccessor = 0x00010000, // Set accessor
Signature = 0x00020000, // Call, construct, or index signature
TypeParameter = 0x00040000, // Type parameter
TypeAlias = 0x00080000, // Type alias
ExportValue = 0x00100000, // Exported value marker (see comment in declareModuleMember in binder)
ExportType = 0x00200000, // Exported type marker (see comment in declareModuleMember in binder)
ExportNamespace = 0x00400000, // Exported namespace marker (see comment in declareModuleMember in binder)
Alias = 0x00800000, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Instantiated = 0x01000000, // Instantiated symbol
Merged = 0x02000000, // Merged symbol (created during program binding)
Transient = 0x04000000, // Transient symbol (created during type check)
Prototype = 0x08000000, // Prototype property (no source representation)
SyntheticProperty = 0x10000000, // Property in union or intersection type
Optional = 0x20000000, // Optional property
ExportStar = 0x40000000, // Export * declaration
FunctionScopedVariable = 1 << 0, // Variable (var) or parameter
BlockScopedVariable = 1 << 1, // A block-scoped variable (let or const)
Property = 1 << 2, // Property or enum member
EnumMember = 1 << 3, // Enum member
Function = 1 << 4, // Function
Class = 1 << 5, // Class
Interface = 1 << 6, // Interface
ConstEnum = 1 << 7, // Const enum
RegularEnum = 1 << 8, // Enum
ValueModule = 1 << 9, // Instantiated module
NamespaceModule = 1 << 10, // Uninstantiated module
TypeLiteral = 1 << 11, // Type Literal or mapped type
ObjectLiteral = 1 << 12, // Object Literal
Method = 1 << 13, // Method
Constructor = 1 << 14, // Constructor
GetAccessor = 1 << 15, // Get accessor
SetAccessor = 1 << 16, // Set accessor
Signature = 1 << 17, // Call, construct, or index signature
TypeParameter = 1 << 18, // Type parameter
TypeAlias = 1 << 19, // Type alias
ExportValue = 1 << 20, // Exported value marker (see comment in declareModuleMember in binder)
ExportType = 1 << 21, // Exported type marker (see comment in declareModuleMember in binder)
ExportNamespace = 1 << 22, // Exported namespace marker (see comment in declareModuleMember in binder)
Alias = 1 << 23, // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
Prototype = 1 << 24, // Prototype property (no source representation)
ExportStar = 1 << 25, // Export * declaration
Optional = 1 << 26, // Optional property
Transient = 1 << 27, // Transient symbol (created during type check)

Enum = RegularEnum | ConstEnum,
Variable = FunctionScopedVariable | BlockScopedVariable,
Expand Down Expand Up @@ -2690,11 +2687,9 @@
name: string; // Name of symbol
declarations?: Declaration[]; // Declarations associated with this symbol
valueDeclaration?: Declaration; // First value declaration of the symbol

members?: SymbolTable; // Class, interface or literal instance members
exports?: SymbolTable; // Module exports
globalExports?: SymbolTable; // Conditional global UMD exports
/* @internal */ isReadonly?: boolean; // readonly? (set only for intersections and unions)
/* @internal */ id?: number; // Unique id (used to look up SymbolLinks)
/* @internal */ mergeId?: number; // Merge id (used to look up merged symbol)
/* @internal */ parent?: Symbol; // Parent symbol
Expand All @@ -2719,8 +2714,6 @@
leftSpread?: Symbol; // Left source for synthetic spread property
rightSpread?: Symbol; // Right source for synthetic spread property
mappedTypeOrigin?: Symbol; // For a property on a mapped type, points back to the orignal 'T' from 'keyof T'.
hasNonUniformType?: boolean; // True if constituents have non-uniform types
isPartial?: boolean; // True if syntheric property of union type occurs in some but not all constituents
isDiscriminantProperty?: boolean; // True if discriminant synthetic property
resolvedExports?: SymbolTable; // Resolved exports of module
exportsChecked?: boolean; // True if exports of external module have been checked
Expand All @@ -2730,7 +2723,22 @@
}

/* @internal */
export interface TransientSymbol extends Symbol, SymbolLinks { }
export const enum CheckFlags {
Instantiated = 1 << 0, // Instantiated symbol
SyntheticProperty = 1 << 1, // Property in union or intersection type
Readonly = 1 << 2, // Readonly transient symbol
Partial = 1 << 3, // Synthetic property present in some but not all constituents
HasNonUniformType = 1 << 4, // Synthetic property with non-uniform type in constituents
ContainsPublic = 1 << 5, // Synthetic property with public constituent(s)
ContainsProtected = 1 << 6, // Synthetic property with protected constituent(s)
ContainsPrivate = 1 << 7, // Synthetic property with private constituent(s)
ContainsStatic = 1 << 8, // Synthetic property with static constituent(s)
}

/* @internal */
export interface TransientSymbol extends Symbol, SymbolLinks {
checkFlags: CheckFlags;
}

export type SymbolTable = Map<Symbol>;

Expand Down
2 changes: 1 addition & 1 deletion src/services/findAllReferences.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ namespace ts.FindAllReferences {

// if this symbol is visible from its parent container, e.g. exported, then bail out
// if symbol correspond to the union property - bail out
if (symbol.parent || (symbol.flags & SymbolFlags.SyntheticProperty)) {
if (symbol.parent || (symbol.flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty)) {
return undefined;
}

Expand Down
2 changes: 1 addition & 1 deletion src/services/symbolDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ namespace ts.SymbolDisplay {
if (flags & SymbolFlags.Constructor) return ScriptElementKind.constructorImplementationElement;

if (flags & SymbolFlags.Property) {
if (flags & SymbolFlags.SyntheticProperty) {
if (flags & SymbolFlags.Transient && (<TransientSymbol>symbol).checkFlags & CheckFlags.SyntheticProperty) {
// If union property is result of union of non method (property/accessors/variables), it is labeled as property
const unionPropertyKind = forEach(typeChecker.getRootSymbols(symbol), rootSymbol => {
const rootSymbolFlags = rootSymbol.getFlags();
Expand Down
156 changes: 156 additions & 0 deletions tests/baselines/reference/mixinAccessModifiers.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
tests/cases/conformance/classes/mixinAccessModifiers.ts(39,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Private2'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(43,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Protected'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(47,4): error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Public'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(51,4): error TS2445: Property 'p' is protected and only accessible within class 'Protected & Protected2' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(66,7): error TS2415: Class 'C1' incorrectly extends base class 'Private & Private2'.
Type 'C1' is not assignable to type 'Private'.
Property 'p' has conflicting declarations and is inaccessible in type 'C1'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(67,7): error TS2415: Class 'C2' incorrectly extends base class 'Private & Protected'.
Type 'C2' is not assignable to type 'Private'.
Property 'p' has conflicting declarations and is inaccessible in type 'C2'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(68,7): error TS2415: Class 'C3' incorrectly extends base class 'Private & Public'.
Type 'C3' is not assignable to type 'Private'.
Property 'p' has conflicting declarations and is inaccessible in type 'C3'.
tests/cases/conformance/classes/mixinAccessModifiers.ts(85,6): error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(90,6): error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(98,6): error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
tests/cases/conformance/classes/mixinAccessModifiers.ts(103,6): error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.


==== tests/cases/conformance/classes/mixinAccessModifiers.ts (11 errors) ====

type Constructable = new (...args: any[]) => object;

class Private {
constructor (...args: any[]) {}
private p: string;
}

class Private2 {
constructor (...args: any[]) {}
private p: string;
}

class Protected {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}

class Protected2 {
constructor (...args: any[]) {}
protected p: string;
protected static s: string;
}

class Public {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}

class Public2 {
constructor (...args: any[]) {}
public p: string;
public static s: string;
}

function f1(x: Private & Private2) {
x.p; // Error, private constituent makes property inaccessible
~
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Private2'.
}

function f2(x: Private & Protected) {
x.p; // Error, private constituent makes property inaccessible
~
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Protected'.
}

function f3(x: Private & Public) {
x.p; // Error, private constituent makes property inaccessible
~
!!! error TS2546: Property 'p' has conflicting declarations and is inaccessible in type 'Private & Public'.
}

function f4(x: Protected & Protected2) {
x.p; // Error, protected when all constituents are protected
~
!!! error TS2445: Property 'p' is protected and only accessible within class 'Protected & Protected2' and its subclasses.
}

function f5(x: Protected & Public) {
x.p; // Ok, public if any constituent is public
}

function f6(x: Public & Public2) {
x.p; // Ok, public if any constituent is public
}

declare function Mix<T, U>(c1: T, c2: U): T & U;

// Can't derive from type with inaccessible properties

class C1 extends Mix(Private, Private2) {}
~~
!!! error TS2415: Class 'C1' incorrectly extends base class 'Private & Private2'.
!!! error TS2415: Type 'C1' is not assignable to type 'Private'.
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C1'.
class C2 extends Mix(Private, Protected) {}
~~
!!! error TS2415: Class 'C2' incorrectly extends base class 'Private & Protected'.
!!! error TS2415: Type 'C2' is not assignable to type 'Private'.
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C2'.
class C3 extends Mix(Private, Public) {}
~~
!!! error TS2415: Class 'C3' incorrectly extends base class 'Private & Public'.
!!! error TS2415: Type 'C3' is not assignable to type 'Private'.
!!! error TS2415: Property 'p' has conflicting declarations and is inaccessible in type 'C3'.

class C4 extends Mix(Protected, Protected2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p;
c5.p;
c6.p;
}
static g() {
C4.s;
C5.s;
C6.s
}
}

class C5 extends Mix(Protected, Public) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
C5.s;
C6.s
}
}

class C6 extends Mix(Public, Public2) {
f(c4: C4, c5: C5, c6: C6) {
c4.p; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 'p' is protected and only accessible within class 'C4' and its subclasses.
c5.p;
c6.p;
}
static g() {
C4.s; // Error, not in class deriving from Protected2
~
!!! error TS2445: Property 's' is protected and only accessible within class 'typeof C4' and its subclasses.
C5.s;
C6.s
}
}

Loading