diff --git a/packages/compiler/src/compiler_util/expression_converter.ts b/packages/compiler/src/compiler_util/expression_converter.ts index d139217b1e..318e548cc4 100644 --- a/packages/compiler/src/compiler_util/expression_converter.ts +++ b/packages/compiler/src/compiler_util/expression_converter.ts @@ -705,7 +705,8 @@ class _AstToIrVisitor implements cdAst.AstVisitor { leftMostSafe, new cdAst.MethodCall( leftMostSafe.span, leftMostSafe.sourceSpan, leftMostSafe.nameSpan, - leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args)); + leftMostSafe.receiver, leftMostSafe.name, leftMostSafe.args, + leftMostSafe.argumentSpan)); } else { this._nodeMap.set( leftMostSafe, diff --git a/packages/compiler/src/expression_parser/ast.ts b/packages/compiler/src/expression_parser/ast.ts index 327311a1dd..fd0b77584f 100644 --- a/packages/compiler/src/expression_parser/ast.ts +++ b/packages/compiler/src/expression_parser/ast.ts @@ -308,7 +308,8 @@ export class NonNullAssert extends AST { export class MethodCall extends ASTWithName { constructor( span: ParseSpan, sourceSpan: AbsoluteSourceSpan, nameSpan: AbsoluteSourceSpan, - public receiver: AST, public name: string, public args: any[]) { + public receiver: AST, public name: string, public args: any[], + public argumentSpan: AbsoluteSourceSpan) { super(span, sourceSpan, nameSpan); } visit(visitor: AstVisitor, context: any = null): any { @@ -319,7 +320,8 @@ export class MethodCall extends ASTWithName { export class SafeMethodCall extends ASTWithName { constructor( span: ParseSpan, sourceSpan: AbsoluteSourceSpan, nameSpan: AbsoluteSourceSpan, - public receiver: AST, public name: string, public args: any[]) { + public receiver: AST, public name: string, public args: any[], + public argumentSpan: AbsoluteSourceSpan) { super(span, sourceSpan, nameSpan); } visit(visitor: AstVisitor, context: any = null): any { @@ -583,13 +585,13 @@ export class AstTransformer implements AstVisitor { visitMethodCall(ast: MethodCall, context: any): AST { return new MethodCall( ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, - this.visitAll(ast.args)); + this.visitAll(ast.args), ast.argumentSpan); } visitSafeMethodCall(ast: SafeMethodCall, context: any): AST { return new SafeMethodCall( ast.span, ast.sourceSpan, ast.nameSpan, ast.receiver.visit(this), ast.name, - this.visitAll(ast.args)); + this.visitAll(ast.args), ast.argumentSpan); } visitFunctionCall(ast: FunctionCall, context: any): AST { @@ -719,7 +721,8 @@ export class AstMemoryEfficientTransformer implements AstVisitor { const receiver = ast.receiver.visit(this); const args = this.visitAll(ast.args); if (receiver !== ast.receiver || args !== ast.args) { - return new MethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args); + return new MethodCall( + ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args, ast.argumentSpan); } return ast; } @@ -728,7 +731,8 @@ export class AstMemoryEfficientTransformer implements AstVisitor { const receiver = ast.receiver.visit(this); const args = this.visitAll(ast.args); if (receiver !== ast.receiver || args !== ast.args) { - return new SafeMethodCall(ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args); + return new SafeMethodCall( + ast.span, ast.sourceSpan, ast.nameSpan, receiver, ast.name, args, ast.argumentSpan); } return ast; } diff --git a/packages/compiler/src/expression_parser/parser.ts b/packages/compiler/src/expression_parser/parser.ts index 4e4c2acaa8..855b60e89c 100644 --- a/packages/compiler/src/expression_parser/parser.ts +++ b/packages/compiler/src/expression_parser/parser.ts @@ -940,14 +940,19 @@ export class _ParseAST { const nameSpan = this.sourceSpan(nameStart); if (this.consumeOptionalCharacter(chars.$LPAREN)) { + const argumentStart = this.inputIndex; this.rparensExpected++; const args = this.parseCallArguments(); + const argumentSpan = + this.span(argumentStart, this.inputIndex).toAbsolute(this.absoluteOffset); + this.expectCharacter(chars.$RPAREN); this.rparensExpected--; const span = this.span(start); const sourceSpan = this.sourceSpan(start); - return isSafe ? new SafeMethodCall(span, sourceSpan, nameSpan, receiver, id, args) : - new MethodCall(span, sourceSpan, nameSpan, receiver, id, args); + return isSafe ? + new SafeMethodCall(span, sourceSpan, nameSpan, receiver, id, args, argumentSpan) : + new MethodCall(span, sourceSpan, nameSpan, receiver, id, args, argumentSpan); } else { if (isSafe) { diff --git a/packages/compiler/test/expression_parser/parser_spec.ts b/packages/compiler/test/expression_parser/parser_spec.ts index f266911a2b..e9f180d3d1 100644 --- a/packages/compiler/test/expression_parser/parser_spec.ts +++ b/packages/compiler/test/expression_parser/parser_spec.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {AbsoluteSourceSpan, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, ParserError, TemplateBinding, VariableBinding} from '@angular/compiler/src/expression_parser/ast'; +import {AbsoluteSourceSpan, ASTWithSource, BindingPipe, EmptyExpr, Interpolation, MethodCall, ParserError, TemplateBinding, VariableBinding} from '@angular/compiler/src/expression_parser/ast'; import {Lexer} from '@angular/compiler/src/expression_parser/lexer'; import {IvyParser, Parser, SplitInterpolation} from '@angular/compiler/src/expression_parser/parser'; import {expect} from '@angular/platform-browser/testing/src/matchers'; @@ -350,6 +350,11 @@ describe('parser', () => { expect(unparseWithSpan(ast)).toContain(['foo()', '[nameSpan] foo']); }); + it('should record method call argument span', () => { + const ast = parseAction('foo(1 + 2)'); + expect(unparseWithSpan(ast)).toContain(['foo(1 + 2)', '[argumentSpan] 1 + 2']); + }); + it('should record accessed method call span', () => { const ast = parseAction('foo.bar()'); expect(unparseWithSpan(ast)).toContain(['foo.bar()', 'foo.bar()']); diff --git a/packages/compiler/test/expression_parser/utils/unparser.ts b/packages/compiler/test/expression_parser/utils/unparser.ts index cb165b246b..04f96b30b1 100644 --- a/packages/compiler/test/expression_parser/utils/unparser.ts +++ b/packages/compiler/test/expression_parser/utils/unparser.ts @@ -229,6 +229,9 @@ export function unparseWithSpan( if (ast.hasOwnProperty('nameSpan')) { this.recordUnparsed(ast, 'nameSpan', unparsedList); } + if (ast.hasOwnProperty('argumentSpan')) { + this.recordUnparsed(ast, 'argumentSpan', unparsedList); + } ast.visit(this, unparsedList); } };