From cd3f7df28c6d7745f15baf251ad429e082c72428 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Fri, 5 Feb 2016 17:19:52 +0300 Subject: [PATCH] Create deep descriptors copy for Change signature It's needed to prevent accesses to invalid PSI after changes has been applied --- .../KotlinChangeSignatureUsageProcessor.kt | 2 +- .../changeSignature/changeSignatureUtils.kt | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt index f447512fa30..2dccc1c82f6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt @@ -831,7 +831,7 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { val descriptorWrapper = usages.firstIsInstanceOrNull() if (descriptorWrapper == null || descriptorWrapper.originalJavaMethodDescriptor != null) return true - val methodDescriptor = method.getJavaMethodDescriptor() ?: return false + val methodDescriptor = method.getJavaMethodDescriptor()?.createDeepCopy() ?: return false descriptorWrapper.originalJavaMethodDescriptor = KotlinChangeSignatureData(methodDescriptor, method, listOf(methodDescriptor)) // This change info is used as a placeholder before primary method update diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt index d563c45d042..66808354118 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/changeSignatureUtils.kt @@ -20,6 +20,7 @@ import com.intellij.psi.PsiElement import com.intellij.refactoring.changeSignature.CallerUsageInfo import com.intellij.refactoring.changeSignature.OverriderUsageInfo import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.DeferredJavaMethodKotlinCallerUsage import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.JavaMethodKotlinUsageWithDelegate import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCallableDefinitionUsage @@ -27,10 +28,9 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.usages.KotlinCaller import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf -import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeSubstitutor -import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.types.substitutions.getCallableSubstitutor +import org.jetbrains.kotlin.types.typeUtil.asTypeProjection fun KtNamedDeclaration.getDeclarationBody(): KtElement? { return when { @@ -77,4 +77,19 @@ fun KotlinType.renderTypeWithSubstitution(substitutor: TypeSubstitutor?, default val newType = substitutor?.substitute(this, Variance.INVARIANT) ?: return defaultText val renderer = if (inArgumentPosition) IdeDescriptorRenderers.SOURCE_CODE_FOR_TYPE_ARGUMENTS else IdeDescriptorRenderers.SOURCE_CODE return renderer.renderType(newType) -} \ No newline at end of file +} + +// This method is used to create full copies of functions (including copies of all types) +// It's needed to prevent accesses to PSI (e.g. using LazyJavaClassifierType properties) when Change signature invalidates it +// See KotlinChangeSignatureTest.testSAMChangeMethodReturnType +fun FunctionDescriptor.createDeepCopy() = substitute(TypeSubstitutor.create(ForceTypeCopySubstitution)) + +private object ForceTypeCopySubstitution : TypeSubstitution() { + override fun get(key: KotlinType) = + with(key) { + KotlinTypeImpl.create( + annotations, constructor, isMarkedNullable, arguments, substitution, memberScope, capabilities).asTypeProjection() + } + + override fun isEmpty() = false +}