From f7cda61b0834979e727ea9d5601c64bce72d76aa Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 8 Jun 2018 21:40:59 +0300 Subject: [PATCH] Copy/Paste: Add imports for non-qualifiable callable references #KT-23407 Fixed --- .../KotlinCopyPasteReferenceProcessor.kt | 36 ++++++------- .../KotlinReferenceTransferableData.kt | 50 +++++++++++-------- .../imports/TopLevelCallableRef.expected.kt | 7 +++ .../TopLevelCallableRef.expected.names | 1 + .../copyPaste/imports/TopLevelCallableRef.kt | 9 ++++ .../imports/TopLevelCallableRef.to.kt | 5 ++ ...onCallableRefWithExpressionLHS.expected.kt | 8 +++ ...allableRefWithExpressionLHS.expected.names | 2 + ...elExtensionCallableRefWithExpressionLHS.kt | 11 ++++ ...xtensionCallableRefWithExpressionLHS.to.kt | 5 ++ ...xtensionCallableRefWithTypeLHS.expected.kt | 8 +++ ...nsionCallableRefWithTypeLHS.expected.names | 2 + ...TopLevelExtensionCallableRefWithTypeLHS.kt | 11 ++++ ...LevelExtensionCallableRefWithTypeLHS.to.kt | 5 ++ ...erCallableRefWithExpressionLHS.expected.kt | 7 +++ ...allableRefWithExpressionLHS.expected.names | 1 + ...LevelMemberCallableRefWithExpressionLHS.kt | 11 ++++ ...elMemberCallableRefWithExpressionLHS.to.kt | 5 ++ ...elMemberCallableRefWithTypeLHS.expected.kt | 7 +++ ...emberCallableRefWithTypeLHS.expected.names | 1 + .../TopLevelMemberCallableRefWithTypeLHS.kt | 11 ++++ ...TopLevelMemberCallableRefWithTypeLHS.to.kt | 5 ++ .../InsertImportOnPasteTestGenerated.java | 50 +++++++++++++++++++ 23 files changed, 218 insertions(+), 40 deletions(-) create mode 100644 idea/testData/copyPaste/imports/TopLevelCallableRef.expected.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelCallableRef.expected.names create mode 100644 idea/testData/copyPaste/imports/TopLevelCallableRef.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelCallableRef.to.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.names create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.to.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.names create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.to.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.names create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.to.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.names create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt create mode 100644 idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.to.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt index 9089f88b4e7..49cc615ff1f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinCopyPasteReferenceProcessor.kt @@ -49,10 +49,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.kdoc.psi.api.KDocElement import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType -import org.jetbrains.kotlin.psi.psiUtil.elementsInRange -import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -170,9 +167,11 @@ class KotlinCopyPasteReferenceProcessor : CopyPastePostProcessor { return file.resolveImportReference(fqName).filterNot { /*TODO: temporary hack until we don't have ability to insert qualified reference into root package*/ diff --git a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinReferenceTransferableData.kt b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinReferenceTransferableData.kt index 95e655381d4..c1b4e2b0408 100644 --- a/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinReferenceTransferableData.kt +++ b/idea/src/org/jetbrains/kotlin/idea/codeInsight/KotlinReferenceTransferableData.kt @@ -18,8 +18,15 @@ package org.jetbrains.kotlin.idea.codeInsight import com.intellij.codeInsight.editorActions.TextBlockTransferableData import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.types.expressions.DoubleColonLHS import java.awt.datatransfer.DataFlavor import java.io.Serializable @@ -56,34 +63,23 @@ class KotlinReferenceData( var startOffset: Int, var endOffset: Int, val fqName: String, + val isQualifiable: Boolean, val kind: KotlinReferenceData.Kind ) : Cloneable, Serializable { enum class Kind { CLASS, PACKAGE, - NON_EXTENSION_CALLABLE, - EXTENSION_FUNCTION, - EXTENSION_PROPERTY; + FUNCTION, + PROPERTY; companion object { - fun fromDescriptor(descriptor: DeclarationDescriptor): KotlinReferenceData.Kind? { - return when (descriptor.getImportableDescriptor()) { - is ClassDescriptor -> - KotlinReferenceData.Kind.CLASS - - is PackageViewDescriptor -> - KotlinReferenceData.Kind.PACKAGE - - is FunctionDescriptor -> - if (descriptor.isExtension) KotlinReferenceData.Kind.EXTENSION_FUNCTION else KotlinReferenceData.Kind.NON_EXTENSION_CALLABLE - - is PropertyDescriptor -> - if (descriptor.isExtension) KotlinReferenceData.Kind.EXTENSION_PROPERTY else KotlinReferenceData.Kind.NON_EXTENSION_CALLABLE - - else -> - null - } + fun fromDescriptor(descriptor: DeclarationDescriptor) = when (descriptor.getImportableDescriptor()) { + is ClassDescriptor -> CLASS + is PackageViewDescriptor -> PACKAGE + is FunctionDescriptor -> FUNCTION + is PropertyDescriptor -> PROPERTY + else -> null } } } @@ -112,5 +108,19 @@ class KotlinReferenceData( null } } + + fun isQualifiable(refElement: KtElement, descriptor: DeclarationDescriptor): Boolean { + refElement.getParentOfTypeAndBranch { callableReference }?.let { + val receiverExpression = it.receiverExpression + + if (receiverExpression != null) { + val lhs = it.analyze(BodyResolveMode.PARTIAL)[BindingContext.DOUBLE_COLON_LHS, receiverExpression] + if (lhs is DoubleColonLHS.Expression) return false + } + return descriptor.containingDeclaration is ClassifierDescriptor + } + + return !descriptor.isExtension + } } } \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelCallableRef.expected.kt b/idea/testData/copyPaste/imports/TopLevelCallableRef.expected.kt new file mode 100644 index 00000000000..727e01de8df --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelCallableRef.expected.kt @@ -0,0 +1,7 @@ +package completion.p23381.apx2 + +import completion.p23381.funReference + +fun pasteHere() { + ::funReference +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelCallableRef.expected.names b/idea/testData/copyPaste/imports/TopLevelCallableRef.expected.names new file mode 100644 index 00000000000..2d82156d505 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelCallableRef.expected.names @@ -0,0 +1 @@ +completion.p23381.funReference \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelCallableRef.kt b/idea/testData/copyPaste/imports/TopLevelCallableRef.kt new file mode 100644 index 00000000000..0e162e02e5d --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelCallableRef.kt @@ -0,0 +1,9 @@ +package completion.p23381 + +fun funReference() {} + +class InputImpl { + fun context() { + ::funReference + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelCallableRef.to.kt b/idea/testData/copyPaste/imports/TopLevelCallableRef.to.kt new file mode 100644 index 00000000000..7e10f3fe77e --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelCallableRef.to.kt @@ -0,0 +1,5 @@ +package completion.p23381.apx2 + +fun pasteHere() { + +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.kt b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.kt new file mode 100644 index 00000000000..8d0bb46191d --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.kt @@ -0,0 +1,8 @@ +package completion.p23381.apx2 + +import completion.p23381.A +import completion.p23381.funReference + +fun pasteHere() { + A()::funReference +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.names b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.names new file mode 100644 index 00000000000..13dab64fb01 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.expected.names @@ -0,0 +1,2 @@ +completion.p23381.A +completion.p23381.funReference \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt new file mode 100644 index 00000000000..a0659a732ef --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt @@ -0,0 +1,11 @@ +package completion.p23381 + +class A + +fun A.funReference() {} + +class InputImpl { + fun context() { + A()::funReference + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.to.kt b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.to.kt new file mode 100644 index 00000000000..7e10f3fe77e --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.to.kt @@ -0,0 +1,5 @@ +package completion.p23381.apx2 + +fun pasteHere() { + +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.kt b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.kt new file mode 100644 index 00000000000..1a6771e39d6 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.kt @@ -0,0 +1,8 @@ +package completion.p23381.apx2 + +import completion.p23381.A +import completion.p23381.funReference + +fun pasteHere() { + A::funReference +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.names b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.names new file mode 100644 index 00000000000..13dab64fb01 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.expected.names @@ -0,0 +1,2 @@ +completion.p23381.A +completion.p23381.funReference \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt new file mode 100644 index 00000000000..98e62ea9227 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt @@ -0,0 +1,11 @@ +package completion.p23381 + +class A + +fun A.funReference() {} + +class InputImpl { + fun context() { + A::funReference + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.to.kt b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.to.kt new file mode 100644 index 00000000000..7e10f3fe77e --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.to.kt @@ -0,0 +1,5 @@ +package completion.p23381.apx2 + +fun pasteHere() { + +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.kt b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.kt new file mode 100644 index 00000000000..a156318b914 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.kt @@ -0,0 +1,7 @@ +package completion.p23381.apx2 + +import completion.p23381.A + +fun pasteHere() { + A()::funReference +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.names b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.names new file mode 100644 index 00000000000..3e36f1b01bb --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.expected.names @@ -0,0 +1 @@ +completion.p23381.A \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt new file mode 100644 index 00000000000..99636154026 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt @@ -0,0 +1,11 @@ +package completion.p23381 + +class A { + fun funReference() {} +} + +class InputImpl { + fun context() { + A()::funReference + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.to.kt b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.to.kt new file mode 100644 index 00000000000..7e10f3fe77e --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.to.kt @@ -0,0 +1,5 @@ +package completion.p23381.apx2 + +fun pasteHere() { + +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.kt b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.kt new file mode 100644 index 00000000000..48b8cd9867a --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.kt @@ -0,0 +1,7 @@ +package completion.p23381.apx2 + +import completion.p23381.A + +fun pasteHere() { + A::funReference +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.names b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.names new file mode 100644 index 00000000000..3e36f1b01bb --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.expected.names @@ -0,0 +1 @@ +completion.p23381.A \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt new file mode 100644 index 00000000000..88a1d72afe5 --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt @@ -0,0 +1,11 @@ +package completion.p23381 + +class A { + fun funReference() {} +} + +class InputImpl { + fun context() { + A::funReference + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.to.kt b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.to.kt new file mode 100644 index 00000000000..7e10f3fe77e --- /dev/null +++ b/idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.to.kt @@ -0,0 +1,5 @@ +package completion.p23381.apx2 + +fun pasteHere() { + +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InsertImportOnPasteTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InsertImportOnPasteTestGenerated.java index c8f82595b29..27c65409358 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InsertImportOnPasteTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InsertImportOnPasteTestGenerated.java @@ -306,6 +306,31 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast runTest("idea/testData/copyPaste/imports/ThisReference.kt"); } + @TestMetadata("TopLevelCallableRef.kt") + public void testTopLevelCallableRef() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelCallableRef.kt"); + } + + @TestMetadata("TopLevelExtensionCallableRefWithExpressionLHS.kt") + public void testTopLevelExtensionCallableRefWithExpressionLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt"); + } + + @TestMetadata("TopLevelExtensionCallableRefWithTypeLHS.kt") + public void testTopLevelExtensionCallableRefWithTypeLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt"); + } + + @TestMetadata("TopLevelMemberCallableRefWithExpressionLHS.kt") + public void testTopLevelMemberCallableRefWithExpressionLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt"); + } + + @TestMetadata("TopLevelMemberCallableRefWithTypeLHS.kt") + public void testTopLevelMemberCallableRefWithTypeLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt"); + } + @TestMetadata("TopLevelProperty.kt") public void testTopLevelProperty() throws Exception { runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt"); @@ -619,6 +644,31 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast runTest("idea/testData/copyPaste/imports/ThisReference.kt"); } + @TestMetadata("TopLevelCallableRef.kt") + public void testTopLevelCallableRef() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelCallableRef.kt"); + } + + @TestMetadata("TopLevelExtensionCallableRefWithExpressionLHS.kt") + public void testTopLevelExtensionCallableRefWithExpressionLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithExpressionLHS.kt"); + } + + @TestMetadata("TopLevelExtensionCallableRefWithTypeLHS.kt") + public void testTopLevelExtensionCallableRefWithTypeLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelExtensionCallableRefWithTypeLHS.kt"); + } + + @TestMetadata("TopLevelMemberCallableRefWithExpressionLHS.kt") + public void testTopLevelMemberCallableRefWithExpressionLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithExpressionLHS.kt"); + } + + @TestMetadata("TopLevelMemberCallableRefWithTypeLHS.kt") + public void testTopLevelMemberCallableRefWithTypeLHS() throws Exception { + runTest("idea/testData/copyPaste/imports/TopLevelMemberCallableRefWithTypeLHS.kt"); + } + @TestMetadata("TopLevelProperty.kt") public void testTopLevelProperty() throws Exception { runTest("idea/testData/copyPaste/imports/TopLevelProperty.kt");