Refactoring

This commit is contained in:
Valentin Kipyatkov
2016-10-23 20:04:19 +03:00
parent 6168e50e7d
commit a42be4f60f
2 changed files with 31 additions and 52 deletions
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.idea.replacement package org.jetbrains.kotlin.idea.replacement
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns
@@ -46,21 +45,20 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
object ReplacementEngine { class CallReplacementEngine<TCallElement : KtElement>(
fun <TCallElement : KtElement> performCallReplacement( private val nameExpression: KtSimpleNameExpression,
element: KtSimpleNameExpression, private val bindingContext: BindingContext,
bindingContext: BindingContext, private val resolvedCall: ResolvedCall<out CallableDescriptor>,
resolvedCall: ResolvedCall<out CallableDescriptor>, private val callElement: TCallElement,
callElement: TCallElement, replacement: ReplacementCode
replacement: ReplacementCode ) {
): KtElement { private val replacement = replacement.toMutable()
@Suppress("NAME_SHADOWING") private val project = nameExpression.project
val replacement = replacement.toMutable() private val psiFactory = KtPsiFactory(project)
val project = element.project fun performReplacement(): KtElement {
val psiFactory = KtPsiFactory(project)
val descriptor = resolvedCall.resultingDescriptor val descriptor = resolvedCall.resultingDescriptor
val file = element.getContainingKtFile() val file = nameExpression.getContainingKtFile()
val elementToBeReplaced = when (callElement) { val elementToBeReplaced = when (callElement) {
is KtExpression -> callElement.getQualifiedExpressionForSelectorOrThis() is KtExpression -> callElement.getQualifiedExpressionForSelectorOrThis()
@@ -69,7 +67,7 @@ object ReplacementEngine {
val commentSaver = CommentSaver(elementToBeReplaced, saveLineBreaks = true) val commentSaver = CommentSaver(elementToBeReplaced, saveLineBreaks = true)
var receiver = element.getReceiverExpression()?.marked(USER_CODE_KEY) var receiver = nameExpression.getReceiverExpression()?.marked(USER_CODE_KEY)
var receiverType = if (receiver != null) bindingContext.getType(receiver) else null var receiverType = if (receiver != null) bindingContext.getType(receiver) else null
if (receiver == null) { if (receiver == null) {
@@ -93,15 +91,15 @@ object ReplacementEngine {
} }
} }
val introduceValuesForParameters = processValueParameterUsages(replacement, resolvedCall, bindingContext, project) val introduceValuesForParameters = processValueParameterUsages()
processTypeParameterUsages(replacement, resolvedCall) processTypeParameterUsages()
if (elementToBeReplaced is KtSafeQualifiedExpression) { if (elementToBeReplaced is KtSafeQualifiedExpression) {
wrapCodeForSafeCall(replacement, receiver!!, receiverType, elementToBeReplaced, bindingContext) wrapCodeForSafeCall(receiver!!, receiverType, elementToBeReplaced)
} }
else if (callElement is KtBinaryExpression && callElement.operationToken == KtTokens.IDENTIFIER) { else if (callElement is KtBinaryExpression && callElement.operationToken == KtTokens.IDENTIFIER) {
keepInfixFormIfPossible(replacement) keepInfixFormIfPossible()
} }
if (elementToBeReplaced is KtExpression) { if (elementToBeReplaced is KtExpression) {
@@ -135,18 +133,13 @@ object ReplacementEngine {
}) })
} }
private fun processValueParameterUsages( private fun processValueParameterUsages(): Collection<IntroduceValueForParameter> {
replacement: MutableReplacementCode,
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
project: Project
): Collection<IntroduceValueForParameter> {
val introduceValuesForParameters = ArrayList<IntroduceValueForParameter>() val introduceValuesForParameters = ArrayList<IntroduceValueForParameter>()
// process parameters in reverse order because default values can use previous parameters // process parameters in reverse order because default values can use previous parameters
val parameters = resolvedCall.resultingDescriptor.valueParameters val parameters = resolvedCall.resultingDescriptor.valueParameters
for (parameter in parameters.asReversed()) { for (parameter in parameters.asReversed()) {
val argument = argumentForParameter(parameter, resolvedCall, bindingContext, project) ?: continue val argument = argumentForParameter(parameter) ?: continue
argument.expression.put(PARAMETER_VALUE_KEY, parameter) argument.expression.put(PARAMETER_VALUE_KEY, parameter)
@@ -180,7 +173,7 @@ object ReplacementEngine {
val value: KtExpression, val value: KtExpression,
val valueType: KotlinType?) val valueType: KotlinType?)
private fun processTypeParameterUsages(replacement: MutableReplacementCode, resolvedCall: ResolvedCall<out CallableDescriptor>) { private fun processTypeParameterUsages() {
val typeParameters = resolvedCall.resultingDescriptor.original.typeParameters val typeParameters = resolvedCall.resultingDescriptor.original.typeParameters
val callElement = resolvedCall.call.callElement val callElement = resolvedCall.call.callElement
@@ -194,14 +187,13 @@ object ReplacementEngine {
it[ReplacementCode.TYPE_PARAMETER_USAGE_KEY] == parameterName it[ReplacementCode.TYPE_PARAMETER_USAGE_KEY] == parameterName
} }
val factory = KtPsiFactory(callElement)
val type = resolvedCall.typeArguments[typeParameter]!! val type = resolvedCall.typeArguments[typeParameter]!!
val typeElement = if (explicitTypeArgs != null) { // we use explicit type arguments if available to avoid shortening val typeElement = if (explicitTypeArgs != null) { // we use explicit type arguments if available to avoid shortening
val _typeElement = explicitTypeArgs[index].typeReference?.typeElement ?: continue val _typeElement = explicitTypeArgs[index].typeReference?.typeElement ?: continue
_typeElement.marked(USER_CODE_KEY) _typeElement.marked(USER_CODE_KEY)
} }
else { else {
factory.createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)).typeElement!! psiFactory.createType(IdeDescriptorRenderers.SOURCE_CODE.renderType(type)).typeElement!!
} }
val typeClassifier = type.constructor.declarationDescriptor val typeClassifier = type.constructor.declarationDescriptor
@@ -213,7 +205,7 @@ object ReplacementEngine {
val arguments = val arguments =
if (typeElement is KtUserType && KotlinBuiltIns.isArray(type)) typeElement.typeArgumentList?.text.orEmpty() if (typeElement is KtUserType && KotlinBuiltIns.isArray(type)) typeElement.typeArgumentList?.text.orEmpty()
else "" else ""
replacement.replaceExpression(usage, KtPsiFactory(usage).createExpression( replacement.replaceExpression(usage, psiFactory.createExpression(
IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(typeClassifier) + arguments IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(typeClassifier) + arguments
)) ))
} }
@@ -222,19 +214,13 @@ object ReplacementEngine {
} }
else { else {
//TODO: tests for this? //TODO: tests for this?
replacement.replaceExpression(usage, KtPsiFactory(usage).createExpression(typeElement.text)) replacement.replaceExpression(usage, psiFactory.createExpression(typeElement.text))
} }
} }
} }
} }
private fun wrapCodeForSafeCall( private fun wrapCodeForSafeCall(receiver: KtExpression, receiverType: KotlinType?, expressionToBeReplaced: KtExpression) {
replacement: MutableReplacementCode,
receiver: KtExpression,
receiverType: KotlinType?,
expressionToBeReplaced: KtExpression,
bindingContext: BindingContext
) {
if (replacement.statementsBefore.isEmpty()) { if (replacement.statementsBefore.isEmpty()) {
val qualified = replacement.mainExpression as? KtQualifiedExpression val qualified = replacement.mainExpression as? KtQualifiedExpression
if (qualified != null) { if (qualified != null) {
@@ -242,7 +228,7 @@ object ReplacementEngine {
if (qualified is KtSafeQualifiedExpression) return // already safe if (qualified is KtSafeQualifiedExpression) return // already safe
val selector = qualified.selectorExpression val selector = qualified.selectorExpression
if (selector != null) { if (selector != null) {
replacement.mainExpression = KtPsiFactory(receiver).createExpressionByPattern("$0?.$1", receiver, selector) replacement.mainExpression = psiFactory.createExpressionByPattern("$0?.$1", receiver, selector)
return return
} }
} }
@@ -254,7 +240,7 @@ object ReplacementEngine {
replacement.introduceValue(receiver, receiverType, thisReplaced, expressionToBeReplaced, safeCall = true) replacement.introduceValue(receiver, receiverType, thisReplaced, expressionToBeReplaced, safeCall = true)
} }
else { else {
val ifExpression = KtPsiFactory(receiver).buildExpression { val ifExpression = psiFactory.buildExpression {
appendFixedText("if (") appendFixedText("if (")
appendExpression(receiver) appendExpression(receiver)
appendFixedText("!=null) {") appendFixedText("!=null) {")
@@ -273,7 +259,7 @@ object ReplacementEngine {
} }
} }
private fun keepInfixFormIfPossible(replacement: MutableReplacementCode) { private fun keepInfixFormIfPossible() {
if (replacement.statementsBefore.isNotEmpty()) return if (replacement.statementsBefore.isNotEmpty()) return
val dotQualified = replacement.mainExpression as? KtDotQualifiedExpression ?: return val dotQualified = replacement.mainExpression as? KtDotQualifiedExpression ?: return
val receiver = dotQualified.receiverExpression val receiver = dotQualified.receiverExpression
@@ -283,7 +269,7 @@ object ReplacementEngine {
val argument = call.valueArguments.singleOrNull() ?: return val argument = call.valueArguments.singleOrNull() ?: return
if (argument.getArgumentName() != null) return if (argument.getArgumentName() != null) return
val argumentExpression = argument.getArgumentExpression() ?: return val argumentExpression = argument.getArgumentExpression() ?: return
replacement.mainExpression = KtPsiFactory(receiver).createExpressionByPattern("$0 ${nameExpression.text} $1", receiver, argumentExpression) replacement.mainExpression = psiFactory.createExpressionByPattern("$0 ${nameExpression.text} $1", receiver, argumentExpression)
} }
private fun KtExpression?.shouldKeepValue(usageCount: Int): Boolean { private fun KtExpression?.shouldKeepValue(usageCount: Int): Boolean {
@@ -311,11 +297,7 @@ object ReplacementEngine {
val isNamed: Boolean = false, val isNamed: Boolean = false,
val isDefaultValue: Boolean = false) val isDefaultValue: Boolean = false)
private fun argumentForParameter( private fun argumentForParameter(parameter: ValueParameterDescriptor): Argument? {
parameter: ValueParameterDescriptor,
resolvedCall: ResolvedCall<out CallableDescriptor>,
bindingContext: BindingContext,
project: Project): Argument? {
val resolvedArgument = resolvedCall.valueArguments[parameter]!! val resolvedArgument = resolvedCall.valueArguments[parameter]!!
when (resolvedArgument) { when (resolvedArgument) {
is ExpressionValueArgument -> { is ExpressionValueArgument -> {
@@ -353,7 +335,7 @@ object ReplacementEngine {
} }
val elementType = parameter.varargElementType!! val elementType = parameter.varargElementType!!
val expression = KtPsiFactory(project).buildExpression { val expression = psiFactory.buildExpression {
appendFixedText(arrayOfFunctionName(elementType)) appendFixedText(arrayOfFunctionName(elementType))
appendFixedText("(") appendFixedText("(")
for ((i, argument) in arguments.withIndex()) { for ((i, argument) in arguments.withIndex()) {
@@ -434,8 +416,6 @@ object ReplacementEngine {
} }
} }
val psiFactory = KtPsiFactory(result)
for (callExpression in callsToProcess) { for (callExpression in callsToProcess) {
val bindingContext = callExpression.analyze(BodyResolveMode.PARTIAL) val bindingContext = callExpression.analyze(BodyResolveMode.PARTIAL)
val resolvedCall = callExpression.getResolvedCall(bindingContext) ?: return val resolvedCall = callExpression.getResolvedCall(bindingContext) ?: return
@@ -44,8 +44,7 @@ class CallableUsageReplacementStrategy(
createReplacer(nameExpression)!!.invoke() createReplacer(nameExpression)!!.invoke()
} }
else { else {
@Suppress("UNCHECKED_CAST") CallReplacementEngine(usage, bindingContext, resolvedCall, callElement, replacement).performReplacement()
ReplacementEngine.performCallReplacement(usage, bindingContext, resolvedCall, callElement, replacement)
} }
} }
} }