Skip to content

Commit cba9543

Browse files
scheglovCommit Queue
authored and
Commit Queue
committed
QuickFix. Issue 55805. Create extension setter.
Bug: #55805 Change-Id: I0972eee46a1b0ab8e14235e9a56fd08949f634f8 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/369522 Commit-Queue: Konstantin Shcheglov <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent b981930 commit cba9543

File tree

4 files changed

+276
-0
lines changed

4 files changed

+276
-0
lines changed

pkg/analysis_server/lib/src/services/correction/dart/create_extension_member.dart

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,85 @@ class CreateExtensionMethod extends _CreateExtensionMember {
171171
}
172172
}
173173

174+
class CreateExtensionSetter extends _CreateExtensionMember {
175+
String _setterName = '';
176+
177+
CreateExtensionSetter({
178+
required super.context,
179+
});
180+
181+
@override
182+
List<String> get fixArguments => [_setterName];
183+
184+
@override
185+
FixKind get fixKind => DartFixKind.CREATE_EXTENSION_SETTER;
186+
187+
@override
188+
Future<void> compute(ChangeBuilder builder) async {
189+
var nameNode = node;
190+
if (nameNode is! SimpleIdentifier) {
191+
return;
192+
}
193+
if (!nameNode.inSetterContext()) {
194+
return;
195+
}
196+
197+
_setterName = nameNode.name;
198+
199+
// prepare target
200+
Expression? target;
201+
switch (nameNode.parent) {
202+
case PrefixedIdentifier prefixedIdentifier:
203+
if (prefixedIdentifier.identifier == nameNode) {
204+
target = prefixedIdentifier.prefix;
205+
}
206+
case PropertyAccess propertyAccess:
207+
if (propertyAccess.propertyName == nameNode) {
208+
target = propertyAccess.realTarget;
209+
}
210+
}
211+
if (target == null) {
212+
return;
213+
}
214+
215+
// We need the type for the extension.
216+
var targetType = target.staticType;
217+
if (targetType == null ||
218+
targetType is DynamicType ||
219+
targetType is InvalidType) {
220+
return;
221+
}
222+
223+
// Try to find the type of the field.
224+
var fieldTypeNode = climbPropertyAccess(nameNode);
225+
var fieldType = inferUndefinedExpressionType(fieldTypeNode);
226+
227+
void writeSetter(DartEditBuilder builder) {
228+
builder.writeSetterDeclaration(
229+
_setterName,
230+
nameGroupName: 'NAME',
231+
parameterType: fieldType,
232+
parameterTypeGroupName: 'TYPE',
233+
);
234+
}
235+
236+
var updatedExisting = await _updateExistingExtension(
237+
builder,
238+
targetType,
239+
(extension, builder) {
240+
builder.insertGetter(extension, (builder) {
241+
writeSetter(builder);
242+
});
243+
},
244+
);
245+
if (updatedExisting) {
246+
return;
247+
}
248+
249+
await _addNewExtension(builder, targetType, nameNode, writeSetter);
250+
}
251+
}
252+
174253
abstract class _CreateExtensionMember extends ResolvedCorrectionProducer {
175254
_CreateExtensionMember({
176255
required super.context,

pkg/analysis_server/lib/src/services/correction/fix.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,11 @@ class DartFixKind {
691691
DartFixKindPriority.DEFAULT - 20,
692692
"Create extension method '{0}'",
693693
);
694+
static const CREATE_EXTENSION_SETTER = FixKind(
695+
'dart.fix.create.extension.setter',
696+
DartFixKindPriority.DEFAULT - 20,
697+
"Create extension setter '{0}'",
698+
);
694699
static const CREATE_FIELD = FixKind(
695700
'dart.fix.create.field',
696701
49,

pkg/analysis_server/lib/src/services/correction/fix_internal.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1297,6 +1297,7 @@ final _builtInNonLintProducers = {
12971297
],
12981298
CompileTimeErrorCode.UNDEFINED_SETTER: [
12991299
ChangeTo.getterOrSetter,
1300+
CreateExtensionSetter.new,
13001301
CreateField.new,
13011302
CreateSetter.new,
13021303
],

pkg/analysis_server/test/src/services/correction/fix/create_extension_member_test.dart

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ void main() {
1212
defineReflectiveSuite(() {
1313
defineReflectiveTests(CreateExtensionGetterTest);
1414
defineReflectiveTests(CreateExtensionMethodTest);
15+
defineReflectiveTests(CreateExtensionSetterTest);
1516
});
1617
}
1718

@@ -488,3 +489,193 @@ extension on List<int> {
488489
''');
489490
}
490491
}
492+
493+
@reflectiveTest
494+
class CreateExtensionSetterTest extends FixProcessorTest {
495+
@override
496+
FixKind get kind => DartFixKind.CREATE_EXTENSION_SETTER;
497+
498+
Future<void> test_existingExtension() async {
499+
await resolveTestCode('''
500+
void f() {
501+
''.test = 0;
502+
}
503+
504+
extension on String {}
505+
''');
506+
await assertHasFix('''
507+
void f() {
508+
''.test = 0;
509+
}
510+
511+
extension on String {
512+
set test(int test) {}
513+
}
514+
''');
515+
}
516+
517+
Future<void> test_existingExtension_generic_matching() async {
518+
await resolveTestCode('''
519+
void f(List<int> a) {
520+
a.test = 0;
521+
}
522+
523+
extension E<T> on Iterable<T> {}
524+
''');
525+
await assertHasFix('''
526+
void f(List<int> a) {
527+
a.test = 0;
528+
}
529+
530+
extension E<T> on Iterable<T> {
531+
set test(int test) {}
532+
}
533+
''');
534+
}
535+
536+
Future<void> test_existingExtension_generic_notMatching() async {
537+
await resolveTestCode('''
538+
void f(List<int> a) {
539+
a.test = 0;
540+
}
541+
542+
extension E<K, V> on Map<K, V> {}
543+
''');
544+
await assertHasFix('''
545+
void f(List<int> a) {
546+
a.test = 0;
547+
}
548+
549+
extension on List<int> {
550+
set test(int test) {}
551+
}
552+
553+
extension E<K, V> on Map<K, V> {}
554+
''');
555+
}
556+
557+
Future<void> test_existingExtension_hasMethod() async {
558+
await resolveTestCode('''
559+
void f() {
560+
''.test = 0;
561+
}
562+
563+
extension E on String {
564+
// ignore:unused_element
565+
void foo() {}
566+
}
567+
''');
568+
await assertHasFix('''
569+
void f() {
570+
''.test = 0;
571+
}
572+
573+
extension E on String {
574+
set test(int test) {}
575+
576+
// ignore:unused_element
577+
void foo() {}
578+
}
579+
''');
580+
}
581+
582+
Future<void> test_existingExtension_notGeneric_matching() async {
583+
await resolveTestCode('''
584+
void f() {
585+
''.test = 0;
586+
}
587+
588+
extension on String {}
589+
''');
590+
await assertHasFix('''
591+
void f() {
592+
''.test = 0;
593+
}
594+
595+
extension on String {
596+
set test(int test) {}
597+
}
598+
''');
599+
}
600+
601+
Future<void> test_existingExtension_notGeneric_notMatching() async {
602+
await resolveTestCode('''
603+
void f() {
604+
''.test = 0;
605+
}
606+
607+
extension on int {}
608+
''');
609+
await assertHasFix('''
610+
void f() {
611+
''.test = 0;
612+
}
613+
614+
extension on String {
615+
set test(int test) {}
616+
}
617+
618+
extension on int {}
619+
''');
620+
}
621+
622+
Future<void> test_parent_nothing() async {
623+
await resolveTestCode('''
624+
void f() {
625+
test = 0;
626+
}
627+
''');
628+
await assertNoFix();
629+
}
630+
631+
Future<void> test_parent_prefixedIdentifier() async {
632+
await resolveTestCode('''
633+
void f(String a) {
634+
a.test = 0;
635+
}
636+
''');
637+
await assertHasFix('''
638+
void f(String a) {
639+
a.test = 0;
640+
}
641+
642+
extension on String {
643+
set test(int test) {}
644+
}
645+
''');
646+
}
647+
648+
Future<void> test_parent_propertyAccess_cascade() async {
649+
await resolveTestCode('''
650+
void f(String a) {
651+
a..test = 0;
652+
}
653+
''');
654+
await assertHasFix('''
655+
void f(String a) {
656+
a..test = 0;
657+
}
658+
659+
extension on String {
660+
set test(int test) {}
661+
}
662+
''');
663+
}
664+
665+
Future<void> test_targetType_hasTypeArguments() async {
666+
await resolveTestCode('''
667+
void f(List<int> a) {
668+
a.test = 0;
669+
}
670+
''');
671+
await assertHasFix('''
672+
void f(List<int> a) {
673+
a.test = 0;
674+
}
675+
676+
extension on List<int> {
677+
set test(int test) {}
678+
}
679+
''');
680+
}
681+
}

0 commit comments

Comments
 (0)