From 12c05fc602f8931fb5daa9621c2449385525b981 Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Mon, 2 Nov 2015 15:40:51 +0300 Subject: [PATCH] Minor: extract functions to separate file --- .../extractableAnalysisUtil.kt | 245 +------------- .../extractionEngine/inferParameterInfo.kt | 300 ++++++++++++++++++ 2 files changed, 308 insertions(+), 237 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt index 19158f4752b..6112d08573b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/extractableAnalysisUtil.kt @@ -35,7 +35,6 @@ import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverseFollowingInstructions import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully @@ -55,29 +54,19 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.approximateWithResolvableType import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.util.isResolvableInScope -import org.jetbrains.kotlin.incremental.components.NoLookupLocation -import org.jetbrains.kotlin.lexer.KtToken import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.isInsideOf import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsStatement import org.jetbrains.kotlin.resolve.calls.callUtil.getCalleeExpressionIfAny -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory -import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke -import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns -import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor import org.jetbrains.kotlin.resolve.scopes.LexicalScope -import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver -import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue -import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver -import org.jetbrains.kotlin.resolve.scopes.utils.findFunction import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.checker.KotlinTypeChecker -import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.typeUtil.makeNullable import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.DFS.* @@ -404,7 +393,7 @@ fun ExtractionData.createTemporaryDeclaration(functionText: String): KtNamedDecl return tmpFile.findElementAt(lookupPosition)?.getNonStrictParentOfType()!! } -private fun ExtractionData.createTemporaryCodeBlock(): KtBlockExpression = +internal fun ExtractionData.createTemporaryCodeBlock(): KtBlockExpression = (createTemporaryDeclaration("fun() {\n$codeFragmentText\n}\n") as KtNamedFunction).bodyExpression as KtBlockExpression private fun KotlinType.collectReferencedTypes(processTypeArguments: Boolean): List { @@ -451,7 +440,7 @@ private fun KotlinType.isExtractable(targetScope: LexicalScope?): Boolean { } } -private fun KotlinType.processTypeIfExtractable( +internal fun KotlinType.processTypeIfExtractable( typeParameters: MutableSet, nonDenotableTypes: MutableSet, options: ExtractionOptions, @@ -487,7 +476,7 @@ private fun KotlinType.processTypeIfExtractable( } } -private class MutableParameter( +internal class MutableParameter( override val argumentText: String, override val originalDescriptor: DeclarationDescriptor, override val receiverCandidate: Boolean, @@ -579,7 +568,7 @@ private class DelegatingParameter( override fun getParameterType(allowSpecialClassNames: Boolean) = parameterType } -private class ParametersInfo { +internal class ParametersInfo { var errorMessage: ErrorMessage? = null val replacementMap: MutableMap = HashMap() val originalRefToParameter: MutableMap = HashMap() @@ -588,225 +577,7 @@ private class ParametersInfo { val nonDenotableTypes: MutableSet = HashSet() } -private fun ExtractionData.inferParametersInfo( - commonParent: PsiElement, - pseudocode: Pseudocode, - bindingContext: BindingContext, - targetScope: LexicalScope, - modifiedVarDescriptors: Set -): ParametersInfo { - val info = ParametersInfo() - val extractedDescriptorToParameter = HashMap() - - fun suggestParameterType( - extractFunctionRef: Boolean, - originalDescriptor: DeclarationDescriptor, - parameterExpression: KtExpression?, - receiverToExtract: ReceiverValue, - resolvedCall: ResolvedCall<*>?, - useSmartCastsIfPossible: Boolean - ): KotlinType { - val builtIns = originalDescriptor.builtIns - return when { - extractFunctionRef -> { - originalDescriptor as FunctionDescriptor - builtIns.getFunctionType(Annotations.EMPTY, - originalDescriptor.extensionReceiverParameter?.type, - originalDescriptor.valueParameters.map { it.type }, - originalDescriptor.returnType ?: builtIns.defaultReturnType) - } - parameterExpression != null -> - (if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null) - ?: bindingContext.getType(parameterExpression) - ?: (parameterExpression as? KtReferenceExpression)?.let { - (bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType - } - ?: if (receiverToExtract.exists()) receiverToExtract.type else null - receiverToExtract is ThisReceiver -> { - val calleeExpression = resolvedCall!!.call.calleeExpression - val typeByDataFlowInfo = if (useSmartCastsIfPossible) { - bindingContext[BindingContext.EXPRESSION_TYPE_INFO, calleeExpression]?.dataFlowInfo?.let { dataFlowInfo -> - val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(receiverToExtract)) - if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null - } - } else null - typeByDataFlowInfo ?: receiverToExtract.type - } - receiverToExtract.exists() -> receiverToExtract.type - else -> null - } ?: builtIns.defaultParameterType - } - - for (refInfo in getBrokenReferencesInfo(createTemporaryCodeBlock())) { - val (originalRef, originalDeclaration, originalDescriptor, resolvedCall) = refInfo.resolveResult - val ref = refInfo.refExpr - - val selector = (ref.parent as? KtCallExpression) ?: ref - val superExpr = (selector.parent as? KtQualifiedExpression)?.receiverExpression as? KtSuperExpression - if (superExpr != null) { - info.errorMessage = ErrorMessage.SUPER_CALL - return info - } - - val extensionReceiver = resolvedCall?.extensionReceiver - val receiverToExtract = when { - extensionReceiver == ReceiverValue.NO_RECEIVER, - isSynthesizedInvoke(originalDescriptor) -> resolvedCall?.dispatchReceiver - else -> extensionReceiver - } ?: ReceiverValue.NO_RECEIVER - - val thisDescriptor = (receiverToExtract as? ThisReceiver)?.declarationDescriptor - val hasThisReceiver = thisDescriptor != null - val thisExpr = ref.parent as? KtThisExpression - - if (hasThisReceiver - && DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(originalElements) }) { - continue - } - - val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let { - when (it) { - is ClassDescriptor -> - when(it.kind) { - ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it - ClassKind.ENUM_ENTRY -> it.containingDeclaration as? ClassDescriptor - else -> if (ref.getNonStrictParentOfType() != null) it else null - } - - is TypeParameterDescriptor -> it - - is ConstructorDescriptor -> it.containingDeclaration - - else -> null - } as? ClassifierDescriptor - } - - if (referencedClassifierDescriptor != null) { - if (!referencedClassifierDescriptor.defaultType.processTypeIfExtractable( - info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor - )) continue - - if (referencedClassifierDescriptor is ClassDescriptor) { - info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe) - } - } - else { - val extractThis = (hasThisReceiver && refInfo.smartCast == null) || thisExpr != null - val extractOrdinaryParameter = - originalDeclaration is KtMultiDeclarationEntry || - originalDeclaration is KtProperty || - originalDeclaration is KtParameter - - val extractFunctionRef = - options.captureLocalFunctions - && originalRef.getReferencedName() == originalDescriptor.name.asString() // to forbid calls by convention - && originalDeclaration is KtNamedFunction && originalDeclaration.isLocal - && targetScope.findFunction(originalDescriptor.name, NoLookupLocation.FROM_IDE) { it == originalDescriptor } == null - - val descriptorToExtract = (if (extractThis) thisDescriptor else null) ?: originalDescriptor - - val extractParameter = extractThis || extractOrdinaryParameter || extractFunctionRef - if (extractParameter) { - val parameterExpression = when { - receiverToExtract is ExpressionReceiver -> { - val receiverExpression = receiverToExtract.expression - // If p.q has a smart-cast, then extract entire qualified expression - if (refInfo.smartCast != null) receiverExpression.parent as KtExpression else receiverExpression - } - receiverToExtract.exists() && refInfo.smartCast == null -> null - else -> (originalRef.parent as? KtThisExpression) ?: originalRef - } - - val parameterType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, true) - - val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) { - var argumentText = - if (hasThisReceiver && extractThis) { - val label = if (descriptorToExtract is ClassDescriptor) "@${descriptorToExtract.name.asString()}" else "" - "this$label" - } - else { - val argumentExpr = (thisExpr ?: ref).getQualifiedExpressionForSelectorOrThis() - if (argumentExpr is KtOperationReferenceExpression) { - val nameElement = argumentExpr.getReferencedNameElement() - val nameElementType = nameElement.node.elementType - (nameElementType as? KtToken)?.let { - OperatorConventions.getNameForOperationSymbol(it)?.asString() - } ?: nameElement.text - } - else argumentExpr.text - ?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText") - } - if (extractFunctionRef) { - val receiverTypeText = (originalDeclaration as KtCallableDeclaration).receiverTypeReference?.text ?: "" - argumentText = "$receiverTypeText::$argumentText" - } - - val originalType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, false) - - MutableParameter(argumentText, descriptorToExtract, extractThis, targetScope, originalType, refInfo.possibleTypes) - } - - if (!extractThis) { - parameter.currentName = originalDeclaration.nameIdentifier?.text - } - - parameter.refCount++ - info.originalRefToParameter[originalRef] = parameter - - parameter.addDefaultType(parameterType) - - if (extractThis && thisExpr == null) { - val callElement = resolvedCall!!.call.callElement - val instruction = pseudocode.getElementValue(callElement)?.createdAt as? InstructionWithReceivers - val receiverValue = instruction?.receiverValues?.entries?.singleOrNull { it.value == receiverToExtract }?.key - if (receiverValue != null) { - parameter.addTypePredicate(getExpectedTypePredicate(receiverValue, bindingContext, targetScope.ownerDescriptor.builtIns)) - } - } - else if (extractFunctionRef) { - parameter.addTypePredicate(SingleType(parameterType)) - } - else { - pseudocode.getElementValuesRecursively(originalRef).forEach { - parameter.addTypePredicate(getExpectedTypePredicate(it, bindingContext, targetScope.ownerDescriptor.builtIns)) - } - } - - info.replacementMap[refInfo.offsetInBody] = - if (hasThisReceiver && extractThis) AddPrefixReplacement(parameter) else RenameReplacement(parameter) - } - } - } - - val varNameValidator = NewDeclarationNameValidator( - commonParent.getNonStrictParentOfType()!!, - originalElements.firstOrNull(), - NewDeclarationNameValidator.Target.VARIABLES - ) - - for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) { - if (!parameter - .getParameterType(options.allowSpecialClassNames) - .processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope)) continue - - with (parameter) { - if (currentName == null) { - currentName = KotlinNameSuggester.suggestNamesByType(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first() - } - mirrorVarName = if (modifiedVarDescriptors.containsRaw(descriptorToExtract)) KotlinNameSuggester.suggestNameByName(name, varNameValidator) else null - info.parameters.add(this) - } - } - - for (typeToCheck in info.typeParameters.flatMapTo(HashSet()) { it.collectReferencedTypes(bindingContext) }) { - typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope) - } - - - return info -} private fun ExtractionData.checkDeclarationsMovingOutOfScope( enclosingDeclaration: KtDeclaration, diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt new file mode 100644 index 00000000000..f12406f1dc9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/introduce/extractionEngine/inferParameterInfo.kt @@ -0,0 +1,300 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.introduce.extractionEngine + +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiNameIdentifierOwner +import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode +import org.jetbrains.kotlin.cfg.pseudocode.SingleType +import org.jetbrains.kotlin.cfg.pseudocode.getElementValuesRecursively +import org.jetbrains.kotlin.cfg.pseudocode.getExpectedTypePredicate +import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithReceivers +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.annotations.Annotations +import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde +import org.jetbrains.kotlin.idea.core.KotlinNameSuggester +import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator +import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.lexer.KtToken +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.psi.psiUtil.isInsideOf +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowValueFactory +import org.jetbrains.kotlin.resolve.calls.tasks.isSynthesizedInvoke +import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor +import org.jetbrains.kotlin.resolve.scopes.LexicalScope +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver +import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue +import org.jetbrains.kotlin.resolve.scopes.receivers.ThisReceiver +import org.jetbrains.kotlin.resolve.scopes.utils.findFunction +import org.jetbrains.kotlin.types.CommonSupertypes +import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.expressions.OperatorConventions +import java.util.* + +internal fun ExtractionData.inferParametersInfo( + commonParent: PsiElement, + pseudocode: Pseudocode, + bindingContext: BindingContext, + targetScope: LexicalScope, + modifiedVarDescriptors: Set +): ParametersInfo { + val info = ParametersInfo() + + val extractedDescriptorToParameter = HashMap() + + for (refInfo in getBrokenReferencesInfo(createTemporaryCodeBlock())) { + val (originalRef, originalDeclaration, originalDescriptor, resolvedCall) = refInfo.resolveResult + val ref = refInfo.refExpr + + val selector = (ref.parent as? KtCallExpression) ?: ref + val superExpr = (selector.parent as? KtQualifiedExpression)?.receiverExpression as? KtSuperExpression + if (superExpr != null) { + info.errorMessage = AnalysisResult.ErrorMessage.SUPER_CALL + return info + } + + val extensionReceiver = resolvedCall?.extensionReceiver + val receiverToExtract = when { + extensionReceiver == ReceiverValue.NO_RECEIVER, + isSynthesizedInvoke(originalDescriptor) -> resolvedCall?.dispatchReceiver + else -> extensionReceiver + } ?: ReceiverValue.NO_RECEIVER + + extractReceiver(receiverToExtract, ref, project, originalElements, + originalDescriptor, info, options, targetScope, refInfo, + originalDeclaration, originalRef, extractedDescriptorToParameter, resolvedCall, pseudocode, codeFragmentText, bindingContext, false) + } + + val varNameValidator = NewDeclarationNameValidator( + commonParent.getNonStrictParentOfType()!!, + originalElements.firstOrNull(), + NewDeclarationNameValidator.Target.VARIABLES + ) + + for ((descriptorToExtract, parameter) in extractedDescriptorToParameter) { + if (!parameter + .getParameterType(options.allowSpecialClassNames) + .processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope)) continue + + with (parameter) { + if (currentName == null) { + currentName = KotlinNameSuggester.suggestNamesByType(getParameterType(options.allowSpecialClassNames), varNameValidator, "p").first() + } + mirrorVarName = if (modifiedVarDescriptors.containsRaw(descriptorToExtract)) KotlinNameSuggester.suggestNameByName(name, varNameValidator) else null + info.parameters.add(this) + } + } + + for (typeToCheck in info.typeParameters.flatMapTo(HashSet()) { it.collectReferencedTypes(bindingContext) }) { + typeToCheck.processTypeIfExtractable(info.typeParameters, info.nonDenotableTypes, options, targetScope) + } + + + return info +} + +private fun extractReceiver( + receiverToExtract: ReceiverValue, + ref: KtSimpleNameExpression, + project: Project, + originalElements: List, + originalDescriptor: DeclarationDescriptor, + info: ParametersInfo, + options: ExtractionOptions, + targetScope: LexicalScope, + refInfo: ResolvedReferenceInfo, + originalDeclaration: PsiNameIdentifierOwner, + originalRef: KtSimpleNameExpression, + extractedDescriptorToParameter: HashMap, + resolvedCall: ResolvedCall<*>?, + pseudocode: Pseudocode, + codeFragmentText: String, + bindingContext: BindingContext, + isMemberExtensionFunction: Boolean +) { + val thisDescriptor = (receiverToExtract as? ThisReceiver)?.declarationDescriptor + val hasThisReceiver = thisDescriptor != null + val thisExpr = ref.parent as? KtThisExpression + + if (hasThisReceiver + && DescriptorToSourceUtilsIde.getAllDeclarations(project, thisDescriptor!!).all { it.isInsideOf(originalElements) }) { + return + } + + val referencedClassifierDescriptor: ClassifierDescriptor? = (thisDescriptor ?: originalDescriptor).let { + when (it) { + is ClassDescriptor -> + when(it.kind) { + ClassKind.OBJECT, ClassKind.ENUM_CLASS -> it + ClassKind.ENUM_ENTRY -> it.containingDeclaration as? ClassDescriptor + else -> if (ref.getNonStrictParentOfType() != null) it else null + } + + is TypeParameterDescriptor -> it + + is ConstructorDescriptor -> it.containingDeclaration + + else -> null + } as? ClassifierDescriptor + } + + if (referencedClassifierDescriptor != null) { + if (!referencedClassifierDescriptor.defaultType.processTypeIfExtractable( + info.typeParameters, info.nonDenotableTypes, options, targetScope, referencedClassifierDescriptor is TypeParameterDescriptor + )) return + + if (referencedClassifierDescriptor is ClassDescriptor) { + info.replacementMap[refInfo.offsetInBody] = FqNameReplacement(originalDescriptor.getImportableDescriptor().fqNameSafe) + } + } + else { + val extractThis = (hasThisReceiver && refInfo.smartCast == null) || thisExpr != null + val extractOrdinaryParameter = + originalDeclaration is KtMultiDeclarationEntry || + originalDeclaration is KtProperty || + originalDeclaration is KtParameter + + val extractFunctionRef = + options.captureLocalFunctions + && originalRef.getReferencedName() == originalDescriptor.name.asString() // to forbid calls by convention + && originalDeclaration is KtNamedFunction && originalDeclaration.isLocal + && targetScope.findFunction(originalDescriptor.name, NoLookupLocation.FROM_IDE) { it == originalDescriptor } == null + + val descriptorToExtract = (if (extractThis) thisDescriptor else null) ?: originalDescriptor + + val extractParameter = extractThis || extractOrdinaryParameter || extractFunctionRef + if (extractParameter) { + val parameterExpression = when { + receiverToExtract is ExpressionReceiver -> { + val receiverExpression = receiverToExtract.expression + // If p.q has a smart-cast, then extract entire qualified expression + if (refInfo.smartCast != null) receiverExpression.parent as KtExpression else receiverExpression + } + receiverToExtract.exists() && refInfo.smartCast == null -> null + else -> (originalRef.parent as? KtThisExpression) ?: originalRef + } + + val parameterType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, true, bindingContext) + + val parameter = extractedDescriptorToParameter.getOrPut(descriptorToExtract) { + var argumentText = + if (hasThisReceiver && extractThis) { + val label = if (descriptorToExtract is ClassDescriptor) "@${descriptorToExtract.name.asString()}" else "" + "this$label" + } + else { + val argumentExpr = (thisExpr ?: ref).getQualifiedExpressionForSelectorOrThis() + if (argumentExpr is KtOperationReferenceExpression) { + val nameElement = argumentExpr.getReferencedNameElement() + val nameElementType = nameElement.node.elementType + (nameElementType as? KtToken)?.let { + OperatorConventions.getNameForOperationSymbol(it)?.asString() + } ?: nameElement.text + } + else argumentExpr.text + ?: throw AssertionError("reference shouldn't be empty: code fragment = $codeFragmentText") + } + if (extractFunctionRef) { + val receiverTypeText = (originalDeclaration as KtCallableDeclaration).receiverTypeReference?.text ?: "" + argumentText = "$receiverTypeText::$argumentText" + } + + val originalType = suggestParameterType(extractFunctionRef, originalDescriptor, parameterExpression, receiverToExtract, resolvedCall, false, bindingContext) + + MutableParameter(argumentText, descriptorToExtract, extractThis, targetScope, originalType, refInfo.possibleTypes) + } + + if (!extractThis) { + parameter.currentName = originalDeclaration.nameIdentifier?.text + } + + parameter.refCount++ + info.originalRefToParameter[originalRef] = parameter + + parameter.addDefaultType(parameterType) + + if (extractThis && thisExpr == null) { + val callElement = resolvedCall!!.call.callElement + val instruction = pseudocode.getElementValue(callElement)?.createdAt as? InstructionWithReceivers + val receiverValue = instruction?.receiverValues?.entries?.singleOrNull { it.value == receiverToExtract }?.key + if (receiverValue != null) { + parameter.addTypePredicate(getExpectedTypePredicate(receiverValue, bindingContext, targetScope.ownerDescriptor.builtIns)) + } + } + else if (extractFunctionRef) { + parameter.addTypePredicate(SingleType(parameterType)) + } + else { + pseudocode.getElementValuesRecursively(originalRef).forEach { + parameter.addTypePredicate(getExpectedTypePredicate(it, bindingContext, targetScope.ownerDescriptor.builtIns)) + } + } + + info.replacementMap[refInfo.offsetInBody] = + when { + hasThisReceiver && extractThis -> AddPrefixReplacement(parameter) + else -> RenameReplacement(parameter) + } + } + } +} + +private fun suggestParameterType( + extractFunctionRef: Boolean, + originalDescriptor: DeclarationDescriptor, + parameterExpression: KtExpression?, + receiverToExtract: ReceiverValue, + resolvedCall: ResolvedCall<*>?, + useSmartCastsIfPossible: Boolean, bindingContext: BindingContext +): KotlinType { + val builtIns = originalDescriptor.builtIns + return when { + extractFunctionRef -> { + originalDescriptor as FunctionDescriptor + builtIns.getFunctionType(Annotations.EMPTY, + originalDescriptor.extensionReceiverParameter?.type, + originalDescriptor.valueParameters.map { it.type }, + originalDescriptor.returnType ?: builtIns.defaultReturnType) + } + parameterExpression != null -> + (if (useSmartCastsIfPossible) bindingContext[BindingContext.SMARTCAST, parameterExpression] else null) + ?: bindingContext.getType(parameterExpression) + ?: (parameterExpression as? KtReferenceExpression)?.let { + (bindingContext[BindingContext.REFERENCE_TARGET, it] as? CallableDescriptor)?.returnType + } + ?: if (receiverToExtract.exists()) receiverToExtract.type else null + receiverToExtract is ThisReceiver -> { + val calleeExpression = resolvedCall!!.call.calleeExpression + val typeByDataFlowInfo = if (useSmartCastsIfPossible) { + bindingContext[BindingContext.EXPRESSION_TYPE_INFO, calleeExpression]?.dataFlowInfo?.let { dataFlowInfo -> + val possibleTypes = dataFlowInfo.getPossibleTypes(DataFlowValueFactory.createDataFlowValue(receiverToExtract)) + if (possibleTypes.isNotEmpty()) CommonSupertypes.commonSupertype(possibleTypes) else null + } + } else null + typeByDataFlowInfo ?: receiverToExtract.type + } + receiverToExtract.exists() -> receiverToExtract.type + else -> null + } ?: builtIns.defaultParameterType +} \ No newline at end of file