Handle private named parameters in "Extract Widget".

When creating the constructor parameters for the widget, if the field
is private, prefer to use a private named parameter unless it collides
with another public field.

Bug: https://github.com/dart-lang/sdk/issues/61644
Change-Id: I2776e4a350f5034e33bd80f4686e4164e8b0bda8
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/479740
Auto-Submit: Bob Nystrom <rnystrom@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Robert Nystrom
2026-02-11 07:19:00 -08:00
committed by Commit Queue
parent 6566f89c89
commit bb13a0e609
2 changed files with 67 additions and 8 deletions
@@ -2,6 +2,7 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:_fe_analyzer_shared/src/scanner/token_impl.dart';
import 'package:analysis_server/src/protocol_server.dart' hide Element;
import 'package:analysis_server/src/services/correction/status.dart';
import 'package:analysis_server/src/services/refactoring/legacy/naming_conventions.dart';
@@ -347,18 +348,24 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl
}
}
// Give each private parameter a public name for the constructor.
// Give each private parameter a non-colliding public name if needed.
for (var parameter in _parameters) {
var name = parameter.name;
if (name.startsWith('_')) {
// If the library supports private named parameters, we only need to
// rename if the public name collides.
var baseName = name.substring(1);
for (var i = 1; ; i++) {
name = i == 1 ? baseName : '$baseName$i';
if (usedNames.add(name)) {
break;
if (!_featureSet.isEnabled(Feature.private_named_parameters) ||
!usedNames.add(baseName)) {
for (var i = 1; ; i++) {
name = i == 1 ? baseName : '$baseName$i';
if (usedNames.add(name)) {
break;
}
}
}
}
parameter.constructorName = name;
}
@@ -589,7 +596,10 @@ class ExtractWidgetRefactoringImpl extends RefactoringImpl
if (parameter != _parameters.first) {
builder.write(', ');
}
builder.write(parameter.constructorName);
builder.write(
correspondingPublicName(parameter.constructorName) ??
parameter.constructorName,
);
builder.write(': ');
builder.write(parameter.name);
}
@@ -1034,8 +1034,8 @@ class MyWidget extends StatelessWidget {
class Test extends StatelessWidget {
const Test({
super.key,
required String field,
}) : _field = field;
required this._field,
});
final String _field;
@@ -1098,6 +1098,55 @@ class Test extends StatelessWidget {
''');
}
Future<void> test_parameters_private_unsupported() async {
await indexTestUnit(r'''
// @dart=3.10
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
final String _field;
MyWidget(this._field);
@override
Widget build(BuildContext context) {
return ^Text(_field);
}
}
''');
_createRefactoring();
await _assertSuccessfulRefactoring('''
// @dart=3.10
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
final String _field;
MyWidget(this._field);
@override
Widget build(BuildContext context) {
return Test(field: _field);
}
}
class Test extends StatelessWidget {
const Test({
super.key,
required String field,
}) : _field = field;
final String _field;
@override
Widget build(BuildContext context) {
return Text(_field);
}
}
''');
}
Future<void> test_parameters_readField_readLocal() async {
await indexTestUnit(r'''
import 'package:flutter/material.dart';