Skip to content

Commit ea73ee7

Browse files
Merge pull request #25477 from Microsoft/youProbablyMeantThisDeclaration
Link back to likely declarations in "did you mean...?" error messages
2 parents 6589e86 + 1d7c954 commit ea73ee7

File tree

95 files changed

+1445
-1330
lines changed

Some content is hidden

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

95 files changed

+1445
-1330
lines changed

package-lock.json

+1,182-1,181
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/compiler/checker.ts

+51-24
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ namespace ts {
295295
getAllPossiblePropertiesOfTypes,
296296
getSuggestionForNonexistentProperty: (node, type) => getSuggestionForNonexistentProperty(node, type),
297297
getSuggestionForNonexistentSymbol: (location, name, meaning) => getSuggestionForNonexistentSymbol(location, escapeLeadingUnderscores(name), meaning),
298-
getSuggestionForNonexistentModule: (node, target) => getSuggestionForNonexistentModule(node, target),
298+
getSuggestionForNonexistentExport: (node, target) => getSuggestionForNonexistentExport(node, target),
299299
getBaseConstraintOfType,
300300
getDefaultFromTypeParameter: type => type && type.flags & TypeFlags.TypeParameter ? getDefaultFromTypeParameter(type as TypeParameter) : undefined,
301301
resolveName(name, location, meaning, excludeGlobals) {
@@ -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);
@@ -17676,7 +17689,7 @@ namespace ts {
1767617689

1767717690
if (diagnosticMessage) {
1767817691
addRelatedInfo(diagnosticMessage,
17679-
createDiagnosticForNode(valueDeclaration, Diagnostics._0_was_declared_here, declarationName)
17692+
createDiagnosticForNode(valueDeclaration, Diagnostics._0_is_declared_here, declarationName)
1768017693
);
1768117694
}
1768217695
}
@@ -17726,6 +17739,7 @@ namespace ts {
1772617739

1772717740
function reportNonexistentProperty(propNode: Identifier, containingType: Type) {
1772817741
let errorInfo: DiagnosticMessageChain | undefined;
17742+
let relatedInfo: Diagnostic | undefined;
1772917743
if (containingType.flags & TypeFlags.Union && !(containingType.flags & TypeFlags.Primitive)) {
1773017744
for (const subtype of (containingType as UnionType).types) {
1773117745
if (!getPropertyOfType(subtype, propNode.escapedText)) {
@@ -17739,30 +17753,34 @@ namespace ts {
1773917753
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_forget_to_use_await, declarationNameToString(propNode), typeToString(containingType));
1774017754
}
1774117755
else {
17742-
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
17756+
const suggestion = getSuggestedSymbolForNonexistentProperty(propNode, containingType);
1774317757
if (suggestion !== undefined) {
17744-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
17758+
const suggestedName = symbolName(suggestion);
17759+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestedName);
17760+
relatedInfo = suggestion.valueDeclaration && createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestedName);
1774517761
}
1774617762
else {
17747-
const suggestion = getSuggestionForNonexistentProperty(propNode, containingType);
17748-
if (suggestion !== undefined) {
17749-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1_Did_you_mean_2, declarationNameToString(propNode), typeToString(containingType), suggestion);
17750-
}
17751-
else {
17752-
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
17753-
}
17763+
errorInfo = chainDiagnosticMessages(errorInfo, Diagnostics.Property_0_does_not_exist_on_type_1, declarationNameToString(propNode), typeToString(containingType));
1775417764
}
1775517765
}
1775617766

17757-
diagnostics.add(createDiagnosticForNodeFromMessageChain(propNode, errorInfo));
17767+
const resultDiagnostic = createDiagnosticForNodeFromMessageChain(propNode, errorInfo);
17768+
if (relatedInfo) {
17769+
addRelatedInfo(resultDiagnostic, relatedInfo);
17770+
}
17771+
diagnostics.add(resultDiagnostic);
17772+
}
17773+
17774+
function getSuggestedSymbolForNonexistentProperty(name: Identifier | string, containingType: Type): Symbol | undefined {
17775+
return getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
1775817776
}
1775917777

1776017778
function getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined {
17761-
const suggestion = getSpellingSuggestionForName(isString(name) ? name : idText(name), getPropertiesOfType(containingType), SymbolFlags.Value);
17779+
const suggestion = getSuggestedSymbolForNonexistentProperty(name, containingType);
1776217780
return suggestion && symbolName(suggestion);
1776317781
}
1776417782

17765-
function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined {
17783+
function getSuggestedSymbolForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): Symbol | undefined {
1776617784
Debug.assert(outerName !== undefined, "outername should always be defined");
1776717785
const result = resolveNameHelper(location, outerName, meaning, /*nameNotFoundMessage*/ undefined, outerName, /*isUse*/ false, /*excludeGlobals*/ false, (symbols, name, meaning) => {
1776817786
Debug.assertEqual(outerName, name, "name should equal outerName");
@@ -17772,11 +17790,20 @@ namespace ts {
1777217790
// However, resolveNameHelper will continue and call this callback again, so we'll eventually get a correct suggestion.
1777317791
return symbol || getSpellingSuggestionForName(unescapeLeadingUnderscores(name), arrayFrom(symbols.values()), meaning);
1777417792
});
17775-
return result && symbolName(result);
17793+
return result;
17794+
}
17795+
17796+
function getSuggestionForNonexistentSymbol(location: Node | undefined, outerName: __String, meaning: SymbolFlags): string | undefined {
17797+
const symbolResult = getSuggestedSymbolForNonexistentSymbol(location, outerName, meaning);
17798+
return symbolResult && symbolName(symbolResult);
17799+
}
17800+
17801+
function getSuggestedSymbolForNonexistentModule(name: Identifier, targetModule: Symbol): Symbol | undefined {
17802+
return targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember);
1777617803
}
1777717804

17778-
function getSuggestionForNonexistentModule(name: Identifier, targetModule: Symbol): string | undefined {
17779-
const suggestion = targetModule.exports && getSpellingSuggestionForName(idText(name), getExportsOfModuleAsArray(targetModule), SymbolFlags.ModuleMember);
17805+
function getSuggestionForNonexistentExport(name: Identifier, targetModule: Symbol): string | undefined {
17806+
const suggestion = getSuggestedSymbolForNonexistentModule(name, targetModule);
1778017807
return suggestion && symbolName(suggestion);
1778117808
}
1778217809

src/compiler/diagnosticMessages.json

+3-3
Original file line numberDiff line numberDiff line change
@@ -2401,8 +2401,8 @@
24012401
"category": "Error",
24022402
"code": 2727
24032403
},
2404-
"'{0}' was declared here.": {
2405-
"category": "Error",
2404+
"'{0}' is declared here.": {
2405+
"category": "Message",
24062406
"code": 2728
24072407
},
24082408
"Property '{0}' is used before its initialization.": {
@@ -3886,7 +3886,7 @@
38863886
"code": 7037
38873887
},
38883888
"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.": {
3889-
"category": "Error",
3889+
"category": "Message",
38903890
"code": 7038
38913891
},
38923892
"Mapped object type implicitly has an 'any' template type.": {

src/compiler/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2996,7 +2996,7 @@ namespace ts {
29962996
getApparentType(type: Type): Type;
29972997
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
29982998
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
2999-
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
2999+
getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
30003000
getBaseConstraintOfType(type: Type): Type | undefined;
30013001
getDefaultFromTypeParameter(type: Type): Type | undefined;
30023002

src/services/codefixes/fixSpelling.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ namespace ts.codefix {
4343
const importDeclaration = findAncestor(node, isImportDeclaration)!;
4444
const resolvedSourceFile = getResolvedSourceFileFromImportDeclaration(sourceFile, context, importDeclaration);
4545
if (resolvedSourceFile && resolvedSourceFile.symbol) {
46-
suggestion = checker.getSuggestionForNonexistentModule(node as Identifier, resolvedSourceFile.symbol);
46+
suggestion = checker.getSuggestionForNonexistentExport(node as Identifier, resolvedSourceFile.symbol);
4747
}
4848
}
4949
else {

tests/baselines/reference/ES5For-of17.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ tests/cases/conformance/statements/for-ofStatements/ES5For-of17.ts(3,20): error
77
for (let v of [v]) {
88
~
99
!!! error TS2448: Block-scoped variable 'v' used before its declaration.
10-
!!! related TS2728 tests/cases/conformance/statements/for-ofStatements/ES5For-of17.ts:3:14: 'v' was declared here.
10+
!!! related TS2728 tests/cases/conformance/statements/for-ofStatements/ES5For-of17.ts:3:14: 'v' is declared here.
1111
var x = v;
1212
v++;
1313
}

tests/baselines/reference/ES5For-of20.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ tests/cases/conformance/statements/for-ofStatements/ES5For-of20.ts(4,15): error
88
for (let v of [v]) {
99
~
1010
!!! error TS2448: Block-scoped variable 'v' used before its declaration.
11-
!!! related TS2728 tests/cases/conformance/statements/for-ofStatements/ES5For-of20.ts:3:14: 'v' was declared here.
11+
!!! related TS2728 tests/cases/conformance/statements/for-ofStatements/ES5For-of20.ts:3:14: 'v' is declared here.
1212
const v;
1313
~
1414
!!! error TS1155: 'const' declarations must be initialized.

tests/baselines/reference/ModuleAndClassWithSameNameAndCommonRoot.errors.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ tests/cases/conformance/internalModules/DeclarationMerging/simple.ts(2,31): erro
3232
export var Instance = new A();
3333
~
3434
!!! error TS2449: Class 'A' used before its declaration.
35-
!!! related TS2728 tests/cases/conformance/internalModules/DeclarationMerging/simple.ts:6:7: 'A' was declared here.
35+
!!! related TS2728 tests/cases/conformance/internalModules/DeclarationMerging/simple.ts:6:7: 'A' is declared here.
3636
}
3737

3838
// duplicate identifier

tests/baselines/reference/ModuleWithExportedAndNonExportedFunctions.errors.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,5 @@ tests/cases/conformance/internalModules/exportDeclarations/ModuleWithExportedAnd
3535
!!! error TS2339: Property 'fn2' does not exist on type 'typeof A'.
3636
var fng2 = A.fng2;
3737
~~~~
38-
!!! error TS2551: Property 'fng2' does not exist on type 'typeof A'. Did you mean 'fng'?
38+
!!! error TS2551: Property 'fng2' does not exist on type 'typeof A'. Did you mean 'fng'?
39+
!!! related TS2728 tests/cases/conformance/internalModules/exportDeclarations/ModuleWithExportedAndNonExportedFunctions.ts:7:21: 'fng' is declared here.

tests/baselines/reference/anonymousClassExpression2.errors.txt

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ tests/cases/compiler/anonymousClassExpression2.ts(13,18): error TS2551: Property
1717
this.methodA; // error
1818
~~~~~~~
1919
!!! error TS2551: Property 'methodA' does not exist on type 'B'. Did you mean 'methodB'?
20+
!!! related TS2728 tests/cases/compiler/anonymousClassExpression2.ts:12:9: 'methodB' is declared here.
2021
this.methodB; // ok
2122
}
2223
}

tests/baselines/reference/api/tsserverlibrary.d.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2675,7 +2675,7 @@ declare namespace ts {
26752675
getApparentType(type: Type): Type;
26762676
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
26772677
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
2678-
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
2678+
getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
26792679
getBaseConstraintOfType(type: Type): Type | undefined;
26802680
getDefaultFromTypeParameter(type: Type): Type | undefined;
26812681
getAnyType(): Type;
@@ -5430,7 +5430,7 @@ declare namespace ts {
54305430
Class_name_cannot_be_Object_when_targeting_ES5_with_module_0: DiagnosticMessage;
54315431
Cannot_find_lib_definition_for_0: DiagnosticMessage;
54325432
Cannot_find_lib_definition_for_0_Did_you_mean_1: DiagnosticMessage;
5433-
_0_was_declared_here: DiagnosticMessage;
5433+
_0_is_declared_here: DiagnosticMessage;
54345434
Property_0_is_used_before_its_initialization: DiagnosticMessage;
54355435
Import_declaration_0_is_using_private_name_1: DiagnosticMessage;
54365436
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: DiagnosticMessage;

tests/baselines/reference/api/typescript.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1920,7 +1920,7 @@ declare namespace ts {
19201920
getApparentType(type: Type): Type;
19211921
getSuggestionForNonexistentProperty(name: Identifier | string, containingType: Type): string | undefined;
19221922
getSuggestionForNonexistentSymbol(location: Node, name: string, meaning: SymbolFlags): string | undefined;
1923-
getSuggestionForNonexistentModule(node: Identifier, target: Symbol): string | undefined;
1923+
getSuggestionForNonexistentExport(node: Identifier, target: Symbol): string | undefined;
19241924
getBaseConstraintOfType(type: Type): Type | undefined;
19251925
getDefaultFromTypeParameter(type: Type): Type | undefined;
19261926
/**

tests/baselines/reference/assignmentRestElementWithErrorSourceType.errors.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ tests/cases/compiler/assignmentRestElementWithErrorSourceType.ts(2,10): error TS
88
~
99
!!! error TS2304: Cannot find name 'c'.
1010
~~~~~
11-
!!! error TS2552: Cannot find name 'tupel'. Did you mean 'tuple'?
11+
!!! error TS2552: Cannot find name 'tupel'. Did you mean 'tuple'?
12+
!!! related TS2728 tests/cases/compiler/assignmentRestElementWithErrorSourceType.ts:1:5: 'tuple' is declared here.

tests/baselines/reference/baseCheck.errors.txt

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ tests/cases/compiler/baseCheck.ts(26,9): error TS2304: Cannot find name 'x'.
2121
super(0, loc);
2222
~~~
2323
!!! error TS2552: Cannot find name 'loc'. Did you mean 'ELoc'?
24+
!!! related TS2728 tests/cases/compiler/baseCheck.ts:2:7: 'ELoc' is declared here.
2425
}
2526

2627
m() {

tests/baselines/reference/blockScopedBindingUsedBeforeDef.errors.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts(8,7): error TS2448: Bloc
88
for (let {[a]: a} of [{ }]) continue;
99
~
1010
!!! error TS2448: Block-scoped variable 'a' used before its declaration.
11-
!!! related TS2728 tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts:2:16: 'a' was declared here.
11+
!!! related TS2728 tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts:2:16: 'a' is declared here.
1212

1313
// 2:
1414
for (let {[a]: a} = { }; false; ) continue;
1515
~
1616
!!! error TS2448: Block-scoped variable 'a' used before its declaration.
17-
!!! related TS2728 tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts:5:16: 'a' was declared here.
17+
!!! related TS2728 tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts:5:16: 'a' is declared here.
1818

1919
// 3:
2020
let {[b]: b} = { };
2121
~
2222
!!! error TS2448: Block-scoped variable 'b' used before its declaration.
23-
!!! related TS2728 tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts:8:11: 'b' was declared here.
23+
!!! related TS2728 tests/cases/compiler/blockScopedBindingUsedBeforeDef.ts:8:11: 'b' is declared here.

0 commit comments

Comments
 (0)