DAS: Make CreateMethodOrFunction.fixKind final

With this change, CreateMethodOrFunction complies with the contract
that `fixKind` not change before/after `compute()` is called.

This change includes some tidying to simplify the code:

* The decision of whether to use `DartFixKind.CREATE_METHOD` or
  `DartFixKind.CREATE_FUNCTION` is solely based on whether the
  "target element" is an InterfaceElement. So in the new factory
  constructor, we compute the "target element", and save that in a
  final field, as well as the fixKind.
* `isStatic` was computed eagerly with other variables in a nest of
  else-if bodies. But it is only needed when creating a _method_, and
  so it's value can be computed nearer to where it is used.
* `compute()` was ~100 lines long, and a lot of that code was dedicated
  to computing the parameter type. Intermediate values like `argument`
  and `parameterElement` sort of clutter the local variables; all of
  that code can be moved to a function that just computes the
  parameter type.

Change-Id: Ibd5d96b752e7e03e52f585bb594c34331b08cdf1
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/436861
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Samuel Rawlins <srawlins@google.com>
diff --git a/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart b/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart
index 9f6c550..a751b0a 100644
--- a/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart
+++ b/pkg/analysis_server/lib/src/services/correction/dart/create_method_or_function.dart
@@ -17,11 +17,57 @@
 import 'package:analyzer_plugin/utilities/range_factory.dart';
 
 class CreateMethodOrFunction extends ResolvedCorrectionProducer {
-  FixKind _fixKind = DartFixKind.CREATE_METHOD;
+  @override
+  final FixKind fixKind;
 
   String _functionName = '';
 
-  CreateMethodOrFunction({required super.context});
+  /// The [Element] for the target of the node's parent, if that is a
+  /// [PrefixedIdentifier] or [PropertyAccess], and `null` otherwise.
+  final Element? _targetElement;
+
+  factory CreateMethodOrFunction({required CorrectionProducerContext context}) {
+    if (context is StubCorrectionProducerContext) {
+      return CreateMethodOrFunction._(context: context);
+    }
+
+    if (context.node case SimpleIdentifier node) {
+      // Prepare the argument expression (to get the parameter).
+      Element? targetElement;
+      var target = getQualifiedPropertyTarget(node);
+      if (target == null) {
+        targetElement = node.enclosingInterfaceElement;
+      } else {
+        var targetType = target.staticType;
+        if (targetType is InterfaceType) {
+          targetElement = targetType.element;
+        } else {
+          targetElement = switch (target) {
+            SimpleIdentifier() => target.element,
+            PrefixedIdentifier() => target.identifier.element,
+            _ => null,
+          };
+        }
+      }
+
+      return CreateMethodOrFunction._(
+        context: context,
+        targetElement: targetElement,
+        fixKind:
+            targetElement is InterfaceElement
+                ? DartFixKind.CREATE_METHOD
+                : DartFixKind.CREATE_FUNCTION,
+      );
+    }
+
+    return CreateMethodOrFunction._(context: context);
+  }
+
+  CreateMethodOrFunction._({
+    required super.context,
+    Element? targetElement,
+    this.fixKind = DartFixKind.CREATE_METHOD,
+  }) : _targetElement = targetElement;
 
   @override
   CorrectionApplicability get applicability =>
@@ -32,102 +78,26 @@
   List<String> get fixArguments => [_functionName];
 
   @override
-  FixKind get fixKind => _fixKind;
-
-  @override
   Future<void> compute(ChangeBuilder builder) async {
-    var isStatic = false;
-    var nameNode = node;
-    if (nameNode is SimpleIdentifier) {
-      // prepare argument expression (to get parameter)
-      InterfaceElement? targetElement;
-      Expression argument;
+    if (node case SimpleIdentifier node) {
       var target = getQualifiedPropertyTarget(node);
-      if (target != null) {
-        var targetType = target.staticType;
-        if (targetType is InterfaceType) {
-          targetElement = targetType.element;
-          argument = target.parent as Expression;
-        } else if (target case SimpleIdentifier(
-          :InterfaceElement? element,
-          :Expression parent,
-        )) {
-          isStatic = true;
-          targetElement = element;
-          argument = parent;
-        } else if (target
-            case SimpleIdentifier identifier ||
-                PrefixedIdentifier(:var identifier)) {
-          if (identifier.element case InterfaceElement element) {
-            isStatic = true;
-            targetElement = element;
-            argument = target.parent as Expression;
-          } else {
-            return;
-          }
-        } else {
-          return;
-        }
-      } else {
-        targetElement = node.enclosingInterfaceElement;
-        argument = nameNode;
-      }
-      argument = stepUpNamedExpression(argument);
-      // should be argument of some invocation
-      // or child of an expression that is one
-      var parameterElement = argument.correspondingParameter;
-      int? recordFieldIndex;
-      if (argument.parent case ConditionalExpression parent) {
-        if (argument == parent.condition) {
-          return;
-        }
-        parameterElement = parent.correspondingParameter;
-      } else if (argument.parent case RecordLiteral record) {
-        parameterElement = record.correspondingParameter;
-        for (var (index, field)
-            in record.fields.whereNotType<NamedExpression>().indexed) {
-          if (field == argument) {
-            recordFieldIndex = index;
-            break;
-          }
-        }
-      }
-      if (parameterElement == null) {
-        return;
-      }
-      // should be parameter of function type
-      var parameterType = parameterElement.type;
-      if (parameterType is RecordType) {
-        // Finds the corresponding field for argument
-        if (argument is NamedExpression) {
-          var fieldName = argument.name.label.name;
-          for (var field in parameterType.namedFields) {
-            if (field.name == fieldName) {
-              parameterType = field.type;
-              break;
-            }
-          }
-        } else if (recordFieldIndex != null) {
-          var field = parameterType.positionalFields[recordFieldIndex];
-          parameterType = field.type;
-        }
-      }
-      if (parameterType is InterfaceType && parameterType.isDartCoreFunction) {
-        parameterType = FunctionTypeImpl(
-          typeFormals: const [],
-          parameters: const [],
-          returnType: DynamicTypeImpl.instance,
-          nullabilitySuffix: NullabilitySuffix.none,
-        );
-      }
+
+      // In order to fix this by creating a function or a method,
+      // `parameterType` should be be a function type.
+      var parameterType = _computeParameterType(node, target);
       if (parameterType is! FunctionType) {
         return;
       }
-      // add proposal
-      if (targetElement != null) {
+
+      if (_targetElement is InterfaceElement) {
+        var isStatic =
+            (target is SimpleIdentifier &&
+                target.element is InterfaceElement) ||
+            (target is PrefixedIdentifier &&
+                target.identifier.element is InterfaceElement);
         await _createMethod(
           builder,
-          targetElement,
+          _targetElement,
           parameterType,
           isStatic: isStatic,
         );
@@ -137,6 +107,69 @@
     }
   }
 
+  DartType? _computeParameterType(SimpleIdentifier node, Expression? target) {
+    // `argument` should be an argument of some invocation or child of an
+    // expression that is one.
+    Expression argument;
+    if (target == null) {
+      argument = node;
+    } else {
+      var targetParent = target.parent;
+      if (targetParent is! Expression) {
+        return null;
+      }
+      argument = targetParent;
+    }
+    argument = stepUpNamedExpression(argument);
+
+    var parameterElement = argument.correspondingParameter;
+    int? recordFieldIndex;
+    if (argument.parent case ConditionalExpression parent) {
+      if (argument == parent.condition) {
+        return null;
+      }
+      parameterElement = parent.correspondingParameter;
+    } else if (argument.parent case RecordLiteral record) {
+      parameterElement = record.correspondingParameter;
+      for (var (index, field)
+          in record.fields.whereNotType<NamedExpression>().indexed) {
+        if (field == argument) {
+          recordFieldIndex = index;
+          break;
+        }
+      }
+    }
+    if (parameterElement == null) {
+      return null;
+    }
+
+    var parameterType = parameterElement.type;
+    if (parameterType is RecordType) {
+      // Finds the corresponding field for argument
+      if (argument is NamedExpression) {
+        var fieldName = argument.name.label.name;
+        for (var field in parameterType.namedFields) {
+          if (field.name == fieldName) {
+            parameterType = field.type;
+            break;
+          }
+        }
+      } else if (recordFieldIndex != null) {
+        var field = parameterType.positionalFields[recordFieldIndex];
+        parameterType = field.type;
+      }
+    }
+    if (parameterType is InterfaceType && parameterType.isDartCoreFunction) {
+      return FunctionTypeImpl(
+        typeFormals: const [],
+        parameters: const [],
+        returnType: DynamicTypeImpl.instance,
+        nullabilitySuffix: NullabilitySuffix.none,
+      );
+    }
+    return parameterType;
+  }
+
   /// Prepares proposal for creating function corresponding to the given
   /// [FunctionType].
   Future<void> _createExecutable(
@@ -211,7 +244,6 @@
       sourcePrefix,
       sourceSuffix,
     );
-    _fixKind = DartFixKind.CREATE_FUNCTION;
     _functionName = name;
   }
 
@@ -270,7 +302,6 @@
       sourcePrefix,
       sourceSuffix,
     );
-    _fixKind = DartFixKind.CREATE_METHOD;
     _functionName = name;
   }
 }
diff --git a/pkg/analysis_server/lib/src/services/correction/util.dart b/pkg/analysis_server/lib/src/services/correction/util.dart
index b79ea69..4ae647d 100644
--- a/pkg/analysis_server/lib/src/services/correction/util.dart
+++ b/pkg/analysis_server/lib/src/services/correction/util.dart
@@ -258,22 +258,11 @@
 
 /// If given [node] is name of qualified property extraction, returns target
 /// from which this property is extracted, otherwise `null`.
-Expression? getQualifiedPropertyTarget(AstNode node) {
-  var parent = node.parent;
-  if (parent is PrefixedIdentifier) {
-    var prefixed = parent;
-    if (prefixed.identifier == node) {
-      return parent.prefix;
-    }
-  }
-  if (parent is PropertyAccess) {
-    var access = parent;
-    if (access.propertyName == node) {
-      return access.realTarget;
-    }
-  }
-  return null;
-}
+Expression? getQualifiedPropertyTarget(AstNode node) => switch (node.parent) {
+  PrefixedIdentifier parent when parent.identifier == node => parent.prefix,
+  PropertyAccess parent when parent.propertyName == node => parent.realTarget,
+  _ => null,
+};
 
 /// Returns the given [statement] if not a block, or the first child statement
 /// if a block, or `null` if more than one child.