Skip to content

Commit 09ec1bb

Browse files
committed
Report error on Class/Interface heritage clause if it cant be accessed
Fixes #78 and #83
1 parent 999b7fe commit 09ec1bb

13 files changed

+1371
-4
lines changed

src/compiler/diagnosticInformationMap.generated.ts

+6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ module ts {
9999
A_constructor_implementation_cannot_be_declared_in_an_ambient_context: { code: 1111, category: DiagnosticCategory.Error, key: "A constructor implementation cannot be declared in an ambient context." },
100100
A_class_member_cannot_be_declared_optional: { code: 1112, category: DiagnosticCategory.Error, key: "A class member cannot be declared optional." },
101101
Duplicate_identifier_0: { code: 2000, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." },
102+
Extends_clause_of_exported_class_0_has_or_is_using_private_name_1: { code: 2018, category: DiagnosticCategory.Error, key: "Extends clause of exported class '{0}' has or is using private name '{1}'." },
103+
Implements_clause_of_exported_class_0_has_or_is_using_private_name_1: { code: 2019, category: DiagnosticCategory.Error, key: "Implements clause of exported class '{0}' has or is using private name '{1}'." },
104+
Extends_clause_of_exported_interface_0_has_or_is_using_private_name_1: { code: 2020, category: DiagnosticCategory.Error, key: "Extends clause of exported interface '{0}' has or is using private name '{1}'." },
105+
Extends_clause_of_exported_class_0_has_or_is_using_name_1_from_private_module_2: { code: 2021, category: DiagnosticCategory.Error, key: "Extends clause of exported class '{0}' has or is using name '{1}' from private module '{2}'." },
106+
Implements_clause_of_exported_class_0_has_or_is_using_name_1_from_private_module_2: { code: 2022, category: DiagnosticCategory.Error, key: "Implements clause of exported class '{0}' has or is using name '{1}' from private module '{2}'." },
107+
Extends_clause_of_exported_interface_0_has_or_is_using_name_1_from_private_module_2: { code: 2023, category: DiagnosticCategory.Error, key: "Extends clause of exported interface '{0}' has or is using name '{1}' from private module '{2}'." },
102108
new_T_cannot_be_used_to_create_an_array_Use_new_Array_T_instead: { code: 2068, category: DiagnosticCategory.Error, key: "'new T[]' cannot be used to create an array. Use 'new Array<T>()' instead." },
103109
Multiple_constructor_implementations_are_not_allowed: { code: 2070, category: DiagnosticCategory.Error, key: "Multiple constructor implementations are not allowed." },
104110
A_class_may_only_implement_another_class_or_interface: { code: 2074, category: DiagnosticCategory.Error, key: "A class may only implement another class or interface." },

src/compiler/diagnosticMessages.json

+24
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,30 @@
390390
"category": "Error",
391391
"code": 2000
392392
},
393+
"Extends clause of exported class '{0}' has or is using private name '{1}'.": {
394+
"category": "Error",
395+
"code": 2018
396+
},
397+
"Implements clause of exported class '{0}' has or is using private name '{1}'.": {
398+
"category": "Error",
399+
"code": 2019
400+
},
401+
"Extends clause of exported interface '{0}' has or is using private name '{1}'.": {
402+
"category": "Error",
403+
"code": 2020
404+
},
405+
"Extends clause of exported class '{0}' has or is using name '{1}' from private module '{2}'.": {
406+
"category": "Error",
407+
"code": 2021
408+
},
409+
"Implements clause of exported class '{0}' has or is using name '{1}' from private module '{2}'.": {
410+
"category": "Error",
411+
"code": 2022
412+
},
413+
"Extends clause of exported interface '{0}' has or is using name '{1}' from private module '{2}'.": {
414+
"category": "Error",
415+
"code": 2023
416+
},
393417
"'new T[]' cannot be used to create an array. Use 'new Array<T>()' instead.": {
394418
"category": "Error",
395419
"code": 2068

src/compiler/emitter.ts

+48
Original file line numberDiff line numberDiff line change
@@ -2027,6 +2027,54 @@ module ts {
20272027

20282028
function emitHeritageClause(typeReferences: TypeReferenceNode[], isImplementsList: boolean) {
20292029
function emitTypeOfTypeReference(node: Node) {
2030+
function getHeritageClauseVisibilityError(symbolAccesibilityResult: SymbolAccessiblityResult) {
2031+
var diagnosticMessage: DiagnosticMessage;
2032+
if (node.parent.kind === SyntaxKind.ClassDeclaration) {
2033+
// Class
2034+
if (symbolAccesibilityResult.accessibility == SymbolAccessibility.NotAccessible) {
2035+
if (symbolAccesibilityResult.errorModuleName) {
2036+
// Module is inaccessible
2037+
diagnosticMessage = isImplementsList ?
2038+
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_name_1_from_private_module_2 :
2039+
Diagnostics.Extends_clause_of_exported_class_0_has_or_is_using_name_1_from_private_module_2;
2040+
}
2041+
else {
2042+
// Class or Interface implemented/extended is inaccessible
2043+
diagnosticMessage = isImplementsList ?
2044+
Diagnostics.Implements_clause_of_exported_class_0_has_or_is_using_private_name_1 :
2045+
Diagnostics.Extends_clause_of_exported_class_0_has_or_is_using_private_name_1;
2046+
}
2047+
}
2048+
else {
2049+
// CannotBeNamed
2050+
// TODO(shkamat): CannotBeNamed error needs to be handled
2051+
}
2052+
}
2053+
else {
2054+
// Interface
2055+
if (symbolAccesibilityResult.accessibility == SymbolAccessibility.NotAccessible) {
2056+
if (symbolAccesibilityResult.errorModuleName) {
2057+
// Module is inaccessible
2058+
diagnosticMessage = Diagnostics.Extends_clause_of_exported_interface_0_has_or_is_using_name_1_from_private_module_2;
2059+
}
2060+
else {
2061+
// interface is inaccessible
2062+
diagnosticMessage = Diagnostics.Extends_clause_of_exported_interface_0_has_or_is_using_private_name_1;
2063+
}
2064+
}
2065+
else {
2066+
// CannotBeNamed
2067+
// TODO(shkamat): CannotBeNamed error needs to be handled
2068+
}
2069+
}
2070+
2071+
return {
2072+
diagnosticMessage: diagnosticMessage,
2073+
errorNode: node,
2074+
typeName: (<Declaration>node.parent).name
2075+
}
2076+
}
2077+
getSymbolVisibilityDiagnosticMessage = getHeritageClauseVisibilityError;
20302078
resolver.writeTypeAtLocation(node, enclosingDeclaration, TypeFormatFlags.WriteArrayAsGenericType, writer);
20312079
}
20322080

src/harness/harness.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -736,21 +736,21 @@ module Harness {
736736
checker.checkProgram();
737737

738738
// only emit if there weren't parse errors
739-
var sourceMapData: ts.SourceMapData[];
739+
var emitResult: ts.EmitResult;
740740
if (!hadParseErrors) {
741-
sourceMapData = checker.emitFiles().sourceMaps;
741+
emitResult = checker.emitFiles();
742742
}
743743

744744
var errors: MinimalDiagnostic[] = [];
745-
program.getDiagnostics().concat(checker.getDiagnostics()).forEach(err => {
745+
program.getDiagnostics().concat(checker.getDiagnostics()).concat(emitResult ? emitResult.errors : []).forEach(err => {
746746
// TODO: new compiler formats errors after this point to add . and newlines so we'll just do it manually for now
747747
errors.push({ filename: err.file && err.file.filename, start: err.start, end: err.start + err.length, line: 0, character: 0, message: err.messageText });
748748
});
749749
this.lastErrors = errors;
750750

751751
var result = new CompilerResult(fileOutputs, errors, []);
752752
// Covert the source Map data into the baseline
753-
result.updateSourceMapRecord(program, sourceMapData);
753+
result.updateSourceMapRecord(program, emitResult ? emitResult.sourceMaps : undefined);
754754
onComplete(result);
755755
return options;
756756
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
==== tests/cases/compiler/privacyClassExtendsClauseDeclFile_externalModule.ts (4 errors) ====
2+
3+
export module publicModule {
4+
export class publicClassInPublicModule {
5+
private f1() {
6+
}
7+
}
8+
9+
class privateClassInPublicModule {
10+
}
11+
12+
class privateClassExtendingPublicClassInModule extends publicClassInPublicModule {
13+
}
14+
class privateClassExtendingPrivateClassInModule extends privateClassInPublicModule {
15+
}
16+
export class publicClassExtendingPublicClassInModule extends publicClassInPublicModule {
17+
}
18+
export class publicClassExtendingPrivateClassInModule extends privateClassInPublicModule { // Should error
19+
~~~~~~~~~~~~~~~~~~~~~~~~~~
20+
!!! Extends clause of exported class 'publicClassExtendingPrivateClassInModule' has or is using private name 'privateClassInPublicModule'.
21+
}
22+
23+
class privateClassExtendingFromPrivateModuleClass extends privateModule.publicClassInPrivateModule {
24+
}
25+
export class publicClassExtendingFromPrivateModuleClass extends privateModule.publicClassInPrivateModule { // Should error
26+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27+
!!! Extends clause of exported class 'publicClassExtendingFromPrivateModuleClass' has or is using name 'privateModule.publicClassInPrivateModule' from private module 'privateModule'.
28+
}
29+
}
30+
31+
module privateModule {
32+
export class publicClassInPrivateModule {
33+
private f1() {
34+
}
35+
}
36+
37+
class privateClassInPrivateModule {
38+
}
39+
40+
class privateClassExtendingPublicClassInModule extends publicClassInPrivateModule {
41+
}
42+
class privateClassExtendingPrivateClassInModule extends privateClassInPrivateModule {
43+
}
44+
export class publicClassExtendingPublicClassInModule extends publicClassInPrivateModule {
45+
}
46+
export class publicClassExtendingPrivateClassInModule extends privateClassInPrivateModule {
47+
}
48+
49+
class privateClassExtendingFromPrivateModuleClass extends privateModule.publicClassInPrivateModule {
50+
}
51+
export class publicClassExtendingFromPrivateModuleClass extends privateModule.publicClassInPrivateModule {
52+
}
53+
}
54+
55+
export class publicClass {
56+
private f1() {
57+
}
58+
}
59+
60+
class privateClass {
61+
}
62+
63+
class privateClassExtendingPublicClass extends publicClass {
64+
}
65+
class privateClassExtendingPrivateClassInModule extends privateClass {
66+
}
67+
export class publicClassExtendingPublicClass extends publicClass {
68+
}
69+
export class publicClassExtendingPrivateClass extends privateClass { // Should error
70+
~~~~~~~~~~~~
71+
!!! Extends clause of exported class 'publicClassExtendingPrivateClass' has or is using private name 'privateClass'.
72+
}
73+
74+
class privateClassExtendingFromPrivateModuleClass extends privateModule.publicClassInPrivateModule {
75+
}
76+
export class publicClassExtendingFromPrivateModuleClass extends privateModule.publicClassInPrivateModule { // Should error
77+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
78+
!!! Extends clause of exported class 'publicClassExtendingFromPrivateModuleClass' has or is using name 'privateModule.publicClassInPrivateModule' from private module 'privateModule'.
79+
}
80+
81+
==== tests/cases/compiler/privacyClassExtendsClauseDeclFile_GlobalFile.ts (1 errors) ====
82+
module publicModuleInGlobal {
83+
export class publicClassInPublicModule {
84+
private f1() {
85+
}
86+
}
87+
88+
class privateClassInPublicModule {
89+
}
90+
91+
class privateClassExtendingPublicClassInModule extends publicClassInPublicModule {
92+
}
93+
class privateClassExtendingPrivateClassInModule extends privateClassInPublicModule {
94+
}
95+
export class publicClassExtendingPublicClassInModule extends publicClassInPublicModule {
96+
}
97+
export class publicClassExtendingPrivateClassInModule extends privateClassInPublicModule { // Should error
98+
~~~~~~~~~~~~~~~~~~~~~~~~~~
99+
!!! Extends clause of exported class 'publicClassExtendingPrivateClassInModule' has or is using private name 'privateClassInPublicModule'.
100+
}
101+
}
102+
class publicClassInGlobal {
103+
}
104+
class publicClassExtendingPublicClassInGlobal extends publicClassInGlobal {
105+
}
106+

0 commit comments

Comments
 (0)