test(compiler): add expression absolute span tests for `TemplateAst`s (#33253)
Previously, we had tested that expressions parsed in a Render3 AST
had correctly-defined absolute spans (spans relative to the entire
template, not the local expression). Sometimes we use Template ASTs
rather than Render3 ASTs, and it's desirable to test for correct
expression spans in the template parser as well.
Adding these tests resolved one bug, similar to the one fixed in
fd4fed14d8935866fe16d30648d115f3ebb5fd43, where expressions in the value
of a template attribute were not given an absolute span corresponding to
the start of the attribute name rather than the start of the attribute
value.
The diff on this commit is large, partially because it involves some
structural changes of the template parser testing layout. In particular,
the following is done:
1. Move `createMeta*`-like functions from `template_parser_spec.ts` to
be exported from a new test utility file.
2. Create an `ExpressionSourceHumanizer`, similar to the one created in
b04488d692ca11c83444da56f51d53c3ac868243, to allow convenient testing
of expressions' locations.
3. Create `template_parser_absolute_span_spec.ts`, testing the spans of
expressions parsed by the template parser. This is very similar to
the `r3_ast_absolute_span_spec`.
PR Close #33253
2019-10-18 10:51:20 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
|
|
|
|
|
|
|
import {AbsoluteSourceSpan} from '@angular/compiler';
|
|
|
|
import * as e from '../../../src/expression_parser/ast';
|
|
|
|
import * as t from '../../../src/template_parser/template_ast';
|
|
|
|
import {unparse} from '../../expression_parser/utils/unparser';
|
|
|
|
|
|
|
|
type HumanizedExpressionSource = [string, AbsoluteSourceSpan];
|
|
|
|
class ExpressionSourceHumanizer extends e.RecursiveAstVisitor implements t.TemplateAstVisitor {
|
|
|
|
result: HumanizedExpressionSource[] = [];
|
|
|
|
|
2020-04-08 13:14:18 -04:00
|
|
|
private recordAst(ast: e.AST) {
|
|
|
|
this.result.push([unparse(ast), ast.sourceSpan]);
|
|
|
|
}
|
test(compiler): add expression absolute span tests for `TemplateAst`s (#33253)
Previously, we had tested that expressions parsed in a Render3 AST
had correctly-defined absolute spans (spans relative to the entire
template, not the local expression). Sometimes we use Template ASTs
rather than Render3 ASTs, and it's desirable to test for correct
expression spans in the template parser as well.
Adding these tests resolved one bug, similar to the one fixed in
fd4fed14d8935866fe16d30648d115f3ebb5fd43, where expressions in the value
of a template attribute were not given an absolute span corresponding to
the start of the attribute name rather than the start of the attribute
value.
The diff on this commit is large, partially because it involves some
structural changes of the template parser testing layout. In particular,
the following is done:
1. Move `createMeta*`-like functions from `template_parser_spec.ts` to
be exported from a new test utility file.
2. Create an `ExpressionSourceHumanizer`, similar to the one created in
b04488d692ca11c83444da56f51d53c3ac868243, to allow convenient testing
of expressions' locations.
3. Create `template_parser_absolute_span_spec.ts`, testing the spans of
expressions parsed by the template parser. This is very similar to
the `r3_ast_absolute_span_spec`.
PR Close #33253
2019-10-18 10:51:20 -04:00
|
|
|
|
refactor(compiler): Remove NullAstVisitor and visitAstChildren (#35619)
This commit removes the `NullAstVisitor` and `visitAstChildren` exported
from `packages/compiler/src/expression_parser/ast.ts` because they
contain duplicate and buggy implementation, and their use cases could be
sufficiently covered by `RecursiveAstVisitor` if the latter implements the
`visit` method. This use case is only needed in the language service.
With this change, any visitor that extends `RecursiveAstVisitor` could
just define their own `visit` function and the parent class will behave
correctly.
A bit of historical context:
In language service, we need a way to tranverse the expression AST in a
selective manner based on where the user's cursor is. This means we need a
"filtering" function to decide which node to visit and which node to not
visit. Instead of refactoring `RecursiveAstVisitor` to support this,
`visitAstChildren` was created. `visitAstChildren` duplicates the
implementation of `RecursiveAstVisitor`, but introduced some bugs along
the way. For example, in `visitKeyedWrite`, it visits
```
obj -> key -> obj
```
instead of
```
obj -> key -> value
```
Moreover, because of the following line
```
visitor.visit && visitor.visit(ast, context) || ast.visit(visitor, context);
```
`visitAstChildren` visits every node *twice*.
PR Close #35619
2020-02-21 13:20:52 -05:00
|
|
|
// This method is defined to reconcile the type of ExpressionSourceHumanizer
|
|
|
|
// since both RecursiveAstVisitor and TemplateAstVisitor define the visit()
|
|
|
|
// method in their interfaces.
|
2020-04-08 13:14:18 -04:00
|
|
|
visit(node: e.AST|t.TemplateAst, context?: any) {
|
|
|
|
node.visit(this, context);
|
|
|
|
}
|
refactor(compiler): Remove NullAstVisitor and visitAstChildren (#35619)
This commit removes the `NullAstVisitor` and `visitAstChildren` exported
from `packages/compiler/src/expression_parser/ast.ts` because they
contain duplicate and buggy implementation, and their use cases could be
sufficiently covered by `RecursiveAstVisitor` if the latter implements the
`visit` method. This use case is only needed in the language service.
With this change, any visitor that extends `RecursiveAstVisitor` could
just define their own `visit` function and the parent class will behave
correctly.
A bit of historical context:
In language service, we need a way to tranverse the expression AST in a
selective manner based on where the user's cursor is. This means we need a
"filtering" function to decide which node to visit and which node to not
visit. Instead of refactoring `RecursiveAstVisitor` to support this,
`visitAstChildren` was created. `visitAstChildren` duplicates the
implementation of `RecursiveAstVisitor`, but introduced some bugs along
the way. For example, in `visitKeyedWrite`, it visits
```
obj -> key -> obj
```
instead of
```
obj -> key -> value
```
Moreover, because of the following line
```
visitor.visit && visitor.visit(ast, context) || ast.visit(visitor, context);
```
`visitAstChildren` visits every node *twice*.
PR Close #35619
2020-02-21 13:20:52 -05:00
|
|
|
|
test(compiler): add expression absolute span tests for `TemplateAst`s (#33253)
Previously, we had tested that expressions parsed in a Render3 AST
had correctly-defined absolute spans (spans relative to the entire
template, not the local expression). Sometimes we use Template ASTs
rather than Render3 ASTs, and it's desirable to test for correct
expression spans in the template parser as well.
Adding these tests resolved one bug, similar to the one fixed in
fd4fed14d8935866fe16d30648d115f3ebb5fd43, where expressions in the value
of a template attribute were not given an absolute span corresponding to
the start of the attribute name rather than the start of the attribute
value.
The diff on this commit is large, partially because it involves some
structural changes of the template parser testing layout. In particular,
the following is done:
1. Move `createMeta*`-like functions from `template_parser_spec.ts` to
be exported from a new test utility file.
2. Create an `ExpressionSourceHumanizer`, similar to the one created in
b04488d692ca11c83444da56f51d53c3ac868243, to allow convenient testing
of expressions' locations.
3. Create `template_parser_absolute_span_spec.ts`, testing the spans of
expressions parsed by the template parser. This is very similar to
the `r3_ast_absolute_span_spec`.
PR Close #33253
2019-10-18 10:51:20 -04:00
|
|
|
visitASTWithSource(ast: e.ASTWithSource) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
this.visitAll([ast.ast], null);
|
|
|
|
}
|
|
|
|
visitBinary(ast: e.Binary) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitBinary(ast, null);
|
|
|
|
}
|
|
|
|
visitChain(ast: e.Chain) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitChain(ast, null);
|
|
|
|
}
|
|
|
|
visitConditional(ast: e.Conditional) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitConditional(ast, null);
|
|
|
|
}
|
|
|
|
visitFunctionCall(ast: e.FunctionCall) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitFunctionCall(ast, null);
|
|
|
|
}
|
|
|
|
visitImplicitReceiver(ast: e.ImplicitReceiver) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitImplicitReceiver(ast, null);
|
|
|
|
}
|
|
|
|
visitInterpolation(ast: e.Interpolation) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitInterpolation(ast, null);
|
|
|
|
}
|
|
|
|
visitKeyedRead(ast: e.KeyedRead) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitKeyedRead(ast, null);
|
|
|
|
}
|
|
|
|
visitKeyedWrite(ast: e.KeyedWrite) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitKeyedWrite(ast, null);
|
|
|
|
}
|
|
|
|
visitLiteralPrimitive(ast: e.LiteralPrimitive) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitLiteralPrimitive(ast, null);
|
|
|
|
}
|
|
|
|
visitLiteralArray(ast: e.LiteralArray) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitLiteralArray(ast, null);
|
|
|
|
}
|
|
|
|
visitLiteralMap(ast: e.LiteralMap) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitLiteralMap(ast, null);
|
|
|
|
}
|
|
|
|
visitMethodCall(ast: e.MethodCall) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitMethodCall(ast, null);
|
|
|
|
}
|
|
|
|
visitNonNullAssert(ast: e.NonNullAssert) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitNonNullAssert(ast, null);
|
|
|
|
}
|
|
|
|
visitPipe(ast: e.BindingPipe) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitPipe(ast, null);
|
|
|
|
}
|
|
|
|
visitPrefixNot(ast: e.PrefixNot) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitPrefixNot(ast, null);
|
|
|
|
}
|
|
|
|
visitPropertyRead(ast: e.PropertyRead) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitPropertyRead(ast, null);
|
|
|
|
}
|
|
|
|
visitPropertyWrite(ast: e.PropertyWrite) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitPropertyWrite(ast, null);
|
|
|
|
}
|
|
|
|
visitSafeMethodCall(ast: e.SafeMethodCall) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitSafeMethodCall(ast, null);
|
|
|
|
}
|
|
|
|
visitSafePropertyRead(ast: e.SafePropertyRead) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitSafePropertyRead(ast, null);
|
|
|
|
}
|
|
|
|
visitQuote(ast: e.Quote) {
|
|
|
|
this.recordAst(ast);
|
|
|
|
super.visitQuote(ast, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
visitNgContent(ast: t.NgContentAst) {}
|
|
|
|
visitEmbeddedTemplate(ast: t.EmbeddedTemplateAst) {
|
|
|
|
t.templateVisitAll(this, ast.attrs);
|
|
|
|
t.templateVisitAll(this, ast.children);
|
|
|
|
t.templateVisitAll(this, ast.directives);
|
|
|
|
t.templateVisitAll(this, ast.outputs);
|
|
|
|
t.templateVisitAll(this, ast.providers);
|
|
|
|
t.templateVisitAll(this, ast.references);
|
|
|
|
t.templateVisitAll(this, ast.variables);
|
|
|
|
}
|
|
|
|
visitElement(ast: t.ElementAst) {
|
|
|
|
t.templateVisitAll(this, ast.attrs);
|
|
|
|
t.templateVisitAll(this, ast.children);
|
|
|
|
t.templateVisitAll(this, ast.directives);
|
|
|
|
t.templateVisitAll(this, ast.inputs);
|
|
|
|
t.templateVisitAll(this, ast.outputs);
|
|
|
|
t.templateVisitAll(this, ast.providers);
|
|
|
|
t.templateVisitAll(this, ast.references);
|
|
|
|
}
|
|
|
|
visitReference(ast: t.ReferenceAst) {}
|
|
|
|
visitVariable(ast: t.VariableAst) {}
|
2020-04-08 13:14:18 -04:00
|
|
|
visitEvent(ast: t.BoundEventAst) {
|
|
|
|
ast.handler.visit(this);
|
|
|
|
}
|
|
|
|
visitElementProperty(ast: t.BoundElementPropertyAst) {
|
|
|
|
ast.value.visit(this);
|
|
|
|
}
|
test(compiler): add expression absolute span tests for `TemplateAst`s (#33253)
Previously, we had tested that expressions parsed in a Render3 AST
had correctly-defined absolute spans (spans relative to the entire
template, not the local expression). Sometimes we use Template ASTs
rather than Render3 ASTs, and it's desirable to test for correct
expression spans in the template parser as well.
Adding these tests resolved one bug, similar to the one fixed in
fd4fed14d8935866fe16d30648d115f3ebb5fd43, where expressions in the value
of a template attribute were not given an absolute span corresponding to
the start of the attribute name rather than the start of the attribute
value.
The diff on this commit is large, partially because it involves some
structural changes of the template parser testing layout. In particular,
the following is done:
1. Move `createMeta*`-like functions from `template_parser_spec.ts` to
be exported from a new test utility file.
2. Create an `ExpressionSourceHumanizer`, similar to the one created in
b04488d692ca11c83444da56f51d53c3ac868243, to allow convenient testing
of expressions' locations.
3. Create `template_parser_absolute_span_spec.ts`, testing the spans of
expressions parsed by the template parser. This is very similar to
the `r3_ast_absolute_span_spec`.
PR Close #33253
2019-10-18 10:51:20 -04:00
|
|
|
visitAttr(ast: t.AttrAst) {}
|
2020-04-08 13:14:18 -04:00
|
|
|
visitBoundText(ast: t.BoundTextAst) {
|
|
|
|
ast.value.visit(this);
|
|
|
|
}
|
test(compiler): add expression absolute span tests for `TemplateAst`s (#33253)
Previously, we had tested that expressions parsed in a Render3 AST
had correctly-defined absolute spans (spans relative to the entire
template, not the local expression). Sometimes we use Template ASTs
rather than Render3 ASTs, and it's desirable to test for correct
expression spans in the template parser as well.
Adding these tests resolved one bug, similar to the one fixed in
fd4fed14d8935866fe16d30648d115f3ebb5fd43, where expressions in the value
of a template attribute were not given an absolute span corresponding to
the start of the attribute name rather than the start of the attribute
value.
The diff on this commit is large, partially because it involves some
structural changes of the template parser testing layout. In particular,
the following is done:
1. Move `createMeta*`-like functions from `template_parser_spec.ts` to
be exported from a new test utility file.
2. Create an `ExpressionSourceHumanizer`, similar to the one created in
b04488d692ca11c83444da56f51d53c3ac868243, to allow convenient testing
of expressions' locations.
3. Create `template_parser_absolute_span_spec.ts`, testing the spans of
expressions parsed by the template parser. This is very similar to
the `r3_ast_absolute_span_spec`.
PR Close #33253
2019-10-18 10:51:20 -04:00
|
|
|
visitText(ast: t.TextAst) {}
|
|
|
|
visitDirective(ast: t.DirectiveAst) {
|
|
|
|
t.templateVisitAll(this, ast.hostEvents);
|
|
|
|
t.templateVisitAll(this, ast.hostProperties);
|
|
|
|
t.templateVisitAll(this, ast.inputs);
|
|
|
|
}
|
2020-04-08 13:14:18 -04:00
|
|
|
visitDirectiveProperty(ast: t.BoundDirectivePropertyAst) {
|
|
|
|
ast.value.visit(this);
|
|
|
|
}
|
test(compiler): add expression absolute span tests for `TemplateAst`s (#33253)
Previously, we had tested that expressions parsed in a Render3 AST
had correctly-defined absolute spans (spans relative to the entire
template, not the local expression). Sometimes we use Template ASTs
rather than Render3 ASTs, and it's desirable to test for correct
expression spans in the template parser as well.
Adding these tests resolved one bug, similar to the one fixed in
fd4fed14d8935866fe16d30648d115f3ebb5fd43, where expressions in the value
of a template attribute were not given an absolute span corresponding to
the start of the attribute name rather than the start of the attribute
value.
The diff on this commit is large, partially because it involves some
structural changes of the template parser testing layout. In particular,
the following is done:
1. Move `createMeta*`-like functions from `template_parser_spec.ts` to
be exported from a new test utility file.
2. Create an `ExpressionSourceHumanizer`, similar to the one created in
b04488d692ca11c83444da56f51d53c3ac868243, to allow convenient testing
of expressions' locations.
3. Create `template_parser_absolute_span_spec.ts`, testing the spans of
expressions parsed by the template parser. This is very similar to
the `r3_ast_absolute_span_spec`.
PR Close #33253
2019-10-18 10:51:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Humanizes expression AST source spans in a template by returning an array of tuples
|
|
|
|
* [unparsed AST, AST source span]
|
|
|
|
* for each expression in the template.
|
|
|
|
* @param templateAsts template AST to humanize
|
|
|
|
*/
|
|
|
|
export function humanizeExpressionSource(templateAsts: t.TemplateAst[]):
|
|
|
|
HumanizedExpressionSource[] {
|
|
|
|
const humanizer = new ExpressionSourceHumanizer();
|
|
|
|
t.templateVisitAll(humanizer, templateAsts);
|
|
|
|
return humanizer.result;
|
|
|
|
}
|