Skip to content

Commit 01362a3

Browse files
authored
feat(part of 40169): add spelling suggestion/quick fix for module/namespace exported members (microsoft#40211)
1 parent b78c722 commit 01362a3

16 files changed

+211
-15
lines changed

src/compiler/checker.ts

+7-2
Original file line numberDiff line numberDiff line change
@@ -2678,7 +2678,7 @@ namespace ts {
26782678
const suggestion = getSuggestedSymbolForNonexistentModule(name, targetSymbol);
26792679
if (suggestion !== undefined) {
26802680
const suggestionName = symbolToString(suggestion);
2681-
const diagnostic = error(name, Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2, moduleName, declarationName, suggestionName);
2681+
const diagnostic = error(name, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, moduleName, declarationName, suggestionName);
26822682
if (suggestion.valueDeclaration) {
26832683
addRelatedInfo(diagnostic,
26842684
createDiagnosticForNode(suggestion.valueDeclaration, Diagnostics._0_is_declared_here, suggestionName)
@@ -3057,7 +3057,12 @@ namespace ts {
30573057
symbol = getMergedSymbol(getSymbol(getExportsOfSymbol(namespace), right.escapedText, meaning));
30583058
if (!symbol) {
30593059
if (!ignoreErrors) {
3060-
error(right, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(namespace), declarationNameToString(right));
3060+
const namespaceName = getFullyQualifiedName(namespace);
3061+
const declarationName = declarationNameToString(right);
3062+
const suggestion = getSuggestedSymbolForNonexistentModule(right, namespace);
3063+
suggestion ?
3064+
error(right, Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2, namespaceName, declarationName, symbolToString(suggestion)) :
3065+
error(right, Diagnostics.Namespace_0_has_no_exported_member_1, namespaceName, declarationName);
30613066
}
30623067
return undefined;
30633068
}

src/compiler/diagnosticMessages.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -2752,7 +2752,7 @@
27522752
"category": "Error",
27532753
"code": 2723
27542754
},
2755-
"Module '{0}' has no exported member '{1}'. Did you mean '{2}'?": {
2755+
"'{0}' has no exported member named '{1}'. Did you mean '{2}'?": {
27562756
"category": "Error",
27572757
"code": 2724
27582758
},
@@ -3029,7 +3029,6 @@
30293029
"code": 2792
30303030
},
30313031

3032-
30333032
"Import declaration '{0}' is using private name '{1}'.": {
30343033
"category": "Error",
30353034
"code": 4000

src/services/codefixes/fixSpelling.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace ts.codefix {
66
Diagnostics.Cannot_find_name_0_Did_you_mean_1.code,
77
Diagnostics.Cannot_find_name_0_Did_you_mean_the_instance_member_this_0.code,
88
Diagnostics.Cannot_find_name_0_Did_you_mean_the_static_member_1_0.code,
9-
Diagnostics.Module_0_has_no_exported_member_1_Did_you_mean_2.code,
9+
Diagnostics._0_has_no_exported_member_named_1_Did_you_mean_2.code,
1010
// for JSX class components
1111
Diagnostics.No_overload_matches_this_call.code,
1212
// for JSX FC
@@ -53,6 +53,12 @@ namespace ts.codefix {
5353
}
5454
suggestedSymbol = checker.getSuggestedSymbolForNonexistentProperty(node, containingType);
5555
}
56+
else if (isQualifiedName(parent) && parent.right === node) {
57+
const symbol = checker.getSymbolAtLocation(parent.left);
58+
if (symbol && symbol.flags & SymbolFlags.Module) {
59+
suggestedSymbol = checker.getSuggestedSymbolForNonexistentModule(parent.right, symbol);
60+
}
61+
}
5662
else if (isImportSpecifier(parent) && parent.name === node) {
5763
Debug.assertNode(node, isIdentifier, "Expected an identifier for spelling (import)");
5864
const importDeclaration = findAncestor(node, isImportDeclaration)!;

tests/baselines/reference/exportSpellingSuggestion.errors.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/es6/modules/b.ts(1,10): error TS2724: Module '"./a"' has no exported member 'assertNevar'. Did you mean 'assertNever'?
1+
tests/cases/conformance/es6/modules/b.ts(1,10): error TS2724: '"./a"' has no exported member named 'assertNevar'. Did you mean 'assertNever'?
22

33

44
==== tests/cases/conformance/es6/modules/a.ts (0 errors) ====
@@ -9,6 +9,6 @@ tests/cases/conformance/es6/modules/b.ts(1,10): error TS2724: Module '"./a"' has
99
==== tests/cases/conformance/es6/modules/b.ts (1 errors) ====
1010
import { assertNevar } from "./a";
1111
~~~~~~~~~~~
12-
!!! error TS2724: Module '"./a"' has no exported member 'assertNevar'. Did you mean 'assertNever'?
12+
!!! error TS2724: '"./a"' has no exported member named 'assertNevar'. Did you mean 'assertNever'?
1313
!!! related TS2728 tests/cases/conformance/es6/modules/a.ts:1:17: 'assertNever' is declared here.
1414

Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
C:/foo/bar/Baz/src/sample.ts(1,10): error TS2724: Module '"./utils.js"' has no exported member 'exit'. Did you mean 'exist'?
1+
C:/foo/bar/Baz/src/sample.ts(1,10): error TS2724: '"./utils.js"' has no exported member named 'exit'. Did you mean 'exist'?
22

33

44
==== C:/foo/bar/Baz/src/utils.ts (0 errors) ====
55
export function exist() {}
66
==== C:/foo/bar/Baz/src/sample.ts (1 errors) ====
77
import { exit } from "./utils.js";
88
~~~~
9-
!!! error TS2724: Module '"./utils.js"' has no exported member 'exit'. Did you mean 'exist'?
9+
!!! error TS2724: '"./utils.js"' has no exported member named 'exit'. Did you mean 'exist'?
1010
!!! related TS2728 C:/foo/bar/Baz/src/utils.ts:1:17: 'exist' is declared here.
1111

1212
exit()

tests/baselines/reference/moduleVisibilityTest3.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
tests/cases/compiler/moduleVisibilityTest3.ts(20,22): error TS2709: Cannot use namespace 'modes' as a type.
2-
tests/cases/compiler/moduleVisibilityTest3.ts(20,39): error TS2694: Namespace '_modes' has no exported member 'Mode'.
3-
tests/cases/compiler/moduleVisibilityTest3.ts(21,22): error TS2694: Namespace '_modes' has no exported member 'Mode'.
2+
tests/cases/compiler/moduleVisibilityTest3.ts(20,39): error TS2724: '_modes' has no exported member named 'Mode'. Did you mean 'IMode'?
3+
tests/cases/compiler/moduleVisibilityTest3.ts(21,22): error TS2724: '_modes' has no exported member named 'Mode'. Did you mean 'IMode'?
44

55

66
==== tests/cases/compiler/moduleVisibilityTest3.ts (3 errors) ====
@@ -27,10 +27,10 @@ tests/cases/compiler/moduleVisibilityTest3.ts(21,22): error TS2694: Namespace '_
2727
~~~~~
2828
!!! error TS2709: Cannot use namespace 'modes' as a type.
2929
~~~~
30-
!!! error TS2694: Namespace '_modes' has no exported member 'Mode'.
30+
!!! error TS2724: '_modes' has no exported member named 'Mode'. Did you mean 'IMode'?
3131
var x:modes.Mode;
3232
~~~~
33-
!!! error TS2694: Namespace '_modes' has no exported member 'Mode'.
33+
!!! error TS2724: '_modes' has no exported member named 'Mode'. Did you mean 'IMode'?
3434
}
3535

3636
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
tests/cases/compiler/moduleVisibilityTest4.ts(9,11): error TS2724: 'M' has no exported member named 'num'. Did you mean 'nums'?
2+
tests/cases/compiler/moduleVisibilityTest4.ts(11,11): error TS2694: Namespace 'M' has no exported member 'bar'.
3+
tests/cases/compiler/moduleVisibilityTest4.ts(13,11): error TS2724: 'N' has no exported member named 'num'. Did you mean 'nums'?
4+
tests/cases/compiler/moduleVisibilityTest4.ts(15,11): error TS2694: Namespace 'N' has no exported member 'bar'.
5+
6+
7+
==== tests/cases/compiler/moduleVisibilityTest4.ts (4 errors) ====
8+
module M {
9+
export type nums = number;
10+
}
11+
12+
namespace N {
13+
export type nums = number;
14+
}
15+
16+
let a1: M.num;
17+
~~~
18+
!!! error TS2724: 'M' has no exported member named 'num'. Did you mean 'nums'?
19+
let b1: M.nums;
20+
let c1: M.bar;
21+
~~~
22+
!!! error TS2694: Namespace 'M' has no exported member 'bar'.
23+
24+
let a2: N.num;
25+
~~~
26+
!!! error TS2724: 'N' has no exported member named 'num'. Did you mean 'nums'?
27+
let b2: N.nums;
28+
let c2: N.bar;
29+
~~~
30+
!!! error TS2694: Namespace 'N' has no exported member 'bar'.
31+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [moduleVisibilityTest4.ts]
2+
module M {
3+
export type nums = number;
4+
}
5+
6+
namespace N {
7+
export type nums = number;
8+
}
9+
10+
let a1: M.num;
11+
let b1: M.nums;
12+
let c1: M.bar;
13+
14+
let a2: N.num;
15+
let b2: N.nums;
16+
let c2: N.bar;
17+
18+
19+
//// [moduleVisibilityTest4.js]
20+
var a1;
21+
var b1;
22+
var c1;
23+
var a2;
24+
var b2;
25+
var c2;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
=== tests/cases/compiler/moduleVisibilityTest4.ts ===
2+
module M {
3+
>M : Symbol(M, Decl(moduleVisibilityTest4.ts, 0, 0))
4+
5+
export type nums = number;
6+
>nums : Symbol(nums, Decl(moduleVisibilityTest4.ts, 0, 10))
7+
}
8+
9+
namespace N {
10+
>N : Symbol(N, Decl(moduleVisibilityTest4.ts, 2, 1))
11+
12+
export type nums = number;
13+
>nums : Symbol(nums, Decl(moduleVisibilityTest4.ts, 4, 13))
14+
}
15+
16+
let a1: M.num;
17+
>a1 : Symbol(a1, Decl(moduleVisibilityTest4.ts, 8, 3))
18+
>M : Symbol(M, Decl(moduleVisibilityTest4.ts, 0, 0))
19+
20+
let b1: M.nums;
21+
>b1 : Symbol(b1, Decl(moduleVisibilityTest4.ts, 9, 3))
22+
>M : Symbol(M, Decl(moduleVisibilityTest4.ts, 0, 0))
23+
>nums : Symbol(M.nums, Decl(moduleVisibilityTest4.ts, 0, 10))
24+
25+
let c1: M.bar;
26+
>c1 : Symbol(c1, Decl(moduleVisibilityTest4.ts, 10, 3))
27+
>M : Symbol(M, Decl(moduleVisibilityTest4.ts, 0, 0))
28+
29+
let a2: N.num;
30+
>a2 : Symbol(a2, Decl(moduleVisibilityTest4.ts, 12, 3))
31+
>N : Symbol(N, Decl(moduleVisibilityTest4.ts, 2, 1))
32+
33+
let b2: N.nums;
34+
>b2 : Symbol(b2, Decl(moduleVisibilityTest4.ts, 13, 3))
35+
>N : Symbol(N, Decl(moduleVisibilityTest4.ts, 2, 1))
36+
>nums : Symbol(N.nums, Decl(moduleVisibilityTest4.ts, 4, 13))
37+
38+
let c2: N.bar;
39+
>c2 : Symbol(c2, Decl(moduleVisibilityTest4.ts, 14, 3))
40+
>N : Symbol(N, Decl(moduleVisibilityTest4.ts, 2, 1))
41+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/moduleVisibilityTest4.ts ===
2+
module M {
3+
export type nums = number;
4+
>nums : number
5+
}
6+
7+
namespace N {
8+
export type nums = number;
9+
>nums : number
10+
}
11+
12+
let a1: M.num;
13+
>a1 : any
14+
>M : any
15+
16+
let b1: M.nums;
17+
>b1 : number
18+
>M : any
19+
20+
let c1: M.bar;
21+
>c1 : any
22+
>M : any
23+
24+
let a2: N.num;
25+
>a2 : any
26+
>N : any
27+
28+
let b2: N.nums;
29+
>b2 : number
30+
>N : any
31+
32+
let c2: N.bar;
33+
>c2 : any
34+
>N : any
35+

tests/baselines/reference/tscWatch/emit/emit-with-outFile-or-out-setting/with---outFile-and-multiple-declaration-files-in-the-program.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Output::
3434
[12:00:31 AM] Starting compilation in watch mode...
3535

3636

37-
[96ma/b/project/src/main2.ts[0m:[93m1[0m:[93m114[0m - [91merror[0m[90m TS2694: [0mNamespace 'Common.SomeComponent.DynamicMenu' has no exported member 'z'.
37+
[96ma/b/project/src/main2.ts[0m:[93m1[0m:[93m114[0m - [91merror[0m[90m TS2724: [0m'Common.SomeComponent.DynamicMenu' has no exported member named 'z'. Did you mean 'Z'?
3838

3939
1 namespace main.file4 { import DynamicMenu = Common.SomeComponent.DynamicMenu; export function foo(a: DynamicMenu.z) { } }
4040
   ~

tests/baselines/reference/tscWatch/emit/emit-with-outFile-or-out-setting/without---outFile-and-multiple-declaration-files-in-the-program.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ Output::
3434
[12:00:31 AM] Starting compilation in watch mode...
3535

3636

37-
[96ma/b/project/src/main2.ts[0m:[93m1[0m:[93m114[0m - [91merror[0m[90m TS2694: [0mNamespace 'Common.SomeComponent.DynamicMenu' has no exported member 'z'.
37+
[96ma/b/project/src/main2.ts[0m:[93m1[0m:[93m114[0m - [91merror[0m[90m TS2724: [0m'Common.SomeComponent.DynamicMenu' has no exported member named 'z'. Did you mean 'Z'?
3838

3939
1 namespace main.file4 { import DynamicMenu = Common.SomeComponent.DynamicMenu; export function foo(a: DynamicMenu.z) { } }
4040
   ~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module M {
2+
export type nums = number;
3+
}
4+
5+
namespace N {
6+
export type nums = number;
7+
}
8+
9+
let a1: M.num;
10+
let b1: M.nums;
11+
let c1: M.bar;
12+
13+
let a2: N.num;
14+
let b2: N.nums;
15+
let c2: N.bar;
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////namespace Foo {
4+
//// export type nums = number;
5+
////}
6+
////let x: Foo.[|num|];
7+
8+
verify.codeFix({
9+
index: 0,
10+
description: [ts.Diagnostics.Change_spelling_to_0.message, "nums"],
11+
newRangeContent: "nums"
12+
});
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////module Foo {
4+
//// export type nums = number;
5+
////}
6+
////let x: Foo.[|num|];
7+
8+
verify.codeFix({
9+
index: 0,
10+
description: [ts.Diagnostics.Change_spelling_to_0.message, "nums"],
11+
newRangeContent: "nums"
12+
});
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
// @filename: a.ts
4+
////module Foo {
5+
//// export type nums = number;
6+
////}
7+
// @filename: b.ts
8+
////let x: Foo.[|num|];
9+
10+
goTo.file("b.ts");
11+
verify.codeFix({
12+
index: 0,
13+
description: [ts.Diagnostics.Change_spelling_to_0.message, "nums"],
14+
newRangeContent: "nums"
15+
});

0 commit comments

Comments
 (0)