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
This commit is contained in:
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead, visitAstChildren} from '@angular/compiler';
|
||||
import {AST, AstVisitor, Binary, BindingPipe, Chain, Conditional, FunctionCall, ImplicitReceiver, Interpolation, KeyedRead, KeyedWrite, LiteralArray, LiteralMap, LiteralPrimitive, MethodCall, NonNullAssert, PrefixNot, PropertyRead, PropertyWrite, Quote, SafeMethodCall, SafePropertyRead} from '@angular/compiler';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
import {BuiltinType, Signature, Symbol, SymbolQuery, SymbolTable} from './symbols';
|
||||
@ -178,14 +178,18 @@ export class AstType implements AstVisitor {
|
||||
|
||||
visitChain(ast: Chain) {
|
||||
// If we are producing diagnostics, visit the children
|
||||
visitAstChildren(ast, this);
|
||||
for (const expr of ast.expressions) {
|
||||
expr.visit(this);
|
||||
}
|
||||
// The type of a chain is always undefined.
|
||||
return this.query.getBuiltinType(BuiltinType.Undefined);
|
||||
}
|
||||
|
||||
visitConditional(ast: Conditional) {
|
||||
// The type of a conditional is the union of the true and false conditions.
|
||||
visitAstChildren(ast, this);
|
||||
ast.condition.visit(this);
|
||||
ast.trueExp.visit(this);
|
||||
ast.falseExp.visit(this);
|
||||
return this.query.getTypeUnion(this.getType(ast.trueExp), this.getType(ast.falseExp));
|
||||
}
|
||||
|
||||
@ -235,7 +239,9 @@ export class AstType implements AstVisitor {
|
||||
|
||||
visitInterpolation(ast: Interpolation): Symbol {
|
||||
// If we are producing diagnostics, visit the children.
|
||||
visitAstChildren(ast, this);
|
||||
for (const expr of ast.expressions) {
|
||||
expr.visit(this);
|
||||
}
|
||||
return this.undefinedType;
|
||||
}
|
||||
|
||||
@ -260,7 +266,9 @@ export class AstType implements AstVisitor {
|
||||
|
||||
visitLiteralMap(ast: LiteralMap): Symbol {
|
||||
// If we are producing diagnostics, visit the children
|
||||
visitAstChildren(ast, this);
|
||||
for (const value of ast.values) {
|
||||
value.visit(this);
|
||||
}
|
||||
// TODO: Return a composite type.
|
||||
return this.anyType;
|
||||
}
|
||||
@ -312,7 +320,7 @@ export class AstType implements AstVisitor {
|
||||
|
||||
visitPrefixNot(ast: PrefixNot) {
|
||||
// If we are producing diagnostics, visit the children
|
||||
visitAstChildren(ast, this);
|
||||
ast.expression.visit(this);
|
||||
// The type of a prefix ! is always boolean.
|
||||
return this.query.getBuiltinType(BuiltinType.Boolean);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {AST, ASTWithSource, AstPath as AstPathBase, NullAstVisitor, visitAstChildren} from '@angular/compiler';
|
||||
import {AST, ASTWithSource, AstPath as AstPathBase, RecursiveAstVisitor} from '@angular/compiler';
|
||||
import {AstType} from './expression_type';
|
||||
|
||||
import {BuiltinType, Span, Symbol, SymbolQuery, SymbolTable} from './types';
|
||||
@ -16,12 +16,12 @@ type AstPath = AstPathBase<AST>;
|
||||
|
||||
function findAstAt(ast: AST, position: number, excludeEmpty: boolean = false): AstPath {
|
||||
const path: AST[] = [];
|
||||
const visitor = new class extends NullAstVisitor {
|
||||
const visitor = new class extends RecursiveAstVisitor {
|
||||
visit(ast: AST) {
|
||||
if ((!excludeEmpty || ast.sourceSpan.start < ast.sourceSpan.end) &&
|
||||
inSpan(position, ast.sourceSpan)) {
|
||||
path.push(ast);
|
||||
visitAstChildren(ast, this);
|
||||
ast.visit(this);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Reference in New Issue
Block a user