From b8adea4624a8591cc2425dfffe21bf87a8b151b6 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 14 Jun 2023 00:18:48 +0800 Subject: [PATCH 1/2] Improve error for unclosed imports and exports --- src/compiler/parser.ts | 16 ++++++- .../unclosedExportClause01.errors.txt | 35 ++++---------- .../reference/unclosedExportClause01.js | 12 ++--- .../reference/unclosedExportClause01.symbols | 5 +- .../reference/unclosedExportClause01.types | 5 +- .../unclosedExportClause02.errors.txt | 46 +++++-------------- .../reference/unclosedExportClause02.js | 17 +++---- .../reference/unclosedExportClause02.symbols | 8 ++-- .../reference/unclosedExportClause02.types | 21 +++------ 9 files changed, 58 insertions(+), 107 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index f49b48bb44295..03c7545d30bb3 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2869,7 +2869,12 @@ namespace Parser { case ParsingContext.HeritageClauses: return isHeritageClause(); case ParsingContext.ImportOrExportSpecifiers: - return tokenIsIdentifierOrKeyword(token()); + // bail out if the next token is Identifier(from) StringLiteral. + // That means we're in something like `import { from "mod"`. Stop here can give better error message. + if (token() === SyntaxKind.FromKeyword && lookAhead(nextTokenIsStringLiteral)) { + return false; + } + return token() === SyntaxKind.StringLiteral || tokenIsIdentifierOrKeyword(token()); case ParsingContext.JsxAttributes: return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken; case ParsingContext.JsxChildren: @@ -3390,7 +3395,11 @@ namespace Parser { case ParsingContext.TypeArguments: return parseErrorAtCurrentToken(Diagnostics.Type_argument_expected); case ParsingContext.TupleElementTypes: return parseErrorAtCurrentToken(Diagnostics.Type_expected); case ParsingContext.HeritageClauses: return parseErrorAtCurrentToken(Diagnostics.Unexpected_token_expected); - case ParsingContext.ImportOrExportSpecifiers: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); + case ParsingContext.ImportOrExportSpecifiers: + if (token() === SyntaxKind.FromKeyword) { + return parseErrorAtCurrentToken(Diagnostics._0_expected, "}"); + } + return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); case ParsingContext.JsxAttributes: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); case ParsingContext.JsxChildren: return parseErrorAtCurrentToken(Diagnostics.Identifier_expected); case ParsingContext.AssertEntries: return parseErrorAtCurrentToken(Diagnostics.Identifier_or_string_literal_expected); // AssertionKey. @@ -7358,6 +7367,9 @@ namespace Parser { } } + function nextTokenIsStringLiteral() { + return nextToken() === SyntaxKind.StringLiteral; + } function nextTokenIsIdentifierOrStringLiteralOnSameLine() { nextToken(); return !scanner.hasPrecedingLineBreak() && (isIdentifier() || token() === SyntaxKind.StringLiteral); diff --git a/tests/baselines/reference/unclosedExportClause01.errors.txt b/tests/baselines/reference/unclosedExportClause01.errors.txt index ec3ef3cf322f0..825ad6a1b3c81 100644 --- a/tests/baselines/reference/unclosedExportClause01.errors.txt +++ b/tests/baselines/reference/unclosedExportClause01.errors.txt @@ -1,43 +1,28 @@ -t2.ts(1,13): error TS2305: Module '"./t1"' has no exported member 'from'. -t2.ts(1,18): error TS1005: ',' expected. -t3.ts(1,10): error TS2305: Module '"./t1"' has no exported member 'from'. -t3.ts(1,15): error TS1005: ',' expected. +t2.ts(1,13): error TS1005: '}' expected. +t3.ts(1,10): error TS1005: '}' expected. t4.ts(1,17): error TS1005: ',' expected. -t4.ts(1,17): error TS2305: Module '"./t1"' has no exported member 'from'. -t4.ts(1,22): error TS1005: ',' expected. -t5.ts(1,18): error TS2305: Module '"./t1"' has no exported member 'from'. -t5.ts(1,23): error TS1005: ',' expected. +t5.ts(1,18): error TS1005: '}' expected. ==== t1.ts (0 errors) ==== export var x = "x"; -==== t2.ts (2 errors) ==== +==== t2.ts (1 errors) ==== export { x, from "./t1" ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. +!!! error TS1005: '}' expected. -==== t3.ts (2 errors) ==== +==== t3.ts (1 errors) ==== export { from "./t1" ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. +!!! error TS1005: '}' expected. -==== t4.ts (3 errors) ==== +==== t4.ts (1 errors) ==== export { x as a from "./t1" ~~~~ !!! error TS1005: ',' expected. - ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. -==== t5.ts (2 errors) ==== +==== t5.ts (1 errors) ==== export { x as a, from "./t1" ~~~~ -!!! error TS2305: Module '"./t1"' has no exported member 'from'. - ~~~~~~ -!!! error TS1005: ',' expected. \ No newline at end of file +!!! error TS1005: '}' expected. \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause01.js b/tests/baselines/reference/unclosedExportClause01.js index ef0d2a5be634c..a7b46515a687d 100644 --- a/tests/baselines/reference/unclosedExportClause01.js +++ b/tests/baselines/reference/unclosedExportClause01.js @@ -23,27 +23,21 @@ exports.x = "x"; //// [t2.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.x = void 0; +exports.x = void 0; var t1_1 = require("./t1"); Object.defineProperty(exports, "x", { enumerable: true, get: function () { return t1_1.x; } }); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); //// [t3.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = void 0; -var t1_1 = require("./t1"); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); //// [t4.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; +exports.a = void 0; var t1_1 = require("./t1"); Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); //// [t5.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; +exports.a = void 0; var t1_1 = require("./t1"); Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); -Object.defineProperty(exports, "from", { enumerable: true, get: function () { return t1_1.from; } }); diff --git a/tests/baselines/reference/unclosedExportClause01.symbols b/tests/baselines/reference/unclosedExportClause01.symbols index 65cfcc297adc8..9b7f85697b97b 100644 --- a/tests/baselines/reference/unclosedExportClause01.symbols +++ b/tests/baselines/reference/unclosedExportClause01.symbols @@ -7,21 +7,18 @@ export var x = "x"; === t2.ts === export { x, from "./t1" >x : Symbol(x, Decl(t2.ts, 0, 8)) ->from : Symbol(from, Decl(t2.ts, 0, 11)) === t3.ts === + export { from "./t1" ->from : Symbol(from, Decl(t3.ts, 0, 8)) === t4.ts === export { x as a from "./t1" >x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t4.ts, 0, 8)) ->from : Symbol(from, Decl(t4.ts, 0, 15)) === t5.ts === export { x as a, from "./t1" >x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t5.ts, 0, 8)) ->from : Symbol(from, Decl(t5.ts, 0, 16)) diff --git a/tests/baselines/reference/unclosedExportClause01.types b/tests/baselines/reference/unclosedExportClause01.types index b9a286a5ab49f..a4b897f9091b0 100644 --- a/tests/baselines/reference/unclosedExportClause01.types +++ b/tests/baselines/reference/unclosedExportClause01.types @@ -8,21 +8,18 @@ export var x = "x"; === t2.ts === export { x, from "./t1" >x : string ->from : any === t3.ts === + export { from "./t1" ->from : any === t4.ts === export { x as a from "./t1" >x : string >a : string ->from : any === t5.ts === export { x as a, from "./t1" >x : string >a : string ->from : any diff --git a/tests/baselines/reference/unclosedExportClause02.errors.txt b/tests/baselines/reference/unclosedExportClause02.errors.txt index 930ebdc40a0e6..2f1978e46bc39 100644 --- a/tests/baselines/reference/unclosedExportClause02.errors.txt +++ b/tests/baselines/reference/unclosedExportClause02.errors.txt @@ -1,56 +1,32 @@ -t2.ts(1,10): error TS2304: Cannot find name 'x'. -t2.ts(1,13): error TS2304: Cannot find name 'from'. -t2.ts(2,5): error TS1005: ',' expected. -t3.ts(1,10): error TS2304: Cannot find name 'from'. -t3.ts(2,5): error TS1005: ',' expected. -t4.ts(1,10): error TS2304: Cannot find name 'x'. +t2.ts(1,13): error TS1005: '}' expected. +t3.ts(1,10): error TS1005: '}' expected. t4.ts(1,17): error TS1005: ',' expected. -t4.ts(1,17): error TS2304: Cannot find name 'from'. -t4.ts(2,5): error TS1005: ',' expected. -t5.ts(1,10): error TS2304: Cannot find name 'x'. -t5.ts(1,18): error TS2304: Cannot find name 'from'. -t5.ts(2,5): error TS1005: ',' expected. +t5.ts(1,18): error TS1005: '}' expected. ==== t1.ts (0 errors) ==== export var x = "x"; -==== t2.ts (3 errors) ==== +==== t2.ts (1 errors) ==== export { x, from - ~ -!!! error TS2304: Cannot find name 'x'. ~~~~ -!!! error TS2304: Cannot find name 'from'. +!!! error TS1005: '}' expected. "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. -==== t3.ts (2 errors) ==== +==== t3.ts (1 errors) ==== export { from ~~~~ -!!! error TS2304: Cannot find name 'from'. +!!! error TS1005: '}' expected. "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. -==== t4.ts (4 errors) ==== +==== t4.ts (1 errors) ==== export { x as a from - ~ -!!! error TS2304: Cannot find name 'x'. ~~~~ !!! error TS1005: ',' expected. - ~~~~ -!!! error TS2304: Cannot find name 'from'. "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. -==== t5.ts (3 errors) ==== +==== t5.ts (1 errors) ==== export { x as a, from - ~ -!!! error TS2304: Cannot find name 'x'. ~~~~ -!!! error TS2304: Cannot find name 'from'. - "./t1"; - ~~~~~~ -!!! error TS1005: ',' expected. \ No newline at end of file +!!! error TS1005: '}' expected. + "./t1"; \ No newline at end of file diff --git a/tests/baselines/reference/unclosedExportClause02.js b/tests/baselines/reference/unclosedExportClause02.js index d553c74911d5f..3403afdac989e 100644 --- a/tests/baselines/reference/unclosedExportClause02.js +++ b/tests/baselines/reference/unclosedExportClause02.js @@ -27,20 +27,21 @@ exports.x = "x"; //// [t2.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.x = void 0; -"./t1"; +exports.x = void 0; +var t1_1 = require("./t1"); +Object.defineProperty(exports, "x", { enumerable: true, get: function () { return t1_1.x; } }); //// [t3.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = void 0; -"./t1"; //// [t4.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; -"./t1"; +exports.a = void 0; +var t1_1 = require("./t1"); +Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); //// [t5.js] "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.from = exports.a = void 0; -"./t1"; +exports.a = void 0; +var t1_1 = require("./t1"); +Object.defineProperty(exports, "a", { enumerable: true, get: function () { return t1_1.x; } }); diff --git a/tests/baselines/reference/unclosedExportClause02.symbols b/tests/baselines/reference/unclosedExportClause02.symbols index fa45dc4a5f2b8..a528768936790 100644 --- a/tests/baselines/reference/unclosedExportClause02.symbols +++ b/tests/baselines/reference/unclosedExportClause02.symbols @@ -7,26 +7,24 @@ export var x = "x"; === t2.ts === export { x, from >x : Symbol(x, Decl(t2.ts, 0, 8)) ->from : Symbol(from, Decl(t2.ts, 0, 11)) "./t1"; === t3.ts === -export { from ->from : Symbol(from, Decl(t3.ts, 0, 8)) +export { from "./t1"; === t4.ts === export { x as a from +>x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t4.ts, 0, 8)) ->from : Symbol(from, Decl(t4.ts, 0, 15)) "./t1"; === t5.ts === export { x as a, from +>x : Symbol(x, Decl(t1.ts, 0, 10)) >a : Symbol(a, Decl(t5.ts, 0, 8)) ->from : Symbol(from, Decl(t5.ts, 0, 16)) "./t1"; diff --git a/tests/baselines/reference/unclosedExportClause02.types b/tests/baselines/reference/unclosedExportClause02.types index 0ddbdbbc6db77..9588153e06c53 100644 --- a/tests/baselines/reference/unclosedExportClause02.types +++ b/tests/baselines/reference/unclosedExportClause02.types @@ -7,34 +7,25 @@ export var x = "x"; === t2.ts === export { x, from ->x : any ->from : any +>x : string "./t1"; ->"./t1" : "./t1" === t3.ts === -export { from ->from : any +export { from "./t1"; ->"./t1" : "./t1" === t4.ts === export { x as a from ->x : any ->a : any ->from : any +>x : string +>a : string "./t1"; ->"./t1" : "./t1" === t5.ts === export { x as a, from ->x : any ->a : any ->from : any +>x : string +>a : string "./t1"; ->"./t1" : "./t1" - From 27d107e478314e09e2d298769a6cd2a009aeb7a4 Mon Sep 17 00:00:00 2001 From: Jack Works Date: Wed, 14 Jun 2023 00:20:54 +0800 Subject: [PATCH 2/2] fix: error --- src/compiler/parser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 03c7545d30bb3..eb2da7e8fc344 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2869,12 +2869,12 @@ namespace Parser { case ParsingContext.HeritageClauses: return isHeritageClause(); case ParsingContext.ImportOrExportSpecifiers: - // bail out if the next token is Identifier(from) StringLiteral. + // bail out if the next token is [FromKeyword StringLiteral]. // That means we're in something like `import { from "mod"`. Stop here can give better error message. if (token() === SyntaxKind.FromKeyword && lookAhead(nextTokenIsStringLiteral)) { return false; } - return token() === SyntaxKind.StringLiteral || tokenIsIdentifierOrKeyword(token()); + return tokenIsIdentifierOrKeyword(token()); case ParsingContext.JsxAttributes: return tokenIsIdentifierOrKeyword(token()) || token() === SyntaxKind.OpenBraceToken; case ParsingContext.JsxChildren: