Add a related span for suggested declaration locations in error messages.

This commit is contained in:
Daniel Rosenwasser 2018-07-05 18:49:43 -07:00
parent fd32cb97a4
commit 5afc8fdcef
2 changed files with 50 additions and 23 deletions

View File

@ -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);
}

View File

@ -2401,7 +2401,7 @@
"category": "Error",
"code": 2727
},
"'{0}' was declared here.": {
"'{0}' is declared here.": {
"category": "Error",
"code": 2728
},