diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 11c8773718a9f..ff11b09f0fe2a 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -403,13 +403,15 @@ namespace ts { messageNeedsName = false; } - if (symbol.declarations && symbol.declarations.length) { + let multipleDefaultExports = false; + if (length(symbol.declarations)) { // If the current node is a default export of some sort, then check if // there are any other default exports that we need to error on. // We'll know whether we have other default exports depending on if `symbol` already has a declaration list set. if (isDefaultExport) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; messageNeedsName = false; + multipleDefaultExports = true; } else { // This is to properly report an error in the case "export default { }" is after export default of class declaration or function declaration. @@ -420,15 +422,26 @@ namespace ts { (node.kind === SyntaxKind.ExportAssignment && !(node).isExportEquals)) { message = Diagnostics.A_module_cannot_have_multiple_default_exports; messageNeedsName = false; + multipleDefaultExports = true; } } } - const addError = (decl: Declaration): void => { - file.bindDiagnostics.push(createDiagnosticForNode(getNameOfDeclaration(decl) || decl, message, messageNeedsName ? getDisplayName(decl) : undefined)); - }; - forEach(symbol.declarations, addError); - addError(node); + const declarationName = getNameOfDeclaration(node) || node; + const relatedInformation: DiagnosticRelatedInformation[] = []; + forEach(symbol.declarations, (declaration, index) => { + const decl = getNameOfDeclaration(declaration) || declaration; + const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined); + file.bindDiagnostics.push( + multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag + ); + if (multipleDefaultExports) { + relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here)); + } + }); + + const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined); + file.bindDiagnostics.push(multipleDefaultExports ? addRelatedInfo(diag, ...relatedInformation) : diag); symbol = createSymbol(SymbolFlags.None, name); } diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 949fc8322ca43..24d69b97611a5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -792,14 +792,6 @@ namespace ts { } } - function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]]) { - if (!diagnostic.relatedInformation) { - diagnostic.relatedInformation = []; - } - diagnostic.relatedInformation.push(...relatedInformation); - return diagnostic; - } - function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { const diagnostic = location ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 53094bf188852..5ef209b0a88da 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2593,6 +2593,14 @@ "category": "Error", "code": 2751 }, + "The first export default is here.": { + "category": "Error", + "code": 2752 + }, + "Another export default is here.": { + "category": "Error", + "code": 2753 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ea83411539eaa..dd04f5f4c2400 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -8516,6 +8516,14 @@ namespace ts { return arr.slice(index); } + export function addRelatedInfo(diagnostic: T, ...relatedInformation: DiagnosticRelatedInformation[]): T { + if (!diagnostic.relatedInformation) { + diagnostic.relatedInformation = []; + } + diagnostic.relatedInformation.push(...relatedInformation); + return diagnostic; + } + export function minAndMax(arr: ReadonlyArray, getValue: (value: T) => number): { readonly min: number, readonly max: number } { Debug.assert(arr.length !== 0); let min = getValue(arr[0]); diff --git a/tests/baselines/reference/duplicateDefaultExport.errors.txt b/tests/baselines/reference/duplicateDefaultExport.errors.txt index e35e416ff6b97..4d7a91368ec4f 100644 --- a/tests/baselines/reference/duplicateDefaultExport.errors.txt +++ b/tests/baselines/reference/duplicateDefaultExport.errors.txt @@ -6,7 +6,9 @@ tests/cases/compiler/duplicateDefaultExport.ts(2,1): error TS2528: A module cann export default 0; ~~~~~~~~~~~~~~~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/compiler/duplicateDefaultExport.ts:2:1: The first export default is here. export default function() {} ~~~~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/compiler/duplicateDefaultExport.ts:1:1: Another export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/exportDefaultAlias_excludesEverything.errors.txt b/tests/baselines/reference/exportDefaultAlias_excludesEverything.errors.txt index 9181278db203b..377b2a122efda 100644 --- a/tests/baselines/reference/exportDefaultAlias_excludesEverything.errors.txt +++ b/tests/baselines/reference/exportDefaultAlias_excludesEverything.errors.txt @@ -6,8 +6,10 @@ tests/cases/compiler/exportDefaultAlias_excludesEverything.ts(3,16): error TS252 export default interface A {} ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/compiler/exportDefaultAlias_excludesEverything.ts:3:16: Another export default is here. interface B {} export default B; ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/compiler/exportDefaultAlias_excludesEverything.ts:1:26: The first export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt b/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt index 246b8f20cac73..b17838b7ac313 100644 --- a/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt +++ b/tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt @@ -9,12 +9,14 @@ tests/cases/compiler/a.js(3,20): error TS2652: Merged declaration 'a' cannot inc export default class a { ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/compiler/a.js:3:15: Another export default is here. ~ !!! error TS2652: Merged declaration 'a' cannot include a default export declaration. Consider adding a separate 'export default a' declaration instead. } export default var a = 10; !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/compiler/a.js:1:22: The first export default is here. ~~~ !!! error TS1109: Expression expected. ~ diff --git a/tests/baselines/reference/multipleDefaultExports01.errors.txt b/tests/baselines/reference/multipleDefaultExports01.errors.txt index a60374943df9e..8500a542aa749 100644 --- a/tests/baselines/reference/multipleDefaultExports01.errors.txt +++ b/tests/baselines/reference/multipleDefaultExports01.errors.txt @@ -1,18 +1,24 @@ tests/cases/conformance/es6/modules/m1.ts(1,22): error TS2528: A module cannot have multiple default exports. tests/cases/conformance/es6/modules/m1.ts(5,25): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/es6/modules/m1.ts(5,25): error TS2528: A module cannot have multiple default exports. tests/cases/conformance/es6/modules/m1.ts(10,16): error TS2528: A module cannot have multiple default exports. -==== tests/cases/conformance/es6/modules/m1.ts (3 errors) ==== +==== tests/cases/conformance/es6/modules/m1.ts (4 errors) ==== export default class foo { ~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/es6/modules/m1.ts:5:25: The first export default is here. } export default function bar() { ~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/es6/modules/m1.ts:1:22: Another export default is here. + ~~~ +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/es6/modules/m1.ts:10:16: Another export default is here. } @@ -20,6 +26,7 @@ tests/cases/conformance/es6/modules/m1.ts(10,16): error TS2528: A module cannot export default x; ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/es6/modules/m1.ts:5:25: The first export default is here. ==== tests/cases/conformance/es6/modules/m2.ts (0 errors) ==== import Entity from "./m1" diff --git a/tests/baselines/reference/multipleDefaultExports03.errors.txt b/tests/baselines/reference/multipleDefaultExports03.errors.txt index 5b4fc84070ba3..500acabecd63b 100644 --- a/tests/baselines/reference/multipleDefaultExports03.errors.txt +++ b/tests/baselines/reference/multipleDefaultExports03.errors.txt @@ -6,9 +6,11 @@ tests/cases/conformance/es6/modules/multipleDefaultExports03.ts(4,22): error TS2 export default class C { ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/es6/modules/multipleDefaultExports03.ts:4:22: Another export default is here. } export default class C { ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/es6/modules/multipleDefaultExports03.ts:1:22: The first export default is here. } \ No newline at end of file diff --git a/tests/baselines/reference/multipleDefaultExports05.errors.txt b/tests/baselines/reference/multipleDefaultExports05.errors.txt new file mode 100644 index 0000000000000..ed3e3156e1b57 --- /dev/null +++ b/tests/baselines/reference/multipleDefaultExports05.errors.txt @@ -0,0 +1,25 @@ +tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(1,22): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(1,22): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(3,22): error TS2528: A module cannot have multiple default exports. +tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(5,22): error TS2528: A module cannot have multiple default exports. + + +==== tests/cases/conformance/es6/modules/multipleDefaultExports05.ts (4 errors) ==== + export default class AA1 {} + ~~~ +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:3:22: Another export default is here. + ~~~ +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:5:22: Another export default is here. + + export default class BB1 {} + ~~~ +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:1:22: The first export default is here. + + export default class CC1 {} + ~~~ +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:1:22: The first export default is here. + \ No newline at end of file diff --git a/tests/baselines/reference/multipleDefaultExports05.js b/tests/baselines/reference/multipleDefaultExports05.js new file mode 100644 index 0000000000000..f4806237b4a5f --- /dev/null +++ b/tests/baselines/reference/multipleDefaultExports05.js @@ -0,0 +1,29 @@ +//// [multipleDefaultExports05.ts] +export default class AA1 {} + +export default class BB1 {} + +export default class CC1 {} + + +//// [multipleDefaultExports05.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +var AA1 = /** @class */ (function () { + function AA1() { + } + return AA1; +}()); +exports.default = AA1; +var BB1 = /** @class */ (function () { + function BB1() { + } + return BB1; +}()); +exports.default = BB1; +var CC1 = /** @class */ (function () { + function CC1() { + } + return CC1; +}()); +exports.default = CC1; diff --git a/tests/baselines/reference/multipleDefaultExports05.symbols b/tests/baselines/reference/multipleDefaultExports05.symbols new file mode 100644 index 0000000000000..254dd3c8a3cbb --- /dev/null +++ b/tests/baselines/reference/multipleDefaultExports05.symbols @@ -0,0 +1,10 @@ +=== tests/cases/conformance/es6/modules/multipleDefaultExports05.ts === +export default class AA1 {} +>AA1 : Symbol(AA1, Decl(multipleDefaultExports05.ts, 0, 0)) + +export default class BB1 {} +>BB1 : Symbol(BB1, Decl(multipleDefaultExports05.ts, 0, 27)) + +export default class CC1 {} +>CC1 : Symbol(CC1, Decl(multipleDefaultExports05.ts, 2, 27)) + diff --git a/tests/baselines/reference/multipleDefaultExports05.types b/tests/baselines/reference/multipleDefaultExports05.types new file mode 100644 index 0000000000000..49cb29ef99a09 --- /dev/null +++ b/tests/baselines/reference/multipleDefaultExports05.types @@ -0,0 +1,10 @@ +=== tests/cases/conformance/es6/modules/multipleDefaultExports05.ts === +export default class AA1 {} +>AA1 : AA1 + +export default class BB1 {} +>BB1 : import("tests/cases/conformance/es6/modules/multipleDefaultExports05").default + +export default class CC1 {} +>CC1 : import("tests/cases/conformance/es6/modules/multipleDefaultExports05").default + diff --git a/tests/baselines/reference/multipleExportDefault1.errors.txt b/tests/baselines/reference/multipleExportDefault1.errors.txt index 3da6afc41e082..12e93c782250b 100644 --- a/tests/baselines/reference/multipleExportDefault1.errors.txt +++ b/tests/baselines/reference/multipleExportDefault1.errors.txt @@ -6,6 +6,7 @@ tests/cases/conformance/externalModules/multipleExportDefault1.ts(5,1): error TS export default function Foo (){ ~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault1.ts:5:1: Another export default is here. } @@ -16,4 +17,5 @@ tests/cases/conformance/externalModules/multipleExportDefault1.ts(5,1): error TS }; ~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault1.ts:1:25: The first export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault2.errors.txt b/tests/baselines/reference/multipleExportDefault2.errors.txt index 0543e25a16330..3acec93731665 100644 --- a/tests/baselines/reference/multipleExportDefault2.errors.txt +++ b/tests/baselines/reference/multipleExportDefault2.errors.txt @@ -10,9 +10,11 @@ tests/cases/conformance/externalModules/multipleExportDefault2.ts(5,25): error T }; ~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault2.ts:5:25: The first export default is here. export default function Foo() { } ~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault2.ts:1:1: Another export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault3.errors.txt b/tests/baselines/reference/multipleExportDefault3.errors.txt index f1e55da9112fb..e3363974bc246 100644 --- a/tests/baselines/reference/multipleExportDefault3.errors.txt +++ b/tests/baselines/reference/multipleExportDefault3.errors.txt @@ -10,9 +10,11 @@ tests/cases/conformance/externalModules/multipleExportDefault3.ts(5,22): error T }; ~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault3.ts:5:22: Another export default is here. export default class C { } ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault3.ts:1:1: The first export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault4.errors.txt b/tests/baselines/reference/multipleExportDefault4.errors.txt index bba15527d004a..348ebd951a94e 100644 --- a/tests/baselines/reference/multipleExportDefault4.errors.txt +++ b/tests/baselines/reference/multipleExportDefault4.errors.txt @@ -6,6 +6,7 @@ tests/cases/conformance/externalModules/multipleExportDefault4.ts(3,1): error TS export default class C { } ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault4.ts:3:1: Another export default is here. export default { ~~~~~~~~~~~~~~~~ @@ -13,4 +14,5 @@ tests/cases/conformance/externalModules/multipleExportDefault4.ts(3,1): error TS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ }; ~~ -!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault4.ts:1:22: The first export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault5.errors.txt b/tests/baselines/reference/multipleExportDefault5.errors.txt index 30c87339ff2c5..f846248bf3818 100644 --- a/tests/baselines/reference/multipleExportDefault5.errors.txt +++ b/tests/baselines/reference/multipleExportDefault5.errors.txt @@ -6,6 +6,8 @@ tests/cases/conformance/externalModules/multipleExportDefault5.ts(2,22): error T export default function bar() { } ~~~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault5.ts:2:22: Another export default is here. export default class C {} ~ -!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault5.ts:1:25: The first export default is here. \ No newline at end of file diff --git a/tests/baselines/reference/multipleExportDefault6.errors.txt b/tests/baselines/reference/multipleExportDefault6.errors.txt index a8bf80e8df070..85529f4a9a9a7 100644 --- a/tests/baselines/reference/multipleExportDefault6.errors.txt +++ b/tests/baselines/reference/multipleExportDefault6.errors.txt @@ -10,6 +10,7 @@ tests/cases/conformance/externalModules/multipleExportDefault6.ts(5,1): error TS } ~ !!! error TS2528: A module cannot have multiple default exports. +!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault6.ts:5:1: Another export default is here. export default { ~~~~~~~~~~~~~~~~ @@ -17,4 +18,5 @@ tests/cases/conformance/externalModules/multipleExportDefault6.ts(5,1): error TS ~~~~~~~~~~ } ~ -!!! error TS2528: A module cannot have multiple default exports. \ No newline at end of file +!!! error TS2528: A module cannot have multiple default exports. +!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault6.ts:1:1: The first export default is here. \ No newline at end of file diff --git a/tests/cases/conformance/es6/modules/multipleDefaultExports05.ts b/tests/cases/conformance/es6/modules/multipleDefaultExports05.ts new file mode 100644 index 0000000000000..38a07c683ded1 --- /dev/null +++ b/tests/cases/conformance/es6/modules/multipleDefaultExports05.ts @@ -0,0 +1,8 @@ +// @module: commonjs +// @target: ES5 + +export default class AA1 {} + +export default class BB1 {} + +export default class CC1 {}