mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
refactor(migrations): provide AST path for resolved template and host references (#57947)
This allows us to detect usages in templates and figure out if they are e.g. invoking a problematic method like `.pipe` for the output migration, or in queries `.toArray` etc. PR Close #57947
This commit is contained in:
parent
af66e2475d
commit
313dfb7e99
4 changed files with 35 additions and 16 deletions
|
|
@ -162,6 +162,7 @@ export function identifyHostBindingReferences<D extends ClassFieldDescriptor>(
|
|||
kind: ReferenceKind.InHostBinding,
|
||||
from: {
|
||||
read: ref.read,
|
||||
readAstPath: ref.readAstPath,
|
||||
isObjectShorthandExpression: ref.isObjectShorthandExpression,
|
||||
isWrite: ref.isWrite,
|
||||
file: projectFile(ref.context.getSourceFile(), programInfo),
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ export function identifyTemplateReferences<D extends ClassFieldDescriptor>(
|
|||
kind: ReferenceKind.InTemplate,
|
||||
from: {
|
||||
read: res.read,
|
||||
readAstPath: res.readAstPath,
|
||||
node: res.context,
|
||||
isObjectShorthandExpression: res.isObjectShorthandExpression,
|
||||
originatingTsFile: projectFile(node.getSourceFile(), programInfo),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
import ts from 'typescript';
|
||||
import {PropertyRead, TmplAstNode} from '@angular/compiler';
|
||||
import {AST, PropertyRead, TmplAstNode} from '@angular/compiler';
|
||||
import {ProjectFile} from '../../../../../utils/tsurge';
|
||||
import {ClassFieldDescriptor} from './known_fields';
|
||||
|
||||
|
|
@ -40,6 +40,11 @@ export interface TemplateReference<D extends ClassFieldDescriptor> {
|
|||
node: TmplAstNode;
|
||||
/** Expression AST node that represents the reference. */
|
||||
read: PropertyRead;
|
||||
/**
|
||||
* Expression AST sequentially visited to reach the given read.
|
||||
* This follows top-down down ordering. The last element is the actual read.
|
||||
*/
|
||||
readAstPath: AST[];
|
||||
/** Whether the reference is part of an object shorthand expression. */
|
||||
isObjectShorthandExpression: boolean;
|
||||
/** Whether this reference is part of a likely-narrowing expression. */
|
||||
|
|
@ -62,6 +67,11 @@ export interface HostBindingReference<D extends ClassFieldDescriptor> {
|
|||
hostPropertyNode: ts.Node;
|
||||
/** Expression AST node that represents the reference. */
|
||||
read: PropertyRead;
|
||||
/**
|
||||
* Expression AST sequentially visited to reach the given read.
|
||||
* This follows top-down down ordering. The last element is the actual read.
|
||||
*/
|
||||
readAstPath: AST[];
|
||||
/** Whether the reference is part of an object shorthand expression. */
|
||||
isObjectShorthandExpression: boolean;
|
||||
/** Whether the reference is a write. E.g. an event assignment. */
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ export interface TmplInputExpressionReference<ExprContext, D extends ClassFieldD
|
|||
targetNode: ts.Node;
|
||||
targetField: D;
|
||||
read: PropertyRead;
|
||||
readAstPath: AST[];
|
||||
context: ExprContext;
|
||||
isObjectShorthandExpression: boolean;
|
||||
isLikelyNarrowed: boolean;
|
||||
|
|
@ -236,7 +237,6 @@ export class TemplateExpressionReferenceVisitor<
|
|||
> extends RecursiveAstVisitor {
|
||||
private activeTmplAstNode: ExprContext | null = null;
|
||||
private detectedInputReferences: TmplInputExpressionReference<ExprContext, D>[] = [];
|
||||
|
||||
private isInsideObjectShorthandExpression = false;
|
||||
|
||||
constructor(
|
||||
|
|
@ -256,10 +256,14 @@ export class TemplateExpressionReferenceVisitor<
|
|||
this.detectedInputReferences = [];
|
||||
this.activeTmplAstNode = activeNode;
|
||||
|
||||
expressionNode.visit(this);
|
||||
expressionNode.visit(this, []);
|
||||
return this.detectedInputReferences;
|
||||
}
|
||||
|
||||
override visit(ast: AST, context: AST[]) {
|
||||
super.visit(ast, [...context, ast]);
|
||||
}
|
||||
|
||||
// Keep track when we are inside an object shorthand expression. This is
|
||||
// necessary as we need to expand the shorthand to invoke a potential new signal.
|
||||
// E.g. `{bla}` may be transformed to `{bla: bla()}`.
|
||||
|
|
@ -269,35 +273,34 @@ export class TemplateExpressionReferenceVisitor<
|
|||
(ast.values[idx] as AST).visit(this, context);
|
||||
this.isInsideObjectShorthandExpression = false;
|
||||
}
|
||||
super.visitLiteralMap(ast, context);
|
||||
}
|
||||
|
||||
override visitPropertyRead(ast: PropertyRead) {
|
||||
this._inspectPropertyAccess(ast);
|
||||
super.visitPropertyRead(ast, null);
|
||||
override visitPropertyRead(ast: PropertyRead, context: AST[]) {
|
||||
this._inspectPropertyAccess(ast, context);
|
||||
super.visitPropertyRead(ast, context);
|
||||
}
|
||||
override visitSafePropertyRead(ast: SafePropertyRead) {
|
||||
this._inspectPropertyAccess(ast);
|
||||
super.visitPropertyRead(ast, null);
|
||||
override visitSafePropertyRead(ast: SafePropertyRead, context: AST[]) {
|
||||
this._inspectPropertyAccess(ast, context);
|
||||
super.visitPropertyRead(ast, context);
|
||||
}
|
||||
|
||||
override visitPropertyWrite(ast: PropertyWrite) {
|
||||
this._inspectPropertyAccess(ast);
|
||||
super.visitPropertyWrite(ast, null);
|
||||
override visitPropertyWrite(ast: PropertyWrite, context: AST[]) {
|
||||
this._inspectPropertyAccess(ast, context);
|
||||
super.visitPropertyWrite(ast, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspects the property access and attempts to resolve whether they access
|
||||
* a known field. If so, the result is captured.
|
||||
*/
|
||||
private _inspectPropertyAccess(ast: PropertyRead | PropertyWrite) {
|
||||
private _inspectPropertyAccess(ast: PropertyRead | PropertyWrite, astPath: AST[]) {
|
||||
const isWrite = !!(
|
||||
ast instanceof PropertyWrite ||
|
||||
(this.activeTmplAstNode && isTwoWayBindingNode(this.activeTmplAstNode))
|
||||
);
|
||||
|
||||
this._checkAccessViaTemplateTypeCheckBlock(ast, isWrite) ||
|
||||
this._checkAccessViaOwningComponentClassType(ast, isWrite);
|
||||
this._checkAccessViaTemplateTypeCheckBlock(ast, isWrite, astPath) ||
|
||||
this._checkAccessViaOwningComponentClassType(ast, isWrite, astPath);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -307,6 +310,7 @@ export class TemplateExpressionReferenceVisitor<
|
|||
private _checkAccessViaTemplateTypeCheckBlock(
|
||||
ast: PropertyRead | PropertyWrite,
|
||||
isWrite: boolean,
|
||||
astPath: AST[],
|
||||
): boolean {
|
||||
// There might be no template type checker. E.g. if we check host bindings.
|
||||
if (this.templateTypeChecker === null) {
|
||||
|
|
@ -331,6 +335,7 @@ export class TemplateExpressionReferenceVisitor<
|
|||
targetNode: targetInput.node,
|
||||
targetField: targetInput,
|
||||
read: ast,
|
||||
readAstPath: astPath,
|
||||
context: this.activeTmplAstNode!,
|
||||
isLikelyNarrowed: false,
|
||||
isObjectShorthandExpression: this.isInsideObjectShorthandExpression,
|
||||
|
|
@ -350,6 +355,7 @@ export class TemplateExpressionReferenceVisitor<
|
|||
private _checkAccessViaOwningComponentClassType(
|
||||
ast: PropertyRead | PropertyWrite,
|
||||
isWrite: boolean,
|
||||
astPath: AST[],
|
||||
): void {
|
||||
// We might check host bindings, which can never point to template variables or local refs.
|
||||
const expressionTemplateTarget =
|
||||
|
|
@ -382,6 +388,7 @@ export class TemplateExpressionReferenceVisitor<
|
|||
targetNode: matchingTarget.node,
|
||||
targetField: matchingTarget,
|
||||
read: ast,
|
||||
readAstPath: astPath,
|
||||
context: this.activeTmplAstNode!,
|
||||
isLikelyNarrowed: false,
|
||||
isObjectShorthandExpression: this.isInsideObjectShorthandExpression,
|
||||
|
|
|
|||
Loading…
Reference in a new issue