Move: Fix processing of implicit extension 'invoke' calls
Also fix bogus "unused import" inspection reported on such calls #KT-17496 Fixed
This commit is contained in:
@@ -18,7 +18,9 @@ package org.jetbrains.kotlin.idea.references
|
||||
|
||||
import com.intellij.psi.*
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
|
||||
import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention
|
||||
@@ -232,8 +234,15 @@ fun KtReference.canBeResolvedViaImport(target: DeclarationDescriptor): Boolean {
|
||||
if (target.isExtension) return true // assume that any type of reference can use imports when resolved to extension
|
||||
|
||||
val referenceExpression = this.element as? KtNameReferenceExpression ?: return false
|
||||
if (CallTypeAndReceiver.detect(referenceExpression).receiver != null) return false
|
||||
val callTypeAndReceiver = CallTypeAndReceiver.detect(referenceExpression)
|
||||
|
||||
if (callTypeAndReceiver.receiver != null) {
|
||||
if (target !is PropertyDescriptor || !target.type.isExtensionFunctionType) return false
|
||||
if (callTypeAndReceiver !is CallTypeAndReceiver.DOT && callTypeAndReceiver !is CallTypeAndReceiver.SAFE) return false
|
||||
}
|
||||
|
||||
if (element.parent is KtThisExpression || element.parent is KtSuperExpression) return false // TODO: it's a bad design of PSI tree, we should change it
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.util.IncorrectOperationException
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.containers.MultiMap
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.asJava.unwrapped
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
|
||||
@@ -47,10 +48,9 @@ import org.jetbrains.kotlin.psi.psiUtil.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isAncestorOf
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.parents
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
@@ -120,7 +120,7 @@ fun KtElement.processInternalReferencesToUpdateOnPackageNameChange(
|
||||
if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null
|
||||
|
||||
val isCallable = descriptor is CallableDescriptor
|
||||
val isExtension = isCallable && (descriptor as CallableDescriptor).extensionReceiverParameter != null
|
||||
val isExtension = isCallable && refExpr.isExtensionRef(bindingContext)
|
||||
val isCallableReference = isCallableReference(refExpr.mainReference)
|
||||
|
||||
if (isCallable) {
|
||||
@@ -238,18 +238,26 @@ private enum class ReferenceKind {
|
||||
IRRELEVANT
|
||||
}
|
||||
|
||||
private fun KtSimpleNameExpression.isExtensionRef(bindingContext: BindingContext? = null): Boolean {
|
||||
val resolvedCall = getResolvedCall(bindingContext ?: analyze(BodyResolveMode.PARTIAL)) ?: return false
|
||||
if (resolvedCall is VariableAsFunctionResolvedCall) {
|
||||
return resolvedCall.variableCall.candidateDescriptor.isExtension || resolvedCall.functionCall.candidateDescriptor.isExtension
|
||||
}
|
||||
return resolvedCall.candidateDescriptor.isExtension
|
||||
}
|
||||
|
||||
private fun getReferenceKind(reference: PsiReference, referencedElement: PsiElement): ReferenceKind {
|
||||
val target = referencedElement.unwrapped
|
||||
val element = reference.element as? KtSimpleNameExpression ?: return ReferenceKind.QUALIFIABLE
|
||||
|
||||
if (element.getStrictParentOfType<KtSuperExpression>() != null) return ReferenceKind.IRRELEVANT
|
||||
|
||||
if ((referencedElement.namedUnwrappedElement as? KtDeclaration)?.isExtensionDeclaration() ?: false
|
||||
&& reference.element.getNonStrictParentOfType<KtImportDirective>() == null) return ReferenceKind.UNQUALIFIABLE
|
||||
if (element.isExtensionRef() && reference.element.getNonStrictParentOfType<KtImportDirective>() == null) return ReferenceKind.UNQUALIFIABLE
|
||||
|
||||
element.getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference }?.let {
|
||||
if (it.receiverExpression != null) return ReferenceKind.IRRELEVANT
|
||||
if (referencedElement is KtDeclaration && referencedElement.parent is KtFile) return ReferenceKind.UNQUALIFIABLE
|
||||
if (referencedElement is PsiMember && referencedElement.getContainingClass() == null) return ReferenceKind.UNQUALIFIABLE
|
||||
if (target is KtDeclaration && target.parent is KtFile) return ReferenceKind.UNQUALIFIABLE
|
||||
if (target is PsiMember && target.containingClass == null) return ReferenceKind.UNQUALIFIABLE
|
||||
}
|
||||
|
||||
return ReferenceKind.QUALIFIABLE
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package foo
|
||||
|
||||
class CrExtended
|
||||
|
||||
val funExtension = fun CrExtended.(): Unit {}
|
||||
|
||||
// WITH_RUNTIME
|
||||
@@ -0,0 +1,10 @@
|
||||
package bar
|
||||
|
||||
import foo.CrExtended
|
||||
import foo.funExtension
|
||||
|
||||
fun test() {
|
||||
CrExtended().funExtension()
|
||||
}
|
||||
|
||||
// WITH_RUNTIME
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package bar
|
||||
|
||||
class CrExtended
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package foo
|
||||
|
||||
import bar.CrExtended
|
||||
|
||||
fun test(ce: CrExtended) {
|
||||
valWithFunType()
|
||||
ce.valWithExtFunType()
|
||||
with(1) {
|
||||
extValWithFunType()
|
||||
ce.extValWithExtFunType()
|
||||
}
|
||||
|
||||
::valWithFunType
|
||||
::valWithExtFunType
|
||||
1::extValWithFunType
|
||||
1::extValWithExtFunType
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package foo
|
||||
|
||||
import bar.CrExtended
|
||||
|
||||
val valWithFunType = fun (): Unit {}
|
||||
val valWithExtFunType = fun CrExtended.(): Unit {}
|
||||
val Int.extValWithFunType get() = fun (): Unit {}
|
||||
val Int.extValWithExtFunType get() = fun CrExtended.(): Unit {}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package bar
|
||||
|
||||
import foo.*
|
||||
|
||||
class CrExtended
|
||||
|
||||
fun <caret>test(ce: CrExtended) {
|
||||
valWithFunType()
|
||||
ce.valWithExtFunType()
|
||||
with(1) {
|
||||
extValWithFunType()
|
||||
ce.extValWithExtFunType()
|
||||
}
|
||||
|
||||
::valWithFunType
|
||||
::valWithExtFunType
|
||||
1::extValWithFunType
|
||||
1::extValWithExtFunType
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package foo
|
||||
|
||||
import bar.CrExtended
|
||||
|
||||
val valWithFunType = fun (): Unit {}
|
||||
val valWithExtFunType = fun CrExtended.(): Unit {}
|
||||
val Int.extValWithFunType get() = fun (): Unit {}
|
||||
val Int.extValWithExtFunType get() = fun CrExtended.(): Unit {}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "bar/test.kt",
|
||||
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
|
||||
"targetPackage": "foo"
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package bar
|
||||
|
||||
class CrExtended
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package baz
|
||||
|
||||
import bar.CrExtended
|
||||
import foo.extValWithExtFunType
|
||||
import foo.extValWithFunType
|
||||
import foo.valWithExtFunType
|
||||
import foo.valWithFunType
|
||||
|
||||
fun test(ce: CrExtended) {
|
||||
valWithFunType()
|
||||
ce.valWithExtFunType()
|
||||
with(1) {
|
||||
extValWithFunType()
|
||||
ce.extValWithExtFunType()
|
||||
}
|
||||
|
||||
::valWithFunType
|
||||
::valWithExtFunType
|
||||
1::extValWithFunType
|
||||
1::extValWithExtFunType
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package foo
|
||||
|
||||
import bar.CrExtended
|
||||
|
||||
val valWithFunType = fun (): Unit {}
|
||||
val valWithExtFunType = fun CrExtended.(): Unit {}
|
||||
val Int.extValWithFunType get() = fun (): Unit {}
|
||||
val Int.extValWithExtFunType get() = fun CrExtended.(): Unit {}
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package bar
|
||||
|
||||
import foo.*
|
||||
|
||||
class CrExtended
|
||||
|
||||
fun <caret>test(ce: CrExtended) {
|
||||
valWithFunType()
|
||||
ce.valWithExtFunType()
|
||||
with(1) {
|
||||
extValWithFunType()
|
||||
ce.extValWithExtFunType()
|
||||
}
|
||||
|
||||
::valWithFunType
|
||||
::valWithExtFunType
|
||||
1::extValWithFunType
|
||||
1::extValWithExtFunType
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package foo
|
||||
|
||||
import bar.CrExtended
|
||||
|
||||
val valWithFunType = fun (): Unit {}
|
||||
val valWithExtFunType = fun CrExtended.(): Unit {}
|
||||
val Int.extValWithFunType get() = fun (): Unit {}
|
||||
val Int.extValWithExtFunType get() = fun CrExtended.(): Unit {}
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "bar/test.kt",
|
||||
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
|
||||
"targetPackage": "baz"
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package bar
|
||||
|
||||
import foo.CrExtended
|
||||
import foo.extValWithExtFunType
|
||||
import foo.extValWithFunType
|
||||
import foo.valWithExtFunType
|
||||
import foo.valWithFunType
|
||||
|
||||
fun test(ce: CrExtended) {
|
||||
valWithFunType()
|
||||
ce.valWithExtFunType()
|
||||
with(1) {
|
||||
extValWithFunType()
|
||||
ce.extValWithExtFunType()
|
||||
}
|
||||
|
||||
::valWithFunType
|
||||
::valWithExtFunType
|
||||
1::extValWithFunType
|
||||
1::extValWithExtFunType
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package foo
|
||||
|
||||
val valWithFunType = fun (): Unit {}
|
||||
val valWithExtFunType = fun CrExtended.(): Unit {}
|
||||
val Int.extValWithFunType get() = fun (): Unit {}
|
||||
val Int.extValWithExtFunType get() = fun CrExtended.(): Unit {}
|
||||
|
||||
class CrExtended
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package foo
|
||||
|
||||
val valWithFunType = fun (): Unit {}
|
||||
val valWithExtFunType = fun CrExtended.(): Unit {}
|
||||
val Int.extValWithFunType get() = fun (): Unit {}
|
||||
val Int.extValWithExtFunType get() = fun CrExtended.(): Unit {}
|
||||
|
||||
class CrExtended
|
||||
|
||||
fun <caret>test(ce: CrExtended) {
|
||||
valWithFunType()
|
||||
ce.valWithExtFunType()
|
||||
with(1) {
|
||||
extValWithFunType()
|
||||
ce.extValWithExtFunType()
|
||||
}
|
||||
|
||||
::valWithFunType
|
||||
::valWithExtFunType
|
||||
1::extValWithFunType
|
||||
1::extValWithExtFunType
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"mainFile": "foo/test.kt",
|
||||
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
|
||||
"targetPackage": "bar"
|
||||
}
|
||||
@@ -468,6 +468,24 @@ public class MoveTestGenerated extends AbstractMoveTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentSource/differentSource.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_implicitInvokeCalls_differentSource_DifferentSource() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentSource/differentSource.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentSourceAndTarget/differentSourceAndTarget.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_implicitInvokeCalls_differentSourceAndTarget_DifferentSourceAndTarget() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentSourceAndTarget/differentSourceAndTarget.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentTarget/differentTarget.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_implicitInvokeCalls_differentTarget_DifferentTarget() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/implicitInvokeCalls/differentTarget/differentTarget.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/companionExtensionMemberRef/companionExtensionMemberRef.test")
|
||||
public void testKotlin_moveTopLevelDeclarations_misc_companionExtensionMemberRef_CompanionExtensionMemberRef() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/companionExtensionMemberRef/companionExtensionMemberRef.test");
|
||||
|
||||
Reference in New Issue
Block a user