diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 292dfb4bfc637..cc02323b291ae 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2972,7 +2972,7 @@ module ts { var targetClass = getDeclaredTypeOfSymbol(targetProp.parent); if (!sourceClass || !hasBaseType(sourceClass, targetClass)) { if (reportErrors) { - reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_derived_from_type_2, + reportError(Diagnostics.Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2, symbolToString(targetProp), typeToString(sourceClass || source), typeToString(targetClass)); } return false; @@ -4012,38 +4012,42 @@ module ts { return s.valueDeclaration ? s.valueDeclaration.flags : s.flags & SymbolFlags.Prototype ? NodeFlags.Public | NodeFlags.Static : 0; } - function isClassPropertyAccessible(node: PropertyAccess, type: Type, prop: Symbol): boolean { + function checkClassPropertyAccess(node: PropertyAccess, type: Type, prop: Symbol) { var flags = getDeclarationFlagsFromSymbol(prop); // Public properties are always accessible if (!(flags & (NodeFlags.Private | NodeFlags.Protected))) { - return true; + return; } // Property is known to be private or protected at this point - // Private and protected properties are never accessible outside a class declaration - var enclosingClassDeclaration = getAncestor(node, SyntaxKind.ClassDeclaration); - if (!enclosingClassDeclaration) { - return false; - } // Get the declaring and enclosing class instance types + var enclosingClassDeclaration = getAncestor(node, SyntaxKind.ClassDeclaration); + var enclosingClass = enclosingClassDeclaration ? getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClassDeclaration)) : undefined; var declaringClass = getDeclaredTypeOfSymbol(prop.parent); - var enclosingClass = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingClassDeclaration)); // Private property is accessible if declaring and enclosing class are the same if (flags & NodeFlags.Private) { - return declaringClass === enclosingClass; + if (declaringClass !== enclosingClass) { + error(node, Diagnostics.Property_0_is_private_and_only_accessible_within_class_1, symbolToString(prop), typeToString(declaringClass)); + } + return; } // Property is known to be protected at this point // All protected properties of a supertype are accessible in a super access if (node.left.kind === SyntaxKind.SuperKeyword) { - return true; + return; + } + // A protected property is accessible in the declaring class and classes derived from it + if (!enclosingClass || !hasBaseType(enclosingClass, declaringClass)) { + error(node, Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses, symbolToString(prop), typeToString(declaringClass)); + return; + } + // No further restrictions for static properties + if (flags & NodeFlags.Static) { + return; } // An instance property must be accessed through an instance of the enclosing class - if (!(flags & NodeFlags.Static)) { - if (!(getTargetType(type).flags & (TypeFlags.Class | TypeFlags.Interface) && hasBaseType(type, enclosingClass))) { - return false; - } + if (!(getTargetType(type).flags & (TypeFlags.Class | TypeFlags.Interface) && hasBaseType(type, enclosingClass))) { + error(node, Diagnostics.Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1, symbolToString(prop), typeToString(enclosingClass)); } - // A protected property is accessible in the declaring class and classes derived from it - return hasBaseType(enclosingClass, declaringClass); } function checkPropertyAccess(node: PropertyAccess) { @@ -4074,8 +4078,8 @@ module ts { if (node.left.kind === SyntaxKind.SuperKeyword && getDeclarationKindFromSymbol(prop) !== SyntaxKind.Method) { error(node.right, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword); } - else if (!isClassPropertyAccessible(node, type, prop)) { - error(node, Diagnostics.Property_0_is_inaccessible, getFullyQualifiedName(prop)); + else { + checkClassPropertyAccess(node, type, prop); } } return getTypeOfSymbol(prop); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 772a386af2c22..ba6f742c4c14c 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -154,7 +154,7 @@ module ts { super_property_access_is_permitted_only_in_a_constructor_member_function_or_member_accessor_of_a_derived_class: { code: 2338, category: DiagnosticCategory.Error, key: "'super' property access is permitted only in a constructor, member function, or member accessor of a derived class" }, Property_0_does_not_exist_on_type_1: { code: 2339, category: DiagnosticCategory.Error, key: "Property '{0}' does not exist on type '{1}'." }, Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword: { code: 2340, category: DiagnosticCategory.Error, key: "Only public and protected methods of the base class are accessible via the 'super' keyword" }, - Property_0_is_inaccessible: { code: 2341, category: DiagnosticCategory.Error, key: "Property '{0}' is inaccessible." }, + Property_0_is_private_and_only_accessible_within_class_1: { code: 2341, category: DiagnosticCategory.Error, key: "Property '{0}' is private and only accessible within class '{1}'." }, An_index_expression_argument_must_be_of_type_string_number_or_any: { code: 2342, category: DiagnosticCategory.Error, key: "An index expression argument must be of type 'string', 'number', or 'any'." }, Type_0_does_not_satisfy_the_constraint_1_Colon: { code: 2343, category: DiagnosticCategory.Error, key: "Type '{0}' does not satisfy the constraint '{1}':" }, Type_0_does_not_satisfy_the_constraint_1: { code: 2344, category: DiagnosticCategory.Error, key: "Type '{0}' does not satisfy the constraint '{1}'." }, @@ -256,8 +256,10 @@ module ts { Import_declaration_conflicts_with_local_declaration_of_0: { code: 2440, category: DiagnosticCategory.Error, key: "Import declaration conflicts with local declaration of '{0}'" }, Duplicate_identifier_0_Compiler_reserves_name_1_in_top_level_scope_of_an_external_module: { code: 2441, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'. Compiler reserves name '{1}' in top level scope of an external module." }, Types_have_separate_declarations_of_a_private_property_0: { code: 2442, category: DiagnosticCategory.Error, key: "Types have separate declarations of a private property '{0}'." }, - Property_0_is_protected_but_type_1_is_not_derived_from_type_2: { code: 2443, category: DiagnosticCategory.Error, key: "Property '{0}' is protected but type '{1}' is not derived from type '{2}'." }, + Property_0_is_protected_but_type_1_is_not_a_class_derived_from_2: { code: 2443, category: DiagnosticCategory.Error, key: "Property '{0}' is protected but type '{1}' is not a class derived from '{2}'." }, Property_0_is_protected_in_type_1_but_public_in_type_2: { code: 2444, category: DiagnosticCategory.Error, key: "Property '{0}' is protected in type '{1}' but public in type '{2}'." }, + Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses: { code: 2445, category: DiagnosticCategory.Error, key: "Property '{0}' is protected and only accessible within class '{1}' and its subclasses." }, + Property_0_is_protected_and_only_accessible_through_an_instance_of_class_1: { code: 2446, category: DiagnosticCategory.Error, key: "Property '{0}' is protected and only accessible through an instance of class '{1}'." }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_name_1_from_private_module_2: { code: 4001, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using name '{1}' from private module '{2}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ba2c87180c92c..0480cd3000c26 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -608,7 +608,7 @@ "category": "Error", "code": 2340 }, - "Property '{0}' is inaccessible.": { + "Property '{0}' is private and only accessible within class '{1}'.": { "category": "Error", "code": 2341 }, @@ -1016,7 +1016,7 @@ "category": "Error", "code": 2442 }, - "Property '{0}' is protected but type '{1}' is not a class derived from type '{2}'.": { + "Property '{0}' is protected but type '{1}' is not a class derived from '{2}'.": { "category": "Error", "code": 2443 }, @@ -1024,6 +1024,14 @@ "category": "Error", "code": 2444 }, + "Property '{0}' is protected and only accessible within class '{1}' and its subclasses.": { + "category": "Error", + "code": 2445 + }, + "Property '{0}' is protected and only accessible through an instance of class '{1}'.": { + "category": "Error", + "code": 2446 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error",