44
55import 'package:analysis_server/src/services/correction/fix.dart' ;
66import 'package:analysis_server/src/services/correction/util.dart' ;
7+ import 'package:analysis_server/src/utilities/extensions/object.dart' ;
78import 'package:analysis_server_plugin/edit/dart/correction_producer.dart' ;
9+ import 'package:analyzer/dart/analysis/results.dart' ;
810import 'package:analyzer/dart/ast/token.dart' ;
911import 'package:analyzer/dart/element/element.dart' ;
12+ import 'package:analyzer/dart/element/nullability_suffix.dart' ;
1013import 'package:analyzer/dart/element/type.dart' ;
1114import 'package:analyzer/src/dart/ast/ast.dart' ;
1215import '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
508643extension on List <DartType ?> {
0 commit comments