Skip to content

Commit 271bbb0

Browse files
author
Andy Hanson
committed
Merge branch 'master' into fixAddMissingMember_all_dedup
2 parents 911658b + c58e298 commit 271bbb0

File tree

136 files changed

+2483
-233
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

136 files changed

+2483
-233
lines changed

src/compiler/checker.ts

Lines changed: 134 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ namespace ts {
422422
const jsObjectLiteralIndexInfo = createIndexInfo(anyType, /*isReadonly*/ false);
423423

424424
const globals = createSymbolTable();
425+
let amalgamatedDuplicates: Map<{ firstFile: SourceFile, secondFile: SourceFile, firstFileInstances: Map<{ instances: Node[], blockScoped: boolean }>, secondFileInstances: Map<{ instances: Node[], blockScoped: boolean }> }> | undefined;
425426
const reverseMappedCache = createMap<Type | undefined>();
426427
let ambientModulesCache: Symbol[] | undefined;
427428
/**
@@ -693,6 +694,28 @@ namespace ts {
693694
return emitResolver;
694695
}
695696

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+
696719
function error(location: Node | undefined, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic {
697720
const diagnostic = location
698721
? createDiagnosticForNode(location, message, arg0, arg1, arg2, arg3)
@@ -803,23 +826,63 @@ namespace ts {
803826
error(getNameOfDeclaration(source.declarations[0]), Diagnostics.Cannot_augment_module_0_with_value_exports_because_it_resolves_to_a_non_module_entity, symbolToString(target));
804827
}
805828
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
807832
? Diagnostics.Enum_declarations_can_only_merge_with_namespace_or_other_enum_declarations
808-
: target.flags & SymbolFlags.BlockScopedVariable || source.flags & SymbolFlags.BlockScopedVariable
833+
: isEitherBlockScoped
809834
? Diagnostics.Cannot_redeclare_block_scoped_variable_0
810835
: 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);
819868
}
820869
return target;
821870
}
822871

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+
823886
function combineSymbolTables(first: SymbolTable | undefined, second: SymbolTable | undefined): SymbolTable | undefined {
824887
if (!hasEntries(first)) return second;
825888
if (!hasEntries(second)) return first;
@@ -1592,14 +1655,25 @@ namespace ts {
15921655
if (declaration === undefined) return Debug.fail("Declaration to checkResolvedBlockScopedVariable is undefined");
15931656

15941657
if (!(declaration.flags & NodeFlags.Ambient) && !isBlockScopedNameDeclaredBeforeUse(declaration, errorLocation)) {
1658+
let diagnosticMessage;
1659+
const declarationName = declarationNameToString(getNameOfDeclaration(declaration));
15951660
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);
15971662
}
15981663
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);
16001665
}
16011666
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+
);
16031677
}
16041678
}
16051679
}
@@ -17397,16 +17471,24 @@ namespace ts {
1739717471
return;
1739817472
}
1739917473

17474+
let diagnosticMessage;
17475+
const declarationName = idText(right);
1740017476
if (isInPropertyInitializer(node) &&
1740117477
!isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
1740217478
&& !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);
1740417480
}
1740517481
else if (valueDeclaration.kind === SyntaxKind.ClassDeclaration &&
1740617482
node.parent.kind !== SyntaxKind.TypeReference &&
1740717483
!(valueDeclaration.flags & NodeFlags.Ambient) &&
1740817484
!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+
);
1741017492
}
1741117493
}
1741217494

@@ -19020,8 +19102,8 @@ namespace ts {
1902019102
if (importNode && !isImportCall(importNode)) {
1902119103
const sigs = getSignaturesOfType(getTypeOfSymbol(getSymbolLinks(apparentType.symbol).target!), kind);
1902219104
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)];
1902519107
}
1902619108
}
1902719109

@@ -27449,6 +27531,8 @@ namespace ts {
2744927531
bindSourceFile(file, compilerOptions);
2745027532
}
2745127533

27534+
amalgamatedDuplicates = createMap();
27535+
2745227536
// Initialize global symbol table
2745327537
let augmentations: ReadonlyArray<StringLiteral | Identifier>[] | undefined;
2745427538
for (const file of host.getSourceFiles()) {
@@ -27526,6 +27610,39 @@ namespace ts {
2752627610
}
2752727611
}
2752827612
}
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+
}
2752927646
}
2753027647

2753127648
function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) {

src/compiler/diagnosticMessages.json

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2397,6 +2397,10 @@
23972397
"category": "Error",
23982398
"code": 2727
23992399
},
2400+
"'{0}' was declared here.": {
2401+
"category": "Error",
2402+
"code": 2728
2403+
},
24002404

24012405
"Import declaration '{0}' is using private name '{1}'.": {
24022406
"category": "Error",
@@ -3603,6 +3607,22 @@
36033607
"code": 6199,
36043608
"reportsUnnecessary": true
36053609
},
3610+
"Definitions of the following identifiers conflict with those in another file: {0}": {
3611+
"category": "Error",
3612+
"code": 6200
3613+
},
3614+
"Conflicts are in this file.": {
3615+
"category": "Message",
3616+
"code": 6201
3617+
},
3618+
"'{0}' was also declared here.": {
3619+
"category": "Message",
3620+
"code": 6203
3621+
},
3622+
"and here.": {
3623+
"category": "Message",
3624+
"code": 6204
3625+
},
36063626

36073627
"Projects to reference": {
36083628
"category": "Message",
@@ -4450,9 +4470,5 @@
44504470
"Add missing enum member '{0}'": {
44514471
"category": "Message",
44524472
"code": 95063
4453-
},
4454-
"Add all missing enum members": {
4455-
"category": "Message",
4456-
"code": 95064
44574473
}
44584474
}

src/compiler/factory.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,13 +1531,6 @@ namespace ts {
15311531
return block;
15321532
}
15331533

1534-
/* @internal */
1535-
export function createExpressionStatement(expression: Expression): ExpressionStatement {
1536-
const node = <ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
1537-
node.expression = expression;
1538-
return node;
1539-
}
1540-
15411534
export function updateBlock(node: Block, statements: ReadonlyArray<Statement>) {
15421535
return node.statements !== statements
15431536
? updateNode(createBlock(statements, node.multiLine), node)
@@ -1563,16 +1556,23 @@ namespace ts {
15631556
return <EmptyStatement>createSynthesizedNode(SyntaxKind.EmptyStatement);
15641557
}
15651558

1566-
export function createStatement(expression: Expression) {
1567-
return createExpressionStatement(parenthesizeExpressionForExpressionStatement(expression));
1559+
export function createExpressionStatement(expression: Expression): ExpressionStatement {
1560+
const node = <ExpressionStatement>createSynthesizedNode(SyntaxKind.ExpressionStatement);
1561+
node.expression = parenthesizeExpressionForExpressionStatement(expression);
1562+
return node;
15681563
}
15691564

1570-
export function updateStatement(node: ExpressionStatement, expression: Expression) {
1565+
export function updateExpressionStatement(node: ExpressionStatement, expression: Expression) {
15711566
return node.expression !== expression
1572-
? updateNode(createStatement(expression), node)
1567+
? updateNode(createExpressionStatement(expression), node)
15731568
: node;
15741569
}
15751570

1571+
/** @deprecated Use `createExpressionStatement` instead. */
1572+
export const createStatement = createExpressionStatement;
1573+
/** @deprecated Use `updateExpressionStatement` instead. */
1574+
export const updateStatement = updateExpressionStatement;
1575+
15761576
export function createIf(expression: Expression, thenStatement: Statement, elseStatement?: Statement) {
15771577
const node = <IfStatement>createSynthesizedNode(SyntaxKind.IfStatement);
15781578
node.expression = expression;

0 commit comments

Comments
 (0)