Change Signature: Invalidate ChangeInfo after the refactoring

The goal is to avoid keeping all semantic information used by
KotlinChangeInfo in UndoableAction produced by the refactoring

#KT-17333 Fixed
This commit is contained in:
Alexey Sedunov
2017-04-17 12:34:04 +03:00
parent b33b06722d
commit 45929fd3e3
4 changed files with 90 additions and 22 deletions
@@ -0,0 +1,51 @@
/*
* Copyright 2010-2017 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.changeSignature
import com.intellij.refactoring.changeSignature.ChangeInfo
class KotlinChangeInfoWrapper(delegate: KotlinChangeInfo) : ChangeInfo {
var delegate: KotlinChangeInfo? = delegate
private set
private val method = delegate.method
override fun getMethod() = method
override fun isGenerateDelegate() = delegate!!.isGenerateDelegate
override fun getNewName() = delegate!!.newName
override fun isParameterTypesChanged() = delegate!!.isParameterTypesChanged
override fun getNewParameters() = delegate!!.newParameters
override fun isParameterSetOrOrderChanged() = delegate!!.isParameterSetOrOrderChanged
override fun isReturnTypeChanged() = delegate!!.isReturnTypeChanged
override fun isParameterNamesChanged() = delegate!!.isParameterNamesChanged
override fun isNameChanged() = delegate!!.isNameChanged
override fun getLanguage() = delegate!!.language
// Only getMethod() may be called after invalidate()
fun invalidate() {
delegate = null
}
}
@@ -232,5 +232,5 @@ class KotlinChangeSignature(project: Project,
adjustedDescriptor, adjustedDescriptor,
defaultValueContext defaultValueContext
) as KotlinChangeSignatureProcessor ) as KotlinChangeSignatureProcessor
return processor.changeInfo return processor.ktChangeInfo
} }
@@ -36,19 +36,24 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinUsageI
import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinWrapperForJavaUsageInfos import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinWrapperForJavaUsageInfos
import java.util.* import java.util.*
class KotlinChangeSignatureProcessor(project: Project, class KotlinChangeSignatureProcessor(
changeInfo: KotlinChangeInfo, project: Project,
private val commandName: String) : ChangeSignatureProcessorBase(project, changeInfo) { changeInfo: KotlinChangeInfo,
private val commandName: String
) : ChangeSignatureProcessorBase(project, KotlinChangeInfoWrapper(changeInfo)) {
val ktChangeInfo
get() = changeInfo.delegate!!
override fun createUsageViewDescriptor(usages: Array<UsageInfo>): UsageViewDescriptor { override fun createUsageViewDescriptor(usages: Array<UsageInfo>): UsageViewDescriptor {
val subject = if (changeInfo.kind.isConstructor) "constructor" else "function" val subject = if (ktChangeInfo.kind.isConstructor) "constructor" else "function"
return KotlinUsagesViewDescriptor(myChangeInfo.method, RefactoringBundle.message("0.to.change.signature", subject)) return KotlinUsagesViewDescriptor(myChangeInfo.method, RefactoringBundle.message("0.to.change.signature", subject))
} }
override fun getChangeInfo() = super.getChangeInfo() as KotlinChangeInfo override fun getChangeInfo() = super.getChangeInfo() as KotlinChangeInfoWrapper
override fun findUsages(): Array<UsageInfo> { override fun findUsages(): Array<UsageInfo> {
val allUsages = ArrayList<UsageInfo>() val allUsages = ArrayList<UsageInfo>()
changeInfo.getOrCreateJavaChangeInfos()?.let { javaChangeInfos -> ktChangeInfo.getOrCreateJavaChangeInfos()?.let { javaChangeInfos ->
val javaProcessor = JavaChangeSignatureUsageProcessor() val javaProcessor = JavaChangeSignatureUsageProcessor()
javaChangeInfos.mapTo(allUsages) { javaChangeInfos.mapTo(allUsages) {
KotlinWrapperForJavaUsageInfos(it, javaProcessor.findUsages(it), changeInfo.method) KotlinWrapperForJavaUsageInfos(it, javaProcessor.findUsages(it), changeInfo.method)
@@ -107,4 +112,13 @@ class KotlinChangeSignatureProcessor(project: Project,
override fun isPreviewUsages(usages: Array<out UsageInfo>): Boolean = isPreviewUsages override fun isPreviewUsages(usages: Array<out UsageInfo>): Boolean = isPreviewUsages
override fun getCommandName() = commandName override fun getCommandName() = commandName
override fun performRefactoring(usages: Array<out UsageInfo>) {
try {
super.performRefactoring(usages)
}
finally {
changeInfo.invalidate()
}
}
} }
@@ -100,8 +100,8 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
result.add(OriginalJavaMethodDescriptorWrapper(info.method)) result.add(OriginalJavaMethodDescriptorWrapper(info.method))
if (info is KotlinChangeInfo) { if (info is KotlinChangeInfoWrapper) {
findAllMethodUsages(info, result) findAllMethodUsages(info.delegate!!, result)
} }
else { else {
findSAMUsages(info, result) findSAMUsages(info, result)
@@ -507,13 +507,15 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
refUsages.set(adjustedUsages.toTypedArray()) refUsages.set(adjustedUsages.toTypedArray())
} }
if (info !is KotlinChangeInfo) return result if (info !is KotlinChangeInfoWrapper) return result
val ktChangeInfo = info.delegate!!
val parameterNames = HashSet<String>() val parameterNames = HashSet<String>()
val function = info.method val function = info.method
val element = function val element = function
val bindingContext = (element as KtElement).analyze(BodyResolveMode.FULL) val bindingContext = (element as KtElement).analyze(BodyResolveMode.FULL)
val oldDescriptor = info.originalBaseFunctionDescriptor val oldDescriptor = ktChangeInfo.originalBaseFunctionDescriptor
val containingDeclaration = oldDescriptor.containingDeclaration val containingDeclaration = oldDescriptor.containingDeclaration
val parametersScope = when { val parametersScope = when {
@@ -527,7 +529,7 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
val callableScope = oldDescriptor.getContainingScope() val callableScope = oldDescriptor.getContainingScope()
val kind = info.kind val kind = ktChangeInfo.kind
if (!kind.isConstructor && callableScope != null && !info.newName.isEmpty()) { if (!kind.isConstructor && callableScope != null && !info.newName.isEmpty()) {
val newName = Name.identifier(info.newName) val newName = Name.identifier(info.newName)
val conflicts = if (oldDescriptor is FunctionDescriptor) val conflicts = if (oldDescriptor is FunctionDescriptor)
@@ -550,7 +552,7 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
} }
} }
for (parameter in info.getNonReceiverParameters()) { for (parameter in ktChangeInfo.getNonReceiverParameters()) {
val valOrVar = parameter.valOrVar val valOrVar = parameter.valOrVar
val parameterName = parameter.name val parameterName = parameter.name
@@ -578,19 +580,19 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
} }
} }
val newReceiverInfo = info.receiverParameterInfo val newReceiverInfo = ktChangeInfo.receiverParameterInfo
val originalReceiverInfo = info.methodDescriptor.receiver val originalReceiverInfo = ktChangeInfo.methodDescriptor.receiver
if (function is KtCallableDeclaration && newReceiverInfo != originalReceiverInfo) { if (function is KtCallableDeclaration && newReceiverInfo != originalReceiverInfo) {
findReceiverIntroducingConflicts(result, function, newReceiverInfo) findReceiverIntroducingConflicts(result, function, newReceiverInfo)
findInternalExplicitReceiverConflicts(refUsages.get(), result, originalReceiverInfo) findInternalExplicitReceiverConflicts(refUsages.get(), result, originalReceiverInfo)
findReceiverToParameterInSafeCallsConflicts(refUsages.get(), result, info) findReceiverToParameterInSafeCallsConflicts(refUsages.get(), result, ktChangeInfo)
findThisLabelConflicts(refUsages, result, info, function) findThisLabelConflicts(refUsages, result, ktChangeInfo, function)
} }
for (usageInfo in usageInfos) { for (usageInfo in usageInfos) {
if (usageInfo !is KotlinCallerUsage) continue if (usageInfo !is KotlinCallerUsage) continue
val callerDescriptor = usageInfo.element?.resolveToDescriptorIfAny() ?: continue val callerDescriptor = usageInfo.element?.resolveToDescriptorIfAny() ?: continue
findParameterDuplicationInCaller(result, info, usageInfo.element!!, callerDescriptor) findParameterDuplicationInCaller(result, ktChangeInfo, usageInfo.element!!, callerDescriptor)
} }
return result return result
@@ -823,9 +825,10 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
val isJavaMethodUsage = isJavaMethodUsage(usageInfo) val isJavaMethodUsage = isJavaMethodUsage(usageInfo)
if (usageInfo is KotlinWrapperForJavaUsageInfos) { if (usageInfo is KotlinWrapperForJavaUsageInfos) {
val javaChangeInfos = (changeInfo as KotlinChangeInfo).getOrCreateJavaChangeInfos() ?: return true val ktChangeInfo = (changeInfo as KotlinChangeInfoWrapper).delegate!!
val javaChangeInfos = ktChangeInfo.getOrCreateJavaChangeInfos() ?: return true
javaChangeInfos.firstOrNull { javaChangeInfos.firstOrNull {
changeInfo.originalToCurrentMethods[usageInfo.javaChangeInfo.method] == it.method ktChangeInfo.originalToCurrentMethods[usageInfo.javaChangeInfo.method] == it.method
}?.let { javaChangeInfo -> }?.let { javaChangeInfo ->
val nullabilityPropagator = NullabilityPropagator(javaChangeInfo.method) val nullabilityPropagator = NullabilityPropagator(javaChangeInfo.method)
val javaUsageInfos = usageInfo.javaUsageInfos val javaUsageInfos = usageInfo.javaUsageInfos
@@ -910,12 +913,12 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor {
} }
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
return (usageInfo as? KotlinUsageInfo<PsiElement>)?.processUsage(changeInfo as KotlinChangeInfo, element, usages) ?: false return (usageInfo as? KotlinUsageInfo<PsiElement>)?.processUsage((changeInfo as KotlinChangeInfoWrapper).delegate!!, element, usages) ?: false
} }
override fun processPrimaryMethod(changeInfo: ChangeInfo): Boolean { override fun processPrimaryMethod(changeInfo: ChangeInfo): Boolean {
val ktChangeInfo = when (changeInfo) { val ktChangeInfo = when (changeInfo) {
is KotlinChangeInfo -> changeInfo is KotlinChangeInfoWrapper -> changeInfo.delegate!!
is JavaChangeInfo -> { is JavaChangeInfo -> {
val method = changeInfo.method as? KtLightMethod ?: return false val method = changeInfo.method as? KtLightMethod ?: return false
var baseFunction = method.kotlinOrigin ?: return false var baseFunction = method.kotlinOrigin ?: return false