Add a fix for initialize_in_field_declaration
The correction producer implements a fix. Pending discussion we might choose to extend it to produce an assist, either in addition to the fix (if we keep the lint) or instead of a fix (if we drop the lint). Whatever we choose, I'd like to do that in a separate CL because I want this to be available for testing purposes as soon as possible. Change-Id: Icdd17ba0130149fc785c196be69e9ef990529800 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/503520 Reviewed-by: Keerti Parthasarathy <keertip@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
This commit is contained in:
committed by
dart-scoped@luci-project-accounts.iam.gserviceaccount.com
parent
f35be6757d
commit
a667cde7c1
+99
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// 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:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
|
||||
import 'package:analyzer/dart/ast/ast.dart';
|
||||
import 'package:analyzer/dart/element/element.dart';
|
||||
import 'package:analyzer/src/utilities/extensions/ast.dart';
|
||||
import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart';
|
||||
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
|
||||
import 'package:analyzer_plugin/utilities/range_factory.dart';
|
||||
|
||||
class MoveInitializationToFieldDeclaration extends ResolvedCorrectionProducer {
|
||||
MoveInitializationToFieldDeclaration({required super.context});
|
||||
|
||||
@override
|
||||
CorrectionApplicability get applicability =>
|
||||
CorrectionApplicability.automatically;
|
||||
|
||||
@override
|
||||
FixKind get fixKind => DartFixKind.moveInitializationToFieldDeclaration;
|
||||
|
||||
@override
|
||||
FixKind get multiFixKind =>
|
||||
DartFixKind.moveInitializationToFieldDeclarationMulti;
|
||||
|
||||
@override
|
||||
Future<void> compute(ChangeBuilder builder) async {
|
||||
var node = this.node;
|
||||
if (node is! SimpleIdentifier) {
|
||||
return;
|
||||
}
|
||||
var initializer = node.parent;
|
||||
if (initializer is! ConstructorFieldInitializer) {
|
||||
return;
|
||||
}
|
||||
var field = node.element;
|
||||
if (field is! FieldElement) {
|
||||
return;
|
||||
}
|
||||
var constructorDeclaration = initializer.parent;
|
||||
if (constructorDeclaration is! ClassMember) {
|
||||
return;
|
||||
}
|
||||
var initializers = switch (constructorDeclaration) {
|
||||
ConstructorDeclaration() => constructorDeclaration.initializers,
|
||||
PrimaryConstructorBody() => constructorDeclaration.initializers,
|
||||
_ => null,
|
||||
};
|
||||
if (initializers == null) {
|
||||
return;
|
||||
}
|
||||
var fieldDeclaration = findFieldDeclaration(field, constructorDeclaration);
|
||||
if (fieldDeclaration == null ||
|
||||
fieldDeclaration.equals != null ||
|
||||
fieldDeclaration.initializer != null) {
|
||||
return;
|
||||
}
|
||||
var expression = utils.getRangeText(initializer.expression.sourceRange);
|
||||
|
||||
await builder.addDartFileEdit(file, (builder) {
|
||||
builder.addDeletion(range.nodeInList(initializers, initializer));
|
||||
builder.addSimpleInsertion(fieldDeclaration.end, ' = $expression');
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns the declaration of the [field].
|
||||
///
|
||||
/// Uses the [initializer] to find the container containing the field's
|
||||
/// declaration.
|
||||
///
|
||||
/// Returns `null` if the declaration can't be found.
|
||||
VariableDeclaration? findFieldDeclaration(
|
||||
FieldElement field,
|
||||
ClassMember constructorDeclaration,
|
||||
) {
|
||||
// TODO(brianwilkerson): When support for augmentations is added, this will
|
||||
// need to look at all of the fragments of the container in order to find
|
||||
// the declaration.
|
||||
var container = constructorDeclaration.parent;
|
||||
var members = switch (container) {
|
||||
ClassBody() => container.members,
|
||||
EnumBody() => container.members,
|
||||
_ => null,
|
||||
};
|
||||
if (members == null) return null;
|
||||
for (var member in members) {
|
||||
if (member is FieldDeclaration) {
|
||||
for (var fieldDeclarartion in member.fields.variables) {
|
||||
if (fieldDeclarartion.declaredFieldElement == field) {
|
||||
return fieldDeclarartion;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -2165,7 +2165,7 @@ implicit_call_tearoffs:
|
||||
implicit_reopen:
|
||||
status: hasFix
|
||||
initialize_in_field_declaration:
|
||||
status: needsFix
|
||||
status: hasFix
|
||||
invalid_case_patterns:
|
||||
status: hasFix
|
||||
invalid_runtime_check_with_js_interop_types_catch_clause_js_interop_type:
|
||||
|
||||
@@ -1243,6 +1243,16 @@ abstract final class DartFixKind {
|
||||
DartFixKindPriority.standard,
|
||||
'Move this doc comment to a library directive',
|
||||
);
|
||||
static const moveInitializationToFieldDeclaration = FixKind(
|
||||
'dart.fix.moveInitializationToFieldDeclaration',
|
||||
DartFixKindPriority.standard,
|
||||
"Move the initialization to the field's declaration",
|
||||
);
|
||||
static const moveInitializationToFieldDeclarationMulti = FixKind(
|
||||
'dart.fix.moveInitializationToFieldDeclaration.multi',
|
||||
DartFixKindPriority.standard,
|
||||
"Move initialization to the field's declaration everywhere",
|
||||
);
|
||||
static const moveTypeArgumentsToClass = FixKind(
|
||||
'dart.fix.moveTypeArgumentsToClass',
|
||||
DartFixKindPriority.standard,
|
||||
|
||||
@@ -131,6 +131,7 @@ import 'package:analysis_server/src/services/correction/dart/make_variable_nulla
|
||||
import 'package:analysis_server/src/services/correction/dart/merge_combinators.dart';
|
||||
import 'package:analysis_server/src/services/correction/dart/move_annotation_to_library_directive.dart';
|
||||
import 'package:analysis_server/src/services/correction/dart/move_doc_comment_to_library_directive.dart';
|
||||
import 'package:analysis_server/src/services/correction/dart/move_initialization_to_field_declaration.dart';
|
||||
import 'package:analysis_server/src/services/correction/dart/move_type_arguments_to_class.dart';
|
||||
import 'package:analysis_server/src/services/correction/dart/organize_imports.dart';
|
||||
import 'package:analysis_server/src/services/correction/dart/qualify_reference.dart';
|
||||
@@ -341,6 +342,7 @@ final _builtInLintGenerators = <DiagnosticCode, List<ProducerGenerator>>{
|
||||
diag.hashAndEquals: [CreateMethod.equalityOrHashCode],
|
||||
diag.implicitCallTearoffs: [AddExplicitCall.new],
|
||||
diag.implicitReopen: [AddReopen.new],
|
||||
diag.initializeInFieldDeclaration: [MoveInitializationToFieldDeclaration.new],
|
||||
diag.invalidCasePatterns: [AddConst.new],
|
||||
diag.leadingNewlinesInMultilineStrings: [AddLeadingNewlineToString.new],
|
||||
diag.libraryAnnotations: [MoveAnnotationToLibraryDirective.new],
|
||||
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
||||
// 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:analysis_server/src/services/correction/fix.dart';
|
||||
import 'package:analyzer_plugin/utilities/fixes/fixes.dart';
|
||||
import 'package:test_reflective_loader/test_reflective_loader.dart';
|
||||
|
||||
import 'fix_processor.dart';
|
||||
|
||||
void main() {
|
||||
defineReflectiveSuite(() {
|
||||
defineReflectiveTests(MoveInitializationToFieldDeclarationTest);
|
||||
});
|
||||
}
|
||||
|
||||
@reflectiveTest
|
||||
class MoveInitializationToFieldDeclarationTest extends FixProcessorLintTest {
|
||||
@override
|
||||
FixKind get kind => DartFixKind.moveInitializationToFieldDeclaration;
|
||||
|
||||
@override
|
||||
String get lintCode => LintNames.initialize_in_field_declaration;
|
||||
|
||||
Future<void> test_class_multipleFields() async {
|
||||
await resolveTestCode('''
|
||||
class C(int x) {
|
||||
this : y = x, z = 0;
|
||||
|
||||
int y, z;
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
class C(int x) {
|
||||
this : z = 0;
|
||||
|
||||
int y = x, z;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_class_multipleInitializers_first() async {
|
||||
await resolveTestCode('''
|
||||
class C(int x) {
|
||||
this : y = x, z = 0;
|
||||
|
||||
int y;
|
||||
int z;
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
class C(int x) {
|
||||
this : z = 0;
|
||||
|
||||
int y = x;
|
||||
int z;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_class_multipleInitializers_last() async {
|
||||
await resolveTestCode('''
|
||||
class C(int x) {
|
||||
this : y = 0, z = x;
|
||||
|
||||
int y;
|
||||
int z;
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
class C(int x) {
|
||||
this : y = 0;
|
||||
|
||||
int y;
|
||||
int z = x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
Future<void> test_class_singleInitializer() async {
|
||||
await resolveTestCode('''
|
||||
class C(int x) {
|
||||
this : y = x;
|
||||
|
||||
int y;
|
||||
}
|
||||
''');
|
||||
await assertHasFix('''
|
||||
class C(int x) {
|
||||
this;
|
||||
|
||||
int y = x;
|
||||
}
|
||||
''');
|
||||
}
|
||||
}
|
||||
@@ -180,6 +180,8 @@ import 'move_annotation_to_library_directive_test.dart'
|
||||
as move_annotation_to_library_directive;
|
||||
import 'move_doc_comment_to_library_directive_test.dart'
|
||||
as move_doc_comment_to_library_directive;
|
||||
import 'move_initialization_to_field_declaration_test.dart'
|
||||
as move_initialization_to_field_declaration;
|
||||
import 'move_type_arguments_to_class_test.dart' as move_type_arguments_to_class;
|
||||
import 'organize_imports_test.dart' as organize_imports;
|
||||
import 'pubspec/test_all.dart' as pubspec;
|
||||
@@ -485,6 +487,7 @@ void main() {
|
||||
merge_combinators.main();
|
||||
move_annotation_to_library_directive.main();
|
||||
move_doc_comment_to_library_directive.main();
|
||||
move_initialization_to_field_declaration.main();
|
||||
move_type_arguments_to_class.main();
|
||||
organize_imports.main();
|
||||
pubspec.main();
|
||||
|
||||
Reference in New Issue
Block a user