From d165708141b6e09302b6c5e0e4670ad8b84f7dec Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 22 Jul 2014 12:09:25 -0700 Subject: [PATCH 1/5] check groups of local symbols --- src/compiler/binder.ts | 9 +- src/compiler/checker.ts | 88 ++++++++++++++++++- .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 + src/compiler/types.ts | 2 + .../reference/anonymousModules.errors.txt | 2 +- .../duplicateSymbolsExportMatching.errors.txt | 20 +++-- .../functionOverloadErrors.errors.txt | 18 ++-- tests/baselines/reference/multivar.errors.txt | 2 +- .../overloadModifiersMustAgree.errors.txt | 2 +- 10 files changed, 113 insertions(+), 35 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ec67b496db6..faf8d96e2be 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -136,30 +136,27 @@ module ts { // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. var exportKind = 0; - var exportExcludes = 0; if (symbolKind & SymbolFlags.Value) { exportKind |= SymbolFlags.ExportValue; - exportExcludes |= SymbolFlags.Value; } if (symbolKind & SymbolFlags.Type) { exportKind |= SymbolFlags.ExportType; - exportExcludes |= SymbolFlags.Type; } if (symbolKind & SymbolFlags.Namespace) { exportKind |= SymbolFlags.ExportNamespace; - exportExcludes |= SymbolFlags.Namespace; } if (node.flags & NodeFlags.Export || (node.kind !== SyntaxKind.ImportDeclaration && isAmbientContext(container))) { if (exportKind) { - var local = declareSymbol(container.locals, undefined, node, exportKind, exportExcludes); + var local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes); local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + node.localSymbol = local; } else { declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); } } else { - declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes | exportKind); + declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7faabfae6ab..9872b7ee42e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4841,15 +4841,89 @@ module ts { // TODO: Check at least one return statement in non-void/any function (except single throw) } + function checkExportsOnMergedDeclarations(node: Node) { + var symbol: Symbol; + + // if node.localSymbol !== undefined - this node has both export and local symbol. + // local symbol includes all declarations (that can be both exported and non exported) + var symbol = node.localSymbol; + if (!symbol) { + // current declaration is not exported. + // pick a symbol for it and check if it contains any exported declaration + symbol = getSymbolOfNode(node); + if (!(symbol.flags & SymbolFlags.Export)) { + // this is a pure local symbol - no need to check anything + return; + } + } + + if (getDeclarationOfKind(symbol, node.kind) !== node) { + return; + } + + // we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace + // to denote disjoint declarationSpaces (without making new enum type). + var declarationSpaces: SymbolFlags = 0; + var hasExport: boolean; + + var declarations = symbol.declarations; + for (var i = 0, len = declarations.length; i < len; ++i) { + var currentDeclarationSpaces = getDeclarationSpaces(declarations[i]); + var currentDeclarationHasExport = (declarations[i].flags & NodeFlags.Export) !== 0; + if (declarationSpaces & currentDeclarationSpaces) { + if (hasExport !== undefined) { + if (hasExport !== currentDeclarationHasExport) { + error(declarations[i].name, Diagnostics.All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported, symbolToString(symbol)); + } + } + } + hasExport = hasExport || currentDeclarationHasExport; + declarationSpaces |= currentDeclarationSpaces; + } + + function getDeclarationSpaces(d: Declaration): SymbolFlags { + switch (d.kind) { + case SyntaxKind.InterfaceDeclaration: + return SymbolFlags.ExportType; + case SyntaxKind.ModuleDeclaration: + return (d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d) + ? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue + : SymbolFlags.ExportNamespace; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + return SymbolFlags.ExportType | SymbolFlags.ExportValue; + case SyntaxKind.ImportDeclaration: + var target = resolveImport(getSymbolOfNode(d)); + return target.flags & SymbolFlags.Export; + default: + return SymbolFlags.ExportValue; + } + } + } + function checkFunctionDeclaration(node: FunctionDeclaration) { checkSignatureDeclaration(node); - var symbol = getSymbolOfNode(node); - var firstDeclaration = getDeclarationOfKind(symbol, node.kind); + var symbol = getSymbolOfNode(node) + // first we want to check the local symbol that contain this declaration + // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol + // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode + var localSymbol = node.localSymbol || symbol; + + var firstDeclaration = getDeclarationOfKind(localSymbol, node.kind); // Only type check the symbol once if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(symbol); + checkFunctionOrConstructorSymbol(localSymbol); } + + if (symbol !== localSymbol) { + // here we'll check exported side of the symbol + Debug.assert(symbol.parent); + if (getDeclarationOfKind(symbol, node.kind) === node) { + checkFunctionOrConstructorSymbol(symbol); + } + } + checkSourceElement(node.body); // If there is no body and no explicit return type, then report an error. @@ -5041,8 +5115,10 @@ module ts { function checkVariableDeclaration(node: VariableDeclaration) { checkSourceElement(node.type); - var symbol = getSymbolOfNode(node); + checkExportsOnMergedDeclarations(node); + var symbol = getSymbolOfNode(node); + var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); var type: Type; var useTypeFromValueDeclaration = node === symbol.valueDeclaration; @@ -5330,6 +5406,7 @@ module ts { checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); checkTypeParameters(node.typeParameters); checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); var type = getDeclaredTypeOfSymbol(symbol); var staticType = getTypeOfSymbol(symbol); @@ -5484,6 +5561,7 @@ module ts { function checkInterfaceDeclaration(node: InterfaceDeclaration) { checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); checkTypeParameters(node.typeParameters); + checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); var firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); if (symbol.declarations.length > 1) { @@ -5529,6 +5607,7 @@ module ts { function checkEnumDeclaration(node: EnumDeclaration) { checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); var enumSymbol = getSymbolOfNode(node); var enumType = getDeclaredTypeOfSymbol(enumSymbol); var autoValue = 0; @@ -5600,6 +5679,7 @@ module ts { function checkModuleDeclaration(node: ModuleDeclaration) { checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !isInAmbientContext(node)) { var classOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 2c967b40b65..59cef3c088f 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -131,6 +131,7 @@ module ts { A_signature_with_an_implementation_cannot_use_a_string_literal_type: { code: 2163, category: DiagnosticCategory.Error, key: "A signature with an implementation cannot use a string literal type." }, Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon: { code: 2189, category: DiagnosticCategory.Error, key: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':" }, Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: { code: 2190, category: DiagnosticCategory.Error, key: "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it." }, + All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported: { code: 2192, category: DiagnosticCategory.Error, key: "All declarations of merged declaration '{0}' must be exported or not exported." }, super_cannot_be_referenced_in_constructor_arguments: { code: 2193, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in constructor arguments." }, Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: { code: 2194, category: DiagnosticCategory.Error, key: "Return type of constructor signature must be assignable to the instance type of the class" }, Ambient_external_module_declaration_cannot_specify_relative_module_name: { code: 2196, category: DiagnosticCategory.Error, key: "Ambient external module declaration cannot specify relative module name." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 55d0ffa2268..ad7c1bdc4dc 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -518,6 +518,10 @@ "category": "Error", "code": 2190 }, + "All declarations of merged declaration '{0}' must be exported or not exported.": { + "category": "Error", + "code": 2192 + }, "'super' cannot be referenced in constructor arguments.":{ "category": "Error", "code": 2193 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5decadcfe17..36cf334d069 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -241,6 +241,7 @@ module ts { symbol?: Symbol; // Symbol declared by node (initialized by binding) locals?: SymbolTable; // Locals associated with node (initialized by binding) nextContainer?: Node; // Next container in declaration order (initialized by binding) + localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) } export interface NodeArray extends Array, TextRange { } @@ -697,6 +698,7 @@ module ts { IsContainer = HasLocals | HasExports | HasMembers, PropertyOrAccessor = Property | Accessor, + Export = ExportNamespace | ExportType | ExportValue, } export interface Symbol { diff --git a/tests/baselines/reference/anonymousModules.errors.txt b/tests/baselines/reference/anonymousModules.errors.txt index bebc718c007..2663477cc3e 100644 --- a/tests/baselines/reference/anonymousModules.errors.txt +++ b/tests/baselines/reference/anonymousModules.errors.txt @@ -24,7 +24,7 @@ var bar = 2; ~~~ -!!! Duplicate identifier 'bar'. +!!! All declarations of merged declaration 'bar' must be exported or not exported. module { ~ diff --git a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt index 3bc10adf7ea..1d712f9c5cb 100644 --- a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt +++ b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/duplicateSymbolsExportMatching.ts (8 errors) ==== +==== tests/cases/compiler/duplicateSymbolsExportMatching.ts (9 errors) ==== module M { export interface E { } interface I { } @@ -25,11 +25,11 @@ interface I { } export interface I { } // error ~ -!!! Duplicate identifier 'I'. +!!! All declarations of merged declaration 'I' must be exported or not exported. export interface E { } interface E { } // error ~ -!!! Duplicate identifier 'E'. +!!! All declarations of merged declaration 'E' must be exported or not exported. } // Should report error only once for instantiated module @@ -39,7 +39,7 @@ } export module inst { // one error ~~~~ -!!! Duplicate identifier 'inst'. +!!! All declarations of merged declaration 'inst' must be exported or not exported. var t; } } @@ -49,20 +49,22 @@ var v: string; export var v: string; // one error (visibility) ~ -!!! Duplicate identifier 'v'. +!!! All declarations of merged declaration 'v' must be exported or not exported. var w: number; export var w: string; // two errors (visibility and type mismatch) ~ -!!! Duplicate identifier 'w'. +!!! All declarations of merged declaration 'w' must be exported or not exported. } module M { module F { + ~ +!!! A module declaration cannot be located prior to a class or function with which it is merged var t; } export function F() { } // Only one error for duplicate identifier (don't consider visibility) ~ -!!! Duplicate identifier 'F'. +!!! All declarations of merged declaration 'F' must be exported or not exported. } module M { @@ -70,7 +72,7 @@ module C { } export module C { // Two visibility errors (one for the clodule symbol, and one for the merged container symbol) ~ -!!! Duplicate identifier 'C'. +!!! All declarations of merged declaration 'C' must be exported or not exported. var t; } } @@ -79,4 +81,4 @@ interface D { } export interface D { } ~ -!!! Duplicate identifier 'D'. \ No newline at end of file +!!! All declarations of merged declaration 'D' must be exported or not exported. \ No newline at end of file diff --git a/tests/baselines/reference/functionOverloadErrors.errors.txt b/tests/baselines/reference/functionOverloadErrors.errors.txt index a839c286f81..394607010ec 100644 --- a/tests/baselines/reference/functionOverloadErrors.errors.txt +++ b/tests/baselines/reference/functionOverloadErrors.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/conformance/functions/functionOverloadErrors.ts (19 errors) ==== +==== tests/cases/conformance/functions/functionOverloadErrors.ts (15 errors) ==== //Function overload signature with initializer function fn1(x = 3); ~~~~~ @@ -88,24 +88,16 @@ export function fn1(); ~~~~~~~~~~~~~~~~~~~~~~ !!! Function implementation expected. + ~~~ +!!! Overload signatures must all be exported or not exported. function fn1(n: string); - ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! Function implementation expected. - ~~~ -!!! Duplicate identifier 'fn1'. function fn1() { } - ~~~ -!!! Duplicate identifier 'fn1'. function fn2(n: string); - ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! Function implementation expected. + ~~~ +!!! Overload signatures must all be exported or not exported. export function fn2(); - ~~~ -!!! Duplicate identifier 'fn2'. export function fn2() { } - ~~~ -!!! Duplicate identifier 'fn2'. } //Function overloads with differing ambience diff --git a/tests/baselines/reference/multivar.errors.txt b/tests/baselines/reference/multivar.errors.txt index bc46109903d..66db194234d 100644 --- a/tests/baselines/reference/multivar.errors.txt +++ b/tests/baselines/reference/multivar.errors.txt @@ -22,7 +22,7 @@ declare var d1, d2; var b2; ~~ -!!! Duplicate identifier 'b2'. +!!! All declarations of merged declaration 'b2' must be exported or not exported. declare var v1; } diff --git a/tests/baselines/reference/overloadModifiersMustAgree.errors.txt b/tests/baselines/reference/overloadModifiersMustAgree.errors.txt index cad91215f06..adb535ce385 100644 --- a/tests/baselines/reference/overloadModifiersMustAgree.errors.txt +++ b/tests/baselines/reference/overloadModifiersMustAgree.errors.txt @@ -13,7 +13,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! Function implementation expected. ~~~ -!!! Duplicate identifier 'bar'. +!!! Overload signatures must all be exported or not exported. function bar(s?: string) { } interface I { From d7f67083b2c39f260a631fde883cb0f22515b4d9 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 22 Jul 2014 12:09:25 -0700 Subject: [PATCH 2/5] check groups of local symbols --- src/compiler/binder.ts | 9 +- src/compiler/checker.ts | 87 ++++++++++++++++++- .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 + src/compiler/types.ts | 2 + .../reference/anonymousModules.errors.txt | 2 +- .../duplicateSymbolsExportMatching.errors.txt | 20 +++-- .../functionOverloadErrors.errors.txt | 18 ++-- tests/baselines/reference/multivar.errors.txt | 2 +- .../overloadModifiersMustAgree.errors.txt | 2 +- 10 files changed, 112 insertions(+), 35 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index ec67b496db6..faf8d96e2be 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -136,30 +136,27 @@ module ts { // but return the export symbol (by calling getExportSymbolOfValueSymbolIfExported). That way // when the emitter comes back to it, it knows not to qualify the name if it was found in a containing scope. var exportKind = 0; - var exportExcludes = 0; if (symbolKind & SymbolFlags.Value) { exportKind |= SymbolFlags.ExportValue; - exportExcludes |= SymbolFlags.Value; } if (symbolKind & SymbolFlags.Type) { exportKind |= SymbolFlags.ExportType; - exportExcludes |= SymbolFlags.Type; } if (symbolKind & SymbolFlags.Namespace) { exportKind |= SymbolFlags.ExportNamespace; - exportExcludes |= SymbolFlags.Namespace; } if (node.flags & NodeFlags.Export || (node.kind !== SyntaxKind.ImportDeclaration && isAmbientContext(container))) { if (exportKind) { - var local = declareSymbol(container.locals, undefined, node, exportKind, exportExcludes); + var local = declareSymbol(container.locals, undefined, node, exportKind, symbolExcludes); local.exportSymbol = declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); + node.localSymbol = local; } else { declareSymbol(container.symbol.exports, container.symbol, node, symbolKind, symbolExcludes); } } else { - declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes | exportKind); + declareSymbol(container.locals, undefined, node, symbolKind, symbolExcludes); } } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c994fc3fe4..7f04f629bd7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4925,14 +4925,87 @@ module ts { } } + function checkExportsOnMergedDeclarations(node: Node) { + var symbol: Symbol; + + // if node.localSymbol !== undefined - this node has both export and local symbol. + // local symbol includes all declarations (that can be both exported and non exported) + var symbol = node.localSymbol; + if (!symbol) { + // current declaration is not exported. + // pick a symbol for it and check if it contains any exported declaration + symbol = getSymbolOfNode(node); + if (!(symbol.flags & SymbolFlags.Export)) { + // this is a pure local symbol - no need to check anything + return; + } + } + + if (getDeclarationOfKind(symbol, node.kind) !== node) { + return; + } + + // we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace + // to denote disjoint declarationSpaces (without making new enum type). + var declarationSpaces: SymbolFlags = 0; + var hasExport: boolean; + + var declarations = symbol.declarations; + for (var i = 0, len = declarations.length; i < len; ++i) { + var currentDeclarationSpaces = getDeclarationSpaces(declarations[i]); + var currentDeclarationHasExport = (declarations[i].flags & NodeFlags.Export) !== 0; + if (declarationSpaces & currentDeclarationSpaces) { + if (hasExport !== undefined) { + if (hasExport !== currentDeclarationHasExport) { + error(declarations[i].name, Diagnostics.All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported, symbolToString(symbol)); + } + } + } + hasExport = hasExport || currentDeclarationHasExport; + declarationSpaces |= currentDeclarationSpaces; + } + + function getDeclarationSpaces(d: Declaration): SymbolFlags { + switch (d.kind) { + case SyntaxKind.InterfaceDeclaration: + return SymbolFlags.ExportType; + case SyntaxKind.ModuleDeclaration: + return (d).name.kind === SyntaxKind.StringLiteral || isInstantiated(d) + ? SymbolFlags.ExportNamespace | SymbolFlags.ExportValue + : SymbolFlags.ExportNamespace; + case SyntaxKind.ClassDeclaration: + case SyntaxKind.EnumDeclaration: + return SymbolFlags.ExportType | SymbolFlags.ExportValue; + case SyntaxKind.ImportDeclaration: + var target = resolveImport(getSymbolOfNode(d)); + return target.flags & SymbolFlags.Export; + default: + return SymbolFlags.ExportValue; + } + } + } + function checkFunctionDeclaration(node: FunctionDeclaration) { checkSignatureDeclaration(node); - var symbol = getSymbolOfNode(node); - var firstDeclaration = getDeclarationOfKind(symbol, node.kind); + var symbol = getSymbolOfNode(node) + // first we want to check the local symbol that contain this declaration + // - if node.localSymbol !== undefined - this is current declaration is exported and localSymbol points to the local symbol + // - if node.localSymbol === undefined - this node is non-exported so we can just pick the result of getSymbolOfNode + var localSymbol = node.localSymbol || symbol; + + var firstDeclaration = getDeclarationOfKind(localSymbol, node.kind); // Only type check the symbol once if (node === firstDeclaration) { - checkFunctionOrConstructorSymbol(symbol); + checkFunctionOrConstructorSymbol(localSymbol); + } + + if (symbol !== localSymbol) { + // here we'll check exported side of the symbol + Debug.assert(symbol.parent); + if (getDeclarationOfKind(symbol, node.kind) === node) { + checkFunctionOrConstructorSymbol(symbol); + } } checkSourceElement(node.body); @@ -5129,8 +5202,10 @@ module ts { function checkVariableDeclaration(node: VariableDeclaration) { checkSourceElement(node.type); - var symbol = getSymbolOfNode(node); + checkExportsOnMergedDeclarations(node); + var symbol = getSymbolOfNode(node); + var typeOfValueDeclaration = getTypeOfVariableOrParameterOrProperty(symbol); var type: Type; var useTypeFromValueDeclaration = node === symbol.valueDeclaration; @@ -5418,6 +5493,7 @@ module ts { checkTypeNameIsReserved(node.name, Diagnostics.Class_name_cannot_be_0); checkTypeParameters(node.typeParameters); checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); var type = getDeclaredTypeOfSymbol(symbol); var staticType = getTypeOfSymbol(symbol); @@ -5572,6 +5648,7 @@ module ts { function checkInterfaceDeclaration(node: InterfaceDeclaration) { checkTypeNameIsReserved(node.name, Diagnostics.Interface_name_cannot_be_0); checkTypeParameters(node.typeParameters); + checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); var firstInterfaceDecl = getDeclarationOfKind(symbol, SyntaxKind.InterfaceDeclaration); if (symbol.declarations.length > 1) { @@ -5617,6 +5694,7 @@ module ts { function checkEnumDeclaration(node: EnumDeclaration) { checkTypeNameIsReserved(node.name, Diagnostics.Enum_name_cannot_be_0); checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); var enumSymbol = getSymbolOfNode(node); var enumType = getDeclaredTypeOfSymbol(enumSymbol); var autoValue = 0; @@ -5688,6 +5766,7 @@ module ts { function checkModuleDeclaration(node: ModuleDeclaration) { checkCollisionWithCapturedThisVariable(node, node.name); + checkExportsOnMergedDeclarations(node); var symbol = getSymbolOfNode(node); if (symbol.flags & SymbolFlags.ValueModule && symbol.declarations.length > 1 && !isInAmbientContext(node)) { var classOrFunc = getFirstNonAmbientClassOrFunctionDeclaration(symbol); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 9927cb9d98e..903dda0ce6d 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -137,6 +137,7 @@ module ts { A_signature_with_an_implementation_cannot_use_a_string_literal_type: { code: 2163, category: DiagnosticCategory.Error, key: "A signature with an implementation cannot use a string literal type." }, Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon: { code: 2189, category: DiagnosticCategory.Error, key: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':" }, Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: { code: 2190, category: DiagnosticCategory.Error, key: "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it." }, + All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported: { code: 2192, category: DiagnosticCategory.Error, key: "All declarations of merged declaration '{0}' must be exported or not exported." }, super_cannot_be_referenced_in_constructor_arguments: { code: 2193, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in constructor arguments." }, Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: { code: 2194, category: DiagnosticCategory.Error, key: "Return type of constructor signature must be assignable to the instance type of the class" }, Ambient_external_module_declaration_cannot_specify_relative_module_name: { code: 2196, category: DiagnosticCategory.Error, key: "Ambient external module declaration cannot specify relative module name." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d61be84b5de..1b99fdd5f9c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -540,6 +540,10 @@ "category": "Error", "code": 2190 }, + "All declarations of merged declaration '{0}' must be exported or not exported.": { + "category": "Error", + "code": 2192 + }, "'super' cannot be referenced in constructor arguments.":{ "category": "Error", "code": 2193 diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 545e6d60310..555d27171a1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -243,6 +243,7 @@ module ts { symbol?: Symbol; // Symbol declared by node (initialized by binding) locals?: SymbolTable; // Locals associated with node (initialized by binding) nextContainer?: Node; // Next container in declaration order (initialized by binding) + localSymbol?: Symbol; // Local symbol declared by node (initialized by binding only for exported nodes) } export interface NodeArray extends Array, TextRange { } @@ -699,6 +700,7 @@ module ts { IsContainer = HasLocals | HasExports | HasMembers, PropertyOrAccessor = Property | Accessor, + Export = ExportNamespace | ExportType | ExportValue, } export interface Symbol { diff --git a/tests/baselines/reference/anonymousModules.errors.txt b/tests/baselines/reference/anonymousModules.errors.txt index bebc718c007..2663477cc3e 100644 --- a/tests/baselines/reference/anonymousModules.errors.txt +++ b/tests/baselines/reference/anonymousModules.errors.txt @@ -24,7 +24,7 @@ var bar = 2; ~~~ -!!! Duplicate identifier 'bar'. +!!! All declarations of merged declaration 'bar' must be exported or not exported. module { ~ diff --git a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt index 3bc10adf7ea..1d712f9c5cb 100644 --- a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt +++ b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/duplicateSymbolsExportMatching.ts (8 errors) ==== +==== tests/cases/compiler/duplicateSymbolsExportMatching.ts (9 errors) ==== module M { export interface E { } interface I { } @@ -25,11 +25,11 @@ interface I { } export interface I { } // error ~ -!!! Duplicate identifier 'I'. +!!! All declarations of merged declaration 'I' must be exported or not exported. export interface E { } interface E { } // error ~ -!!! Duplicate identifier 'E'. +!!! All declarations of merged declaration 'E' must be exported or not exported. } // Should report error only once for instantiated module @@ -39,7 +39,7 @@ } export module inst { // one error ~~~~ -!!! Duplicate identifier 'inst'. +!!! All declarations of merged declaration 'inst' must be exported or not exported. var t; } } @@ -49,20 +49,22 @@ var v: string; export var v: string; // one error (visibility) ~ -!!! Duplicate identifier 'v'. +!!! All declarations of merged declaration 'v' must be exported or not exported. var w: number; export var w: string; // two errors (visibility and type mismatch) ~ -!!! Duplicate identifier 'w'. +!!! All declarations of merged declaration 'w' must be exported or not exported. } module M { module F { + ~ +!!! A module declaration cannot be located prior to a class or function with which it is merged var t; } export function F() { } // Only one error for duplicate identifier (don't consider visibility) ~ -!!! Duplicate identifier 'F'. +!!! All declarations of merged declaration 'F' must be exported or not exported. } module M { @@ -70,7 +72,7 @@ module C { } export module C { // Two visibility errors (one for the clodule symbol, and one for the merged container symbol) ~ -!!! Duplicate identifier 'C'. +!!! All declarations of merged declaration 'C' must be exported or not exported. var t; } } @@ -79,4 +81,4 @@ interface D { } export interface D { } ~ -!!! Duplicate identifier 'D'. \ No newline at end of file +!!! All declarations of merged declaration 'D' must be exported or not exported. \ No newline at end of file diff --git a/tests/baselines/reference/functionOverloadErrors.errors.txt b/tests/baselines/reference/functionOverloadErrors.errors.txt index a839c286f81..394607010ec 100644 --- a/tests/baselines/reference/functionOverloadErrors.errors.txt +++ b/tests/baselines/reference/functionOverloadErrors.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/conformance/functions/functionOverloadErrors.ts (19 errors) ==== +==== tests/cases/conformance/functions/functionOverloadErrors.ts (15 errors) ==== //Function overload signature with initializer function fn1(x = 3); ~~~~~ @@ -88,24 +88,16 @@ export function fn1(); ~~~~~~~~~~~~~~~~~~~~~~ !!! Function implementation expected. + ~~~ +!!! Overload signatures must all be exported or not exported. function fn1(n: string); - ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! Function implementation expected. - ~~~ -!!! Duplicate identifier 'fn1'. function fn1() { } - ~~~ -!!! Duplicate identifier 'fn1'. function fn2(n: string); - ~~~~~~~~~~~~~~~~~~~~~~~~ -!!! Function implementation expected. + ~~~ +!!! Overload signatures must all be exported or not exported. export function fn2(); - ~~~ -!!! Duplicate identifier 'fn2'. export function fn2() { } - ~~~ -!!! Duplicate identifier 'fn2'. } //Function overloads with differing ambience diff --git a/tests/baselines/reference/multivar.errors.txt b/tests/baselines/reference/multivar.errors.txt index bc46109903d..66db194234d 100644 --- a/tests/baselines/reference/multivar.errors.txt +++ b/tests/baselines/reference/multivar.errors.txt @@ -22,7 +22,7 @@ declare var d1, d2; var b2; ~~ -!!! Duplicate identifier 'b2'. +!!! All declarations of merged declaration 'b2' must be exported or not exported. declare var v1; } diff --git a/tests/baselines/reference/overloadModifiersMustAgree.errors.txt b/tests/baselines/reference/overloadModifiersMustAgree.errors.txt index cad91215f06..adb535ce385 100644 --- a/tests/baselines/reference/overloadModifiersMustAgree.errors.txt +++ b/tests/baselines/reference/overloadModifiersMustAgree.errors.txt @@ -13,7 +13,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! Function implementation expected. ~~~ -!!! Duplicate identifier 'bar'. +!!! Overload signatures must all be exported or not exported. function bar(s?: string) { } interface I { From bb193fedb9cb27f9858de01926ca0b412ccf7eac Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 24 Jul 2014 16:44:52 -0700 Subject: [PATCH 3/5] augment check for colliding declaration spaces --- src/compiler/checker.ts | 46 +++++++++++-------- .../diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../reference/anonymousModules.errors.txt | 6 ++- .../duplicateSymbolsExportMatching.errors.txt | 36 +++++++++++---- tests/baselines/reference/mixedExports.js | 14 ++++++ tests/baselines/reference/multivar.errors.txt | 6 ++- tests/cases/compiler/mixedExports.ts | 11 +++++ 8 files changed, 89 insertions(+), 34 deletions(-) create mode 100644 tests/baselines/reference/mixedExports.js create mode 100644 tests/cases/compiler/mixedExports.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f04f629bd7..c6c339e37a8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4941,28 +4941,34 @@ module ts { } } + // run the check only for the first declaration in the list if (getDeclarationOfKind(symbol, node.kind) !== node) { return; } // we use SymbolFlags.ExportValue, SymbolFlags.ExportType and SymbolFlags.ExportNamespace // to denote disjoint declarationSpaces (without making new enum type). - var declarationSpaces: SymbolFlags = 0; - var hasExport: boolean; - - var declarations = symbol.declarations; - for (var i = 0, len = declarations.length; i < len; ++i) { - var currentDeclarationSpaces = getDeclarationSpaces(declarations[i]); - var currentDeclarationHasExport = (declarations[i].flags & NodeFlags.Export) !== 0; - if (declarationSpaces & currentDeclarationSpaces) { - if (hasExport !== undefined) { - if (hasExport !== currentDeclarationHasExport) { - error(declarations[i].name, Diagnostics.All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported, symbolToString(symbol)); - } - } + var exportedDeclarationSpaces: SymbolFlags = 0; + var nonExportedDeclarationSpaces: SymbolFlags = 0; + forEach(symbol.declarations, d => { + var declarationSpaces = getDeclarationSpaces(d); + if (d.flags & NodeFlags.Export) { + exportedDeclarationSpaces |= declarationSpaces; } - hasExport = hasExport || currentDeclarationHasExport; - declarationSpaces |= currentDeclarationSpaces; + else { + nonExportedDeclarationSpaces |= declarationSpaces; + } + }); + + var commonDeclarationSpace = exportedDeclarationSpaces & nonExportedDeclarationSpaces + + if (commonDeclarationSpace) { + // declaration spaces for exported and non-exported declarations intersect + forEach(symbol.declarations, d => { + if (getDeclarationSpaces(d) & commonDeclarationSpace) { + error(d.name, Diagnostics.Individual_declarations_in_a_merged_declaration_0_must_be_all_exported_or_all_local, identifierToString(d.name)); + } + }); } function getDeclarationSpaces(d: Declaration): SymbolFlags { @@ -4977,8 +4983,10 @@ module ts { case SyntaxKind.EnumDeclaration: return SymbolFlags.ExportType | SymbolFlags.ExportValue; case SyntaxKind.ImportDeclaration: + var result: SymbolFlags = 0; var target = resolveImport(getSymbolOfNode(d)); - return target.flags & SymbolFlags.Export; + forEach(target.declarations, d => { result |= getDeclarationSpaces(d); } ) + return result; default: return SymbolFlags.ExportValue; } @@ -5000,10 +5008,10 @@ module ts { checkFunctionOrConstructorSymbol(localSymbol); } - if (symbol !== localSymbol) { - // here we'll check exported side of the symbol - Debug.assert(symbol.parent); + if (symbol.parent) { + // run check once for the first declaration if (getDeclarationOfKind(symbol, node.kind) === node) { + // run check on export symbol to check that modifiers agree across all exported declarations checkFunctionOrConstructorSymbol(symbol); } } diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 903dda0ce6d..11809aca067 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -137,7 +137,7 @@ module ts { A_signature_with_an_implementation_cannot_use_a_string_literal_type: { code: 2163, category: DiagnosticCategory.Error, key: "A signature with an implementation cannot use a string literal type." }, Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon: { code: 2189, category: DiagnosticCategory.Error, key: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':" }, Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: { code: 2190, category: DiagnosticCategory.Error, key: "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it." }, - All_declarations_of_merged_declaration_0_must_be_exported_or_not_exported: { code: 2192, category: DiagnosticCategory.Error, key: "All declarations of merged declaration '{0}' must be exported or not exported." }, + Individual_declarations_in_a_merged_declaration_0_must_be_all_exported_or_all_local: { code: 2192, category: DiagnosticCategory.Error, key: "Individual declarations in a merged declaration {0} must be all exported or all local." }, super_cannot_be_referenced_in_constructor_arguments: { code: 2193, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in constructor arguments." }, Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: { code: 2194, category: DiagnosticCategory.Error, key: "Return type of constructor signature must be assignable to the instance type of the class" }, Ambient_external_module_declaration_cannot_specify_relative_module_name: { code: 2196, category: DiagnosticCategory.Error, key: "Ambient external module declaration cannot specify relative module name." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 1b99fdd5f9c..d3a34d2733e 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -540,7 +540,7 @@ "category": "Error", "code": 2190 }, - "All declarations of merged declaration '{0}' must be exported or not exported.": { + "Individual declarations in a merged declaration {0} must be all exported or all local.": { "category": "Error", "code": 2192 }, diff --git a/tests/baselines/reference/anonymousModules.errors.txt b/tests/baselines/reference/anonymousModules.errors.txt index 2663477cc3e..9f07d998510 100644 --- a/tests/baselines/reference/anonymousModules.errors.txt +++ b/tests/baselines/reference/anonymousModules.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/anonymousModules.ts (12 errors) ==== +==== tests/cases/compiler/anonymousModules.ts (13 errors) ==== module { ~ !!! ';' expected. @@ -18,13 +18,15 @@ export var bar = 1; ~~~~~~ !!! Statement expected. + ~~~ +!!! Individual declarations in a merged declaration bar must be all exported or all local. } ~ !!! Declaration or statement expected. var bar = 2; ~~~ -!!! All declarations of merged declaration 'bar' must be exported or not exported. +!!! Individual declarations in a merged declaration bar must be all exported or all local. module { ~ diff --git a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt index 1d712f9c5cb..ba20d24b55c 100644 --- a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt +++ b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt @@ -1,4 +1,4 @@ -==== tests/cases/compiler/duplicateSymbolsExportMatching.ts (9 errors) ==== +==== tests/cases/compiler/duplicateSymbolsExportMatching.ts (18 errors) ==== module M { export interface E { } interface I { } @@ -23,23 +23,29 @@ module N2 { interface I { } + ~ +!!! Individual declarations in a merged declaration I must be all exported or all local. export interface I { } // error ~ -!!! All declarations of merged declaration 'I' must be exported or not exported. +!!! Individual declarations in a merged declaration I must be all exported or all local. export interface E { } + ~ +!!! Individual declarations in a merged declaration E must be all exported or all local. interface E { } // error ~ -!!! All declarations of merged declaration 'E' must be exported or not exported. +!!! Individual declarations in a merged declaration E must be all exported or all local. } // Should report error only once for instantiated module module M { module inst { + ~~~~ +!!! Individual declarations in a merged declaration inst must be all exported or all local. var t; } export module inst { // one error ~~~~ -!!! All declarations of merged declaration 'inst' must be exported or not exported. +!!! Individual declarations in a merged declaration inst must be all exported or all local. var t; } } @@ -47,38 +53,50 @@ // Variables of the same / different type module M2 { var v: string; + ~ +!!! Individual declarations in a merged declaration v must be all exported or all local. export var v: string; // one error (visibility) ~ -!!! All declarations of merged declaration 'v' must be exported or not exported. +!!! Individual declarations in a merged declaration v must be all exported or all local. var w: number; + ~ +!!! Individual declarations in a merged declaration w must be all exported or all local. export var w: string; // two errors (visibility and type mismatch) ~ -!!! All declarations of merged declaration 'w' must be exported or not exported. +!!! Individual declarations in a merged declaration w must be all exported or all local. } module M { module F { ~ !!! A module declaration cannot be located prior to a class or function with which it is merged + ~ +!!! Individual declarations in a merged declaration F must be all exported or all local. var t; } export function F() { } // Only one error for duplicate identifier (don't consider visibility) ~ -!!! All declarations of merged declaration 'F' must be exported or not exported. +!!! Individual declarations in a merged declaration F must be all exported or all local. } module M { class C { } + ~ +!!! Individual declarations in a merged declaration C must be all exported or all local. module C { } + ~ +!!! Individual declarations in a merged declaration C must be all exported or all local. export module C { // Two visibility errors (one for the clodule symbol, and one for the merged container symbol) ~ -!!! All declarations of merged declaration 'C' must be exported or not exported. +!!! Individual declarations in a merged declaration C must be all exported or all local. var t; } } // Top level interface D { } + ~ +!!! Individual declarations in a merged declaration D must be all exported or all local. export interface D { } ~ -!!! All declarations of merged declaration 'D' must be exported or not exported. \ No newline at end of file +!!! Individual declarations in a merged declaration D must be all exported or all local. \ No newline at end of file diff --git a/tests/baselines/reference/mixedExports.js b/tests/baselines/reference/mixedExports.js new file mode 100644 index 00000000000..eb650667e8d --- /dev/null +++ b/tests/baselines/reference/mixedExports.js @@ -0,0 +1,14 @@ +//// [mixedExports.ts] +declare module M { + function foo(); + export function foo(); + function foo(); +} + +module A { + interface X {x} + export module X {} + interface X {y} +} + +//// [mixedExports.js] diff --git a/tests/baselines/reference/multivar.errors.txt b/tests/baselines/reference/multivar.errors.txt index 66db194234d..0a1496f4a92 100644 --- a/tests/baselines/reference/multivar.errors.txt +++ b/tests/baselines/reference/multivar.errors.txt @@ -1,10 +1,12 @@ -==== tests/cases/compiler/multivar.ts (1 errors) ==== +==== tests/cases/compiler/multivar.ts (2 errors) ==== var a,b,c; var x=1,y=2,z=3; module m2 { export var a, b2: number = 10, b; + ~~ +!!! Individual declarations in a merged declaration b2 must be all exported or all local. var m1; var a2, b22: number = 10, b222; var m3; @@ -22,7 +24,7 @@ declare var d1, d2; var b2; ~~ -!!! All declarations of merged declaration 'b2' must be exported or not exported. +!!! Individual declarations in a merged declaration b2 must be all exported or all local. declare var v1; } diff --git a/tests/cases/compiler/mixedExports.ts b/tests/cases/compiler/mixedExports.ts new file mode 100644 index 00000000000..bad962c8e1e --- /dev/null +++ b/tests/cases/compiler/mixedExports.ts @@ -0,0 +1,11 @@ +declare module M { + function foo(); + export function foo(); + function foo(); +} + +module A { + interface X {x} + export module X {} + interface X {y} +} \ No newline at end of file From 81da2cb7b95307a1f6f0b80990066d680e0dd51e Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 24 Jul 2014 23:22:40 -0700 Subject: [PATCH 4/5] update comments --- src/compiler/checker.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c6c339e37a8..e39ad3fcbca 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4928,15 +4928,17 @@ module ts { function checkExportsOnMergedDeclarations(node: Node) { var symbol: Symbol; - // if node.localSymbol !== undefined - this node has both export and local symbol. - // local symbol includes all declarations (that can be both exported and non exported) + // Exports should be checked only if enclosing module contains both exported and non exported declarations. + // In case if all declarations are non-exported check is unnecesary. + + // if localSymbol is defined on node then node itself is exported - check is required var symbol = node.localSymbol; if (!symbol) { - // current declaration is not exported. - // pick a symbol for it and check if it contains any exported declaration + // local symbol is undefined => this declaration is non-exported. + // however symbol might contain other declarations that are exported symbol = getSymbolOfNode(node); if (!(symbol.flags & SymbolFlags.Export)) { - // this is a pure local symbol - no need to check anything + // this is a pure local symbol (all declarations are non-exported) - no need to check anything return; } } From 42df260eb5f57e1662ae0217e01aee531e7d2bd6 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 25 Jul 2014 11:15:19 -0700 Subject: [PATCH 5/5] use getEffectiveDeclarationFlags to get Export flag --- src/compiler/checker.ts | 39 +++++++++---------- .../diagnosticInformationMap.generated.ts | 2 +- src/compiler/diagnosticMessages.json | 2 +- .../reference/anonymousModules.errors.txt | 4 +- .../duplicateSymbolsExportMatching.errors.txt | 34 ++++++++-------- tests/baselines/reference/mixedExports.js | 11 ++++-- tests/baselines/reference/multivar.errors.txt | 4 +- tests/cases/compiler/mixedExports.ts | 11 ++++-- 8 files changed, 57 insertions(+), 50 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index e39ad3fcbca..2ed99353dff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -4793,24 +4793,21 @@ module ts { error(signatureDeclarationNode, Diagnostics.Specialized_overload_signature_is_not_assignable_to_any_non_specialized_signature); } - function checkFunctionOrConstructorSymbol(symbol: Symbol) { - function getEffectiveFlagsForFunctionCheck(n: Node) { - var flags = n.flags; - // We want to determine if an overload is effectively ambient, which can happen if it - // is nested in an ambient context. However, do not treat members of interfaces differently - // based on whether the interface itself is in an ambient context. Interfaces should never - // be considered ambient for purposes of comparing overload attributes. - if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isInAmbientContext(n)) { - if (!(flags & NodeFlags.Ambient)) { - // It is nested in an ambient context, which means it is automatically exported - flags |= NodeFlags.Export; - } - flags |= NodeFlags.Ambient; + function getEffectiveDeclarationFlags(n: Node, flagsToCheck: NodeFlags) { + var flags = n.flags; + if (n.parent.kind !== SyntaxKind.InterfaceDeclaration && isInAmbientContext(n)) { + if (!(flags & NodeFlags.Ambient)) { + // It is nested in an ambient context, which means it is automatically exported + flags |= NodeFlags.Export; } - - return flags & flagsToCheck; + flags |= NodeFlags.Ambient; } + return flags & flagsToCheck; + } + + function checkFunctionOrConstructorSymbol(symbol: Symbol) { + function checkFlagAgreementBetweenOverloads(overloads: Declaration[], implementation: FunctionDeclaration, flagsToCheck: NodeFlags, someOverloadFlags: NodeFlags, allOverloadFlags: NodeFlags): void { // Error if some overloads have a flag that is not shared by all overloads. To find the // deviations, we XOR someOverloadFlags with allOverloadFlags @@ -4823,10 +4820,10 @@ module ts { // the canonical signature only if it is in the same container as the first overload var implementationSharesContainerWithFirstOverload = implementation !== undefined && implementation.parent === overloads[0].parent; var canonicalFlags = implementationSharesContainerWithFirstOverload - ? getEffectiveFlagsForFunctionCheck(implementation) - : getEffectiveFlagsForFunctionCheck(overloads[0]); + ? getEffectiveDeclarationFlags(implementation, flagsToCheck) + : getEffectiveDeclarationFlags(overloads[0], flagsToCheck); forEach(overloads, o => { - var deviation = getEffectiveFlagsForFunctionCheck(o) ^ canonicalFlags; + var deviation = getEffectiveDeclarationFlags(o, flagsToCheck) ^ canonicalFlags; if (deviation & NodeFlags.Export) { error(o.name, Diagnostics.Overload_signatures_must_all_be_exported_or_not_exported); } @@ -4854,7 +4851,7 @@ module ts { for (var i = 0; i < declarations.length; i++) { var node = declarations[i]; if (node.kind === SyntaxKind.FunctionDeclaration || node.kind === SyntaxKind.Method || node.kind === SyntaxKind.Constructor) { - var currentNodeFlags = getEffectiveFlagsForFunctionCheck(node); + var currentNodeFlags = getEffectiveDeclarationFlags(node, flagsToCheck); someNodeFlags |= currentNodeFlags; allNodeFlags &= currentNodeFlags; @@ -4954,7 +4951,7 @@ module ts { var nonExportedDeclarationSpaces: SymbolFlags = 0; forEach(symbol.declarations, d => { var declarationSpaces = getDeclarationSpaces(d); - if (d.flags & NodeFlags.Export) { + if (getEffectiveDeclarationFlags(d, NodeFlags.Export)) { exportedDeclarationSpaces |= declarationSpaces; } else { @@ -4968,7 +4965,7 @@ module ts { // declaration spaces for exported and non-exported declarations intersect forEach(symbol.declarations, d => { if (getDeclarationSpaces(d) & commonDeclarationSpace) { - error(d.name, Diagnostics.Individual_declarations_in_a_merged_declaration_0_must_be_all_exported_or_all_local, identifierToString(d.name)); + error(d.name, Diagnostics.Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local, identifierToString(d.name)); } }); } diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 11809aca067..e26442355f0 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -137,7 +137,7 @@ module ts { A_signature_with_an_implementation_cannot_use_a_string_literal_type: { code: 2163, category: DiagnosticCategory.Error, key: "A signature with an implementation cannot use a string literal type." }, Interface_0_cannot_simultaneously_extend_types_1_and_2_Colon: { code: 2189, category: DiagnosticCategory.Error, key: "Interface '{0}' cannot simultaneously extend types '{1}' and '{2}':" }, Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it: { code: 2190, category: DiagnosticCategory.Error, key: "Initializer of parameter '{0}' cannot reference identifier '{1}' declared after it." }, - Individual_declarations_in_a_merged_declaration_0_must_be_all_exported_or_all_local: { code: 2192, category: DiagnosticCategory.Error, key: "Individual declarations in a merged declaration {0} must be all exported or all local." }, + Individual_declarations_in_merged_declaration_0_must_be_all_exported_or_all_local: { code: 2192, category: DiagnosticCategory.Error, key: "Individual declarations in merged declaration {0} must be all exported or all local." }, super_cannot_be_referenced_in_constructor_arguments: { code: 2193, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in constructor arguments." }, Return_type_of_constructor_signature_must_be_assignable_to_the_instance_type_of_the_class: { code: 2194, category: DiagnosticCategory.Error, key: "Return type of constructor signature must be assignable to the instance type of the class" }, Ambient_external_module_declaration_cannot_specify_relative_module_name: { code: 2196, category: DiagnosticCategory.Error, key: "Ambient external module declaration cannot specify relative module name." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index d3a34d2733e..105a1fbcdb4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -540,7 +540,7 @@ "category": "Error", "code": 2190 }, - "Individual declarations in a merged declaration {0} must be all exported or all local.": { + "Individual declarations in merged declaration {0} must be all exported or all local.": { "category": "Error", "code": 2192 }, diff --git a/tests/baselines/reference/anonymousModules.errors.txt b/tests/baselines/reference/anonymousModules.errors.txt index 9f07d998510..b50f7591968 100644 --- a/tests/baselines/reference/anonymousModules.errors.txt +++ b/tests/baselines/reference/anonymousModules.errors.txt @@ -19,14 +19,14 @@ ~~~~~~ !!! Statement expected. ~~~ -!!! Individual declarations in a merged declaration bar must be all exported or all local. +!!! Individual declarations in merged declaration bar must be all exported or all local. } ~ !!! Declaration or statement expected. var bar = 2; ~~~ -!!! Individual declarations in a merged declaration bar must be all exported or all local. +!!! Individual declarations in merged declaration bar must be all exported or all local. module { ~ diff --git a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt index ba20d24b55c..c6d3b03e9fb 100644 --- a/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt +++ b/tests/baselines/reference/duplicateSymbolsExportMatching.errors.txt @@ -24,28 +24,28 @@ module N2 { interface I { } ~ -!!! Individual declarations in a merged declaration I must be all exported or all local. +!!! Individual declarations in merged declaration I must be all exported or all local. export interface I { } // error ~ -!!! Individual declarations in a merged declaration I must be all exported or all local. +!!! Individual declarations in merged declaration I must be all exported or all local. export interface E { } ~ -!!! Individual declarations in a merged declaration E must be all exported or all local. +!!! Individual declarations in merged declaration E must be all exported or all local. interface E { } // error ~ -!!! Individual declarations in a merged declaration E must be all exported or all local. +!!! Individual declarations in merged declaration E must be all exported or all local. } // Should report error only once for instantiated module module M { module inst { ~~~~ -!!! Individual declarations in a merged declaration inst must be all exported or all local. +!!! Individual declarations in merged declaration inst must be all exported or all local. var t; } export module inst { // one error ~~~~ -!!! Individual declarations in a merged declaration inst must be all exported or all local. +!!! Individual declarations in merged declaration inst must be all exported or all local. var t; } } @@ -54,16 +54,16 @@ module M2 { var v: string; ~ -!!! Individual declarations in a merged declaration v must be all exported or all local. +!!! Individual declarations in merged declaration v must be all exported or all local. export var v: string; // one error (visibility) ~ -!!! Individual declarations in a merged declaration v must be all exported or all local. +!!! Individual declarations in merged declaration v must be all exported or all local. var w: number; ~ -!!! Individual declarations in a merged declaration w must be all exported or all local. +!!! Individual declarations in merged declaration w must be all exported or all local. export var w: string; // two errors (visibility and type mismatch) ~ -!!! Individual declarations in a merged declaration w must be all exported or all local. +!!! Individual declarations in merged declaration w must be all exported or all local. } module M { @@ -71,24 +71,24 @@ ~ !!! A module declaration cannot be located prior to a class or function with which it is merged ~ -!!! Individual declarations in a merged declaration F must be all exported or all local. +!!! Individual declarations in merged declaration F must be all exported or all local. var t; } export function F() { } // Only one error for duplicate identifier (don't consider visibility) ~ -!!! Individual declarations in a merged declaration F must be all exported or all local. +!!! Individual declarations in merged declaration F must be all exported or all local. } module M { class C { } ~ -!!! Individual declarations in a merged declaration C must be all exported or all local. +!!! Individual declarations in merged declaration C must be all exported or all local. module C { } ~ -!!! Individual declarations in a merged declaration C must be all exported or all local. +!!! Individual declarations in merged declaration C must be all exported or all local. export module C { // Two visibility errors (one for the clodule symbol, and one for the merged container symbol) ~ -!!! Individual declarations in a merged declaration C must be all exported or all local. +!!! Individual declarations in merged declaration C must be all exported or all local. var t; } } @@ -96,7 +96,7 @@ // Top level interface D { } ~ -!!! Individual declarations in a merged declaration D must be all exported or all local. +!!! Individual declarations in merged declaration D must be all exported or all local. export interface D { } ~ -!!! Individual declarations in a merged declaration D must be all exported or all local. \ No newline at end of file +!!! Individual declarations in merged declaration D must be all exported or all local. \ No newline at end of file diff --git a/tests/baselines/reference/mixedExports.js b/tests/baselines/reference/mixedExports.js index eb650667e8d..1e99ad3c720 100644 --- a/tests/baselines/reference/mixedExports.js +++ b/tests/baselines/reference/mixedExports.js @@ -5,10 +5,15 @@ declare module M { function foo(); } +declare module M1 { + export interface Foo {} + interface Foo {} +} + module A { - interface X {x} - export module X {} - interface X {y} + interface X {x} + export module X {} + interface X {y} } //// [mixedExports.js] diff --git a/tests/baselines/reference/multivar.errors.txt b/tests/baselines/reference/multivar.errors.txt index 0a1496f4a92..655a5f42e6d 100644 --- a/tests/baselines/reference/multivar.errors.txt +++ b/tests/baselines/reference/multivar.errors.txt @@ -6,7 +6,7 @@ export var a, b2: number = 10, b; ~~ -!!! Individual declarations in a merged declaration b2 must be all exported or all local. +!!! Individual declarations in merged declaration b2 must be all exported or all local. var m1; var a2, b22: number = 10, b222; var m3; @@ -24,7 +24,7 @@ declare var d1, d2; var b2; ~~ -!!! Individual declarations in a merged declaration b2 must be all exported or all local. +!!! Individual declarations in merged declaration b2 must be all exported or all local. declare var v1; } diff --git a/tests/cases/compiler/mixedExports.ts b/tests/cases/compiler/mixedExports.ts index bad962c8e1e..c3ad54846a9 100644 --- a/tests/cases/compiler/mixedExports.ts +++ b/tests/cases/compiler/mixedExports.ts @@ -4,8 +4,13 @@ declare module M { function foo(); } +declare module M1 { + export interface Foo {} + interface Foo {} +} + module A { - interface X {x} - export module X {} - interface X {y} + interface X {x} + export module X {} + interface X {y} } \ No newline at end of file