Skip to content

Commit ed5008d

Browse files
Jack-Workssandersn
andauthored
Improve error for unclosed imports and exports (#54634)
Co-authored-by: Nathan Shively-Sanders <[email protected]>
1 parent 18e1949 commit ed5008d

9 files changed

+57
-106
lines changed

src/compiler/parser.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2869,6 +2869,11 @@ namespace Parser {
28692869
case ParsingContext.HeritageClauses:
28702870
return isHeritageClause();
28712871
case ParsingContext.ImportOrExportSpecifiers:
2872+
// bail out if the next token is [FromKeyword StringLiteral].
2873+
// That means we're in something like `import { from "mod"`. Stop here can give better error message.
2874+
if (token() === SyntaxKind.FromKeyword && lookAhead(nextTokenIsStringLiteral)) {
2875+
return false;
2876+
}
28722877
return tokenIsIdentifierOrKeyword(token());
28732878
case ParsingContext.JsxAttributes:
28742879
return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken;
@@ -3390,7 +3395,11 @@ namespace Parser {
33903395
case ParsingContext.TypeArguments: return parseErrorAtCurrentToken(Diagnostics.Type_argument_expected);
33913396
case ParsingContext.TupleElementTypes: return parseErrorAtCurrentToken(Diagnostics.Type_expected);
33923397
case ParsingContext.HeritageClauses: return parseErrorAtCurrentToken(Diagnostics.Unexpected_token_expected);
3393-
case ParsingContext.ImportOrExportSpecifiers: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
3398+
case ParsingContext.ImportOrExportSpecifiers:
3399+
if (token() === SyntaxKind.FromKeyword) {
3400+
return parseErrorAtCurrentToken(Diagnostics._0_expected, "}");
3401+
}
3402+
return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
33943403
case ParsingContext.JsxAttributes: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
33953404
case ParsingContext.JsxChildren: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected);
33963405
case ParsingContext.AssertEntries: return parseErrorAtCurrentToken(Diagnostics.Identifier_or_string_literal_expected); // AssertionKey.
@@ -7413,6 +7422,9 @@ namespace Parser {
74137422
}
74147423
}
74157424

7425+
function nextTokenIsStringLiteral() {
7426+
return nextToken() === SyntaxKind.StringLiteral;
7427+
}
74167428
function nextTokenIsIdentifierOrStringLiteralOnSameLine() {
74177429
nextToken();
74187430
return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral);
Lines changed: 10 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,28 @@
1-
t2.ts(1,13): error TS2305: Module '"./t1"' has no exported member 'from'.
2-
t2.ts(1,18): error TS1005: ',' expected.
3-
t3.ts(1,10): error TS2305: Module '"./t1"' has no exported member 'from'.
4-
t3.ts(1,15): error TS1005: ',' expected.
1+
t2.ts(1,13): error TS1005: '}' expected.
2+
t3.ts(1,10): error TS1005: '}' expected.
53
t4.ts(1,17): error TS1005: ',' expected.
6-
t4.ts(1,17): error TS2305: Module '"./t1"' has no exported member 'from'.
7-
t4.ts(1,22): error TS1005: ',' expected.
8-
t5.ts(1,18): error TS2305: Module '"./t1"' has no exported member 'from'.
9-
t5.ts(1,23): error TS1005: ',' expected.
4+
t5.ts(1,18): error TS1005: '}' expected.
105

116

127
==== t1.ts (0 errors) ====
138
export var x = "x";
149

15-
==== t2.ts (2 errors) ====
10+
==== t2.ts (1 errors) ====
1611
export { x, from "./t1"
1712
~~~~
18-
!!! error TS2305: Module '"./t1"' has no exported member 'from'.
19-
~~~~~~
20-
!!! error TS1005: ',' expected.
13+
!!! error TS1005: '}' expected.
2114

22-
==== t3.ts (2 errors) ====
15+
==== t3.ts (1 errors) ====
2316
export { from "./t1"
2417
~~~~
25-
!!! error TS2305: Module '"./t1"' has no exported member 'from'.
26-
~~~~~~
27-
!!! error TS1005: ',' expected.
18+
!!! error TS1005: '}' expected.
2819

29-
==== t4.ts (3 errors) ====
20+
==== t4.ts (1 errors) ====
3021
export { x as a from "./t1"
3122
~~~~
3223
!!! error TS1005: ',' expected.
33-
~~~~
34-
!!! error TS2305: Module '"./t1"' has no exported member 'from'.
35-
~~~~~~
36-
!!! error TS1005: ',' expected.
3724

38-
==== t5.ts (2 errors) ====
25+
==== t5.ts (1 errors) ====
3926
export { x as a, from "./t1"
4027
~~~~
41-
!!! error TS2305: Module '"./t1"' has no exported member 'from'.
42-
~~~~~~
43-
!!! error TS1005: ',' expected.
28+
!!! error TS1005: '}' expected.

tests/baselines/reference/unclosedExportClause01.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,21 @@ exports.x = "x";
2323
//// [t2.js]
2424
"use strict";
2525
Object.defineProperty(exports, "__esModule", { value: true });
26-
exports.from = exports.x = void 0;
26+
exports.x = void 0;
2727
var t1_1 = require("./t1");
2828
Object.defineProperty(exports, "x", { enumerable: true, get: function () { return t1_1.x; } });
29-
Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } });
3029
//// [t3.js]
3130
"use strict";
3231
Object.defineProperty(exports, "__esModule", { value: true });
33-
exports.from = void 0;
34-
var t1_1 = require("./t1");
35-
Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } });
3632
//// [t4.js]
3733
"use strict";
3834
Object.defineProperty(exports, "__esModule", { value: true });
39-
exports.from = exports.a = void 0;
35+
exports.a = void 0;
4036
var t1_1 = require("./t1");
4137
Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } });
42-
Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } });
4338
//// [t5.js]
4439
"use strict";
4540
Object.defineProperty(exports, "__esModule", { value: true });
46-
exports.from = exports.a = void 0;
41+
exports.a = void 0;
4742
var t1_1 = require("./t1");
4843
Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } });
49-
Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } });

tests/baselines/reference/unclosedExportClause01.symbols

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,18 @@ export var x = "x";
77
=== t2.ts ===
88
export { x, from "./t1"
99
>x : Symbol(x, Decl(t2.ts, 0, 8))
10-
>from : Symbol(from, Decl(t2.ts, 0, 11))
1110

1211
=== t3.ts ===
12+
1313
export { from "./t1"
14-
>from : Symbol(from, Decl(t3.ts, 0, 8))
1514

1615
=== t4.ts ===
1716
export { x as a from "./t1"
1817
>x : Symbol(x, Decl(t1.ts, 0, 10))
1918
>a : Symbol(a, Decl(t4.ts, 0, 8))
20-
>from : Symbol(from, Decl(t4.ts, 0, 15))
2119

2220
=== t5.ts ===
2321
export { x as a, from "./t1"
2422
>x : Symbol(x, Decl(t1.ts, 0, 10))
2523
>a : Symbol(a, Decl(t5.ts, 0, 8))
26-
>from : Symbol(from, Decl(t5.ts, 0, 16))
2724

tests/baselines/reference/unclosedExportClause01.types

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,18 @@ export var x = "x";
88
=== t2.ts ===
99
export { x, from "./t1"
1010
>x : string
11-
>from : any
1211

1312
=== t3.ts ===
13+
1414
export { from "./t1"
15-
>from : any
1615

1716
=== t4.ts ===
1817
export { x as a from "./t1"
1918
>x : string
2019
>a : string
21-
>from : any
2220

2321
=== t5.ts ===
2422
export { x as a, from "./t1"
2523
>x : string
2624
>a : string
27-
>from : any
2825

Lines changed: 11 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,32 @@
1-
t2.ts(1,10): error TS2304: Cannot find name 'x'.
2-
t2.ts(1,13): error TS2304: Cannot find name 'from'.
3-
t2.ts(2,5): error TS1005: ',' expected.
4-
t3.ts(1,10): error TS2304: Cannot find name 'from'.
5-
t3.ts(2,5): error TS1005: ',' expected.
6-
t4.ts(1,10): error TS2304: Cannot find name 'x'.
1+
t2.ts(1,13): error TS1005: '}' expected.
2+
t3.ts(1,10): error TS1005: '}' expected.
73
t4.ts(1,17): error TS1005: ',' expected.
8-
t4.ts(1,17): error TS2304: Cannot find name 'from'.
9-
t4.ts(2,5): error TS1005: ',' expected.
10-
t5.ts(1,10): error TS2304: Cannot find name 'x'.
11-
t5.ts(1,18): error TS2304: Cannot find name 'from'.
12-
t5.ts(2,5): error TS1005: ',' expected.
4+
t5.ts(1,18): error TS1005: '}' expected.
135

146

157
==== t1.ts (0 errors) ====
168
export var x = "x";
179

18-
==== t2.ts (3 errors) ====
10+
==== t2.ts (1 errors) ====
1911
export { x, from
20-
~
21-
!!! error TS2304: Cannot find name 'x'.
2212
~~~~
23-
!!! error TS2304: Cannot find name 'from'.
13+
!!! error TS1005: '}' expected.
2414
"./t1";
25-
~~~~~~
26-
!!! error TS1005: ',' expected.
2715

28-
==== t3.ts (2 errors) ====
16+
==== t3.ts (1 errors) ====
2917
export { from
3018
~~~~
31-
!!! error TS2304: Cannot find name 'from'.
19+
!!! error TS1005: '}' expected.
3220
"./t1";
33-
~~~~~~
34-
!!! error TS1005: ',' expected.
3521

36-
==== t4.ts (4 errors) ====
22+
==== t4.ts (1 errors) ====
3723
export { x as a from
38-
~
39-
!!! error TS2304: Cannot find name 'x'.
4024
~~~~
4125
!!! error TS1005: ',' expected.
42-
~~~~
43-
!!! error TS2304: Cannot find name 'from'.
4426
"./t1";
45-
~~~~~~
46-
!!! error TS1005: ',' expected.
4727

48-
==== t5.ts (3 errors) ====
28+
==== t5.ts (1 errors) ====
4929
export { x as a, from
50-
~
51-
!!! error TS2304: Cannot find name 'x'.
5230
~~~~
53-
!!! error TS2304: Cannot find name 'from'.
54-
"./t1";
55-
~~~~~~
56-
!!! error TS1005: ',' expected.
31+
!!! error TS1005: '}' expected.
32+
"./t1";

tests/baselines/reference/unclosedExportClause02.js

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,21 @@ exports.x = "x";
2727
//// [t2.js]
2828
"use strict";
2929
Object.defineProperty(exports, "__esModule", { value: true });
30-
exports.from = exports.x = void 0;
31-
"./t1";
30+
exports.x = void 0;
31+
var t1_1 = require("./t1");
32+
Object.defineProperty(exports, "x", { enumerable: true, get: function () { return t1_1.x; } });
3233
//// [t3.js]
3334
"use strict";
3435
Object.defineProperty(exports, "__esModule", { value: true });
35-
exports.from = void 0;
36-
"./t1";
3736
//// [t4.js]
3837
"use strict";
3938
Object.defineProperty(exports, "__esModule", { value: true });
40-
exports.from = exports.a = void 0;
41-
"./t1";
39+
exports.a = void 0;
40+
var t1_1 = require("./t1");
41+
Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } });
4242
//// [t5.js]
4343
"use strict";
4444
Object.defineProperty(exports, "__esModule", { value: true });
45-
exports.from = exports.a = void 0;
46-
"./t1";
45+
exports.a = void 0;
46+
var t1_1 = require("./t1");
47+
Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } });

tests/baselines/reference/unclosedExportClause02.symbols

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,24 @@ export var x = "x";
77
=== t2.ts ===
88
export { x, from
99
>x : Symbol(x, Decl(t2.ts, 0, 8))
10-
>from : Symbol(from, Decl(t2.ts, 0, 11))
1110

1211
"./t1";
1312

1413
=== t3.ts ===
15-
export { from
16-
>from : Symbol(from, Decl(t3.ts, 0, 8))
1714

15+
export { from
1816
"./t1";
1917

2018
=== t4.ts ===
2119
export { x as a from
20+
>x : Symbol(x, Decl(t1.ts, 0, 10))
2221
>a : Symbol(a, Decl(t4.ts, 0, 8))
23-
>from : Symbol(from, Decl(t4.ts, 0, 15))
2422

2523
"./t1";
2624

2725
=== t5.ts ===
2826
export { x as a, from
27+
>x : Symbol(x, Decl(t1.ts, 0, 10))
2928
>a : Symbol(a, Decl(t5.ts, 0, 8))
30-
>from : Symbol(from, Decl(t5.ts, 0, 16))
3129

3230
"./t1";

tests/baselines/reference/unclosedExportClause02.types

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,25 @@ export var x = "x";
77

88
=== t2.ts ===
99
export { x, from
10-
>x : any
11-
>from : any
10+
>x : string
1211

1312
"./t1";
14-
>"./t1" : "./t1"
1513

1614
=== t3.ts ===
17-
export { from
18-
>from : any
1915

16+
export { from
2017
"./t1";
21-
>"./t1" : "./t1"
2218

2319
=== t4.ts ===
2420
export { x as a from
25-
>x : any
26-
>a : any
27-
>from : any
21+
>x : string
22+
>a : string
2823

2924
"./t1";
30-
>"./t1" : "./t1"
3125

3226
=== t5.ts ===
3327
export { x as a, from
34-
>x : any
35-
>a : any
36-
>from : any
28+
>x : string
29+
>a : string
3730

3831
"./t1";
39-
>"./t1" : "./t1"
40-

0 commit comments

Comments
 (0)