diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3a19ca63b3f..ab187f57c40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -710,7 +710,7 @@ namespace ts { } } - function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: DiagnosticRelatedInformation[]) { + function addRelatedInfo(diagnostic: Diagnostic, ...relatedInformation: [DiagnosticRelatedInformation, ...DiagnosticRelatedInformation[]]) { if (!diagnostic.relatedInformation) { diagnostic.relatedInformation = []; } @@ -1434,11 +1434,18 @@ namespace ts { !checkAndReportErrorForUsingTypeAsNamespace(errorLocation, name, meaning) && !checkAndReportErrorForUsingTypeAsValue(errorLocation, name, meaning) && !checkAndReportErrorForUsingNamespaceModuleAsValue(errorLocation, name, meaning)) { - let suggestion: string | undefined; + let suggestion: Symbol | undefined; if (suggestedNameNotFoundMessage && suggestionCount < maximumSuggestionCount) { - suggestion = getSuggestionForNonexistentSymbol(originalLocation, name, meaning); + suggestion = getSuggestedSymbolForNonexistentSymbol(originalLocation, name, meaning); if (suggestion) { - error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestion); + const suggestionName = symbolToString(suggestion); + const diagnostic = error(errorLocation, suggestedNameNotFoundMessage, diagnosticName(nameArg!), suggestionName); + if (suggestion.valueDeclaration) { + addRelatedInfo( + diagnostic, + createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName) + ); + } } } if (!suggestion) { @@ -1674,7 +1681,7 @@ namespace ts { if (diagnosticMessage) { addRelatedInfo(diagnosticMessage, - createDiagnosticForNode(declaration, Diagnostics._0_was_declared_here, declarationName) + createDiagnosticForNode(declaration, Diagnostics._0_is_declared_here, declarationName) ); } } @@ -1866,9 +1873,15 @@ namespace ts { if (!symbol) { const moduleName = getFullyQualifiedName(moduleSymbol); const declarationName = declarationNameToString(name); - const suggestion = getSuggestionForNonexistentModule(name, targetSymbol); + const suggestion = getSuggestedSymbolForNonexistentModule(name, targetSymbol); if (suggestion !== undefined) { - error(name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2, moduleName, declarationName, suggestion); + const suggestionName = symbolToString(suggestion); + const diagnostic = error(name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2, moduleName, declarationName, suggestionName); + if (suggestion.valueDeclaration) { + addRelatedInfo(diagnostic, + createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName) + ); + } } else { error(name, Diagnostics.Module_0_has_no_exported_member_1, moduleName, declarationName); @@ -17661,7 +17674,7 @@ namespace ts { if (diagnosticMessage) { addRelatedInfo(diagnosticMessage, - createDiagnosticForNode(valueDeclaration, Diagnostics._0_was_declared_here, declarationName) + createDiagnosticForNode(valueDeclaration, Diagnostics._0_is_declared_here, declarationName) ); } } @@ -17711,6 +17724,7 @@ namespace ts { function reportNonexistentProperty(propNode: Identifier, containingType: Type) { let errorInfo: DiagnosticMessageChain | undefined; + let relatedInfo: Diagnostic | undefined; if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) { for (const subtype of (containingType as UnionType).types) { if (!getPropertyOfType(subtype, propNode.escapedText)) { @@ -17724,30 +17738,34 @@ namespace ts { errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await, declarationNameToString(propNode), typeToString(containingType)); } else { - const suggestion = getSuggestionForNonexistentProperty(propNode, containingType); + const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType); if (suggestion !== undefined) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion); + const suggestedName = symbolName(suggestion); + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName); + relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName); } else { - const suggestion = getSuggestionForNonexistentProperty(propNode, containingType); - if (suggestion !== undefined) { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion); - } - else { - errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); - } + errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType)); } } - diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo)); + const resultDiagnostic = createDiagnosticForNodeFromMessageChain(propNode, errorInfo); + if (relatedInfo) { + addRelatedInfo(resultDiagnostic, relatedInfo); + } + diagnostics.add(resultDiagnostic); + } + + function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined { + return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value); } function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined { - const suggestion = getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value); + const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType); return suggestion && symbolName(suggestion); } - function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined { + function getSuggestedSymbolForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): Symbol | undefined { Debug.assert(outerName !== undefined, "outername should always be defined"); const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, (symbols, name, meaning) => { Debug.assertEqual(outerName, name, "name should equal outerName"); @@ -17757,11 +17775,20 @@ namespace ts { // However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion. return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning); }); - return result && symbolName(result); + return result; + } + + function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined { + const symbolResult = getSuggestedSymbolForNonexistentSymbol(location, outerName, meaning); + return symbolResult && symbolName(symbolResult) + } + + function getSuggestedSymbolForNonexistentModule(name: Identifier, targetModule: Symbol): Symbol | undefined { + return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember); } function getSuggestionForNonexistentModule(name: Identifier, targetModule: Symbol): string | undefined { - const suggestion = targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember); + const suggestion = getSuggestedSymbolForNonexistentModule(name, targetModule); return suggestion && symbolName(suggestion); } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 87d8297e74d..21b4cf3bf3f 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2401,7 +2401,7 @@ "category": "Error", "code": 2727 }, - "'{0}' was declared here.": { + "'{0}' is declared here.": { "category": "Error", "code": 2728 },