Skip to content

Commit d247675

Browse files
Kingwlweswigham
authored andcommitted
add related error span for default exports (#25396)
* add related error span for default exports * accept baseline * stash * accept baseline and fix lint * update testcase * Add missing semicolon
1 parent bd27296 commit d247675

20 files changed

+148
-18
lines changed

src/compiler/binder.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -403,13 +403,15 @@ namespace ts {
403403
messageNeedsName = false;
404404
}
405405

406-
if (symbol.declarations && symbol.declarations.length) {
406+
let multipleDefaultExports = false;
407+
if (length(symbol.declarations)) {
407408
// If the current node is a default export of some sort, then check if
408409
// there are any other default exports that we need to error on.
409410
// We'll know whether we have other default exports depending on if `symbol` already has a declaration list set.
410411
if (isDefaultExport) {
411412
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
412413
messageNeedsName = false;
414+
multipleDefaultExports = true;
413415
}
414416
else {
415417
// 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 {
420422
(node.kind === SyntaxKind.ExportAssignment && !(<ExportAssignment>node).isExportEquals)) {
421423
message = Diagnostics.A_module_cannot_have_multiple_default_exports;
422424
messageNeedsName = false;
425+
multipleDefaultExports = true;
423426
}
424427
}
425428
}
426429

427-
const addError = (decl: Declaration): void => {
428-
file.bindDiagnostics.push(createDiagnosticForNode(getNameOfDeclaration(decl) || decl, message, messageNeedsName ? getDisplayName(decl) : undefined));
429-
};
430-
forEach(symbol.declarations, addError);
431-
addError(node);
430+
const declarationName = getNameOfDeclaration(node) || node;
431+
const relatedInformation: DiagnosticRelatedInformation[] = [];
432+
forEach(symbol.declarations, (declaration, index) => {
433+
const decl = getNameOfDeclaration(declaration) || declaration;
434+
const diag = createDiagnosticForNode(decl, message, messageNeedsName ? getDisplayName(declaration) : undefined);
435+
file.bindDiagnostics.push(
436+
multipleDefaultExports ? addRelatedInfo(diag, createDiagnosticForNode(declarationName, index === 0 ? Diagnostics.Another_export_default_is_here : Diagnostics.and_here)) : diag
437+
);
438+
if (multipleDefaultExports) {
439+
relatedInformation.push(createDiagnosticForNode(decl, Diagnostics.The_first_export_default_is_here));
440+
}
441+
});
442+
443+
const diag = createDiagnosticForNode(declarationName, message, messageNeedsName ? getDisplayName(node) : undefined);
444+
file.bindDiagnostics.push(multipleDefaultExports ? addRelatedInfo(diag, ...relatedInformation) : diag);
432445

433446
symbol = createSymbol(SymbolFlags.None, name);
434447
}

src/compiler/checker.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -792,14 +792,6 @@ namespace ts {
792792
}
793793
}
794794

795-
function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]]) {
796-
if (!diagnostic.relatedInformation) {
797-
diagnostic.relatedInformation = [];
798-
}
799-
diagnostic.relatedInformation.push(...relatedInformation);
800-
return diagnostic;
801-
}
802-
803795
function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
804796
const diagnostic = location
805797
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2593,6 +2593,14 @@
25932593
"category": "Error",
25942594
"code": 2751
25952595
},
2596+
"The first export default is here.": {
2597+
"category": "Error",
2598+
"code": 2752
2599+
},
2600+
"Another export default is here.": {
2601+
"category": "Error",
2602+
"code": 2753
2603+
},
25962604

25972605
"Import declaration '{0}' is using private name '{1}'.": {
25982606
"category": "Error",

src/compiler/utilities.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8516,6 +8516,14 @@ namespace ts {
85168516
return arr.slice(index);
85178517
}
85188518

8519+
export function addRelatedInfo<T extends Diagnostic>(diagnostic: T, ...relatedInformation: DiagnosticRelatedInformation[]): T {
8520+
if (!diagnostic.relatedInformation) {
8521+
diagnostic.relatedInformation = [];
8522+
}
8523+
diagnostic.relatedInformation.push(...relatedInformation);
8524+
return diagnostic;
8525+
}
8526+
85198527
export function minAndMax<T>(arr: ReadonlyArray<T>, getValue: (value: T) => number): { readonly min: number, readonly max: number } {
85208528
Debug.assert(arr.length !== 0);
85218529
let min = getValue(arr[0]);

tests/baselines/reference/duplicateDefaultExport.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ tests/cases/compiler/duplicateDefaultExport.ts(2,1): error TS2528: A module cann
66
export default 0;
77
~~~~~~~~~~~~~~~~~
88
!!! error TS2528: A module cannot have multiple default exports.
9+
!!! related TS2752 tests/cases/compiler/duplicateDefaultExport.ts:2:1: The first export default is here.
910
export default function() {}
1011
~~~~~~
1112
!!! error TS2528: A module cannot have multiple default exports.
13+
!!! related TS2753 tests/cases/compiler/duplicateDefaultExport.ts:1:1: Another export default is here.
1214

tests/baselines/reference/exportDefaultAlias_excludesEverything.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ tests/cases/compiler/exportDefaultAlias_excludesEverything.ts(3,16): error TS252
66
export default interface A {}
77
~
88
!!! error TS2528: A module cannot have multiple default exports.
9+
!!! related TS2753 tests/cases/compiler/exportDefaultAlias_excludesEverything.ts:3:16: Another export default is here.
910
interface B {}
1011
export default B;
1112
~
1213
!!! error TS2528: A module cannot have multiple default exports.
14+
!!! related TS2752 tests/cases/compiler/exportDefaultAlias_excludesEverything.ts:1:26: The first export default is here.
1315

tests/baselines/reference/jsFileCompilationBindMultipleDefaultExports.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ tests/cases/compiler/a.js(3,20): error TS2652: Merged declaration 'a' cannot inc
99
export default class a {
1010
~
1111
!!! error TS2528: A module cannot have multiple default exports.
12+
!!! related TS2753 tests/cases/compiler/a.js:3:15: Another export default is here.
1213
~
1314
!!! error TS2652: Merged declaration 'a' cannot include a default export declaration. Consider adding a separate 'export default a' declaration instead.
1415
}
1516
export default var a = 10;
1617

1718
!!! error TS2528: A module cannot have multiple default exports.
19+
!!! related TS2752 tests/cases/compiler/a.js:1:22: The first export default is here.
1820
~~~
1921
!!! error TS1109: Expression expected.
2022
~

tests/baselines/reference/multipleDefaultExports01.errors.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
tests/cases/conformance/es6/modules/m1.ts(1,22): error TS2528: A module cannot have multiple default exports.
22
tests/cases/conformance/es6/modules/m1.ts(5,25): error TS2528: A module cannot have multiple default exports.
3+
tests/cases/conformance/es6/modules/m1.ts(5,25): error TS2528: A module cannot have multiple default exports.
34
tests/cases/conformance/es6/modules/m1.ts(10,16): error TS2528: A module cannot have multiple default exports.
45

56

6-
==== tests/cases/conformance/es6/modules/m1.ts (3 errors) ====
7+
==== tests/cases/conformance/es6/modules/m1.ts (4 errors) ====
78
export default class foo {
89
~~~
910
!!! error TS2528: A module cannot have multiple default exports.
11+
!!! related TS2752 tests/cases/conformance/es6/modules/m1.ts:5:25: The first export default is here.
1012

1113
}
1214

1315
export default function bar() {
1416
~~~
1517
!!! error TS2528: A module cannot have multiple default exports.
18+
!!! related TS2753 tests/cases/conformance/es6/modules/m1.ts:1:22: Another export default is here.
19+
~~~
20+
!!! error TS2528: A module cannot have multiple default exports.
21+
!!! related TS2753 tests/cases/conformance/es6/modules/m1.ts:10:16: Another export default is here.
1622

1723
}
1824

1925
var x = 10;
2026
export default x;
2127
~
2228
!!! error TS2528: A module cannot have multiple default exports.
29+
!!! related TS2752 tests/cases/conformance/es6/modules/m1.ts:5:25: The first export default is here.
2330

2431
==== tests/cases/conformance/es6/modules/m2.ts (0 errors) ====
2532
import Entity from "./m1"

tests/baselines/reference/multipleDefaultExports03.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ tests/cases/conformance/es6/modules/multipleDefaultExports03.ts(4,22): error TS2
66
export default class C {
77
~
88
!!! error TS2528: A module cannot have multiple default exports.
9+
!!! related TS2753 tests/cases/conformance/es6/modules/multipleDefaultExports03.ts:4:22: Another export default is here.
910
}
1011

1112
export default class C {
1213
~
1314
!!! error TS2528: A module cannot have multiple default exports.
15+
!!! related TS2752 tests/cases/conformance/es6/modules/multipleDefaultExports03.ts:1:22: The first export default is here.
1416
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(1,22): error TS2528: A module cannot have multiple default exports.
2+
tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(1,22): error TS2528: A module cannot have multiple default exports.
3+
tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(3,22): error TS2528: A module cannot have multiple default exports.
4+
tests/cases/conformance/es6/modules/multipleDefaultExports05.ts(5,22): error TS2528: A module cannot have multiple default exports.
5+
6+
7+
==== tests/cases/conformance/es6/modules/multipleDefaultExports05.ts (4 errors) ====
8+
export default class AA1 {}
9+
~~~
10+
!!! error TS2528: A module cannot have multiple default exports.
11+
!!! related TS2753 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:3:22: Another export default is here.
12+
~~~
13+
!!! error TS2528: A module cannot have multiple default exports.
14+
!!! related TS2753 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:5:22: Another export default is here.
15+
16+
export default class BB1 {}
17+
~~~
18+
!!! error TS2528: A module cannot have multiple default exports.
19+
!!! related TS2752 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:1:22: The first export default is here.
20+
21+
export default class CC1 {}
22+
~~~
23+
!!! error TS2528: A module cannot have multiple default exports.
24+
!!! related TS2752 tests/cases/conformance/es6/modules/multipleDefaultExports05.ts:1:22: The first export default is here.
25+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [multipleDefaultExports05.ts]
2+
export default class AA1 {}
3+
4+
export default class BB1 {}
5+
6+
export default class CC1 {}
7+
8+
9+
//// [multipleDefaultExports05.js]
10+
"use strict";
11+
Object.defineProperty(exports, "__esModule", { value: true });
12+
var AA1 = /** @class */ (function () {
13+
function AA1() {
14+
}
15+
return AA1;
16+
}());
17+
exports.default = AA1;
18+
var BB1 = /** @class */ (function () {
19+
function BB1() {
20+
}
21+
return BB1;
22+
}());
23+
exports.default = BB1;
24+
var CC1 = /** @class */ (function () {
25+
function CC1() {
26+
}
27+
return CC1;
28+
}());
29+
exports.default = CC1;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/es6/modules/multipleDefaultExports05.ts ===
2+
export default class AA1 {}
3+
>AA1 : Symbol(AA1, Decl(multipleDefaultExports05.ts, 0, 0))
4+
5+
export default class BB1 {}
6+
>BB1 : Symbol(BB1, Decl(multipleDefaultExports05.ts, 0, 27))
7+
8+
export default class CC1 {}
9+
>CC1 : Symbol(CC1, Decl(multipleDefaultExports05.ts, 2, 27))
10+
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
=== tests/cases/conformance/es6/modules/multipleDefaultExports05.ts ===
2+
export default class AA1 {}
3+
>AA1 : AA1
4+
5+
export default class BB1 {}
6+
>BB1 : import("tests/cases/conformance/es6/modules/multipleDefaultExports05").default
7+
8+
export default class CC1 {}
9+
>CC1 : import("tests/cases/conformance/es6/modules/multipleDefaultExports05").default
10+

tests/baselines/reference/multipleExportDefault1.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ tests/cases/conformance/externalModules/multipleExportDefault1.ts(5,1): error TS
66
export default function Foo (){
77
~~~
88
!!! error TS2528: A module cannot have multiple default exports.
9+
!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault1.ts:5:1: Another export default is here.
910

1011
}
1112

@@ -16,4 +17,5 @@ tests/cases/conformance/externalModules/multipleExportDefault1.ts(5,1): error TS
1617
};
1718
~~
1819
!!! error TS2528: A module cannot have multiple default exports.
20+
!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault1.ts:1:25: The first export default is here.
1921

tests/baselines/reference/multipleExportDefault2.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ tests/cases/conformance/externalModules/multipleExportDefault2.ts(5,25): error T
1010
};
1111
~~
1212
!!! error TS2528: A module cannot have multiple default exports.
13+
!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault2.ts:5:25: The first export default is here.
1314

1415
export default function Foo() { }
1516
~~~
1617
!!! error TS2528: A module cannot have multiple default exports.
18+
!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault2.ts:1:1: Another export default is here.
1719

1820

tests/baselines/reference/multipleExportDefault3.errors.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ tests/cases/conformance/externalModules/multipleExportDefault3.ts(5,22): error T
1010
};
1111
~~
1212
!!! error TS2528: A module cannot have multiple default exports.
13+
!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault3.ts:5:22: Another export default is here.
1314

1415
export default class C { }
1516
~
1617
!!! error TS2528: A module cannot have multiple default exports.
18+
!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault3.ts:1:1: The first export default is here.
1719

1820

tests/baselines/reference/multipleExportDefault4.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ tests/cases/conformance/externalModules/multipleExportDefault4.ts(3,1): error TS
66
export default class C { }
77
~
88
!!! error TS2528: A module cannot have multiple default exports.
9+
!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault4.ts:3:1: Another export default is here.
910

1011
export default {
1112
~~~~~~~~~~~~~~~~
1213
uhoh: "another default",
1314
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1415
};
1516
~~
16-
!!! error TS2528: A module cannot have multiple default exports.
17+
!!! error TS2528: A module cannot have multiple default exports.
18+
!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault4.ts:1:22: The first export default is here.

tests/baselines/reference/multipleExportDefault5.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ tests/cases/conformance/externalModules/multipleExportDefault5.ts(2,22): error T
66
export default function bar() { }
77
~~~
88
!!! error TS2528: A module cannot have multiple default exports.
9+
!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault5.ts:2:22: Another export default is here.
910
export default class C {}
1011
~
11-
!!! error TS2528: A module cannot have multiple default exports.
12+
!!! error TS2528: A module cannot have multiple default exports.
13+
!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault5.ts:1:25: The first export default is here.

tests/baselines/reference/multipleExportDefault6.errors.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ tests/cases/conformance/externalModules/multipleExportDefault6.ts(5,1): error TS
1010
}
1111
~
1212
!!! error TS2528: A module cannot have multiple default exports.
13+
!!! related TS2753 tests/cases/conformance/externalModules/multipleExportDefault6.ts:5:1: Another export default is here.
1314

1415
export default {
1516
~~~~~~~~~~~~~~~~
1617
lol: 2
1718
~~~~~~~~~~
1819
}
1920
~
20-
!!! error TS2528: A module cannot have multiple default exports.
21+
!!! error TS2528: A module cannot have multiple default exports.
22+
!!! related TS2752 tests/cases/conformance/externalModules/multipleExportDefault6.ts:1:1: The first export default is here.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// @module: commonjs
2+
// @target: ES5
3+
4+
export default class AA1 {}
5+
6+
export default class BB1 {}
7+
8+
export default class CC1 {}

0 commit comments

Comments
 (0)