Include named super formal parameters into CREATE_CONSTRUCTOR_FOR_FINAL_FIELDS_REQUIRED_NAMED.
This is convenient feature when building hierarchies of data classes.
Change-Id: Id4c7040e0515150661165079ad01d9d05e0c1185
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/308541
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
index 406e801..1e6c3a8 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_constructor_for_final_fields.dart
@@ -8,7 +8,9 @@
import 'package:analysis_server/src/utilities/extensions/object.dart';
import 'package:analyzer/dart/analysis/features.dart';
import 'package:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
+import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart';
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
@@ -84,6 +86,7 @@
final fixContext = _FixContext(
builder: builder,
containerName: className,
+ superType: superType,
location: targetLocation,
variableLists: variableLists,
);
@@ -115,6 +118,7 @@
fixContext: _FixContext(
builder: builder,
containerName: enumName,
+ superType: null,
location: targetLocation,
variableLists: variableLists,
),
@@ -230,22 +234,40 @@
switch (_style) {
case _Style.requiredNamed:
builder.write('{');
- fields.forEachIndexed((index, field) {
- if (index > 0) {
+ var hasWritten = false;
+ final superNamed = fixContext.superNamed;
+ if (superNamed != null) {
+ for (final formalParameter in superNamed) {
+ if (hasWritten) {
+ builder.write(', ');
+ }
+ if (formalParameter.isRequiredNamed) {
+ builder.write('required ');
+ }
+ builder.write('super.');
+ builder.write(formalParameter.name);
+ hasWritten = true;
+ }
+ }
+ for (final field in fields) {
+ if (hasWritten) {
builder.write(', ');
}
builder.write('required this.');
builder.write(field.name);
- });
+ hasWritten = true;
+ }
builder.write('}');
case _Style.requiredPositional:
- fields.forEachIndexed((index, field) {
- if (index > 0) {
+ var hasWritten = false;
+ for (final field in fields) {
+ if (hasWritten) {
builder.write(', ');
}
builder.write('this.');
builder.write(field.name);
- });
+ hasWritten = true;
+ }
}
builder.write(');');
builder.write(location.suffix);
@@ -301,15 +323,27 @@
class _FixContext {
final ChangeBuilder builder;
final String containerName;
+ final InterfaceType? superType;
final InsertionLocation location;
final List<VariableDeclarationList> variableLists;
_FixContext({
required this.builder,
required this.containerName,
+ required this.superType,
required this.location,
required this.variableLists,
});
+
+ List<ParameterElement>? get superNamed {
+ final superConstructor = superType?.constructors.singleOrNull;
+ if (superConstructor != null) {
+ final superAll = superConstructor.parameters;
+ final superNamed = superAll.where((e) => e.isNamed).toList();
+ return superNamed.length == superAll.length ? superNamed : null;
+ }
+ return null;
+ }
}
enum _Style {
diff --git a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
index b83fd01..d01b995 100644
--- a/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
+++ b/pkg/analysis_server/test/src/services/correction/fix/create_constructor_for_final_fields_test.dart
@@ -44,6 +44,72 @@
''');
}
+ Future<void> test_class_hasSuperClass_withOptionalNamed() async {
+ await resolveTestCode('''
+class A {
+ final int? f11;
+ final int? f12;
+
+ A({this.f11, this.f12})
+}
+
+class B extends A {
+ final int f21;
+ final int f22;
+}
+''');
+ await assertHasFix('''
+class A {
+ final int? f11;
+ final int? f12;
+
+ A({this.f11, this.f12})
+}
+
+class B extends A {
+ final int f21;
+ final int f22;
+
+ B({super.f11, super.f12, required this.f21, required this.f22});
+}
+''', errorFilter: (error) {
+ return error.message.contains("'f21'");
+ });
+ }
+
+ Future<void> test_class_hasSuperClass_withRequiredNamed() async {
+ await resolveTestCode('''
+class A {
+ final int f11;
+ final int f12;
+
+ A({required this.f11, required this.f12})
+}
+
+class B extends A {
+ final int f21;
+ final int f22;
+}
+''');
+ await assertHasFix('''
+class A {
+ final int f11;
+ final int f12;
+
+ A({required this.f11, required this.f12})
+}
+
+class B extends A {
+ final int f21;
+ final int f22;
+
+ B({required super.f11, required super.f12, required this.f21, required this.f22});
+}
+''', errorFilter: (error) {
+ return error.message.contains("'f21'");
+ });
+ }
+
Future<void> test_class_lint_sortConstructorsFirst() async {
createAnalysisOptionsFile(lints: [LintNames.sort_constructors_first]);
await resolveTestCode('''