Skip to content

Commit 5afc8fd

Browse files
Add a related span for suggested declaration locations in error messages.
1 parent fd32cb9 commit 5afc8fd

File tree

2 files changed

+50
-23
lines changed

2 files changed

+50
-23
lines changed

src/compiler/checker.ts

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,7 @@ namespace ts {
710710
}
711711
}
712712

713-
function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: DiagnosticRelatedInformation[]) {
713+
function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]]) {
714714
if (!diagnostic.relatedInformation) {
715715
diagnostic.relatedInformation = [];
716716
}
@@ -1434,11 +1434,18 @@ namespace ts {
14341434
!checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) &&
14351435
!checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) &&
14361436
!checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) {
1437-
let suggestion: string | undefined;
1437+
let suggestion: Symbol | undefined;
14381438
if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) {
1439-
suggestion = getSuggestionForNonexistentSymbol(originalLocation, name, meaning);
1439+
suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning);
14401440
if (suggestion) {
1441-
error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestion);
1441+
const suggestionName = symbolToString(suggestion);
1442+
const diagnostic = error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName);
1443+
if (suggestion.valueDeclaration) {
1444+
addRelatedInfo(
1445+
diagnostic,
1446+
createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName)
1447+
);
1448+
}
14421449
}
14431450
}
14441451
if (!suggestion) {
@@ -1674,7 +1681,7 @@ namespace ts {
16741681

16751682
if (diagnosticMessage) {
16761683
addRelatedInfo(diagnosticMessage,
1677-
createDiagnosticForNode(declaration, Diagnostics._0_was_declared_here, declarationName)
1684+
createDiagnosticForNode(declaration, Diagnostics._0_is_declared_here, declarationName)
16781685
);
16791686
}
16801687
}
@@ -1866,9 +1873,15 @@ namespace ts {
18661873
if (!symbol) {
18671874
const moduleName = getFullyQualifiedName(moduleSymbol);
18681875
const declarationName = declarationNameToString(name);
1869-
const suggestion = getSuggestionForNonexistentModule(name, targetSymbol);
1876+
const suggestion = getSuggestedSymbolForNonexistentModule(name, targetSymbol);
18701877
if (suggestion !== undefined) {
1871-
error(name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2, moduleName, declarationName, suggestion);
1878+
const suggestionName = symbolToString(suggestion);
1879+
const diagnostic = error(name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2, moduleName, declarationName, suggestionName);
1880+
if (suggestion.valueDeclaration) {
1881+
addRelatedInfo(diagnostic,
1882+
createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName)
1883+
);
1884+
}
18721885
}
18731886
else {
18741887
error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName);
@@ -17661,7 +17674,7 @@ namespace ts {
1766117674

1766217675
if (diagnosticMessage) {
1766317676
addRelatedInfo(diagnosticMessage,
17664-
createDiagnosticForNode(valueDeclaration, Diagnostics._0_was_declared_here, declarationName)
17677+
createDiagnosticForNode(valueDeclaration, Diagnostics._0_is_declared_here, declarationName)
1766517678
);
1766617679
}
1766717680
}
@@ -17711,6 +17724,7 @@ namespace ts {
1771117724

1771217725
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
1771317726
let errorInfo: DiagnosticMessageChain | undefined;
17727+
let relatedInfo: Diagnostic | undefined;
1771417728
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
1771517729
for (const subtype of (containingType as UnionType).types) {
1771617730
if (!getPropertyOfType(subtype, propNode.escapedText)) {
@@ -17724,30 +17738,34 @@ namespace ts {
1772417738
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await, declarationNameToString(propNode), typeToString(containingType));
1772517739
}
1772617740
else {
17727-
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
17741+
const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType);
1772817742
if (suggestion !== undefined) {
17729-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
17743+
const suggestedName = symbolName(suggestion);
17744+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName);
17745+
relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName);
1773017746
}
1773117747
else {
17732-
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
17733-
if (suggestion !== undefined) {
17734-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
17735-
}
17736-
else {
17737-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
17738-
}
17748+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
1773917749
}
1774017750
}
1774117751

17742-
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
17752+
const resultDiagnostic = createDiagnosticForNodeFromMessageChain(propNode, errorInfo);
17753+
if (relatedInfo) {
17754+
addRelatedInfo(resultDiagnostic, relatedInfo);
17755+
}
17756+
diagnostics.add(resultDiagnostic);
17757+
}
17758+
17759+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
17760+
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1774317761
}
1774417762

1774517763
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
17746-
const suggestion = getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
17764+
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1774717765
return suggestion && symbolName(suggestion);
1774817766
}
1774917767

17750-
function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined {
17768+
function getSuggestedSymbolForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): Symbol | undefined {
1775117769
Debug.assert(outerName !== undefined, "outername should always be defined");
1775217770
const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, (symbols, name, meaning) => {
1775317771
Debug.assertEqual(outerName, name, "name should equal outerName");
@@ -17757,11 +17775,20 @@ namespace ts {
1775717775
// However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion.
1775817776
return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning);
1775917777
});
17760-
return result && symbolName(result);
17778+
return result;
17779+
}
17780+
17781+
function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined {
17782+
const symbolResult = getSuggestedSymbolForNonexistentSymbol(location, outerName, meaning);
17783+
return symbolResult && symbolName(symbolResult)
17784+
}
17785+
17786+
function getSuggestedSymbolForNonexistentModule(name: Identifier, targetModule: Symbol): Symbol | undefined {
17787+
return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember);
1776117788
}
1776217789

1776317790
function getSuggestionForNonexistentModule(name: Identifier, targetModule: Symbol): string | undefined {
17764-
const suggestion = targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember);
17791+
const suggestion = getSuggestedSymbolForNonexistentModule(name, targetModule);
1776517792
return suggestion && symbolName(suggestion);
1776617793
}
1776717794

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2401,7 +2401,7 @@
24012401
"category": "Error",
24022402
"code": 2727
24032403
},
2404-
"'{0}' was declared here.": {
2404+
"'{0}' is declared here.": {
24052405
"category": "Error",
24062406
"code": 2728
24072407
},

0 commit comments

Comments
 (0)