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:
Alexey Sedunov
2017-04-06 16:14:46 +03:00
parent 5c35061afa
commit c1066b98f0
7 changed files with 77 additions and 4 deletions
@@ -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)
@@ -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
@@ -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)
}
}
}
@@ -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() {
}
@@ -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");