Support converting a declaring param to an initializing param

Server already supports the opposite, so this just makes it possible to
go both ways.

This doesn't support converting a declaring function typed parameter.
We could add that, but I'm hoping it's rare enough to not be worth doing.

Other than that, I think I've covered all the cases, but please pay
special attention to the test coverage.

Change-Id: I400204ca820d0e0f1cc44075dd54946db79b2115
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/506740
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
Brian Wilkerson
2026-05-27 14:39:09 -07:00
committed by dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent 8b66930364
commit 8b555128be
2 changed files with 58 additions and 1 deletions
@@ -7,6 +7,7 @@ import 'package:analysis_server/src/services/correction/fix.dart';
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/src/dart/ast/ast.dart';
import 'package:analyzer_plugin/utilities/assist/assist.dart';
@@ -336,6 +337,32 @@ class ConvertToInitializingFormal extends ResolvedCorrectionProducer {
field,
assignment: statement,
);
} else if (parameter is RegularFormalParameter &&
parameterElement is FieldFormalParameterElement) {
// This is a declaring parameter in a primary constructor.
var keyword = parameter.constFinalOrVarKeyword;
if (keyword == null) return;
var parameterName = parameter.name;
if (parameterName == null) return;
var container = node.thisOrAncestorOfType<CompilationUnitMember>();
if (container == null) return;
if (parameter.functionTypedSuffix != null) return;
await builder.addDartFileEdit(file, (builder) {
var type = parameter.type;
// Remove the `var` or `final` keyword, and add `this.`.
builder.addSimpleReplacement(
range.startStart(keyword, parameterName),
'this.',
);
// Add the field.
builder.insertField(container, (builder) {
builder.writeFieldDeclaration(
parameterName.lexeme,
isFinal: keyword.keyword == Keyword.FINAL,
type: type?.type,
);
});
});
}
}
@@ -279,7 +279,37 @@ class A {
''');
}
Future<void> test_parameterDeclaration_primary() async {
Future<void> test_parameterDeclaration_primary_declaring_final() async {
await resolveTestCode('''
class C(final int ^test);
''');
await assertHasAssist('''
class C(this.test) {
final int test;
}
''');
}
Future<void>
test_parameterDeclaration_primary_declaring_functionTyped() async {
await resolveTestCode('''
class C(var int ^test(String));
''');
await assertNoAssist();
}
Future<void> test_parameterDeclaration_primary_declaring_var() async {
await resolveTestCode('''
class C(var int ^test);
''');
await assertHasAssist('''
class C(this.test) {
int test;
}
''');
}
Future<void> test_parameterDeclaration_primary_regular() async {
await resolveTestCode('''
class C(int ^test) {
int test;