From 4b01d6b8035883f218cd6f2674b5491d011cefc3 Mon Sep 17 00:00:00 2001 From: Danny Tuppeny Date: Fri, 10 Apr 2026 09:05:11 -0700 Subject: [PATCH] [analysis_server] In "Encapsulate Field", update references to renamed parameters in initializers Fixes https://github.com/dart-lang/sdk/issues/63145 Change-Id: I6dd672e5ae189a023419d2c41157fa7cdc43e46b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/494141 Reviewed-by: Brian Wilkerson Reviewed-by: Keerti Parthasarathy Commit-Queue: Keerti Parthasarathy --- .../correction/dart/encapsulate_field.dart | 37 ++++++++++ .../assist/encapsulate_field_test.dart | 67 +++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart b/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart index ded17b73eae..e8fb180ee99 100644 --- a/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart +++ b/pkg/analysis_server/lib/src/services/correction/dart/encapsulate_field.dart @@ -7,6 +7,7 @@ import 'package:analysis_server/src/utilities/extensions/ast.dart'; import 'package:analysis_server_plugin/edit/dart/correction_producer.dart'; import 'package:analyzer/dart/analysis/features.dart'; import 'package:analyzer/dart/ast/token.dart'; +import 'package:analyzer/dart/ast/visitor.dart'; import 'package:analyzer/dart/element/element.dart'; import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/ast/extensions.dart'; @@ -305,6 +306,10 @@ class EncapsulateField extends ResolvedCorrectionProducer { required Token? separator, required NodeList? initializers, }) { + // If we rename a parameter, keep track of it so we can update any + // references in initializers. + FieldFormalParameterElement? renamedParameterElement; + // Update any field formal parameter that refers to the field. for (var parameter in parameters.parameters) { var identifier = parameter.name; @@ -340,6 +345,7 @@ class EncapsulateField extends ResolvedCorrectionProducer { } else { // Rename the parameter. builder.addSimpleReplacement(range.token(identifier), '_$name'); + renamedParameterElement = parameterElement; } // Change `final` to `var` in declaring parameters. @@ -361,6 +367,17 @@ class EncapsulateField extends ResolvedCorrectionProducer { '_$name', ); } + + // Update any references to the renamed parameter in initializers. + if (renamedParameterElement != null) { + initializer.accept( + _InitializerReferenceUpdater( + builder, + renamedParameterElement, + '_$name', + ), + ); + } } } } @@ -402,3 +419,23 @@ class EncapsulateField extends ResolvedCorrectionProducer { } } } + +/// Updates references to [parameterElement] to use [newName]. +class _InitializerReferenceUpdater extends RecursiveAstVisitor { + final DartFileEditBuilder builder; + final FieldFormalParameterElement parameterElement; + final String newName; + + _InitializerReferenceUpdater( + this.builder, + this.parameterElement, + this.newName, + ); + + @override + void visitSimpleIdentifier(SimpleIdentifier node) { + if (node.element == parameterElement) { + builder.addSimpleReplacement(range.token(node.token), newName); + } + } +} diff --git a/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart b/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart index 0a1f0b168cb..4d180203dc8 100644 --- a/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart +++ b/pkg/analysis_server/test/src/services/correction/assist/encapsulate_field_test.dart @@ -335,6 +335,73 @@ void f(A a) { '''); } + Future test_initializer_reference() async { + await resolveTestCode(''' +class Foo { + final int ^a; + final int b; + + Foo(this.a) : b = a + 1 {} +} +'''); + await assertHasAssist(''' +class Foo { + int _a; + + int get a => _a; + final int b; + + Foo(this._a) : b = _a + 1 {} +} +'''); + } + + Future test_initializer_reference_noPrivateNamedParameters() async { + await resolveTestCode(''' +// @dart=3.10 + +class Foo { + final int ^a; + final int b; + + Foo({required this.a}) : b = a + 1 {} +} +'''); + await assertHasAssist(''' +// @dart=3.10 + +class Foo { + int _a; + + int get a => _a; + final int b; + + Foo({required int a}) : _a = a, b = a + 1 {} +} +'''); + } + + Future test_initializer_reference_privateNamedParameters() async { + await resolveTestCode(''' +class Foo { + final int ^a; + final int b; + + Foo({required this.a}) : b = a + 1 {} +} +'''); + await assertHasAssist(''' +class Foo { + int _a; + + int get a => _a; + final int b; + + Foo({required this._a}) : b = _a + 1 {} +} +'''); + } + Future test_mixin_hasType() async { await resolveTestCode(''' mixin M {