From e44e10bb815ec6f93b9737c0fe42f5587d68e7b5 Mon Sep 17 00:00:00 2001 From: ayazhafiz Date: Fri, 2 Oct 2020 23:25:38 -0500 Subject: [PATCH] feat(compiler): support recovery of malformed property writes (#39103) This feature is trivial to support since 89c5255b8ca59eed27ede9e1fad69857ab0c6f4f has landed. PR Close #39103 --- .../compiler/src/expression_parser/parser.ts | 8 ++++- .../test/expression_parser/parser_spec.ts | 32 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index 707f4f02a2..751e8533ab 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -848,7 +848,13 @@ export class _ParseAST { parseAccessMemberOrMethodCall(receiver: AST, isSafe: boolean = false): AST { const start = receiver.span.start; const nameStart = this.inputIndex; - const id = this.expectIdentifierOrKeyword(); + const id = this.withContext(ParseContextFlags.Writable, () => { + const id = this.expectIdentifierOrKeyword(); + if (id.length === 0) { + this.error(`Expected identifier for property access`, receiver.span.end); + } + return id; + }); const nameSpan = this.sourceSpan(nameStart); if (this.consumeOptionalCharacter(chars.$LPAREN)) { diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index 91a7de4621..f6786bc267 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -146,6 +146,38 @@ describe('parser', () => { }); }); + describe('property write', () => { + it('should parse property writes', () => { + checkAction('a.a = 1 + 2'); + checkAction('this.a.a = 1 + 2', 'a.a = 1 + 2'); + checkAction('a.a.a = 1 + 2'); + }); + + describe('malformed property writes', () => { + it('should recover on empty rvalues', () => { + checkActionWithError('a.a = ', 'a.a = ', 'Unexpected end of expression'); + }); + + it('should recover on incomplete rvalues', () => { + checkActionWithError('a.a = 1 + ', 'a.a = 1 + ', 'Unexpected end of expression'); + }); + + it('should recover on missing properties', () => { + checkActionWithError( + 'a. = 1', 'a. = 1', 'Expected identifier for property access at column 2'); + }); + + it('should error on writes after a property write', () => { + const ast = parseAction('a.a = 1 = 2'); + expect(unparse(ast)).toEqual('a.a = 1'); + validate(ast); + + expect(ast.errors.length).toBe(1); + expect(ast.errors[0].message).toContain('Unexpected token \'=\''); + }); + }); + }); + describe('method calls', () => { it('should parse method calls', () => { checkAction('fn()');