Refactorings: Support header/impl functions in member hierarchy transformations
#KT-18904 Fixed #KT-18905 Fixed
This commit is contained in:
@@ -22,6 +22,8 @@ import com.intellij.psi.PsiElement
|
|||||||
import com.intellij.psi.PsiMethod
|
import com.intellij.psi.PsiMethod
|
||||||
import com.intellij.psi.PsiReference
|
import com.intellij.psi.PsiReference
|
||||||
import com.intellij.psi.search.SearchScope
|
import com.intellij.psi.search.SearchScope
|
||||||
|
import com.intellij.psi.search.searches.MethodReferencesSearch
|
||||||
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.psi.util.MethodSignatureUtil
|
import com.intellij.psi.util.MethodSignatureUtil
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
import org.jetbrains.kotlin.asJava.elements.KtLightElement
|
||||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||||
@@ -250,3 +252,13 @@ fun PsiReference.isCallableOverrideUsage(declaration: KtNamedDeclaration): Boole
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun PsiElement.searchReferencesOrMethodReferences(): Collection<PsiReference> {
|
||||||
|
val lightMethods = toLightMethods()
|
||||||
|
return if (lightMethods.isNotEmpty()) {
|
||||||
|
lightMethods.flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ReferencesSearch.search(this).findAll()
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-4
@@ -23,11 +23,9 @@ import com.intellij.openapi.util.text.StringUtil
|
|||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiNamedElement
|
import com.intellij.psi.PsiNamedElement
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
import com.intellij.refactoring.util.RefactoringUIUtil
|
||||||
import com.intellij.util.containers.MultiMap
|
import com.intellij.util.containers.MultiMap
|
||||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
@@ -41,6 +39,7 @@ import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.KotlinI
|
|||||||
import org.jetbrains.kotlin.idea.references.KtReference
|
import org.jetbrains.kotlin.idea.references.KtReference
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -196,8 +195,7 @@ class ConvertFunctionTypeParameterToReceiverIntention : SelfTargetingRangeIntent
|
|||||||
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
||||||
}
|
}
|
||||||
|
|
||||||
val references = callable.toLightMethods().flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) }
|
usageLoop@ for (ref in callable.searchReferencesOrMethodReferences()) {
|
||||||
usageLoop@ for (ref in references) {
|
|
||||||
val refElement = ref.element ?: continue
|
val refElement = ref.element ?: continue
|
||||||
when (ref) {
|
when (ref) {
|
||||||
is KtSimpleReference<*> -> processExternalUsage(conflicts, refElement, usages)
|
is KtSimpleReference<*> -> processExternalUsage(conflicts, refElement, usages)
|
||||||
|
|||||||
+2
-4
@@ -21,11 +21,9 @@ import com.intellij.openapi.progress.ProgressManager
|
|||||||
import com.intellij.openapi.util.TextRange
|
import com.intellij.openapi.util.TextRange
|
||||||
import com.intellij.psi.PsiElement
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.searches.MethodReferencesSearch
|
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.refactoring.util.RefactoringUIUtil
|
import com.intellij.refactoring.util.RefactoringUIUtil
|
||||||
import com.intellij.util.containers.MultiMap
|
import com.intellij.util.containers.MultiMap
|
||||||
import org.jetbrains.kotlin.asJava.toLightMethods
|
|
||||||
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
import org.jetbrains.kotlin.builtins.getReceiverTypeFromFunctionType
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
@@ -38,6 +36,7 @@ import org.jetbrains.kotlin.idea.refactoring.checkConflictsInteractively
|
|||||||
import org.jetbrains.kotlin.idea.refactoring.getAffectedCallables
|
import org.jetbrains.kotlin.idea.refactoring.getAffectedCallables
|
||||||
import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
import org.jetbrains.kotlin.idea.references.KtSimpleReference
|
||||||
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
import org.jetbrains.kotlin.idea.runSynchronouslyWithProgress
|
||||||
|
import org.jetbrains.kotlin.idea.search.usagesSearch.searchReferencesOrMethodReferences
|
||||||
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
|
||||||
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
import org.jetbrains.kotlin.idea.util.application.runReadAction
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -135,8 +134,7 @@ class ConvertFunctionTypeReceiverToParameterIntention : SelfTargetingRangeIntent
|
|||||||
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
conflicts.putValue(callable, "Can't modify $renderedCallable")
|
||||||
}
|
}
|
||||||
|
|
||||||
val references = callable.toLightMethods().flatMapTo(LinkedHashSet()) { MethodReferencesSearch.search(it) }
|
for (ref in callable.searchReferencesOrMethodReferences()) {
|
||||||
for (ref in references) {
|
|
||||||
if (ref !is KtSimpleReference<*>) continue
|
if (ref !is KtSimpleReference<*>) continue
|
||||||
processExternalUsage(ref, usages)
|
processExternalUsage(ref, usages)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,8 +35,12 @@ import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
|||||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||||
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
||||||
import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations
|
import org.jetbrains.kotlin.idea.core.getDirectlyOverriddenDeclarations
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.markers.headerImplementations
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.markers.isHeaderOrHeaderClassMember
|
||||||
|
import org.jetbrains.kotlin.idea.highlighter.markers.liftToHeader
|
||||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||||
import org.jetbrains.kotlin.psi.KtBlockExpression
|
import org.jetbrains.kotlin.psi.KtBlockExpression
|
||||||
|
import org.jetbrains.kotlin.psi.KtDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
import org.jetbrains.kotlin.psi.KtDeclarationWithBody
|
||||||
import org.jetbrains.kotlin.psi.KtExpression
|
import org.jetbrains.kotlin.psi.KtExpression
|
||||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||||
@@ -48,10 +52,13 @@ import java.util.*
|
|||||||
|
|
||||||
abstract class CallableRefactoring<out T: CallableDescriptor>(
|
abstract class CallableRefactoring<out T: CallableDescriptor>(
|
||||||
val project: Project,
|
val project: Project,
|
||||||
val callableDescriptor: T,
|
callableDescriptor: T,
|
||||||
val commandName: String) {
|
val commandName: String) {
|
||||||
private val LOG = Logger.getInstance(CallableRefactoring::class.java)
|
private val LOG = Logger.getInstance(CallableRefactoring::class.java)
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val callableDescriptor = callableDescriptor.liftToHeader() as? T ?: callableDescriptor
|
||||||
|
|
||||||
private val kind = (callableDescriptor as? CallableMemberDescriptor)?.kind ?: CallableMemberDescriptor.Kind.DECLARATION
|
private val kind = (callableDescriptor as? CallableMemberDescriptor)?.kind ?: CallableMemberDescriptor.Kind.DECLARATION
|
||||||
|
|
||||||
protected open fun forcePerformForSelectedFunctionOnly(): Boolean {
|
protected open fun forcePerformForSelectedFunctionOnly(): Boolean {
|
||||||
@@ -69,7 +76,7 @@ abstract class CallableRefactoring<out T: CallableDescriptor>(
|
|||||||
else -> {
|
else -> {
|
||||||
throw IllegalStateException("Unexpected callable kind: $kind")
|
throw IllegalStateException("Unexpected callable kind: $kind")
|
||||||
}
|
}
|
||||||
}
|
}.map { it.liftToHeader() as? CallableDescriptor ?: it }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showSuperFunctionWarningDialog(superCallables: Collection<CallableDescriptor>,
|
private fun showSuperFunctionWarningDialog(superCallables: Collection<CallableDescriptor>,
|
||||||
@@ -176,9 +183,16 @@ abstract class CallableRefactoring<out T: CallableDescriptor>(
|
|||||||
|
|
||||||
fun getAffectedCallables(project: Project, descriptorsForChange: Collection<CallableDescriptor>): List<PsiElement> {
|
fun getAffectedCallables(project: Project, descriptorsForChange: Collection<CallableDescriptor>): List<PsiElement> {
|
||||||
val baseCallables = descriptorsForChange.mapNotNull { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
|
val baseCallables = descriptorsForChange.mapNotNull { DescriptorToSourceUtilsIde.getAnyDeclaration(project, it) }
|
||||||
return baseCallables + baseCallables.flatMap { it.toLightMethods() }.flatMapTo(HashSet<PsiElement>()) { psiMethod ->
|
return baseCallables + baseCallables.flatMapTo(HashSet<PsiElement>()) { callable ->
|
||||||
val overrides = OverridingMethodsSearch.search(psiMethod).findAll()
|
if (callable is KtDeclaration && callable.isHeaderOrHeaderClassMember()) {
|
||||||
overrides.map { method -> method.namedUnwrappedElement ?: method}
|
callable.headerImplementations()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
callable.toLightMethods().flatMap { psiMethod ->
|
||||||
|
val overrides = OverridingMethodsSearch.search(psiMethod).findAll()
|
||||||
|
overrides.map { method -> method.namedUnwrappedElement ?: method}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert '(Int) -> Int' to 'Int.() -> Int'" "true"
|
||||||
|
|
||||||
|
header fun foo(n: Int, action: (<caret>Int) -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { n -> n + 1 }
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert '(Int) -> Int' to 'Int.() -> Int'" "true"
|
||||||
|
|
||||||
|
header fun foo(n: Int, action: Int.() -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { n -> n + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { n -> n + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
header fun foo(n: Int, action: (Int) -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { n -> n + 1 }
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
header fun foo(n: Int, action: Int.() -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert '(Int) -> Int' to 'Int.() -> Int'" "true"
|
||||||
|
|
||||||
|
impl fun foo(n: Int, action: (<caret>Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { n -> n + 1 }
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert '(Int) -> Int' to 'Int.() -> Int'" "true"
|
||||||
|
|
||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { n -> n + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert 'Int.() -> Int' to '(Int) -> Int'" "true"
|
||||||
|
|
||||||
|
header fun foo(n: Int, action: <caret>Int.() -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert 'Int.() -> Int' to '(Int) -> Int'" "true"
|
||||||
|
|
||||||
|
header fun foo(n: Int, action: (Int) -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { i -> i + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { i -> i + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { i -> i + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
header fun foo(n: Int, action: Int.() -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
header fun foo(n: Int, action: (Int) -> Int): Int
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { i -> i + 1 }
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert 'Int.() -> Int' to '(Int) -> Int'" "true"
|
||||||
|
|
||||||
|
impl fun foo(n: Int, action: <caret>Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
// "Convert 'Int.() -> Int' to '(Int) -> Int'" "true"
|
||||||
|
|
||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { i -> i + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: Int.() -> Int) = n.action()
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { this + 1 }
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
impl fun foo(n: Int, action: (Int) -> Int) = action(n)
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo(1) { i -> i + 1 }
|
||||||
|
}
|
||||||
@@ -141,4 +141,16 @@ class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testMemberFunParameterToReceiverByImpl() = doTestHeaderWithJvmAndJs()
|
fun testMemberFunParameterToReceiverByImpl() = doTestHeaderWithJvmAndJs()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFunctionTypeParameterToReceiverByHeader() = doTestHeaderWithJvmAndJs()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFunctionTypeParameterToReceiverByImpl() = doTestHeaderWithJvmAndJs()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFunctionTypeReceiverToParameterByHeader() = doTestHeaderWithJvmAndJs()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testFunctionTypeReceiverToParameterByImpl() = doTestHeaderWithJvmAndJs()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user