Handle export * reexport errors smartly

This commit is contained in:
Wesley Wigham
2015-10-06 15:50:35 -07:00
parent ff7169530b
commit b9800f09b7
4 changed files with 55 additions and 17 deletions
+37 -15
View File
@@ -1020,41 +1020,62 @@ namespace ts {
return links.resolvedExports || (links.resolvedExports = getExportsForModule(moduleSymbol));
}
function extendExportSymbols(originalModule: Symbol, target: SymbolTable, source: SymbolTable) {
interface ExportStarDiagnosticsLookupTable {
[id: string]: {specifierText: string, exportsWithDuplicate: ExportDeclaration[]};
}
function extendExportSymbols(target: SymbolTable, source: SymbolTable, lookupTable?: ExportStarDiagnosticsLookupTable, exportNode?: ExportDeclaration) {
for (let id in source) {
if (id !== "default" && !hasProperty(target, id)) {
target[id] = source[id];
if (lookupTable && exportNode) {
lookupTable[id] = {
specifierText: getTextOfNode(exportNode.moduleSpecifier),
exportsWithDuplicate: []
};
}
}
else if (id !== "default" && hasProperty(target, id)) {
diagnostics.add(createFileDiagnostic(getSourceFileOfNode(originalModule.valueDeclaration), 0, 0, Diagnostics.Duplicate_identifier_0, id));
else if (lookupTable && exportNode && id !== "default" && hasProperty(target, id)) {
lookupTable[id].exportsWithDuplicate.push(exportNode);
}
}
}
function getExportsForModule(moduleSymbol: Symbol): SymbolTable {
let result: SymbolTable;
let visitedSymbols: Symbol[] = [];
visit(moduleSymbol);
return result || moduleSymbol.exports;
return visit(moduleSymbol) || moduleSymbol.exports;
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
function visit(symbol: Symbol) {
function visit(symbol: Symbol): SymbolTable {
if (symbol && symbol.flags & SymbolFlags.HasExports && !contains(visitedSymbols, symbol)) {
visitedSymbols.push(symbol);
if (symbol !== moduleSymbol) {
if (!result) {
result = cloneSymbolTable(moduleSymbol.exports);
}
extendExportSymbols(moduleSymbol, result, symbol.exports);
}
let symbols: SymbolTable = cloneSymbolTable(symbol.exports);
// All export * declarations are collected in an __export symbol by the binder
let exportStars = symbol.exports["__export"];
if (exportStars) {
let nestedSymbols: SymbolTable = {};
let lookupTable: ExportStarDiagnosticsLookupTable = {};
for (let node of exportStars.declarations) {
visit(resolveExternalModuleName(node, (<ExportDeclaration>node).moduleSpecifier));
let resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier);
let exportedSymbols = visit(resolvedModule);
extendExportSymbols(
nestedSymbols,
exportedSymbols,
lookupTable,
node as ExportDeclaration
);
}
for (let id in lookupTable) {
if (id !== "export=" && lookupTable[id].exportsWithDuplicate.length && !(id in symbols)) { // It's not an error if the file with multiple export *'s with duplicate names exports a member with that name itself
for (let node of lookupTable[id].exportsWithDuplicate) {
diagnostics.add(createDiagnosticForNode(node, Diagnostics.An_export_Asterisk_from_0_declaration_has_already_exported_a_member_named_1_Consider_explicitly_re_exporting_to_resolve_the_ambiguity, lookupTable[id].specifierText, id));
}
}
}
extendExportSymbols(symbols, nestedSymbols);
}
return symbols;
}
}
}
@@ -13701,13 +13722,14 @@ namespace ts {
function checkExternalModuleExports(node: SourceFile | ModuleDeclaration) {
let moduleSymbol = getSymbolOfNode(node);
let links = getSymbolLinks(moduleSymbol);
let links: SymbolLinks = getSymbolLinks(moduleSymbol);
if (!links.exportsChecked) {
let exportEqualsSymbol = moduleSymbol.exports["export="];
if (exportEqualsSymbol && hasExportedMembers(moduleSymbol)) {
let declaration = getDeclarationOfAliasSymbol(exportEqualsSymbol) || exportEqualsSymbol.valueDeclaration;
error(declaration, Diagnostics.An_export_assignment_cannot_be_used_in_a_module_with_other_exported_elements);
}
getExportsOfModule(moduleSymbol); // Checks for export * conflicts
links.exportsChecked = true;
}
}
+4
View File
@@ -832,6 +832,10 @@
"category": "Error",
"code": 2307
},
"An 'export * from {0}' declaration has already exported a member named '{1}'. Consider explicitly re-exporting to resolve the ambiguity.": {
"category": "Error",
"code": 2308
},
"An export assignment cannot be used in a module with other exported elements.": {
"category": "Error",
"code": 2309
@@ -1,4 +1,6 @@
tests/cases/conformance/es6/modules/main.ts(1,8): error TS1192: Module '"tests/cases/conformance/es6/modules/t4"' has no default export.
tests/cases/conformance/es6/modules/t4.ts(3,1): error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'x'. Consider explicitly re-exporting to resolve the ambiguity.
tests/cases/conformance/es6/modules/t4.ts(3,1): error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'y'. Consider explicitly re-exporting to resolve the ambiguity.
==== tests/cases/conformance/es6/modules/t1.ts (0 errors) ====
@@ -16,10 +18,14 @@ tests/cases/conformance/es6/modules/main.ts(1,8): error TS1192: Module '"tests/c
var z = "z";
export { x, y, z };
==== tests/cases/conformance/es6/modules/t4.ts (0 errors) ====
==== tests/cases/conformance/es6/modules/t4.ts (2 errors) ====
export * from "./t1";
export * from "./t2";
export * from "./t3";
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'x'. Consider explicitly re-exporting to resolve the ambiguity.
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'y'. Consider explicitly re-exporting to resolve the ambiguity.
==== tests/cases/conformance/es6/modules/main.ts (1 errors) ====
import hello, { x, y, z, foo } from "./t4";
@@ -1,4 +1,6 @@
tests/cases/conformance/es6/modules/main.ts(1,8): error TS1192: Module '"tests/cases/conformance/es6/modules/t4"' has no default export.
tests/cases/conformance/es6/modules/t4.ts(3,1): error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'x'. Consider explicitly re-exporting to resolve the ambiguity.
tests/cases/conformance/es6/modules/t4.ts(3,1): error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'y'. Consider explicitly re-exporting to resolve the ambiguity.
==== tests/cases/conformance/es6/modules/t1.ts (0 errors) ====
@@ -16,10 +18,14 @@ tests/cases/conformance/es6/modules/main.ts(1,8): error TS1192: Module '"tests/c
var z = "z";
export { x, y, z };
==== tests/cases/conformance/es6/modules/t4.ts (0 errors) ====
==== tests/cases/conformance/es6/modules/t4.ts (2 errors) ====
export * from "./t1";
export * from "./t2";
export * from "./t3";
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'x'. Consider explicitly re-exporting to resolve the ambiguity.
~~~~~~~~~~~~~~~~~~~~~
!!! error TS2308: An 'export * from "./t1"' declaration has already exported a member named 'y'. Consider explicitly re-exporting to resolve the ambiguity.
==== tests/cases/conformance/es6/modules/main.ts (1 errors) ====
import hello, { x, y, z, foo } from "./t4";