Imports on paste: don't try to import symbols not visible from current module
This commit is contained in:
+16
-27
@@ -164,32 +164,13 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
|||||||
val fqName = descriptor.importableFqName ?: continue
|
val fqName = descriptor.importableFqName ?: continue
|
||||||
if (!descriptor.canBeReferencedViaImport()) continue
|
if (!descriptor.canBeReferencedViaImport()) continue
|
||||||
|
|
||||||
val kind = referenceDataKind(descriptor) ?: continue
|
val kind = KotlinReferenceData.Kind.fromDescriptor(descriptor) ?: continue
|
||||||
add(KotlinReferenceData(element.range.start - startOffset, element.range.end - startOffset, fqName.asString(), kind))
|
add(KotlinReferenceData(element.range.start - startOffset, element.range.end - startOffset, fqName.asString(), kind))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun referenceDataKind(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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class ReferenceToRestoreData(
|
private data class ReferenceToRestoreData(
|
||||||
val reference: JetReference,
|
val reference: JetReference,
|
||||||
val refData: KotlinReferenceData
|
val refData: KotlinReferenceData
|
||||||
@@ -234,7 +215,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
|||||||
return referenceData.map {
|
return referenceData.map {
|
||||||
val referenceElement = findReference(it, file, blockStart)
|
val referenceElement = findReference(it, file, blockStart)
|
||||||
if (referenceElement != null)
|
if (referenceElement != null)
|
||||||
createReferenceToRestoreData(referenceElement, it, fileResolutionScope)
|
createReferenceToRestoreData(referenceElement, it, file, fileResolutionScope)
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
}.filterNotNull()
|
}.filterNotNull()
|
||||||
@@ -261,7 +242,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createReferenceToRestoreData(element: JetElement, refData: KotlinReferenceData, fileResolutionScope: JetScope): ReferenceToRestoreData? {
|
private fun createReferenceToRestoreData(element: JetElement, refData: KotlinReferenceData, file: JetFile, fileResolutionScope: JetScope): ReferenceToRestoreData? {
|
||||||
val originalFqName = FqName(refData.fqName)
|
val originalFqName = FqName(refData.fqName)
|
||||||
|
|
||||||
if (refData.kind == KotlinReferenceData.Kind.EXTENSION_FUNCTION) {
|
if (refData.kind == KotlinReferenceData.Kind.EXTENSION_FUNCTION) {
|
||||||
@@ -285,6 +266,12 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
|||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.toSet()
|
.toSet()
|
||||||
if (referencedFqNames.singleOrNull() == originalFqName) return null
|
if (referencedFqNames.singleOrNull() == originalFqName) return null
|
||||||
|
|
||||||
|
// check that descriptor to import exists and is accessible from the current module
|
||||||
|
if (!findImportableDescriptors(originalFqName, file).any { KotlinReferenceData.Kind.fromDescriptor(it) == refData.kind }) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
return ReferenceToRestoreData(reference, refData)
|
return ReferenceToRestoreData(reference, refData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,12 +289,12 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
|||||||
for ((reference, refData) in referencesToRestore) {
|
for ((reference, refData) in referencesToRestore) {
|
||||||
val fqName = FqName(refData.fqName)
|
val fqName = FqName(refData.fqName)
|
||||||
|
|
||||||
if (!refData.kind.isExtensionCallable() && reference is JetSimpleNameReference) {
|
if (!refData.kind.isExtension() && reference is JetSimpleNameReference) {
|
||||||
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.getElement(), file)
|
val pointer = smartPointerManager.createSmartPsiElementPointer(reference.getElement(), file)
|
||||||
bindingRequests.add(BindingRequest(pointer, fqName))
|
bindingRequests.add(BindingRequest(pointer, fqName))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (refData.kind.isExtensionCallable()) {
|
if (refData.kind.isExtension()) {
|
||||||
extensionsToImport.addIfNotNull(findCallableToImport(fqName, file))
|
extensionsToImport.addIfNotNull(findCallableToImport(fqName, file))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -322,19 +309,21 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
|||||||
performDelayedShortening(file.getProject())
|
performDelayedShortening(file.getProject())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KotlinReferenceData.Kind.isExtensionCallable()
|
private fun KotlinReferenceData.Kind.isExtension()
|
||||||
= this == KotlinReferenceData.Kind.EXTENSION_FUNCTION || this == KotlinReferenceData.Kind.EXTENSION_PROPERTY
|
= this == KotlinReferenceData.Kind.EXTENSION_FUNCTION || this == KotlinReferenceData.Kind.EXTENSION_PROPERTY
|
||||||
|
|
||||||
private fun findCallableToImport(fqName: FqName, file: JetFile): CallableDescriptor? {
|
private fun findImportableDescriptors(fqName: FqName, file: JetFile): Collection<DeclarationDescriptor> {
|
||||||
val importDirective = JetPsiFactory(file.getProject()).createImportDirective(ImportPath(fqName, false))
|
val importDirective = JetPsiFactory(file.getProject()).createImportDirective(ImportPath(fqName, false))
|
||||||
val moduleDescriptor = file.getResolutionFacade().findModuleDescriptor(file)
|
val moduleDescriptor = file.getResolutionFacade().findModuleDescriptor(file)
|
||||||
val scope = JetModuleUtil.getSubpackagesOfRootScope(moduleDescriptor)
|
val scope = JetModuleUtil.getSubpackagesOfRootScope(moduleDescriptor)
|
||||||
return QualifiedExpressionResolver()
|
return QualifiedExpressionResolver()
|
||||||
.processImportReference(importDirective, scope, scope, BindingTraceContext(), LookupMode.EVERYTHING)
|
.processImportReference(importDirective, scope, scope, BindingTraceContext(), LookupMode.EVERYTHING)
|
||||||
.getAllDescriptors()
|
.getAllDescriptors()
|
||||||
.firstIsInstanceOrNull<CallableDescriptor>()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun findCallableToImport(fqName: FqName, file: JetFile): CallableDescriptor?
|
||||||
|
= findImportableDescriptors(fqName, file).firstIsInstanceOrNull<CallableDescriptor>()
|
||||||
|
|
||||||
private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> {
|
private fun showRestoreReferencesDialog(project: Project, referencesToRestore: List<ReferenceToRestoreData>): Collection<ReferenceToRestoreData> {
|
||||||
val fqNames = referencesToRestore.map { it.refData.fqName }.toSortedSet()
|
val fqNames = referencesToRestore.map { it.refData.fqName }.toSortedSet()
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.idea.codeInsight
|
package org.jetbrains.kotlin.idea.codeInsight
|
||||||
|
|
||||||
import com.intellij.codeInsight.editorActions.ReferenceData
|
|
||||||
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
|
import com.intellij.codeInsight.editorActions.TextBlockTransferableData
|
||||||
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor
|
||||||
|
import org.jetbrains.kotlin.resolve.descriptorUtil.isExtension
|
||||||
import java.awt.datatransfer.DataFlavor
|
import java.awt.datatransfer.DataFlavor
|
||||||
import java.io.Serializable
|
import java.io.Serializable
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
@@ -64,6 +66,27 @@ public class KotlinReferenceData(
|
|||||||
NON_EXTENSION_CALLABLE
|
NON_EXTENSION_CALLABLE
|
||||||
EXTENSION_FUNCTION
|
EXTENSION_FUNCTION
|
||||||
EXTENSION_PROPERTY
|
EXTENSION_PROPERTY
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
public 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override fun clone(): KotlinReferenceData {
|
public override fun clone(): KotlinReferenceData {
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
A
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package pack.dependency
|
||||||
|
|
||||||
|
class Dependency
|
||||||
|
|
||||||
|
fun topLevelFun(){}
|
||||||
|
|
||||||
|
fun String.extensionFun(){}
|
||||||
|
|
||||||
|
fun String.extensionProp: Int = 1
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// ERROR: Unresolved reference: Dependency
|
||||||
|
// ERROR: Unresolved reference: topLevelFun
|
||||||
|
// ERROR: Unresolved reference: extensionFun
|
||||||
|
// ERROR: Unresolved reference: extensionProp
|
||||||
|
// ERROR: Unresolved reference: dependency
|
||||||
|
package to
|
||||||
|
|
||||||
|
|
||||||
|
fun foo(d: Dependency) {
|
||||||
|
topLevelFun()
|
||||||
|
"".extensionFun()
|
||||||
|
println("".extensionProp)
|
||||||
|
dependency.Dependency()
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// RUNTIME
|
||||||
|
// DELETE_DEPENDENCIES_BEFORE_PASTE
|
||||||
|
|
||||||
|
import pack.dependency.*
|
||||||
|
import pack.dependency
|
||||||
|
|
||||||
|
<selection>
|
||||||
|
fun foo(d: Dependency) {
|
||||||
|
topLevelFun()
|
||||||
|
"".extensionFun()
|
||||||
|
println("".extensionProp)
|
||||||
|
dependency.Dependency()
|
||||||
|
}
|
||||||
|
</selection>
|
||||||
+24
-12
@@ -18,9 +18,11 @@ package org.jetbrains.kotlin.idea.codeInsight
|
|||||||
|
|
||||||
import com.intellij.openapi.actionSystem.IdeActions
|
import com.intellij.openapi.actionSystem.IdeActions
|
||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
import org.jetbrains.kotlin.idea.AbstractCopyPasteTest
|
import org.jetbrains.kotlin.idea.AbstractCopyPasteTest
|
||||||
import org.jetbrains.kotlin.idea.PluginTestCaseBase
|
import org.jetbrains.kotlin.idea.PluginTestCaseBase
|
||||||
import org.jetbrains.kotlin.idea.testUtils.dumpTextWithErrors
|
import org.jetbrains.kotlin.idea.testUtils.dumpTextWithErrors
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
import org.jetbrains.kotlin.psi.JetFile
|
import org.jetbrains.kotlin.psi.JetFile
|
||||||
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
import org.jetbrains.kotlin.test.InTextDirectivesUtils
|
||||||
import org.jetbrains.kotlin.test.JetTestUtils
|
import org.jetbrains.kotlin.test.JetTestUtils
|
||||||
@@ -29,7 +31,9 @@ import java.io.File
|
|||||||
public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest() {
|
public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest() {
|
||||||
private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/imports"
|
private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/imports"
|
||||||
private val DEFAULT_TO_FILE_TEXT = "package to\n\n<caret>"
|
private val DEFAULT_TO_FILE_TEXT = "package to\n\n<caret>"
|
||||||
|
|
||||||
private val NO_ERRORS_DUMP_DIRECTIVE = "// NO_ERRORS_DUMP"
|
private val NO_ERRORS_DUMP_DIRECTIVE = "// NO_ERRORS_DUMP"
|
||||||
|
private val DELETE_DEPENDENCIES_BEFORE_PASTE_DIRECTIVE = "// DELETE_DEPENDENCIES_BEFORE_PASTE"
|
||||||
|
|
||||||
override fun getTestDataPath() = BASE_PATH
|
override fun getTestDataPath() = BASE_PATH
|
||||||
|
|
||||||
@@ -44,13 +48,22 @@ public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest()
|
|||||||
private fun doTestAction(cutOrCopy: String, path: String) {
|
private fun doTestAction(cutOrCopy: String, path: String) {
|
||||||
myFixture.setTestDataPath(BASE_PATH)
|
myFixture.setTestDataPath(BASE_PATH)
|
||||||
val testFile = File(path)
|
val testFile = File(path)
|
||||||
|
val testFileText = FileUtil.loadFile(testFile, true)
|
||||||
val testFileName = testFile.getName()
|
val testFileName = testFile.getName()
|
||||||
|
|
||||||
configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.kt"))
|
val dependencyPsiFile1 = configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.kt"))
|
||||||
configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.java"))
|
val dependencyPsiFile2 = configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.java"))
|
||||||
myFixture.configureByFile(testFileName)
|
myFixture.configureByFile(testFileName)
|
||||||
myFixture.performEditorAction(cutOrCopy)
|
myFixture.performEditorAction(cutOrCopy)
|
||||||
|
|
||||||
|
if (InTextDirectivesUtils.isDirectiveDefined(testFileText, DELETE_DEPENDENCIES_BEFORE_PASTE_DIRECTIVE)) {
|
||||||
|
assert(dependencyPsiFile1 != null || dependencyPsiFile2 != null)
|
||||||
|
runWriteAction {
|
||||||
|
dependencyPsiFile1?.getVirtualFile()?.delete(null)
|
||||||
|
dependencyPsiFile2?.getVirtualFile()?.delete(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested = emptyList()
|
KotlinCopyPasteReferenceProcessor.declarationsToImportSuggested = emptyList()
|
||||||
|
|
||||||
configureToFile(testFileName.replace(".kt", ".to.kt"))
|
configureToFile(testFileName.replace(".kt", ".to.kt"))
|
||||||
@@ -60,7 +73,7 @@ public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest()
|
|||||||
JetTestUtils.assertEqualsToFile(File(path.replace(".kt", ".expected.names")), namesToImportDump)
|
JetTestUtils.assertEqualsToFile(File(path.replace(".kt", ".expected.names")), namesToImportDump)
|
||||||
|
|
||||||
val resultFile = myFixture.getFile() as JetFile
|
val resultFile = myFixture.getFile() as JetFile
|
||||||
val resultText = if (InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(testFile, true), NO_ERRORS_DUMP_DIRECTIVE))
|
val resultText = if (InTextDirectivesUtils.isDirectiveDefined(testFileText, NO_ERRORS_DUMP_DIRECTIVE))
|
||||||
resultFile.getText()
|
resultFile.getText()
|
||||||
else
|
else
|
||||||
resultFile.dumpTextWithErrors()
|
resultFile.dumpTextWithErrors()
|
||||||
@@ -76,16 +89,15 @@ public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun configureByDependencyIfExists(dependencyFileName: String) {
|
private fun configureByDependencyIfExists(dependencyFileName: String): PsiFile? {
|
||||||
val file = File(BASE_PATH + "/" + dependencyFileName)
|
val file = File(BASE_PATH + "/" + dependencyFileName)
|
||||||
if (file.exists()) {
|
if (!file.exists()) return null
|
||||||
if (dependencyFileName.endsWith(".java")) {
|
return if (dependencyFileName.endsWith(".java")) {
|
||||||
//allow test framework to put it under right directory
|
//allow test framework to put it under right directory
|
||||||
myFixture.addClass(FileUtil.loadFile(file, true))
|
myFixture.addClass(FileUtil.loadFile(file, true)).getContainingFile()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
myFixture.configureByFile(dependencyFileName)
|
myFixture.configureByFile(dependencyFileName)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+12
@@ -132,6 +132,12 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
|||||||
doTestCopy(fileName);
|
doTestCopy(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("DependenciesNotAccessibleOnPaste.kt")
|
||||||
|
public void testDependenciesNotAccessibleOnPaste() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependenciesNotAccessibleOnPaste.kt");
|
||||||
|
doTestCopy(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("DependencyOnJava.kt")
|
@TestMetadata("DependencyOnJava.kt")
|
||||||
public void testDependencyOnJava() throws Exception {
|
public void testDependencyOnJava() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependencyOnJava.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependencyOnJava.kt");
|
||||||
@@ -465,6 +471,12 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
|||||||
doTestCut(fileName);
|
doTestCut(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("DependenciesNotAccessibleOnPaste.kt")
|
||||||
|
public void testDependenciesNotAccessibleOnPaste() throws Exception {
|
||||||
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependenciesNotAccessibleOnPaste.kt");
|
||||||
|
doTestCut(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("DependencyOnJava.kt")
|
@TestMetadata("DependencyOnJava.kt")
|
||||||
public void testDependencyOnJava() throws Exception {
|
public void testDependencyOnJava() throws Exception {
|
||||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependencyOnJava.kt");
|
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependencyOnJava.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user