Move: Fix warning on companion used as implicit dispatch receiver
Do not report conflict if target class is a sublcass of companion object's containing class
This commit is contained in:
@@ -405,4 +405,6 @@ fun DeclarationDescriptor.isPublishedApi(): Boolean {
|
||||
fun DeclarationDescriptor.isAncestorOf(descriptor: DeclarationDescriptor, strict: Boolean): Boolean
|
||||
= DescriptorUtils.isAncestor(this, descriptor, strict)
|
||||
|
||||
fun DeclarationDescriptor.isCompanionObject(): Boolean = DescriptorUtils.isCompanionObject(this)
|
||||
fun DeclarationDescriptor.isCompanionObject(): Boolean = DescriptorUtils.isCompanionObject(this)
|
||||
|
||||
fun ClassDescriptor.isSubclassOf(superclass: ClassDescriptor): Boolean = DescriptorUtils.isSubclass(this, superclass)
|
||||
+19
-1
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
|
||||
|
||||
sealed class MoveDeclarationsDelegate {
|
||||
abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo
|
||||
@@ -82,6 +83,23 @@ sealed class MoveDeclarationsDelegate {
|
||||
return collectOuterInstanceReferences(classToMove)
|
||||
}
|
||||
|
||||
private fun isValidTargetForImplicitCompanionAsDispatchReceiver(
|
||||
moveDescriptor: MoveDeclarationsDescriptor,
|
||||
companionDescriptor: ClassDescriptor
|
||||
): Boolean {
|
||||
val moveTarget = moveDescriptor.moveTarget
|
||||
return when (moveTarget) {
|
||||
is KotlinMoveTargetForCompanion -> true
|
||||
is KotlinMoveTargetForExistingElement -> {
|
||||
val targetClass = moveTarget.targetElement as? KtClassOrObject ?: return false
|
||||
val targetClassDescriptor = targetClass.resolveToDescriptor() as ClassDescriptor
|
||||
val companionClassDescriptor = companionDescriptor.containingDeclaration as? ClassDescriptor ?: return false
|
||||
targetClassDescriptor.isSubclassOf(companionClassDescriptor)
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun collectConflicts(
|
||||
descriptor: MoveDeclarationsDescriptor,
|
||||
internalUsages: MutableSet<UsageInfo>,
|
||||
@@ -94,7 +112,7 @@ sealed class MoveDeclarationsDelegate {
|
||||
|
||||
val isConflict = when (usage) {
|
||||
is ImplicitCompanionAsDispatchReceiverUsageInfo -> {
|
||||
if (descriptor.moveTarget !is KotlinMoveTargetForCompanion) {
|
||||
if (!isValidTargetForImplicitCompanionAsDispatchReceiver(descriptor, usage.companionDescriptor)) {
|
||||
conflicts.putValue(element, "Implicit companion object will be inaccessible: ${element.text}")
|
||||
}
|
||||
true
|
||||
|
||||
@@ -129,7 +129,7 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
|
||||
val dispatchReceiver = refExpr.getResolvedCall(bindingContext)?.dispatchReceiver
|
||||
val implicitClass = (dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor
|
||||
if (implicitClass?.isCompanionObject ?: false) {
|
||||
return ::ImplicitCompanionAsDispatchReceiverUsageInfo
|
||||
return { ImplicitCompanionAsDispatchReceiverUsageInfo(it, implicitClass!!) }
|
||||
}
|
||||
if (dispatchReceiver != null || containingDescriptor.kind != ClassKind.OBJECT) return null
|
||||
}
|
||||
@@ -178,7 +178,10 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
|
||||
}
|
||||
}
|
||||
|
||||
class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee)
|
||||
class ImplicitCompanionAsDispatchReceiverUsageInfo(
|
||||
callee: KtSimpleNameExpression,
|
||||
val companionDescriptor: ClassDescriptor
|
||||
) : UsageInfo(callee)
|
||||
|
||||
interface KotlinMoveUsage {
|
||||
val isInternal: Boolean
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package test
|
||||
|
||||
open class A {
|
||||
companion object {
|
||||
fun Int.extFoo(n: Int) {}
|
||||
|
||||
val Int.extBar: Int get() = 1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class C : A() {
|
||||
class B {
|
||||
fun test() {
|
||||
1.extFoo(1.extBar)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package test
|
||||
|
||||
open class A {
|
||||
companion object {
|
||||
fun Int.extFoo(n: Int) {}
|
||||
|
||||
val Int.extBar: Int get() = 1
|
||||
}
|
||||
|
||||
class <caret>B {
|
||||
fun test() {
|
||||
1.extFoo(1.extBar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class C : A() {
|
||||
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"mainFile": "test.kt",
|
||||
"type": "MOVE_KOTLIN_NESTED_CLASS",
|
||||
"targetClass": "test.C",
|
||||
"withRuntime": "true"
|
||||
}
|
||||
@@ -372,6 +372,12 @@ public class MoveTestGenerated extends AbstractMoveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveNestedClass/nonInnerToSubclassWithCompanionAsImplicitDispatchReceiver/nonInnerToSubclassWithCompanionAsImplicitDispatchReceiver.test")
|
||||
public void testKotlin_moveNestedClass_nonInnerToSubclassWithCompanionAsImplicitDispatchReceiver_NonInnerToSubclassWithCompanionAsImplicitDispatchReceiver() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/nonInnerToSubclassWithCompanionAsImplicitDispatchReceiver/nonInnerToSubclassWithCompanionAsImplicitDispatchReceiver.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveNestedClass/nonInnerToTopLevel/nonInnerToTopLevel.test")
|
||||
public void testKotlin_moveNestedClass_nonInnerToTopLevel_NonInnerToTopLevel() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveNestedClass/nonInnerToTopLevel/nonInnerToTopLevel.test");
|
||||
|
||||
Reference in New Issue
Block a user