Skip to content

Commit 2419e97

Browse files
FMorschelCommit Queue
authored andcommitted
[DAS] Fixes create method, create getter and create mixin fixes
Fixes: #60826 Change-Id: I16cb1db16bcb8d7e57cb92723f058302f347e40e Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/432580 Reviewed-by: Brian Wilkerson <[email protected]> Auto-Submit: Felipe Morschel <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]> Reviewed-by: Samuel Rawlins <[email protected]>
1 parent 3cc6b8f commit 2419e97

File tree

12 files changed

+927
-198
lines changed

12 files changed

+927
-198
lines changed

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

Lines changed: 183 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@
44

55
import 'package:analysis_server/src/services/correction/fix.dart';
66
import 'package:analysis_server/src/services/correction/util.dart';
7+
import 'package:analysis_server/src/utilities/extensions/object.dart';
78
import 'package:analysis_server_plugin/edit/dart/correction_producer.dart';
9+
import 'package:analyzer/dart/analysis/results.dart';
810
import 'package:analyzer/dart/ast/token.dart';
911
import 'package:analyzer/dart/element/element.dart';
12+
import 'package:analyzer/dart/element/nullability_suffix.dart';
1013
import 'package:analyzer/dart/element/type.dart';
1114
import 'package:analyzer/src/dart/ast/ast.dart';
1215
import 'package:analyzer/src/dart/element/type.dart';
@@ -42,6 +45,7 @@ class CreateExtensionGetter extends _CreateExtensionMember {
4245

4346
// prepare target
4447
DartType? targetType;
48+
ExtensionElement? extensionElement;
4549
switch (nameNode.parent) {
4650
case PrefixedIdentifier prefixedIdentifier:
4751
if (prefixedIdentifier.identifier == nameNode) {
@@ -50,11 +54,19 @@ class CreateExtensionGetter extends _CreateExtensionMember {
5054
case PropertyAccess propertyAccess:
5155
if (propertyAccess.propertyName == nameNode) {
5256
targetType = propertyAccess.realTarget.staticType;
57+
if (propertyAccess.realTarget case ExtensionOverride(
58+
:var element,
59+
) when targetType == null) {
60+
extensionElement = element;
61+
targetType = extensionElement.thisType;
62+
}
5363
}
5464
case ExpressionFunctionBody expressionFunctionBody:
5565
if (expressionFunctionBody.expression == nameNode) {
5666
targetType = node.enclosingInstanceElement?.thisType;
5767
}
68+
default:
69+
targetType = nameNode.enclosingInstanceElement?.thisType;
5870
}
5971

6072
// TODO(FMorschel): We should take into account if the target type contains
@@ -71,6 +83,9 @@ class CreateExtensionGetter extends _CreateExtensionMember {
7183
var fieldType = inferUndefinedExpressionType(fieldTypeNode);
7284

7385
void writeGetter(DartEditBuilder builder) {
86+
if (inStaticContext) {
87+
builder.write('static ');
88+
}
7489
if (fieldType != null) {
7590
builder.writeType(fieldType, methodBeingCopied: methodBeingCopied);
7691
builder.write(' ');
@@ -82,14 +97,27 @@ class CreateExtensionGetter extends _CreateExtensionMember {
8297
builder.write(';');
8398
}
8499

85-
var updatedExisting = await _updateExistingExtension(builder, targetType, (
86-
extension,
87-
builder,
88-
) {
89-
builder.insertGetter(extension, (builder) {
90-
writeGetter(builder);
100+
bool updatedExisting;
101+
if (extensionElement != null) {
102+
updatedExisting = await _updateExistingExtension2(
103+
builder,
104+
extensionElement,
105+
(extension, builder) {
106+
builder.insertGetter(extension, (builder) {
107+
writeGetter(builder);
108+
});
109+
},
110+
);
111+
} else {
112+
updatedExisting = await _updateExistingExtension(builder, targetType, (
113+
extension,
114+
builder,
115+
) {
116+
builder.insertGetter(extension, (builder) {
117+
writeGetter(builder);
118+
});
91119
});
92-
});
120+
}
93121
if (updatedExisting) {
94122
return;
95123
}
@@ -117,39 +145,82 @@ class CreateExtensionMethod extends _CreateExtensionMember {
117145

118146
@override
119147
Future<void> compute(ChangeBuilder builder) async {
148+
var static = false;
120149
var nameNode = node;
121150
if (nameNode is! SimpleIdentifier) {
122151
return;
123152
}
124153

125-
var invocation = nameNode.parent;
126-
if (invocation is! MethodInvocation) {
127-
return;
128-
}
129-
if (invocation.methodName != nameNode) {
154+
var parent = nameNode.parent;
155+
var isInvocation = true;
156+
MethodInvocation? invocation;
157+
Expression? target;
158+
if (parent is! MethodInvocation) {
159+
isInvocation = false;
160+
target = switch (parent) {
161+
PropertyAccess(:var realTarget) => realTarget,
162+
PrefixedIdentifier(:var prefix) => prefix,
163+
_ => null,
164+
};
165+
} else if (parent.methodName == nameNode) {
166+
invocation = parent;
167+
target = invocation.realTarget;
168+
} else {
130169
return;
131170
}
132171
_methodName = nameNode.name;
133172

134-
var target = invocation.realTarget;
135-
if (target == null) {
136-
return;
173+
DartType? targetType;
174+
ExtensionElement? extensionElement;
175+
if (target is ExtensionOverride) {
176+
targetType = target.extendedType;
177+
extensionElement = target.element;
178+
} else if (target == null) {
179+
extensionElement = node.enclosingInstanceElement?.ifTypeOrNull();
180+
targetType = extensionElement?.thisType;
181+
} else {
182+
// We need the type for the extension.
183+
targetType = target.staticType;
184+
}
185+
if (targetType == null && target is SimpleIdentifier) {
186+
extensionElement = target.element?.ifTypeOrNull();
187+
targetType = extensionElement?.thisType;
188+
static = true;
137189
}
138-
139-
// We need the type for the extension.
140-
var targetType = target.staticType;
141190
if (targetType == null ||
142191
targetType is DynamicType ||
143192
targetType is InvalidType) {
144193
return;
145194
}
146195

147196
// Try to find the return type.
148-
var returnType = inferUndefinedExpressionType(invocation);
197+
DartType? returnType;
198+
if (invocation ?? parent case Expression exp) {
199+
returnType = inferUndefinedExpressionType(exp);
200+
}
201+
202+
if (returnType is InterfaceType && returnType.isDartCoreFunction) {
203+
returnType = FunctionTypeImpl(
204+
typeParameters: const [],
205+
parameters: const [],
206+
returnType: DynamicTypeImpl.instance,
207+
nullabilitySuffix: NullabilitySuffix.none,
208+
);
209+
}
210+
211+
if (returnType is! FunctionType && !isInvocation) {
212+
return;
213+
}
214+
215+
var functionType = !isInvocation ? returnType as FunctionType : null;
149216

150217
void writeMethod(DartEditBuilder builder) {
218+
if (static) {
219+
builder.write('static ');
220+
}
221+
151222
if (builder.writeType(
152-
returnType,
223+
isInvocation ? returnType : functionType?.returnType,
153224
groupName: 'RETURN_TYPE',
154225
methodBeingCopied: methodBeingCopied,
155226
)) {
@@ -161,30 +232,53 @@ class CreateExtensionMethod extends _CreateExtensionMember {
161232
});
162233

163234
builder.writeTypeParameters(
164-
[
165-
returnType,
166-
...invocation.argumentList.arguments.map((e) => e.staticType),
167-
].typeParameters
235+
([
236+
isInvocation ? returnType : functionType?.returnType,
237+
...?functionType?.formalParameters.map((e) => e.type),
238+
...?invocation?.argumentList.arguments.map((e) => e.staticType),
239+
].typeParameters
240+
..addAll([...?functionType?.typeParameters]))
168241
.whereNot([targetType].typeParameters.contains)
169242
.toList(),
170243
);
171244

172-
builder.write('(');
173-
builder.writeParametersMatchingArguments(
174-
invocation.argumentList,
175-
methodBeingCopied: methodBeingCopied,
176-
);
177-
builder.write(') {}');
245+
if (invocation?.argumentList case var arguments?) {
246+
builder.write('(');
247+
builder.writeParametersMatchingArguments(
248+
arguments,
249+
methodBeingCopied: methodBeingCopied,
250+
);
251+
builder.write(')');
252+
} else if (functionType != null) {
253+
builder.writeFormalParameters(
254+
functionType.formalParameters,
255+
methodBeingCopied: methodBeingCopied,
256+
);
257+
}
258+
builder.write(' {}');
178259
}
179260

180-
var updatedExisting = await _updateExistingExtension(builder, targetType, (
181-
extension,
182-
builder,
183-
) {
184-
builder.insertMethod(extension, (builder) {
185-
writeMethod(builder);
261+
bool updatedExisting;
262+
if (extensionElement != null) {
263+
updatedExisting = await _updateExistingExtension2(
264+
builder,
265+
extensionElement,
266+
(extension, builder) {
267+
builder.insertMethod(extension, (builder) {
268+
writeMethod(builder);
269+
});
270+
},
271+
);
272+
} else {
273+
updatedExisting = await _updateExistingExtension(builder, targetType, (
274+
extension,
275+
builder,
276+
) {
277+
builder.insertMethod(extension, (builder) {
278+
writeMethod(builder);
279+
});
186280
});
187-
});
281+
}
188282
if (updatedExisting) {
189283
return;
190284
}
@@ -471,22 +565,45 @@ abstract class _CreateExtensionMember extends ResolvedCorrectionProducer {
471565
}
472566

473567
ExtensionDeclaration? _existingExtension(DartType targetType) {
474-
for (var existingExtension in unitResult.unit.declarations) {
475-
if (existingExtension is ExtensionDeclaration) {
476-
var element = existingExtension.declaredFragment!.element;
477-
var instantiated = [element].applicableTo(
478-
targetLibrary: libraryElement2,
479-
targetType: targetType as TypeImpl,
480-
strictCasts: true,
481-
);
482-
if (instantiated.isNotEmpty) {
483-
return existingExtension;
484-
}
568+
for (var existingExtension
569+
in unitResult.unit.declarations.whereType<ExtensionDeclaration>()) {
570+
var extendedType =
571+
existingExtension.declaredFragment!.element.extendedType;
572+
if (extendedType == targetType) {
573+
return existingExtension;
485574
}
486575
}
487576
return null;
488577
}
489578

579+
Future<(String, ExtensionDeclaration)?> _existingExtension2(
580+
ExtensionElement extension,
581+
) async {
582+
var library = extension.library;
583+
if (library.isInSdk) {
584+
return null;
585+
}
586+
var path = library.library.firstFragment.source.fullName;
587+
var unit = await unitResult.session.getResolvedUnit(path);
588+
if (unit is! ResolvedUnitResult) {
589+
return null;
590+
}
591+
var existingExtension = unit.unit.declarations
592+
.whereType<ExtensionDeclaration>()
593+
.firstWhere(
594+
(declaration) => declaration.declaredFragment!.element == extension,
595+
);
596+
var instantiated = [extension].applicableTo(
597+
targetLibrary: libraryElement2,
598+
targetType: extension.thisType as TypeImpl,
599+
strictCasts: true,
600+
);
601+
if (instantiated.isNotEmpty) {
602+
return (path, existingExtension);
603+
}
604+
return null;
605+
}
606+
490607
Future<bool> _updateExistingExtension(
491608
ChangeBuilder builder,
492609
DartType targetType,
@@ -503,6 +620,24 @@ abstract class _CreateExtensionMember extends ResolvedCorrectionProducer {
503620
});
504621
return true;
505622
}
623+
624+
Future<bool> _updateExistingExtension2(
625+
ChangeBuilder builder,
626+
ExtensionElement extensionElement,
627+
void Function(ExtensionDeclaration existing, DartFileEditBuilder builder)
628+
write,
629+
) async {
630+
var record = await _existingExtension2(extensionElement);
631+
if (record == null) {
632+
return false;
633+
}
634+
var (file, extension) = record;
635+
636+
await builder.addDartFileEdit(file, (builder) {
637+
write(extension, builder);
638+
});
639+
return true;
640+
}
506641
}
507642

508643
extension on List<DartType?> {

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,11 @@ class CreateGetter extends CreateFieldOrGetter {
164164
} else {
165165
staticModifier = inStaticContext;
166166
targetElement = nameNode.enclosingInstanceElement;
167-
if (targetElement is ExtensionElement && !staticModifier) {
167+
if (targetElement is ExtensionElement) {
168+
if (staticModifier) {
169+
// This should be handled by create extension member fixes
170+
return;
171+
}
168172
targetElement = targetElement.extendedInterfaceElement;
169173
}
170174
if (targetElement == null) {

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

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,22 +100,11 @@ class CreateMethod extends ResolvedCorrectionProducer {
100100
CompilationUnitMember? targetNode;
101101
var target = invocation.realTarget;
102102
if (target is ExtensionOverride) {
103-
targetFragment = target.element.firstFragment;
104-
if (targetFragment is ExtensionFragment) {
105-
targetNode = await getExtensionDeclaration(targetFragment);
106-
if (targetNode == null) {
107-
return;
108-
}
109-
}
103+
// This case should be handled by the "Add extension method" quick fix
104+
return;
110105
} else if (target is Identifier && target.element is ExtensionElement) {
111-
targetFragment = (target.element as ExtensionElement).firstFragment;
112-
if (targetFragment is ExtensionFragment) {
113-
targetNode = await getExtensionDeclaration(targetFragment);
114-
if (targetNode == null) {
115-
return;
116-
}
117-
}
118-
staticModifier = true;
106+
// This case should be handled by the "Add extension method" quick fix
107+
return;
119108
} else if (target == null) {
120109
targetFragment = unit.declaredFragment;
121110
var enclosingMember = node.thisOrAncestorOfType<ClassMember>();
@@ -125,9 +114,18 @@ class CreateMethod extends ResolvedCorrectionProducer {
125114
return;
126115
}
127116
var enclosingMemberParent = enclosingMember.parent;
128-
if (enclosingMemberParent is CompilationUnitMember) {
117+
if (enclosingMemberParent is CompilationUnitMember &&
118+
enclosingMemberParent is! ExtensionDeclaration) {
129119
targetNode = enclosingMemberParent;
130-
staticModifier = inStaticContext;
120+
staticModifier = switch (enclosingMember) {
121+
ConstructorDeclaration(:var factoryKeyword) => factoryKeyword != null,
122+
MethodDeclaration(:var isStatic) => isStatic,
123+
FieldDeclaration(
124+
:var isStatic,
125+
fields: VariableDeclarationList(:var isLate),
126+
) =>
127+
isStatic || !isLate,
128+
};
131129
}
132130
} else {
133131
var targetClassElement = getTargetInterfaceElement(target);

0 commit comments

Comments
 (0)