Skip to content

Commit f630365

Browse files
authored
Improve errors for incorrectly nested export default (#43967)
* Improve errors for incorrectly nested export default The compiler and services don't handle incorrectly nested `export default` well right now: ```ts export = (x,y) => { export default { } } ``` Asking for document highlights, find all references or quick info on 'export' or 'default' cause a crash. After the crash is fixed, the error message is confusing and wrong: "An export assignment cannot be used outside a module." This PR: 1. Skips document highlights for incorrectly nested export default. 2. Skips find all refs for incorrectly nested export default. 3. Switches the fallback binding for incorrectly nested export default from Alias to Property. Neither is correct, but Property doesn't cause a crash in alias resolution. 4. Improves the error message to reflect a post-ES module world, which has export default and 'module' means 'ES module', not 'namespace'. Fixes #40082 and the related bugs mentioned above. * address PR comments
1 parent ad6ca7a commit f630365

12 files changed

+387
-29
lines changed

src/compiler/binder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,8 +2768,8 @@ namespace ts {
27682768

27692769
function bindExportAssignment(node: ExportAssignment) {
27702770
if (!container.symbol || !container.symbol.exports) {
2771-
// Export assignment in some sort of block construct
2772-
bindAnonymousDeclaration(node, SymbolFlags.Alias, getDeclarationName(node)!);
2771+
// Incorrect export assignment in some sort of block construct
2772+
bindAnonymousDeclaration(node, SymbolFlags.Value, getDeclarationName(node)!);
27732773
}
27742774
else {
27752775
const flags = exportAssignmentIsAlias(node)

src/compiler/checker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38315,7 +38315,10 @@ namespace ts {
3831538315
}
3831638316

3831738317
function checkExportAssignment(node: ExportAssignment) {
38318-
if (checkGrammarModuleElementContext(node, Diagnostics.An_export_assignment_can_only_be_used_in_a_module)) {
38318+
const illegalContextMessage = node.isExportEquals
38319+
? Diagnostics.An_export_assignment_must_be_at_the_top_level_of_a_file_or_module_declaration
38320+
: Diagnostics.A_default_export_must_be_at_the_top_level_of_a_file_or_module_declaration;
38321+
if (checkGrammarModuleElementContext(node, illegalContextMessage)) {
3831938322
// If we hit an export assignment in an illegal context, just bail out to avoid cascading errors.
3832038323
return;
3832138324
}

src/compiler/diagnosticMessages.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -743,7 +743,7 @@
743743
"category": "Error",
744744
"code": 1230
745745
},
746-
"An export assignment can only be used in a module.": {
746+
"An export assignment must be at the top level of a file or module declaration.": {
747747
"category": "Error",
748748
"code": 1231
749749
},
@@ -847,6 +847,10 @@
847847
"category": "Error",
848848
"code": 1257
849849
},
850+
"A default export must be at the top level of a file or module declaration.": {
851+
"category": "Error",
852+
"code": 1258
853+
},
850854
"Module '{0}' can only be default-imported using the '{1}' flag": {
851855
"category": "Error",
852856
"code": 1259

src/services/findAllReferences.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -921,9 +921,9 @@ namespace ts.FindAllReferences {
921921
// When renaming at an export specifier, rename the export and not the thing being exported.
922922
getReferencesAtExportSpecifier(exportSpecifier.name, symbol, exportSpecifier, state.createSearch(node, originalSymbol, /*comingFrom*/ undefined), state, /*addReferencesHere*/ true, /*alwaysGetReferences*/ true);
923923
}
924-
else if (node && node.kind === SyntaxKind.DefaultKeyword && symbol.escapedName === InternalSymbolName.Default) {
924+
else if (node && node.kind === SyntaxKind.DefaultKeyword && symbol.escapedName === InternalSymbolName.Default && symbol.parent) {
925925
addReference(node, symbol, state);
926-
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: Debug.checkDefined(symbol.parent, "Expected export symbol to have a parent"), exportKind: ExportKind.Default }, state);
926+
searchForImportsOfExport(node, symbol, { exportingModuleSymbol: symbol.parent, exportKind: ExportKind.Default }, state);
927927
}
928928
else {
929929
const search = state.createSearch(node, symbol, /*comingFrom*/ undefined, { allSearchSymbols: node ? populateSearchSymbolSet(symbol, node, checker, options.use === FindReferencesUse.Rename, !!options.providePrefixAndSuffixTextForRename, !!options.implementations) : [symbol] });

src/services/importTracker.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -465,13 +465,13 @@ namespace ts.FindAllReferences {
465465

466466
function getExport(): ExportedSymbol | ImportedSymbol | undefined {
467467
const { parent } = node;
468-
const grandParent = parent.parent;
468+
const grandparent = parent.parent;
469469
if (symbol.exportSymbol) {
470470
if (parent.kind === SyntaxKind.PropertyAccessExpression) {
471471
// When accessing an export of a JS module, there's no alias. The symbol will still be flagged as an export even though we're at the use.
472472
// So check that we are at the declaration.
473-
return symbol.declarations?.some(d => d === parent) && isBinaryExpression(grandParent)
474-
? getSpecialPropertyExport(grandParent, /*useLhsSymbol*/ false)
473+
return symbol.declarations?.some(d => d === parent) && isBinaryExpression(grandparent)
474+
? getSpecialPropertyExport(grandparent, /*useLhsSymbol*/ false)
475475
: undefined;
476476
}
477477
else {
@@ -502,26 +502,26 @@ namespace ts.FindAllReferences {
502502
return getExportAssignmentExport(parent);
503503
}
504504
// If we are in `export = class A {};` (or `export = class A {};`) at `A`, `parent.parent` is the export assignment.
505-
else if (isExportAssignment(grandParent)) {
506-
return getExportAssignmentExport(grandParent);
505+
else if (isExportAssignment(grandparent)) {
506+
return getExportAssignmentExport(grandparent);
507507
}
508508
// Similar for `module.exports =` and `exports.A =`.
509509
else if (isBinaryExpression(parent)) {
510510
return getSpecialPropertyExport(parent, /*useLhsSymbol*/ true);
511511
}
512-
else if (isBinaryExpression(grandParent)) {
513-
return getSpecialPropertyExport(grandParent, /*useLhsSymbol*/ true);
512+
else if (isBinaryExpression(grandparent)) {
513+
return getSpecialPropertyExport(grandparent, /*useLhsSymbol*/ true);
514514
}
515515
else if (isJSDocTypedefTag(parent)) {
516516
return exportInfo(symbol, ExportKind.Named);
517517
}
518518
}
519519

520-
function getExportAssignmentExport(ex: ExportAssignment): ExportedSymbol {
520+
function getExportAssignmentExport(ex: ExportAssignment): ExportedSymbol | undefined {
521521
// Get the symbol for the `export =` node; its parent is the module it's the export of.
522-
const exportingModuleSymbol = Debug.checkDefined(ex.symbol.parent, "Expected export symbol to have a parent");
522+
if (!ex.symbol.parent) return undefined;
523523
const exportKind = ex.isExportEquals ? ExportKind.ExportEquals : ExportKind.Default;
524-
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol, exportKind } };
524+
return { kind: ImportExport.Export, symbol, exportInfo: { exportingModuleSymbol: ex.symbol.parent, exportKind } };
525525
}
526526

527527
function getSpecialPropertyExport(node: BinaryExpression, useLhsSymbol: boolean): ExportedSymbol | undefined {

src/services/symbolDisplay.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ namespace ts.SymbolDisplay {
174174
}
175175

176176
let signature: Signature | undefined;
177-
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol.exportSymbol || symbol, location);
177+
type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location);
178178

179179
if (location.parent && location.parent.kind === SyntaxKind.PropertyAccessExpression) {
180180
const right = (<PropertyAccessExpression>location.parent).name;

tests/baselines/reference/moduleElementsInWrongContext.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ tests/cases/compiler/moduleElementsInWrongContext.ts(2,5): error TS1235: A names
22
tests/cases/compiler/moduleElementsInWrongContext.ts(3,5): error TS1235: A namespace declaration is only allowed in a namespace or module.
33
tests/cases/compiler/moduleElementsInWrongContext.ts(7,5): error TS1235: A namespace declaration is only allowed in a namespace or module.
44
tests/cases/compiler/moduleElementsInWrongContext.ts(9,5): error TS1234: An ambient module declaration is only allowed at the top level in a file.
5-
tests/cases/compiler/moduleElementsInWrongContext.ts(13,5): error TS1231: An export assignment can only be used in a module.
5+
tests/cases/compiler/moduleElementsInWrongContext.ts(13,5): error TS1231: An export assignment must be at the top level of a file or module declaration.
66
tests/cases/compiler/moduleElementsInWrongContext.ts(17,5): error TS1233: An export declaration can only be used in a module.
77
tests/cases/compiler/moduleElementsInWrongContext.ts(18,5): error TS1233: An export declaration can only be used in a module.
88
tests/cases/compiler/moduleElementsInWrongContext.ts(19,5): error TS1233: An export declaration can only be used in a module.
9-
tests/cases/compiler/moduleElementsInWrongContext.ts(20,5): error TS1231: An export assignment can only be used in a module.
9+
tests/cases/compiler/moduleElementsInWrongContext.ts(20,5): error TS1258: A default export must be at the top level of a file or module declaration.
1010
tests/cases/compiler/moduleElementsInWrongContext.ts(21,5): error TS1184: Modifiers cannot appear here.
1111
tests/cases/compiler/moduleElementsInWrongContext.ts(22,5): error TS1184: Modifiers cannot appear here.
1212
tests/cases/compiler/moduleElementsInWrongContext.ts(23,5): error TS1232: An import declaration can only be used in a namespace or module.
@@ -40,7 +40,7 @@ tests/cases/compiler/moduleElementsInWrongContext.ts(28,5): error TS1232: An imp
4040

4141
export = M;
4242
~~~~~~
43-
!!! error TS1231: An export assignment can only be used in a module.
43+
!!! error TS1231: An export assignment must be at the top level of a file or module declaration.
4444

4545
var v;
4646
function foo() { }
@@ -55,7 +55,7 @@ tests/cases/compiler/moduleElementsInWrongContext.ts(28,5): error TS1232: An imp
5555
!!! error TS1233: An export declaration can only be used in a module.
5656
export default v;
5757
~~~~~~
58-
!!! error TS1231: An export assignment can only be used in a module.
58+
!!! error TS1258: A default export must be at the top level of a file or module declaration.
5959
export default class C { }
6060
~~~~~~
6161
!!! error TS1184: Modifiers cannot appear here.

tests/baselines/reference/moduleElementsInWrongContext2.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ tests/cases/compiler/moduleElementsInWrongContext2.ts(2,5): error TS1235: A name
22
tests/cases/compiler/moduleElementsInWrongContext2.ts(3,5): error TS1235: A namespace declaration is only allowed in a namespace or module.
33
tests/cases/compiler/moduleElementsInWrongContext2.ts(7,5): error TS1235: A namespace declaration is only allowed in a namespace or module.
44
tests/cases/compiler/moduleElementsInWrongContext2.ts(9,5): error TS1234: An ambient module declaration is only allowed at the top level in a file.
5-
tests/cases/compiler/moduleElementsInWrongContext2.ts(13,5): error TS1231: An export assignment can only be used in a module.
5+
tests/cases/compiler/moduleElementsInWrongContext2.ts(13,5): error TS1231: An export assignment must be at the top level of a file or module declaration.
66
tests/cases/compiler/moduleElementsInWrongContext2.ts(17,5): error TS1233: An export declaration can only be used in a module.
77
tests/cases/compiler/moduleElementsInWrongContext2.ts(18,5): error TS1233: An export declaration can only be used in a module.
88
tests/cases/compiler/moduleElementsInWrongContext2.ts(19,5): error TS1233: An export declaration can only be used in a module.
9-
tests/cases/compiler/moduleElementsInWrongContext2.ts(20,5): error TS1231: An export assignment can only be used in a module.
9+
tests/cases/compiler/moduleElementsInWrongContext2.ts(20,5): error TS1258: A default export must be at the top level of a file or module declaration.
1010
tests/cases/compiler/moduleElementsInWrongContext2.ts(21,5): error TS1184: Modifiers cannot appear here.
1111
tests/cases/compiler/moduleElementsInWrongContext2.ts(22,5): error TS1184: Modifiers cannot appear here.
1212
tests/cases/compiler/moduleElementsInWrongContext2.ts(23,5): error TS1232: An import declaration can only be used in a namespace or module.
@@ -40,7 +40,7 @@ tests/cases/compiler/moduleElementsInWrongContext2.ts(28,5): error TS1232: An im
4040

4141
export = M;
4242
~~~~~~
43-
!!! error TS1231: An export assignment can only be used in a module.
43+
!!! error TS1231: An export assignment must be at the top level of a file or module declaration.
4444

4545
var v;
4646
function foo() { }
@@ -55,7 +55,7 @@ tests/cases/compiler/moduleElementsInWrongContext2.ts(28,5): error TS1232: An im
5555
!!! error TS1233: An export declaration can only be used in a module.
5656
export default v;
5757
~~~~~~
58-
!!! error TS1231: An export assignment can only be used in a module.
58+
!!! error TS1258: A default export must be at the top level of a file or module declaration.
5959
export default class C { }
6060
~~~~~~
6161
!!! error TS1184: Modifiers cannot appear here.

tests/baselines/reference/moduleElementsInWrongContext3.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ tests/cases/compiler/moduleElementsInWrongContext3.ts(3,9): error TS1235: A name
22
tests/cases/compiler/moduleElementsInWrongContext3.ts(4,9): error TS1235: A namespace declaration is only allowed in a namespace or module.
33
tests/cases/compiler/moduleElementsInWrongContext3.ts(8,9): error TS1235: A namespace declaration is only allowed in a namespace or module.
44
tests/cases/compiler/moduleElementsInWrongContext3.ts(10,9): error TS1234: An ambient module declaration is only allowed at the top level in a file.
5-
tests/cases/compiler/moduleElementsInWrongContext3.ts(14,9): error TS1231: An export assignment can only be used in a module.
5+
tests/cases/compiler/moduleElementsInWrongContext3.ts(14,9): error TS1231: An export assignment must be at the top level of a file or module declaration.
66
tests/cases/compiler/moduleElementsInWrongContext3.ts(18,9): error TS1233: An export declaration can only be used in a module.
77
tests/cases/compiler/moduleElementsInWrongContext3.ts(19,9): error TS1233: An export declaration can only be used in a module.
88
tests/cases/compiler/moduleElementsInWrongContext3.ts(20,9): error TS1233: An export declaration can only be used in a module.
9-
tests/cases/compiler/moduleElementsInWrongContext3.ts(21,9): error TS1231: An export assignment can only be used in a module.
9+
tests/cases/compiler/moduleElementsInWrongContext3.ts(21,9): error TS1258: A default export must be at the top level of a file or module declaration.
1010
tests/cases/compiler/moduleElementsInWrongContext3.ts(22,9): error TS1184: Modifiers cannot appear here.
1111
tests/cases/compiler/moduleElementsInWrongContext3.ts(23,9): error TS1184: Modifiers cannot appear here.
1212
tests/cases/compiler/moduleElementsInWrongContext3.ts(24,9): error TS1232: An import declaration can only be used in a namespace or module.
@@ -41,7 +41,7 @@ tests/cases/compiler/moduleElementsInWrongContext3.ts(29,9): error TS1232: An im
4141

4242
export = M;
4343
~~~~~~
44-
!!! error TS1231: An export assignment can only be used in a module.
44+
!!! error TS1231: An export assignment must be at the top level of a file or module declaration.
4545

4646
var v;
4747
function foo() { }
@@ -56,7 +56,7 @@ tests/cases/compiler/moduleElementsInWrongContext3.ts(29,9): error TS1232: An im
5656
!!! error TS1233: An export declaration can only be used in a module.
5757
export default v;
5858
~~~~~~
59-
!!! error TS1231: An export assignment can only be used in a module.
59+
!!! error TS1258: A default export must be at the top level of a file or module declaration.
6060
export default class C { }
6161
~~~~~~
6262
!!! error TS1184: Modifiers cannot appear here.

0 commit comments

Comments
 (0)