Skip to content

add related error span for default exports #25396

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Mar 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/compiler/binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -420,15 +422,26 @@ namespace ts {
(node.kind === SyntaxKind.ExportAssignment && !(<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);
}
Expand Down
8 changes: 0 additions & 8 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8516,6 +8516,14 @@ namespace ts {
return arr.slice(index);
}

export function addRelatedInfo<T extends Diagnostic>(diagnostic: T, ...relatedInformation: DiagnosticRelatedInformation[]): T {
if (!diagnostic.relatedInformation) {
diagnostic.relatedInformation = [];
}
diagnostic.relatedInformation.push(...relatedInformation);
return diagnostic;
}

export function minAndMax<T>(arr: ReadonlyArray<T>, getValue: (value: T) => number): { readonly min: number, readonly max: number } {
Debug.assert(arr.length !== 0);
let min = getValue(arr[0]);
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/duplicateDefaultExport.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -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.

Original file line number Diff line number Diff line change
Expand Up @@ -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.
~
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
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.

}

var x = 10;
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"
Expand Down
2 changes: 2 additions & 0 deletions tests/baselines/reference/multipleDefaultExports03.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}
25 changes: 25 additions & 0 deletions tests/baselines/reference/multipleDefaultExports05.errors.txt
Original file line number Diff line number Diff line change
@@ -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.

29 changes: 29 additions & 0 deletions tests/baselines/reference/multipleDefaultExports05.js
Original file line number Diff line number Diff line change
@@ -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;
10 changes: 10 additions & 0 deletions tests/baselines/reference/multipleDefaultExports05.symbols
Original file line number Diff line number Diff line change
@@ -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))

10 changes: 10 additions & 0 deletions tests/baselines/reference/multipleDefaultExports05.types
Original file line number Diff line number Diff line change
@@ -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

2 changes: 2 additions & 0 deletions tests/baselines/reference/multipleExportDefault1.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.

}

Expand All @@ -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.

2 changes: 2 additions & 0 deletions tests/baselines/reference/multipleExportDefault2.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.


2 changes: 2 additions & 0 deletions tests/baselines/reference/multipleExportDefault3.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.


4 changes: 3 additions & 1 deletion tests/baselines/reference/multipleExportDefault4.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ 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 {
~~~~~~~~~~~~~~~~
uhoh: "another default",
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
};
~~
!!! error TS2528: A module cannot have multiple default exports.
!!! 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.
4 changes: 3 additions & 1 deletion tests/baselines/reference/multipleExportDefault5.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
!!! 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.
4 changes: 3 additions & 1 deletion tests/baselines/reference/multipleExportDefault6.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ 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 {
~~~~~~~~~~~~~~~~
lol: 2
~~~~~~~~~~
}
~
!!! error TS2528: A module cannot have multiple default exports.
!!! 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.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// @module: commonjs
// @target: ES5

export default class AA1 {}

export default class BB1 {}

export default class CC1 {}