diff --git a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_host_references.ts b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_host_references.ts index 17e93c5c7b1..8486425fc0b 100644 --- a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_host_references.ts +++ b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_host_references.ts @@ -162,6 +162,7 @@ export function identifyHostBindingReferences( kind: ReferenceKind.InHostBinding, from: { read: ref.read, + readAstPath: ref.readAstPath, isObjectShorthandExpression: ref.isObjectShorthandExpression, isWrite: ref.isWrite, file: projectFile(ref.context.getSourceFile(), programInfo), diff --git a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_template_references.ts b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_template_references.ts index e4aa3713b03..d593efcd66b 100644 --- a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_template_references.ts +++ b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/identify_template_references.ts @@ -78,6 +78,7 @@ export function identifyTemplateReferences( kind: ReferenceKind.InTemplate, from: { read: res.read, + readAstPath: res.readAstPath, node: res.context, isObjectShorthandExpression: res.isObjectShorthandExpression, originatingTsFile: projectFile(node.getSourceFile(), programInfo), diff --git a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/reference_kinds.ts b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/reference_kinds.ts index b19c87f32d4..c830e01bc8b 100644 --- a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/reference_kinds.ts +++ b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/reference_kinds.ts @@ -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 { 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 { 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. */ diff --git a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/template_reference_visitor.ts b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/template_reference_visitor.ts index e4547f97230..1a05b756c52 100644 --- a/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/template_reference_visitor.ts +++ b/packages/core/schematics/migrations/signal-migration/src/passes/reference_resolution/template_reference_visitor.ts @@ -45,6 +45,7 @@ export interface TmplInputExpressionReference extends RecursiveAstVisitor { private activeTmplAstNode: ExprContext | null = null; private detectedInputReferences: TmplInputExpressionReference[] = []; - 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,