@@ -422,6 +422,7 @@ namespace ts {
422
422
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
423
423
424
424
const globals = createSymbolTable();
425
+ let amalgamatedDuplicates: Map<{ firstFile: SourceFile, secondFile: SourceFile, firstFileInstances: Map<{ instances: Node[], blockScoped: boolean }>, secondFileInstances: Map<{ instances: Node[], blockScoped: boolean }> }> | undefined;
425
426
const reverseMappedCache = createMap<Type | undefined>();
426
427
let ambientModulesCache: Symbol[] | undefined;
427
428
/**
@@ -693,6 +694,28 @@ namespace ts {
693
694
return emitResolver;
694
695
}
695
696
697
+ function lookupOrIssueError(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
698
+ const diagnostic = location
699
+ ? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
700
+ : createCompilerDiagnostic(message, arg0, arg1, arg2, arg3);
701
+ const existing = diagnostics.lookup(diagnostic);
702
+ if (existing) {
703
+ return existing;
704
+ }
705
+ else {
706
+ diagnostics.add(diagnostic);
707
+ return diagnostic;
708
+ }
709
+ }
710
+
711
+ function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: DiagnosticRelatedInformation[]) {
712
+ if (!diagnostic.relatedInformation) {
713
+ diagnostic.relatedInformation = [];
714
+ }
715
+ diagnostic.relatedInformation.push(...relatedInformation);
716
+ return diagnostic;
717
+ }
718
+
696
719
function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
697
720
const diagnostic = location
698
721
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
@@ -803,23 +826,63 @@ namespace ts {
803
826
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
804
827
}
805
828
else {
806
- const message = target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum
829
+ const isEitherEnum = !!(target.flags & SymbolFlags.Enum || source.flags & SymbolFlags.Enum);
830
+ const isEitherBlockScoped = !!(target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable);
831
+ const message = isEitherEnum
807
832
? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations
808
- : target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
833
+ : isEitherBlockScoped
809
834
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
810
835
: Diagnostics.Duplicate_identifier_0;
811
- forEach(source.declarations, node => {
812
- const errorNode = (getJavascriptInitializer(node, /*isPrototypeAssignment*/ false) ? getOuterNameOfJsInitializer(node) : getNameOfDeclaration(node)) || node;
813
- error(errorNode, message, symbolToString(source));
814
- });
815
- forEach(target.declarations, node => {
816
- const errorNode = (getJavascriptInitializer(node, /*isPrototypeAssignment*/ false) ? getOuterNameOfJsInitializer(node) : getNameOfDeclaration(node)) || node;
817
- error(errorNode, message, symbolToString(source));
818
- });
836
+ const sourceSymbolFile = source.declarations && getSourceFileOfNode(source.declarations[0]);
837
+ const targetSymbolFile = target.declarations && getSourceFileOfNode(target.declarations[0]);
838
+
839
+ // Collect top-level duplicate identifier errors into one mapping, so we can then merge their diagnostics if there are a bunch
840
+ if (sourceSymbolFile && targetSymbolFile && amalgamatedDuplicates && !isEitherEnum && sourceSymbolFile !== targetSymbolFile) {
841
+ const firstFile = comparePaths(sourceSymbolFile.path, targetSymbolFile.path) === Comparison.LessThan ? sourceSymbolFile : targetSymbolFile;
842
+ const secondFile = firstFile === sourceSymbolFile ? targetSymbolFile : sourceSymbolFile;
843
+ const cacheKey = `${firstFile.path}|${secondFile.path}`;
844
+ const existing = amalgamatedDuplicates.get(cacheKey) || { firstFile, secondFile, firstFileInstances: createMap(), secondFileInstances: createMap() };
845
+ const symbolName = symbolToString(source);
846
+ const firstInstanceList = existing.firstFileInstances.get(symbolName) || { instances: [], blockScoped: isEitherBlockScoped };
847
+ const secondInstanceList = existing.secondFileInstances.get(symbolName) || { instances: [], blockScoped: isEitherBlockScoped };
848
+
849
+ forEach(source.declarations, node => {
850
+ const errorNode = (getJavascriptInitializer(node, /*isPrototypeAssignment*/ false) ? getOuterNameOfJsInitializer(node) : getNameOfDeclaration(node)) || node;
851
+ const targetList = sourceSymbolFile === firstFile ? firstInstanceList : secondInstanceList;
852
+ targetList.instances.push(errorNode);
853
+ });
854
+ forEach(target.declarations, node => {
855
+ const errorNode = (getJavascriptInitializer(node, /*isPrototypeAssignment*/ false) ? getOuterNameOfJsInitializer(node) : getNameOfDeclaration(node)) || node;
856
+ const targetList = targetSymbolFile === firstFile ? firstInstanceList : secondInstanceList;
857
+ targetList.instances.push(errorNode);
858
+ });
859
+
860
+ existing.firstFileInstances.set(symbolName, firstInstanceList);
861
+ existing.secondFileInstances.set(symbolName, secondInstanceList);
862
+ amalgamatedDuplicates.set(cacheKey, existing);
863
+ return target;
864
+ }
865
+ const symbolName = symbolToString(source);
866
+ addDuplicateDeclarationErrorsForSymbols(source, message, symbolName, target);
867
+ addDuplicateDeclarationErrorsForSymbols(target, message, symbolName, source);
819
868
}
820
869
return target;
821
870
}
822
871
872
+ function addDuplicateDeclarationErrorsForSymbols(target: Symbol, message: DiagnosticMessage, symbolName: string, source: Symbol) {
873
+ forEach(target.declarations, node => {
874
+ const errorNode = (getJavascriptInitializer(node, /*isPrototypeAssignment*/ false) ? getOuterNameOfJsInitializer(node) : getNameOfDeclaration(node)) || node;
875
+ addDuplicateDeclarationError(errorNode, message, symbolName, source.declarations && source.declarations[0]);
876
+ });
877
+ }
878
+
879
+ function addDuplicateDeclarationError(errorNode: Node, message: DiagnosticMessage, symbolName: string, relatedNode: Node | undefined) {
880
+ const err = lookupOrIssueError(errorNode, message, symbolName);
881
+ if (relatedNode && length(err.relatedInformation) < 5) {
882
+ addRelatedInfo(err, !length(err.relatedInformation) ? createDiagnosticForNode(relatedNode, Diagnostics._0_was_also_declared_here, symbolName) : createDiagnosticForNode(relatedNode, Diagnostics.and_here));
883
+ }
884
+ }
885
+
823
886
function combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined {
824
887
if (!hasEntries(first)) return second;
825
888
if (!hasEntries(second)) return first;
@@ -1592,14 +1655,25 @@ namespace ts {
1592
1655
if (declaration === undefined) return Debug.fail("Declaration to checkResolvedBlockScopedVariable is undefined");
1593
1656
1594
1657
if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
1658
+ let diagnosticMessage;
1659
+ const declarationName = declarationNameToString(getNameOfDeclaration(declaration));
1595
1660
if (result.flags & SymbolFlags.BlockScopedVariable) {
1596
- error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)) );
1661
+ diagnosticMessage = error(errorLocation, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName );
1597
1662
}
1598
1663
else if (result.flags & SymbolFlags.Class) {
1599
- error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)) );
1664
+ diagnosticMessage = error(errorLocation, Diagnostics.Class_0_used_before_its_declaration, declarationName );
1600
1665
}
1601
1666
else if (result.flags & SymbolFlags.RegularEnum) {
1602
- error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationNameToString(getNameOfDeclaration(declaration)));
1667
+ diagnosticMessage = error(errorLocation, Diagnostics.Enum_0_used_before_its_declaration, declarationName);
1668
+ }
1669
+ else {
1670
+ Debug.assert(!!(result.flags & SymbolFlags.ConstEnum));
1671
+ }
1672
+
1673
+ if (diagnosticMessage) {
1674
+ addRelatedInfo(diagnosticMessage,
1675
+ createDiagnosticForNode(declaration, Diagnostics._0_was_declared_here, declarationName)
1676
+ );
1603
1677
}
1604
1678
}
1605
1679
}
@@ -17397,16 +17471,24 @@ namespace ts {
17397
17471
return;
17398
17472
}
17399
17473
17474
+ let diagnosticMessage;
17475
+ const declarationName = idText(right);
17400
17476
if (isInPropertyInitializer(node) &&
17401
17477
!isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
17402
17478
&& !isPropertyDeclaredInAncestorClass(prop)) {
17403
- error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, idText(right) );
17479
+ diagnosticMessage = error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, declarationName );
17404
17480
}
17405
17481
else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration &&
17406
17482
node.parent.kind !== SyntaxKind.TypeReference &&
17407
17483
!(valueDeclaration.flags & NodeFlags.Ambient) &&
17408
17484
!isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)) {
17409
- error(right, Diagnostics.Class_0_used_before_its_declaration, idText(right));
17485
+ diagnosticMessage = error(right, Diagnostics.Class_0_used_before_its_declaration, declarationName);
17486
+ }
17487
+
17488
+ if (diagnosticMessage) {
17489
+ addRelatedInfo(diagnosticMessage,
17490
+ createDiagnosticForNode(valueDeclaration, Diagnostics._0_was_declared_here, declarationName)
17491
+ );
17410
17492
}
17411
17493
}
17412
17494
@@ -19020,8 +19102,8 @@ namespace ts {
19020
19102
if (importNode && !isImportCall(importNode)) {
19021
19103
const sigs = getSignaturesOfType(getTypeOfSymbol(getSymbolLinks(apparentType.symbol).target!), kind);
19022
19104
if (!sigs || !sigs.length) return;
19023
- diagnostic.relatedInformation = diagnostic.relatedInformation || [] ;
19024
- diagnostic.relatedInformation.push( createDiagnosticForNode(importNode, Diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead)) ;
19105
+ Debug.assert(! diagnostic.relatedInformation) ;
19106
+ diagnostic.relatedInformation = [ createDiagnosticForNode(importNode, Diagnostics.Type_originates_at_this_import_A_namespace_style_import_cannot_be_called_or_constructed_and_will_cause_a_failure_at_runtime_Consider_using_a_default_import_or_import_require_here_instead)] ;
19025
19107
}
19026
19108
}
19027
19109
@@ -27449,6 +27531,8 @@ namespace ts {
27449
27531
bindSourceFile(file, compilerOptions);
27450
27532
}
27451
27533
27534
+ amalgamatedDuplicates = createMap();
27535
+
27452
27536
// Initialize global symbol table
27453
27537
let augmentations: ReadonlyArray<StringLiteral | Identifier>[] | undefined;
27454
27538
for (const file of host.getSourceFiles()) {
@@ -27526,6 +27610,39 @@ namespace ts {
27526
27610
}
27527
27611
}
27528
27612
}
27613
+
27614
+ amalgamatedDuplicates.forEach(({ firstFile, secondFile, firstFileInstances, secondFileInstances }) => {
27615
+ const conflictingKeys = arrayFrom(firstFileInstances.keys());
27616
+ // If not many things conflict, issue individual errors
27617
+ if (conflictingKeys.length < 8) {
27618
+ addErrorsForDuplicates(firstFileInstances, secondFileInstances);
27619
+ addErrorsForDuplicates(secondFileInstances, firstFileInstances);
27620
+ return;
27621
+ }
27622
+ // Otheriwse issue top-level error since the files appear very identical in terms of what they appear
27623
+ const list = conflictingKeys.join(", ");
27624
+ diagnostics.add(addRelatedInfo(
27625
+ createDiagnosticForNode(firstFile, Diagnostics.Definitions_of_the_following_identifiers_conflict_with_those_in_another_file_Colon_0, list),
27626
+ createDiagnosticForNode(secondFile, Diagnostics.Conflicts_are_in_this_file)
27627
+ ));
27628
+ diagnostics.add(addRelatedInfo(
27629
+ createDiagnosticForNode(secondFile, Diagnostics.Definitions_of_the_following_identifiers_conflict_with_those_in_another_file_Colon_0, list),
27630
+ createDiagnosticForNode(firstFile, Diagnostics.Conflicts_are_in_this_file)
27631
+ ));
27632
+ });
27633
+ amalgamatedDuplicates = undefined;
27634
+
27635
+ function addErrorsForDuplicates(secondFileInstances: Map<{ instances: Node[]; blockScoped: boolean; }>, firstFileInstances: Map<{ instances: Node[]; blockScoped: boolean; }>) {
27636
+ secondFileInstances.forEach((locations, symbolName) => {
27637
+ const firstFileEquivalent = firstFileInstances.get(symbolName)!;
27638
+ const message = locations.blockScoped
27639
+ ? Diagnostics.Cannot_redeclare_block_scoped_variable_0
27640
+ : Diagnostics.Duplicate_identifier_0;
27641
+ locations.instances.forEach(node => {
27642
+ addDuplicateDeclarationError(node, message, symbolName, firstFileEquivalent.instances[0]);
27643
+ });
27644
+ });
27645
+ }
27529
27646
}
27530
27647
27531
27648
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {
0 commit comments