mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
Currently the compiler has three different classes to represent a "call to something": 1. `MethodCall` - `foo.bar()` 2. `SafeMethodCall` - `foo?.bar()`. 3. `FunctionCall` - Any calls that don't fit into the first two classes. E.g. `foo.bar()()`. There are a few problems with this approach: 1. It is inconistent with the TypeScript AST which only has one node: `CallExpression`. 2. It means that we have to maintain more code, because the various parts of the compiler need to know about three node types. 3. It doesn't allow us to easily implement some new JS features like safe calls (e.g. `foo.bar?.())`). These changes rework the compiler so that it produces only one node: `Call`. The new node behaves similarly to the TypeScript `CallExpression` whose `receiver` can be any expression. There was a similar situation in the output AST where we had an `InvokeMethodExpression` and `InvokeFunctionExpression`. I've combined both of them into `InvokeFunctionExpression`. PR Close #42882
43 lines
1.5 KiB
TypeScript
43 lines
1.5 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google LLC 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 {AST, Lexer, Parser, RecursiveAstVisitor} from '@angular/compiler';
|
|
import {Call, ImplicitReceiver, PropertyRead} from '@angular/compiler/src/compiler';
|
|
|
|
describe('RecursiveAstVisitor', () => {
|
|
it('should visit every node', () => {
|
|
const parser = new Parser(new Lexer());
|
|
const ast = parser.parseBinding('x.y()', '', 0 /* absoluteOffset */);
|
|
const visitor = new Visitor();
|
|
const path: AST[] = [];
|
|
visitor.visit(ast.ast, path);
|
|
// If the visitor method of RecursiveAstVisitor is implemented correctly,
|
|
// then we should have collected the full path from root to leaf.
|
|
expect(path.length).toBe(4);
|
|
const [call, yRead, xRead, implicitReceiver] = path;
|
|
expectType(call, Call);
|
|
expectType(yRead, PropertyRead);
|
|
expectType(xRead, PropertyRead);
|
|
expectType(implicitReceiver, ImplicitReceiver);
|
|
expect(xRead.name).toBe('x');
|
|
expect(yRead.name).toBe('y');
|
|
expect(call.args).toEqual([]);
|
|
});
|
|
});
|
|
|
|
class Visitor extends RecursiveAstVisitor {
|
|
override visit(node: AST, path: AST[]) {
|
|
path.push(node);
|
|
node.visit(this, path);
|
|
}
|
|
}
|
|
|
|
type Newable = new (...args: any) => any;
|
|
function expectType<T extends Newable>(val: any, t: T): asserts val is InstanceType<T> {
|
|
expect(val instanceof t).toBe(true, `expect ${val.constructor.name} to be ${t.name}`);
|
|
}
|