From dc78c4f4de381e3afbb0066b3ee42afa0812bdce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E6=96=87=E7=92=90?= Date: Sun, 8 Apr 2018 16:14:29 +0800 Subject: [PATCH] improve parser and error message if definite assignment assertions in object short hand --- src/compiler/checker.ts | 15 ++++++++++-- src/compiler/parser.ts | 6 ++++- src/compiler/types.ts | 2 ++ .../reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 2 ++ ...ntAssertionsWithObjectShortHand.errors.txt | 15 ++++++++++++ ...AssignmentAssertionsWithObjectShortHand.js | 23 +++++++++++++++++++ ...nmentAssertionsWithObjectShortHand.symbols | 14 +++++++++++ ...ignmentAssertionsWithObjectShortHand.types | 17 ++++++++++++++ ...AssignmentAssertionsWithObjectShortHand.ts | 9 ++++++++ 10 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.errors.txt create mode 100644 tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.js create mode 100644 tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.symbols create mode 100644 tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.types create mode 100644 tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e6a16aa75f..45eed69974f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26871,12 +26871,18 @@ namespace ts { } } - function checkGrammarForInvalidQuestionMark(questionToken: Node, message: DiagnosticMessage): boolean { + function checkGrammarForInvalidQuestionMark(questionToken: QuestionToken, message: DiagnosticMessage): boolean { if (questionToken) { return grammarErrorOnNode(questionToken, message); } } + function checkGrammarForInvalidExclamationToken(exclamationToken: ExclamationToken, message: DiagnosticMessage): boolean { + if (exclamationToken) { + return grammarErrorOnNode(exclamationToken, message); + } + } + function checkGrammarObjectLiteralExpression(node: ObjectLiteralExpression, inDestructuring: boolean) { const enum Flags { Property = 1, @@ -26921,8 +26927,10 @@ namespace ts { // and either both previous and propId.descriptor have[[Get]] fields or both previous and propId.descriptor have[[Set]] fields let currentKind: Flags; switch (prop.kind) { - case SyntaxKind.PropertyAssignment: case SyntaxKind.ShorthandPropertyAssignment: + checkGrammarForInvalidExclamationToken(prop.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context); + /* tslint:disable:no-switch-case-fall-through */ + case SyntaxKind.PropertyAssignment: // Grammar checking for computedPropertyName and shorthandPropertyAssignment checkGrammarForInvalidQuestionMark(prop.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional); if (name.kind === SyntaxKind.NumericLiteral) { @@ -27162,6 +27170,9 @@ namespace ts { else if (checkGrammarForInvalidQuestionMark(node.questionToken, Diagnostics.An_object_member_cannot_be_declared_optional)) { return true; } + else if (checkGrammarForInvalidExclamationToken(node.exclamationToken, Diagnostics.A_definite_assignment_assertion_is_not_permitted_in_this_context)) { + return true; + } else if (node.body === undefined) { return grammarErrorAtPos(node, node.end - 1, ";".length, Diagnostics._0_expected, "{"); } diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 3c118d943a2..dbc989e0c51 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -90,6 +90,7 @@ namespace ts { visitNodes(cbNode, cbNodes, node.modifiers) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).exclamationToken) || visitNode(cbNode, (node).equalsToken) || visitNode(cbNode, (node).objectAssignmentInitializer); case SyntaxKind.SpreadAssignment: @@ -160,6 +161,7 @@ namespace ts { visitNode(cbNode, (node).asteriskToken) || visitNode(cbNode, (node).name) || visitNode(cbNode, (node).questionToken) || + visitNode(cbNode, (node).exclamationToken) || visitNodes(cbNode, cbNodes, (node).typeParameters) || visitNodes(cbNode, cbNodes, (node).parameters) || visitNode(cbNode, (node).type) || @@ -4580,8 +4582,10 @@ namespace ts { const asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken); const tokenIsIdentifier = isIdentifier(); node.name = parsePropertyName(); - // Disallowing of optional property assignments happens in the grammar checker. + // Disallowing of optional property assignments and definite assignment assertion happens in the grammar checker. (node).questionToken = parseOptionalToken(SyntaxKind.QuestionToken); + (node).exclamationToken = parseOptionalToken(SyntaxKind.ExclamationToken); + if (asteriskToken || token() === SyntaxKind.OpenParenToken || token() === SyntaxKind.LessThanToken) { return parseMethodDeclaration(node, asteriskToken); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 4edfc29a2a1..085d2c95acf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -912,6 +912,7 @@ namespace ts { kind: SyntaxKind.ShorthandPropertyAssignment; name: Identifier; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; // used when ObjectLiteralExpression is used in ObjectAssignmentPattern // it is grammar error to appear in actual object initializer equalsToken?: Token; @@ -970,6 +971,7 @@ namespace ts { asteriskToken?: AsteriskToken; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; body?: Block | Expression; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index aca4e956181..1ac5055e0c7 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -608,6 +608,7 @@ declare namespace ts { kind: SyntaxKind.ShorthandPropertyAssignment; name: Identifier; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; equalsToken?: Token; objectAssignmentInitializer?: Expression; } @@ -644,6 +645,7 @@ declare namespace ts { _functionLikeDeclarationBrand: any; asteriskToken?: AsteriskToken; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; body?: Block | Expression; } type FunctionLikeDeclaration = FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index aa77372db45..4def1db34f8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -608,6 +608,7 @@ declare namespace ts { kind: SyntaxKind.ShorthandPropertyAssignment; name: Identifier; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; equalsToken?: Token; objectAssignmentInitializer?: Expression; } @@ -644,6 +645,7 @@ declare namespace ts { _functionLikeDeclarationBrand: any; asteriskToken?: AsteriskToken; questionToken?: QuestionToken; + exclamationToken?: ExclamationToken; body?: Block | Expression; } type FunctionLikeDeclaration = FunctionDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ConstructorDeclaration | FunctionExpression | ArrowFunction; diff --git a/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.errors.txt b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.errors.txt new file mode 100644 index 00000000000..30a1cc39aeb --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts(2,16): error TS1255: A definite assignment assertion '!' is not permitted in this context. +tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts(5,7): error TS1162: An object member cannot be declared optional. + + +==== tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts (2 errors) ==== + const a: string | undefined = 'ff'; + const foo = { a! } + ~ +!!! error TS1255: A definite assignment assertion '!' is not permitted in this context. + + const bar = { + a ? () { } + ~ +!!! error TS1162: An object member cannot be declared optional. + } \ No newline at end of file diff --git a/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.js b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.js new file mode 100644 index 00000000000..c3bfe922374 --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.js @@ -0,0 +1,23 @@ +//// [definiteAssignmentAssertionsWithObjectShortHand.ts] +const a: string | undefined = 'ff'; +const foo = { a! } + +const bar = { + a ? () { } +} + +//// [definiteAssignmentAssertionsWithObjectShortHand.js] +"use strict"; +var a = 'ff'; +var foo = { a: a }; +var bar = { + a: function () { } +}; + + +//// [definiteAssignmentAssertionsWithObjectShortHand.d.ts] +declare const a: string | undefined; +declare const foo: { + a: string; +}; +declare const bar: {}; diff --git a/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.symbols b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.symbols new file mode 100644 index 00000000000..7b8afc1d657 --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.symbols @@ -0,0 +1,14 @@ +=== tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts === +const a: string | undefined = 'ff'; +>a : Symbol(a, Decl(definiteAssignmentAssertionsWithObjectShortHand.ts, 0, 5)) + +const foo = { a! } +>foo : Symbol(foo, Decl(definiteAssignmentAssertionsWithObjectShortHand.ts, 1, 5)) +>a : Symbol(a, Decl(definiteAssignmentAssertionsWithObjectShortHand.ts, 1, 13)) + +const bar = { +>bar : Symbol(bar, Decl(definiteAssignmentAssertionsWithObjectShortHand.ts, 3, 5)) + + a ? () { } +>a : Symbol(a, Decl(definiteAssignmentAssertionsWithObjectShortHand.ts, 3, 13)) +} diff --git a/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.types b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.types new file mode 100644 index 00000000000..73c3edfdaa5 --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentAssertionsWithObjectShortHand.types @@ -0,0 +1,17 @@ +=== tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts === +const a: string | undefined = 'ff'; +>a : string | undefined +>'ff' : "ff" + +const foo = { a! } +>foo : { a: string; } +>{ a! } : { a: string; } +>a : string + +const bar = { +>bar : {} +>{ a ? () { }} : {} + + a ? () { } +>a : (() => void) | undefined +} diff --git a/tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts b/tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts new file mode 100644 index 00000000000..56c22310433 --- /dev/null +++ b/tests/cases/conformance/controlFlow/definiteAssignmentAssertionsWithObjectShortHand.ts @@ -0,0 +1,9 @@ +// @strict: true +// @declaration: true + +const a: string | undefined = 'ff'; +const foo = { a! } + +const bar = { + a ? () { } +} \ No newline at end of file