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 9f6c550a313..a751b0ae3b6 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/fixes/fixes.dart'; 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 => @@ -31,103 +77,27 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer { @override List get fixArguments => [_functionName]; - @override - FixKind get fixKind => _fixKind; - @override Future 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().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 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer { } } + 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().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 _createExecutable( @@ -211,7 +244,6 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer { sourcePrefix, sourceSuffix, ); - _fixKind = DartFixKind.CREATE_FUNCTION; _functionName = name; } @@ -270,7 +302,6 @@ class CreateMethodOrFunction extends ResolvedCorrectionProducer { 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 b79ea690222..4ae647d1f69 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 @@ List getParents(AstNode node) { /// 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.