Skip to content

Commit b7ce35f

Browse files
committed
fix(migrations): migrating input with more than 1 usage in a method
When the migration command was run for inputs, if the input had more than one reference in a method the migration would generate incorrect code Fixes #63018
1 parent e34776a commit b7ce35f

File tree

3 files changed

+87
-1
lines changed

3 files changed

+87
-1
lines changed

packages/core/schematics/migrations/signal-migration/src/passes/reference_migration/helpers/standard_reference.ts

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {analyzeControlFlow, ControlFlowAnalysisNode} from '../../../flow_analysi
1111
import {ProgramInfo, projectFile, Replacement, TextUpdate} from '../../../../../../utils/tsurge';
1212
import {traverseAccess} from '../../../utils/traverse_access';
1313
import {UniqueNamesGenerator} from '../../../utils/unique_names';
14-
import {createNewBlockToInsertVariable} from '../helpers/create_block_arrow_function';
14+
import {createNewBlockToInsertVariable} from './create_block_arrow_function';
1515
import assert from 'assert';
1616

1717
export interface NarrowableTsReferences {
@@ -123,6 +123,23 @@ export function migrateStandardTsReference(
123123
replacements.push(
124124
...createNewBlockToInsertVariable(parent, filePath, temporaryVariableStr),
125125
);
126+
} else if (shouldInsertAtMethodStart(reference, recommendedNode, referenceNodeInBlock)) {
127+
const blockNode = recommendedNode as ts.Block;
128+
const firstStatement = blockNode.statements[0];
129+
const leadingSpace = firstStatement
130+
? ts.getLineAndCharacterOfPosition(sf, firstStatement.getStart())
131+
: ts.getLineAndCharacterOfPosition(sf, referenceNodeInBlock.getStart());
132+
133+
replacements.push(
134+
new Replacement(
135+
filePath,
136+
new TextUpdate({
137+
position: firstStatement.getStart(),
138+
end: firstStatement.getStart(),
139+
toInsert: `${temporaryVariableStr}\n${' '.repeat(leadingSpace.character)}`,
140+
}),
141+
),
142+
);
126143
} else {
127144
const leadingSpace = ts.getLineAndCharacterOfPosition(sf, referenceNodeInBlock.getStart());
128145

@@ -151,3 +168,43 @@ export function migrateStandardTsReference(
151168
}
152169
}
153170
}
171+
172+
/**
173+
* Determines if a temporary variable should be inserted at the start of a method.
174+
*
175+
* This function performs several checks to ensure it's safe to insert a temporary variable:
176+
* 1. Verifies the recommended node is a method declaration block
177+
* 2. Ensures all references are contained within the method body
178+
* 3. Confirms the reference node is the first statement in the method
179+
* 4. Validates the reference node is an expression statement with an assignment operation
180+
*/
181+
function shouldInsertAtMethodStart(
182+
references: NarrowableTsReferences,
183+
recommendedNode: ts.Node,
184+
referenceNodeInBlock: ts.Node,
185+
): boolean {
186+
if (!ts.isBlock(recommendedNode) || !ts.isMethodDeclaration(recommendedNode.parent)) {
187+
return false;
188+
}
189+
190+
const methodBody = recommendedNode;
191+
const allReferencesInMethod = references.accesses.every((access) => {
192+
let current: ts.Node | undefined = access;
193+
while (current && current !== methodBody) {
194+
current = current.parent;
195+
}
196+
return current === methodBody;
197+
});
198+
199+
if (!allReferencesInMethod) {
200+
return false;
201+
}
202+
203+
return (
204+
methodBody.statements.length > 0 &&
205+
ts.isExpressionStatement(referenceNodeInBlock) &&
206+
methodBody.statements[0] === referenceNodeInBlock &&
207+
ts.isBinaryExpression(referenceNodeInBlock.expression) &&
208+
referenceNodeInBlock.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken
209+
);
210+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// tslint:disable
2+
3+
import {Input} from '@angular/core';
4+
5+
export class TestMigrationComponent {
6+
@Input() public model: any;
7+
8+
public onSaveClick(): void {
9+
this.model.requisitionId = 145;
10+
this.model.comment = 'value';
11+
}
12+
}

packages/core/schematics/migrations/signal-migration/test/golden.txt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,3 +1509,20 @@ export class TestCmp {
15091509
fixture = this.notMutated();
15101510
}
15111511
}
1512+
1513+
1514+
@@@@@@ multiple_references_in_method.ts @@@@@@
1515+
1516+
// tslint:disable
1517+
1518+
import {input} from '@angular/core';
1519+
1520+
export class TestMigrationComponent {
1521+
public readonly model = input<any>();
1522+
1523+
public onSaveClick(): void {
1524+
const model = this.model();
1525+
model.requisitionId = 145;
1526+
model.comment = 'value';
1527+
}
1528+
}

0 commit comments

Comments
 (0)