Skip to content

Commit 35cd0e4

Browse files
Ayaz Hafizayazhafiz
Ayaz Hafiz
authored andcommitted
Token hints for missing closing braces: classes, enums, jsx, modules, types
This commit adds token hints for missing close braces in - Class definitions - Enum definitions - JSX expressions - Module definitions (this includes augmentations and namespaces) - Type member lists (this includes interfaces) The token hint refers to the opening brace for which the parser expected a corresponding closing brace. Note that this commit *does not* update all occurences of an expectation for a close brace to provide token hints. Constructs for which this token hint is not provided include: - Mapped types - Switch statements - Object binding patterns - JSX spread attributes (`<div {...props}></div>`) - JSDoc implements/augments tags (`/* @implements {SomeInterface} */`) Part of microsoft#35597.
1 parent c1f676d commit 35cd0e4

File tree

48 files changed

+349
-34
lines changed

Some content is hidden

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

48 files changed

+349
-34
lines changed

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"category": "Error",
1616
"code": 1006
1717
},
18-
"The parser expected to find a '}' to match the '{' token here.": {
18+
"The parser expected to find a '{0}' to match the '{1}' token here.": {
1919
"category": "Error",
2020
"code": 1007
2121
},

src/compiler/parser.ts

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,6 +1308,23 @@ namespace ts {
13081308
return false;
13091309
}
13101310

1311+
function parseExpectedCloseToken(closeTokenKind: SyntaxKind, openTokenKind: SyntaxKind, openTokenPos: number, shouldAdvance = true): boolean {
1312+
if (!parseExpected(closeTokenKind, /*diagnosticMessage*/ undefined, shouldAdvance)) {
1313+
const lastError = lastOrUndefined(parseDiagnostics);
1314+
if (lastError && lastError.code === Diagnostics._0_expected.code) {
1315+
const openTokenStr = tokenToString(openTokenKind)!;
1316+
const closeTokenStr = tokenToString(closeTokenKind)!;
1317+
addRelatedInfo(
1318+
lastError,
1319+
createFileDiagnostic(sourceFile, openTokenPos, openTokenStr.length,
1320+
Diagnostics.The_parser_expected_to_find_a_0_to_match_the_1_token_here, closeTokenStr, openTokenStr)
1321+
);
1322+
}
1323+
return false;
1324+
}
1325+
return true;
1326+
}
1327+
13111328
function parseExpectedJSDoc(kind: JSDocSyntaxKind) {
13121329
if (token() === kind) {
13131330
nextTokenJSDoc();
@@ -2988,9 +3005,10 @@ namespace ts {
29883005

29893006
function parseObjectTypeMembers(): NodeArray<TypeElement> {
29903007
let members: NodeArray<TypeElement>;
3008+
const openBracePosition = scanner.getTokenPos();
29913009
if (parseExpected(SyntaxKind.OpenBraceToken)) {
29923010
members = parseList(ParsingContext.TypeMembers, parseTypeMember);
2993-
parseExpected(SyntaxKind.CloseBraceToken);
3011+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
29943012
}
29953013
else {
29963014
members = createMissingList<TypeElement>();
@@ -4654,6 +4672,7 @@ namespace ts {
46544672
function parseJsxExpression(inExpressionContext: boolean): JsxExpression | undefined {
46554673
const node = <JsxExpression>createNode(SyntaxKind.JsxExpression);
46564674

4675+
const openBracePosition = scanner.getTokenPos();
46574676
if (!parseExpected(SyntaxKind.OpenBraceToken)) {
46584677
return undefined;
46594678
}
@@ -4666,10 +4685,10 @@ namespace ts {
46664685
node.expression = parseExpression();
46674686
}
46684687
if (inExpressionContext) {
4669-
parseExpected(SyntaxKind.CloseBraceToken);
4688+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
46704689
}
46714690
else {
4672-
if (parseExpected(SyntaxKind.CloseBraceToken, /*message*/ undefined, /*shouldAdvance*/ false)) {
4691+
if (parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition, /*shouldAdvance*/ false)) {
46734692
scanJsxText();
46744693
}
46754694
}
@@ -5143,15 +5162,7 @@ namespace ts {
51435162
}
51445163

51455164
node.properties = parseDelimitedList(ParsingContext.ObjectLiteralMembers, parseObjectLiteralElement, /*considerSemicolonAsDelimiter*/ true);
5146-
if (!parseExpected(SyntaxKind.CloseBraceToken)) {
5147-
const lastError = lastOrUndefined(parseDiagnostics);
5148-
if (lastError && lastError.code === Diagnostics._0_expected.code) {
5149-
addRelatedInfo(
5150-
lastError,
5151-
createFileDiagnostic(sourceFile, openBracePosition, 1, Diagnostics.The_parser_expected_to_find_a_to_match_the_token_here)
5152-
);
5153-
}
5154-
}
5165+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
51555166
return finishNode(node);
51565167
}
51575168

@@ -5239,15 +5250,7 @@ namespace ts {
52395250
}
52405251

52415252
node.statements = parseList(ParsingContext.BlockStatements, parseStatement);
5242-
if (!parseExpected(SyntaxKind.CloseBraceToken)) {
5243-
const lastError = lastOrUndefined(parseDiagnostics);
5244-
if (lastError && lastError.code === Diagnostics._0_expected.code) {
5245-
addRelatedInfo(
5246-
lastError,
5247-
createFileDiagnostic(sourceFile, openBracePosition, 1, Diagnostics.The_parser_expected_to_find_a_to_match_the_token_here)
5248-
);
5249-
}
5250-
}
5253+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
52515254
}
52525255
else {
52535256
node.statements = createMissingList<Statement>();
@@ -6287,11 +6290,12 @@ namespace ts {
62876290
node.typeParameters = parseTypeParameters();
62886291
node.heritageClauses = parseHeritageClauses();
62896292

6293+
const openBracePosition = scanner.getTokenPos();
62906294
if (parseExpected(SyntaxKind.OpenBraceToken)) {
62916295
// ClassTail[Yield,Await] : (Modified) See 14.5
62926296
// ClassHeritage[?Yield,?Await]opt { ClassBody[?Yield,?Await]opt }
62936297
node.members = parseClassMembers();
6294-
parseExpected(SyntaxKind.CloseBraceToken);
6298+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
62956299
}
62966300
else {
62976301
node.members = createMissingList<ClassElement>();
@@ -6392,9 +6396,10 @@ namespace ts {
63926396
node.kind = SyntaxKind.EnumDeclaration;
63936397
parseExpected(SyntaxKind.EnumKeyword);
63946398
node.name = parseIdentifier();
6399+
const openBracePosition = scanner.getTokenPos();
63956400
if (parseExpected(SyntaxKind.OpenBraceToken)) {
63966401
node.members = doOutsideOfYieldAndAwaitContext(() => parseDelimitedList(ParsingContext.EnumMembers, parseEnumMember));
6397-
parseExpected(SyntaxKind.CloseBraceToken);
6402+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
63986403
}
63996404
else {
64006405
node.members = createMissingList<EnumMember>();
@@ -6404,9 +6409,10 @@ namespace ts {
64046409

64056410
function parseModuleBlock(): ModuleBlock {
64066411
const node = <ModuleBlock>createNode(SyntaxKind.ModuleBlock);
6412+
const openBracePosition = scanner.getTokenPos();
64076413
if (parseExpected(SyntaxKind.OpenBraceToken)) {
64086414
node.statements = parseList(ParsingContext.BlockStatements, parseStatement);
6409-
parseExpected(SyntaxKind.CloseBraceToken);
6415+
parseExpectedCloseToken(SyntaxKind.CloseBraceToken, SyntaxKind.OpenBraceToken, openBracePosition);
64106416
}
64116417
else {
64126418
node.statements = createMissingList<Statement>();

tests/baselines/reference/classMemberWithMissingIdentifier.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ tests/cases/compiler/classMemberWithMissingIdentifier.ts(3,1): error TS1128: Dec
1010
!!! error TS1146: Declaration expected.
1111
~
1212
!!! error TS1005: ';' expected.
13+
!!! related TS1007 tests/cases/compiler/classMemberWithMissingIdentifier.ts:1:9: The parser expected to find a '}' to match the '{' token here.
1314
}
1415
~
1516
!!! error TS1128: Declaration or statement expected.

tests/baselines/reference/classMemberWithMissingIdentifier2.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ tests/cases/compiler/classMemberWithMissingIdentifier2.ts(3,1): error TS1128: De
1414
!!! error TS1146: Declaration expected.
1515
~
1616
!!! error TS1005: ';' expected.
17+
!!! related TS1007 tests/cases/compiler/classMemberWithMissingIdentifier2.ts:1:9: The parser expected to find a '}' to match the '{' token here.
1718
~
1819
!!! error TS1005: ',' expected.
1920
~~~~~~

tests/baselines/reference/constructorWithIncompleteTypeAnnotation.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts(261,1): error TS
142142
if (retValue != 0) {
143143
~~
144144
!!! error TS1005: ',' expected.
145+
!!! related TS1007 tests/cases/compiler/constructorWithIncompleteTypeAnnotation.ts:15:26: The parser expected to find a '}' to match the '{' token here.
145146
~
146147
!!! error TS1005: ';' expected.
147148

tests/baselines/reference/derivedClassSuperCallsInNonConstructorMembers.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ tests/cases/conformance/classes/constructorDeclarations/superCalls/derivedClassS
5050
!!! error TS2304: Cannot find name 'super'.
5151
~
5252
!!! error TS1005: ';' expected.
53+
!!! related TS1007 tests/cases/conformance/classes/constructorDeclarations/superCalls/derivedClassSuperCallsInNonConstructorMembers.ts:7:28: The parser expected to find a '}' to match the '{' token here.
5354
~
5455
!!! error TS1109: Expression expected.
5556
b() {

tests/baselines/reference/errorRecoveryWithDotFollowedByNamespaceKeyword.errors.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ tests/cases/compiler/errorRecoveryWithDotFollowedByNamespaceKeyword.ts(9,2): err
1717

1818
!!! error TS1005: '}' expected.
1919
!!! related TS1007 tests/cases/compiler/errorRecoveryWithDotFollowedByNamespaceKeyword.ts:3:19: The parser expected to find a '}' to match the '{' token here.
20-
!!! related TS1007 tests/cases/compiler/errorRecoveryWithDotFollowedByNamespaceKeyword.ts:2:20: The parser expected to find a '}' to match the '{' token here.
20+
!!! related TS1007 tests/cases/compiler/errorRecoveryWithDotFollowedByNamespaceKeyword.ts:2:20: The parser expected to find a '}' to match the '{' token here.
21+
!!! related TS1007 tests/cases/compiler/errorRecoveryWithDotFollowedByNamespaceKeyword.ts:1:13: The parser expected to find a '}' to match the '{' token here.

tests/baselines/reference/jsxAndTypeAssertion.errors.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,69): error TS1381: Unexpe
2121
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(18,76): error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
2222
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: ':' expected.
2323
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' expected.
24+
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' expected.
25+
tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' expected.
2426

2527

26-
==== tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx (23 errors) ====
28+
==== tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx (25 errors) ====
2729
declare var createElement: any;
2830

2931
class foo {}
@@ -36,6 +38,7 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
3638
!!! error TS2582: Cannot find name 'test'. Do you need to install type definitions for a test runner? Try `npm i @types/jest` or `npm i @types/mocha`.
3739
~
3840
!!! error TS1005: '}' expected.
41+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:6:11: The parser expected to find a '}' to match the '{' token here.
3942
~
4043
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
4144

@@ -50,6 +53,7 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
5053
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
5154
~
5255
!!! error TS1005: '}' expected.
56+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:10:16: The parser expected to find a '}' to match the '{' token here.
5357

5458
x = <foo test={<foo>{}}>hello</foo>;
5559
~
@@ -58,6 +62,7 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
5862
!!! error TS1382: Unexpected token. Did you mean `{'>'}` or `&gt;`?
5963
~
6064
!!! error TS1005: '}' expected.
65+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:12:15: The parser expected to find a '}' to match the '{' token here.
6166

6267
x = <foo test={<foo>{}}>hello{<foo>{}}</foo>;
6368
~~~
@@ -70,6 +75,7 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
7075
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
7176
~
7277
!!! error TS1005: '}' expected.
78+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:14:30: The parser expected to find a '}' to match the '{' token here.
7379

7480
x = <foo>x</foo>, x = <foo/>;
7581

@@ -89,5 +95,12 @@ tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx(21,1): error TS1005: '</' ex
8995

9096

9197
!!! error TS1005: ':' expected.
98+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:18:17: The parser expected to find a '}' to match the '{' token here.
99+
100+
!!! error TS1005: '</' expected.
101+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:14:15: The parser expected to find a '}' to match the '{' token here.
102+
103+
!!! error TS1005: '</' expected.
104+
!!! related TS1007 tests/cases/conformance/jsx/jsxAndTypeAssertion.tsx:18:6: The parser expected to find a '}' to match the '{' token here.
92105

93106
!!! error TS1005: '</' expected.

tests/baselines/reference/jsxInvalidEsprimaTestSuite.errors.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ tests/cases/conformance/jsx/9.tsx(1,16): error TS1109: Expression expected.
244244
<a>{"str";}</a>;
245245
~
246246
!!! error TS1005: '}' expected.
247+
!!! related TS1007 tests/cases/conformance/jsx/20.tsx:1:4: The parser expected to find a '}' to match the '{' token here.
247248
~
248249
!!! error TS1381: Unexpected token. Did you mean `{'}'}` or `&rbrace;`?
249250
==== tests/cases/conformance/jsx/21.tsx (1 errors) ====
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/compiler/missingCloseBraceInClassDeclaration.ts(7,1): error TS1005: '}' expected.
2+
3+
4+
==== tests/cases/compiler/missingCloseBraceInClassDeclaration.ts (1 errors) ====
5+
class TestCls {
6+
prop = 0;
7+
method() {
8+
return this.prop;
9+
}
10+
11+
12+
13+
!!! error TS1005: '}' expected.
14+
!!! related TS1007 tests/cases/compiler/missingCloseBraceInClassDeclaration.ts:1:15: The parser expected to find a '}' to match the '{' token here.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [missingCloseBraceInClassDeclaration.ts]
2+
class TestCls {
3+
prop = 0;
4+
method() {
5+
return this.prop;
6+
}
7+
8+
9+
10+
//// [missingCloseBraceInClassDeclaration.js]
11+
var TestCls = /** @class */ (function () {
12+
function TestCls() {
13+
this.prop = 0;
14+
}
15+
TestCls.prototype.method = function () {
16+
return this.prop;
17+
};
18+
return TestCls;
19+
}());
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/compiler/missingCloseBraceInClassDeclaration.ts ===
2+
class TestCls {
3+
>TestCls : Symbol(TestCls, Decl(missingCloseBraceInClassDeclaration.ts, 0, 0))
4+
5+
prop = 0;
6+
>prop : Symbol(TestCls.prop, Decl(missingCloseBraceInClassDeclaration.ts, 0, 15))
7+
8+
method() {
9+
>method : Symbol(TestCls.method, Decl(missingCloseBraceInClassDeclaration.ts, 1, 11))
10+
11+
return this.prop;
12+
>this.prop : Symbol(TestCls.prop, Decl(missingCloseBraceInClassDeclaration.ts, 0, 15))
13+
>this : Symbol(TestCls, Decl(missingCloseBraceInClassDeclaration.ts, 0, 0))
14+
>prop : Symbol(TestCls.prop, Decl(missingCloseBraceInClassDeclaration.ts, 0, 15))
15+
}
16+
17+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/compiler/missingCloseBraceInClassDeclaration.ts ===
2+
class TestCls {
3+
>TestCls : TestCls
4+
5+
prop = 0;
6+
>prop : number
7+
>0 : 0
8+
9+
method() {
10+
>method : () => number
11+
12+
return this.prop;
13+
>this.prop : number
14+
>this : this
15+
>prop : number
16+
}
17+
18+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
tests/cases/compiler/missingCloseBraceInEnum.ts(6,1): error TS1005: '}' expected.
2+
3+
4+
==== tests/cases/compiler/missingCloseBraceInEnum.ts (1 errors) ====
5+
enum Colors {
6+
Red,
7+
Green,
8+
Blue,
9+
10+
11+
12+
!!! error TS1005: '}' expected.
13+
!!! related TS1007 tests/cases/compiler/missingCloseBraceInEnum.ts:1:13: The parser expected to find a '}' to match the '{' token here.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [missingCloseBraceInEnum.ts]
2+
enum Colors {
3+
Red,
4+
Green,
5+
Blue,
6+
7+
8+
9+
//// [missingCloseBraceInEnum.js]
10+
var Colors;
11+
(function (Colors) {
12+
Colors[Colors["Red"] = 0] = "Red";
13+
Colors[Colors["Green"] = 1] = "Green";
14+
Colors[Colors["Blue"] = 2] = "Blue";
15+
})(Colors || (Colors = {}));
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/compiler/missingCloseBraceInEnum.ts ===
2+
enum Colors {
3+
>Colors : Symbol(Colors, Decl(missingCloseBraceInEnum.ts, 0, 0))
4+
5+
Red,
6+
>Red : Symbol(Colors.Red, Decl(missingCloseBraceInEnum.ts, 0, 13))
7+
8+
Green,
9+
>Green : Symbol(Colors.Green, Decl(missingCloseBraceInEnum.ts, 1, 6))
10+
11+
Blue,
12+
>Blue : Symbol(Colors.Blue, Decl(missingCloseBraceInEnum.ts, 2, 8))
13+
14+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
=== tests/cases/compiler/missingCloseBraceInEnum.ts ===
2+
enum Colors {
3+
>Colors : Colors
4+
5+
Red,
6+
>Red : Colors.Red
7+
8+
Green,
9+
>Green : Colors.Green
10+
11+
Blue,
12+
>Blue : Colors.Blue
13+
14+

0 commit comments

Comments
 (0)