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
|
||||
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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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(
|
||||
val reference: JetReference,
|
||||
val refData: KotlinReferenceData
|
||||
@@ -234,7 +215,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
||||
return referenceData.map {
|
||||
val referenceElement = findReference(it, file, blockStart)
|
||||
if (referenceElement != null)
|
||||
createReferenceToRestoreData(referenceElement, it, fileResolutionScope)
|
||||
createReferenceToRestoreData(referenceElement, it, file, fileResolutionScope)
|
||||
else
|
||||
null
|
||||
}.filterNotNull()
|
||||
@@ -261,7 +242,7 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
||||
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)
|
||||
|
||||
if (refData.kind == KotlinReferenceData.Kind.EXTENSION_FUNCTION) {
|
||||
@@ -285,6 +266,12 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
||||
.filterNotNull()
|
||||
.toSet()
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -302,12 +289,12 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
||||
for ((reference, refData) in referencesToRestore) {
|
||||
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)
|
||||
bindingRequests.add(BindingRequest(pointer, fqName))
|
||||
}
|
||||
|
||||
if (refData.kind.isExtensionCallable()) {
|
||||
if (refData.kind.isExtension()) {
|
||||
extensionsToImport.addIfNotNull(findCallableToImport(fqName, file))
|
||||
}
|
||||
}
|
||||
@@ -322,19 +309,21 @@ public class KotlinCopyPasteReferenceProcessor() : CopyPastePostProcessor<Kotlin
|
||||
performDelayedShortening(file.getProject())
|
||||
}
|
||||
|
||||
private fun KotlinReferenceData.Kind.isExtensionCallable()
|
||||
private fun KotlinReferenceData.Kind.isExtension()
|
||||
= 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 moduleDescriptor = file.getResolutionFacade().findModuleDescriptor(file)
|
||||
val scope = JetModuleUtil.getSubpackagesOfRootScope(moduleDescriptor)
|
||||
return QualifiedExpressionResolver()
|
||||
.processImportReference(importDirective, scope, scope, BindingTraceContext(), LookupMode.EVERYTHING)
|
||||
.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> {
|
||||
val fqNames = referencesToRestore.map { it.refData.fqName }.toSortedSet()
|
||||
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.codeInsight
|
||||
|
||||
import com.intellij.codeInsight.editorActions.ReferenceData
|
||||
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.io.Serializable
|
||||
import kotlin.properties.Delegates
|
||||
@@ -64,6 +66,27 @@ public class KotlinReferenceData(
|
||||
NON_EXTENSION_CALLABLE
|
||||
EXTENSION_FUNCTION
|
||||
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 {
|
||||
|
||||
@@ -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.util.io.FileUtil
|
||||
import com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.idea.AbstractCopyPasteTest
|
||||
import org.jetbrains.kotlin.idea.PluginTestCaseBase
|
||||
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.test.InTextDirectivesUtils
|
||||
import org.jetbrains.kotlin.test.JetTestUtils
|
||||
@@ -29,7 +31,9 @@ import java.io.File
|
||||
public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest() {
|
||||
private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/imports"
|
||||
private val DEFAULT_TO_FILE_TEXT = "package to\n\n<caret>"
|
||||
|
||||
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
|
||||
|
||||
@@ -44,13 +48,22 @@ public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest()
|
||||
private fun doTestAction(cutOrCopy: String, path: String) {
|
||||
myFixture.setTestDataPath(BASE_PATH)
|
||||
val testFile = File(path)
|
||||
val testFileText = FileUtil.loadFile(testFile, true)
|
||||
val testFileName = testFile.getName()
|
||||
|
||||
configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.kt"))
|
||||
configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.java"))
|
||||
val dependencyPsiFile1 = configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.kt"))
|
||||
val dependencyPsiFile2 = configureByDependencyIfExists(testFileName.replace(".kt", ".dependency.java"))
|
||||
myFixture.configureByFile(testFileName)
|
||||
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()
|
||||
|
||||
configureToFile(testFileName.replace(".kt", ".to.kt"))
|
||||
@@ -60,7 +73,7 @@ public abstract class AbstractInsertImportOnPasteTest : AbstractCopyPasteTest()
|
||||
JetTestUtils.assertEqualsToFile(File(path.replace(".kt", ".expected.names")), namesToImportDump)
|
||||
|
||||
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()
|
||||
else
|
||||
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)
|
||||
if (file.exists()) {
|
||||
if (dependencyFileName.endsWith(".java")) {
|
||||
//allow test framework to put it under right directory
|
||||
myFixture.addClass(FileUtil.loadFile(file, true))
|
||||
}
|
||||
else {
|
||||
myFixture.configureByFile(dependencyFileName)
|
||||
}
|
||||
if (!file.exists()) return null
|
||||
return if (dependencyFileName.endsWith(".java")) {
|
||||
//allow test framework to put it under right directory
|
||||
myFixture.addClass(FileUtil.loadFile(file, true)).getContainingFile()
|
||||
}
|
||||
else {
|
||||
myFixture.configureByFile(dependencyFileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -132,6 +132,12 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
||||
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")
|
||||
public void testDependencyOnJava() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependencyOnJava.kt");
|
||||
@@ -465,6 +471,12 @@ public class InsertImportOnPasteTestGenerated extends AbstractInsertImportOnPast
|
||||
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")
|
||||
public void testDependencyOnJava() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("idea/testData/copyPaste/imports/DependencyOnJava.kt");
|
||||
|
||||
Reference in New Issue
Block a user