diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf35c1c02f5..99b387dd18c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -92,7 +92,8 @@ module ts { getContextualType: getContextualType, getFullyQualifiedName: getFullyQualifiedName, getResolvedSignature: getResolvedSignature, - getEnumMemberValue: getEnumMemberValue + getEnumMemberValue: getEnumMemberValue, + isValidPropertyAccess: isValidPropertyAccess }; var undefinedSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, "undefined"); @@ -4239,6 +4240,25 @@ module ts { return anyType; } + function isValidPropertyAccess(node: PropertyAccess, propertyName: string): boolean { + var type = checkExpression(node.left); + if (type !== unknownType && type !== anyType) { + var apparentType = getApparentType(getWidenedType(type)); + var prop = getPropertyOfApparentType(apparentType, propertyName); + if (prop && prop.parent && prop.parent.flags & SymbolFlags.Class) { + if (node.left.kind === SyntaxKind.SuperKeyword && getDeclarationKindFromSymbol(prop) !== SyntaxKind.Method) { + return false; + } + else { + var diagnosticsCount = diagnostics.length; + checkClassPropertyAccess(node, type, prop); + return diagnostics.length === diagnosticsCount + } + } + } + return true; + } + function checkIndexedAccess(node: IndexedAccess): Type { var objectType = checkExpression(node.object); var indexType = checkExpression(node.index); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index dcc42bf3358..b564dba560b 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -655,6 +655,8 @@ module ts { // Returns the constant value of this enum member, or 'undefined' if the enum member has a // computed value. getEnumMemberValue(node: EnumMember): number; + + isValidPropertyAccess(node: PropertyAccess, propertyName: string): boolean; } export interface TextWriter { diff --git a/src/services/services.ts b/src/services/services.ts index 7d11d9ab32d..06a1dd49e27 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2038,15 +2038,6 @@ module ts { return (SyntaxKind.FirstPunctuation <= kind && kind <= SyntaxKind.LastPunctuation); } - function isVisibleWithinClassDeclaration(symbol: Symbol, containingClass: Declaration): boolean { - var declaration = symbol.declarations && symbol.declarations[0]; - if (declaration && (declaration.flags & NodeFlags.Private)) { - var declarationClass = getAncestor(declaration, SyntaxKind.ClassDeclaration); - return containingClass === declarationClass; - } - return true; - } - function filterContextualMembersList(contextualMemberSymbols: Symbol[], existingMembers: Declaration[]): Symbol[] { if (!existingMembers || existingMembers.length === 0) { return contextualMemberSymbols; @@ -2150,7 +2141,6 @@ module ts { // Right of dot member completion list if (isRightOfDot) { var symbols: Symbol[] = []; - var containingClass = getAncestor(mappedNode, SyntaxKind.ClassDeclaration); isMemberCompletion = true; if (mappedNode.kind === SyntaxKind.Identifier || mappedNode.kind === SyntaxKind.QualifiedName || mappedNode.kind === SyntaxKind.PropertyAccess) { @@ -2158,7 +2148,7 @@ module ts { if (symbol && symbol.flags & SymbolFlags.HasExports) { // Extract module or enum members forEachValue(symbol.exports, symbol => { - if (isVisibleWithinClassDeclaration(symbol, containingClass)) { + if (typeInfoResolver.isValidPropertyAccess((mappedNode.parent), symbol.name)) { symbols.push(symbol); } }); @@ -2170,7 +2160,7 @@ module ts { if (apparentType) { // Filter private properties forEach(apparentType.getApparentProperties(), symbol => { - if (isVisibleWithinClassDeclaration(symbol, containingClass)) { + if (typeInfoResolver.isValidPropertyAccess((mappedNode.parent), symbol.name)) { symbols.push(symbol); } }); diff --git a/tests/cases/fourslash/completionListInstanceProtectedMembers.ts b/tests/cases/fourslash/completionListInstanceProtectedMembers.ts new file mode 100644 index 00000000000..de09f059680 --- /dev/null +++ b/tests/cases/fourslash/completionListInstanceProtectedMembers.ts @@ -0,0 +1,63 @@ +/// + +////class Base { +//// private privateMethod() { } +//// private privateProperty; +//// +//// protected protectedMethod() { } +//// protected protectedProperty; +//// +//// public publicMethod() { } +//// public publicProperty; +//// +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +//// +//// test() { +//// this./*1*/; +//// +//// var b: Base; +//// var c: C1; +//// +//// b./*2*/; +//// c./*3*/; +//// } +////} +//// +////class C1 extends Base { +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +////} + + +// Same class, everything is visible +goTo.marker("1"); +verify.memberListContains('privateMethod'); +verify.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +goTo.marker("2"); +verify.memberListContains('privateMethod'); +verify.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +// Can not access protected properties overridden in subclass +goTo.marker("3"); +verify.memberListContains('privateMethod'); +verify.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListInstanceProtectedMembers2.ts b/tests/cases/fourslash/completionListInstanceProtectedMembers2.ts new file mode 100644 index 00000000000..72b6f3a1f7b --- /dev/null +++ b/tests/cases/fourslash/completionListInstanceProtectedMembers2.ts @@ -0,0 +1,76 @@ +/// + +////class Base { +//// private privateMethod() { } +//// private privateProperty; +//// +//// protected protectedMethod() { } +//// protected protectedProperty; +//// +//// public publicMethod() { } +//// public publicProperty; +//// +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +////} +//// +////class C1 extends Base { +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +//// +//// test() { +//// this./*1*/; +//// super./*2*/; +//// +//// var b: Base; +//// var c: C1; +//// +//// b./*3*/; +//// c./*4*/; +//// } +////} + + +// Same class, everything is visible +goTo.marker("1"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +// Can not access properties on super +goTo.marker("2"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.not.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); + +// Can not access protected properties through base class +goTo.marker("3"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); + +// Same class, everything is visible +goTo.marker("4"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); diff --git a/tests/cases/fourslash/completionListInstanceProtectedMembers3.ts b/tests/cases/fourslash/completionListInstanceProtectedMembers3.ts new file mode 100644 index 00000000000..2b3bf2ac2d9 --- /dev/null +++ b/tests/cases/fourslash/completionListInstanceProtectedMembers3.ts @@ -0,0 +1,46 @@ +/// + +////class Base { +//// private privateMethod() { } +//// private privateProperty; +//// +//// protected protectedMethod() { } +//// protected protectedProperty; +//// +//// public publicMethod() { } +//// public publicProperty; +//// +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +////} +//// +////class C1 extends Base { +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +////} +//// +//// var b: Base; +//// var c: C1; +//// b./*1*/; +//// c./*2*/; + +// Only public properties are visible outside the class +goTo.marker("1"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); + +goTo.marker("2"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); diff --git a/tests/cases/fourslash/completionListInstanceProtectedMembers4.ts b/tests/cases/fourslash/completionListInstanceProtectedMembers4.ts new file mode 100644 index 00000000000..27f9bb5a1cf --- /dev/null +++ b/tests/cases/fourslash/completionListInstanceProtectedMembers4.ts @@ -0,0 +1,34 @@ +/// + +////class Base { +//// private privateMethod() { } +//// private privateProperty; +//// +//// protected protectedMethod() { } +//// protected protectedProperty; +//// +//// public publicMethod() { } +//// public publicProperty; +//// +//// protected protectedOverriddenMethod() { } +//// protected protectedOverriddenProperty; +////} +//// +////class C1 extends Base { +//// public protectedOverriddenMethod() { } +//// public protectedOverriddenProperty; +////} +//// +//// var c: C1; +//// c./*1*/ + + +goTo.marker("1"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); diff --git a/tests/cases/fourslash/completionListProtectedMembers.ts b/tests/cases/fourslash/completionListProtectedMembers.ts new file mode 100644 index 00000000000..4715a9fb714 --- /dev/null +++ b/tests/cases/fourslash/completionListProtectedMembers.ts @@ -0,0 +1,44 @@ +/// + +////class Base { +//// protected y; +//// constructor(protected x) {} +//// method() { this./*1*/; } +////} +////class D1 extends Base { +//// protected z; +//// method1() { this./*2*/; } +////} +////class D2 extends Base { +//// method2() { this./*3*/; } +////} +////class D3 extends D1 { +//// method2() { this./*4*/; } +////} +////var b: Base; +////f./*5*/ + +goTo.marker("1"); +verify.memberListContains("y"); +verify.memberListContains("x"); +verify.not.memberListContains("z"); + +goTo.marker("2"); +verify.memberListContains("y"); +verify.memberListContains("x"); +verify.memberListContains("z"); + +goTo.marker("3"); +verify.memberListContains("y"); +verify.memberListContains("x"); +verify.not.memberListContains("z"); + +goTo.marker("4"); +verify.memberListContains("y"); +verify.memberListContains("x"); +verify.memberListContains("z"); + +goTo.marker("5"); +verify.not.memberListContains("x"); +verify.not.memberListContains("y"); +verify.not.memberListContains("z"); diff --git a/tests/cases/fourslash/completionListStaticProtectedMembers.ts b/tests/cases/fourslash/completionListStaticProtectedMembers.ts new file mode 100644 index 00000000000..af56f28ef06 --- /dev/null +++ b/tests/cases/fourslash/completionListStaticProtectedMembers.ts @@ -0,0 +1,59 @@ +/// + +////class Base { +//// private static privateMethod() { } +//// private static privateProperty; +//// +//// protected static protectedMethod() { } +//// protected static protectedProperty; +//// +//// public static publicMethod() { } +//// public static publicProperty; +//// +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +//// +//// static test() { +//// Base./*1*/; +//// this./*2*/; +//// C1./*3*/; +//// } +////} +//// +////class C1 extends Base { +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +////} + + +// Same class, everything is visible +goTo.marker("1"); +verify.memberListContains('privateMethod'); +verify.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +goTo.marker("2"); +verify.memberListContains('privateMethod'); +verify.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +// Can not access protected properties overridden in subclass +goTo.marker("3"); +verify.memberListContains('privateMethod'); +verify.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListStaticProtectedMembers2.ts b/tests/cases/fourslash/completionListStaticProtectedMembers2.ts new file mode 100644 index 00000000000..9964a91d21b --- /dev/null +++ b/tests/cases/fourslash/completionListStaticProtectedMembers2.ts @@ -0,0 +1,70 @@ +/// + +////class Base { +//// private static privateMethod() { } +//// private static privateProperty; +//// +//// protected static protectedMethod() { } +//// protected static protectedProperty; +//// +//// public static publicMethod() { } +//// public static publicProperty; +//// +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +////} +//// +////class C2 extends Base { +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +//// +//// static test() { +//// Base./*1*/; +//// C2./*2*/; +//// this./*3*/; +//// super./*4*/; +//// } +////} + + +// Same class, everything is visible +goTo.marker("1"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +goTo.marker("2"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +goTo.marker("3"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +// only public and protected methods of the base class are accessible through super +goTo.marker("4"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.not.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListStaticProtectedMembers3.ts b/tests/cases/fourslash/completionListStaticProtectedMembers3.ts new file mode 100644 index 00000000000..f80186c93c0 --- /dev/null +++ b/tests/cases/fourslash/completionListStaticProtectedMembers3.ts @@ -0,0 +1,45 @@ +/// + +////class Base { +//// private static privateMethod() { } +//// private static privateProperty; +//// +//// protected static protectedMethod() { } +//// protected static protectedProperty; +//// +//// public static publicMethod() { } +//// public static publicProperty; +//// +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +////} +//// +////class C3 extends Base { +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +////} +//// +////Base./*1*/; +////C3./*2*/; + + +// Only public properties are visible outside the class +goTo.marker("1"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); + +goTo.marker("2"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.not.memberListContains('protectedOverriddenMethod'); +verify.not.memberListContains('protectedOverriddenProperty'); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListStaticProtectedMembers4.ts b/tests/cases/fourslash/completionListStaticProtectedMembers4.ts new file mode 100644 index 00000000000..72cc081f2fc --- /dev/null +++ b/tests/cases/fourslash/completionListStaticProtectedMembers4.ts @@ -0,0 +1,49 @@ +/// + +////class Base { +//// private static privateMethod() { } +//// private static privateProperty; +//// +//// protected static protectedMethod() { } +//// protected static protectedProperty; +//// +//// public static publicMethod() { } +//// public static publicProperty; +//// +//// protected static protectedOverriddenMethod() { } +//// protected static protectedOverriddenProperty; +////} +//// +/////// Make the protected members public +////class C4 extends Base { +//// public static protectedOverriddenMethod() { } +//// public static protectedOverriddenProperty; +////} +////class Derived extends C4 { +//// test() { +//// Derived./*1*/ +//// } +////} +//// Derived./*2*/ + +// Sub class, everything but private is visible +goTo.marker("1"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.memberListContains('protectedMethod'); +verify.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); + +// Can see protected methods elevated to public +goTo.marker("2"); +verify.not.memberListContains('privateMethod'); +verify.not.memberListContains('privateProperty'); +verify.not.memberListContains('protectedMethod'); +verify.not.memberListContains('protectedProperty'); +verify.memberListContains('publicMethod'); +verify.memberListContains('publicProperty'); +verify.memberListContains('protectedOverriddenMethod'); +verify.memberListContains('protectedOverriddenProperty'); diff --git a/tests/cases/fourslash/completionListSuperMembers.ts b/tests/cases/fourslash/completionListSuperMembers.ts index c203a8774fb..6c7ddd5c7b3 100644 --- a/tests/cases/fourslash/completionListSuperMembers.ts +++ b/tests/cases/fourslash/completionListSuperMembers.ts @@ -25,7 +25,7 @@ goTo.marker(); -verify.memberListContains("publicProperty"); +verify.not.memberListContains("publicProperty"); verify.memberListContains("publicInstanceMethod"); // No statics verify.not.memberListContains("publicStaticProperty");