KT-14815 alt + enter -> "import" over a constructor reference is not working

#KT-14815 fixed
This commit is contained in:
Simon Ogorodnik
2016-12-23 18:47:01 +03:00
committed by Simon Ogorodnik
parent f9b2929bca
commit db8edb01c3
10 changed files with 146 additions and 15 deletions

View File

@@ -59,8 +59,8 @@ internal fun createSingleImportAction(
element: KtElement,
fqNames: Collection<FqName>
): KotlinAddImportAction {
val file = element.getContainingKtFile()
val prioritizer = Prioritizer(element.getContainingKtFile())
val file = element.containingKtFile
val prioritizer = Prioritizer(element.containingKtFile)
val variants = fqNames
.map { fqName ->
val sameFqNameDescriptors = file.resolveImportReference(fqName)
@@ -73,6 +73,29 @@ internal fun createSingleImportAction(
return KotlinAddImportAction(project, editor, element, variants)
}
internal fun createSingleImportActionForConstructor(
project: Project,
editor: Editor,
element: KtElement,
fqNames: Collection<FqName>
): KotlinAddImportAction {
val file = element.containingKtFile
val prioritizer = Prioritizer(element.containingKtFile)
val variants = fqNames
.map { fqName ->
val sameFqNameDescriptors = file.resolveImportReference(fqName.parent())
.filterIsInstance<ClassDescriptor>()
.flatMap { it.constructors }
val priority = sameFqNameDescriptors.map { prioritizer.priority(it) }.min() ?: return@map null
Prioritizer.VariantWithPriority(SingleImportVariant(fqName, sameFqNameDescriptors), priority)
}
.filterNotNull()
.sortedBy { it.priority }
.map { it.variant }
return KotlinAddImportAction(project, editor, element, variants)
}
internal fun createGroupedImportsAction(
project: Project,
editor: Editor,

View File

@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction
import org.jetbrains.kotlin.idea.actions.createGroupedImportsAction
import org.jetbrains.kotlin.idea.actions.createSingleImportAction
import org.jetbrains.kotlin.idea.actions.createSingleImportActionForConstructor
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
@@ -238,17 +239,9 @@ internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, f
if (expression is KtSimpleNameExpression) {
if (!expression.isImportDirectiveExpression() && !isSelectorInQualified(expression)) {
val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
val filterByCallType = callTypeAndReceiver.toFilter()
when (TargetPlatformDetector.getPlatform(expression.getContainingKtFile())) {
JsPlatform -> indicesHelper
// Enum entries should be contributes with members import fix
.getKotlinClasses({ it == name },
psiFilter = { ktDeclaration -> ktDeclaration !is KtEnumEntry },
kindFilter = { kind -> kind != ClassKind.ENUM_ENTRY })
.filterTo(result, filterByCallType)
JvmPlatform -> indicesHelper.getJvmClassesByName(name).filterTo(result, filterByCallType)
}
indicesHelper.getClassesByName(expression, name).filterTo(result, filterByCallType)
indicesHelper.getTopLevelTypeAliases { it == name }.filterTo(result, filterByCallType)
@@ -272,6 +265,31 @@ internal class ImportFix(expression: KtSimpleNameExpression) : OrdinaryImportFix
}
}
internal class ImportConstructorReferenceFix(expression: KtSimpleNameExpression) : ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) {
override fun getCallTypeAndReceiver() = element?.let {
CallTypeAndReceiver.detect(it) as? CallTypeAndReceiver.CALLABLE_REFERENCE
}
override fun fillCandidates(name: String, callTypeAndReceiver: CallTypeAndReceiver<*, *>, bindingContext: BindingContext, indicesHelper: KotlinIndicesHelper): List<DeclarationDescriptor> {
val expression = element ?: return emptyList()
val filterByCallType = callTypeAndReceiver.toFilter()
// TODO Type-aliases
return indicesHelper.getClassesByName(expression, name).asSequence().map { it.constructors }.flatten().filter(filterByCallType).toList()
}
override fun createAction(project: Project, editor: Editor, element: KtExpression): KotlinAddImportAction {
return createSingleImportActionForConstructor(project, editor, element, suggestions)
}
override val importNames = element?.mainReference?.resolvesByNames ?: emptyList()
companion object MyFactory : Factory() {
override fun createAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtSimpleNameExpression)?.let(::ImportConstructorReferenceFix)
}
}
internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase<KtExpression>(expression, MyFactory) {
override val importNames = OperatorNameConventions.INVOKE.singletonList()
@@ -279,7 +297,7 @@ internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase
companion object MyFactory : Factory() {
override fun createAction(diagnostic: Diagnostic) =
(diagnostic.psiElement as? KtExpression)?.let { InvokeImportFix(it) }
(diagnostic.psiElement as? KtExpression)?.let(::InvokeImportFix)
}
}
@@ -406,7 +424,7 @@ internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBa
val result = ArrayList<DeclarationDescriptor>()
val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
val filterByCallType = callTypeAndReceiver.toFilter()
indicesHelper.getKotlinEnumsByName(name).filterTo(result, filterByCallType)
@@ -552,4 +570,17 @@ object ImportForMissingOperatorFactory : KotlinSingleIntentionActionFactory() {
return null
}
}
}
private fun KotlinIndicesHelper.getClassesByName(expressionForPlatform: KtExpression, name: String) =
when (TargetPlatformDetector.getPlatform(expressionForPlatform.containingKtFile)) {
JsPlatform -> getKotlinClasses({ it == name },
// Enum entries should be contributes with members import fix
psiFilter = { ktDeclaration -> ktDeclaration !is KtEnumEntry },
kindFilter = { kind -> kind != ClassKind.ENUM_ENTRY })
JvmPlatform -> getJvmClassesByName(name)
else -> emptyList()
}
private fun CallTypeAndReceiver<*, *>.toFilter() = { descriptor: DeclarationDescriptor -> this.callType.descriptorKindFilter.accepts(descriptor) }

View File

@@ -140,6 +140,7 @@ class QuickFixRegistrar : QuickFixContributor {
UNRESOLVED_REFERENCE.registerFactory(ImportMemberFix)
UNRESOLVED_REFERENCE.registerFactory(ImportFix)
UNRESOLVED_REFERENCE.registerFactory(ImportConstructorReferenceFix)
TOO_MANY_ARGUMENTS.registerFactory(ImportForMismatchingArgumentsFix)
NO_VALUE_FOR_PARAMETER.registerFactory(ImportForMismatchingArgumentsFix)

View File

@@ -0,0 +1,10 @@
// "Import" "true"
// ERROR: Unresolved reference: Some
package p1
import p2.Some
import p2.receiveSomeCtor
fun a() {
receiveSomeCtor(::Some<caret>)
}

View File

@@ -0,0 +1,10 @@
package p2
class Some {
}
fun receiveSomeCtor(ctor: () -> Some) {
}

View File

@@ -0,0 +1,9 @@
// "Import" "true"
// ERROR: Unresolved reference: Some
package p1
import p2.receiveSomeCtor
fun a() {
receiveSomeCtor(::Some<caret>)
}

View File

@@ -0,0 +1,14 @@
// "Import" "false"
// ERROR: Unresolved reference: Some
// ACTION: Convert reference to lambda
// ACTION: Convert to expression body
// ACTION: Create function 'Some'
// ACTION: Rename reference
package p1
import p2.receiveSomeCtor
fun some() {
receiveSomeCtor(::Some<caret>)
}

View File

@@ -0,0 +1,7 @@
package p2
interface Some
fun receiveSomeCtor(ctor: () -> Some) {
}

View File

@@ -0,0 +1,14 @@
// "Import" "false"
// ERROR: Unresolved reference: Some
// ACTION: Convert reference to lambda
// ACTION: Convert to expression body
// ACTION: Create function 'Some'
// ACTION: Rename reference
package p1
import p2.receiveSomeCtor
fun some() {
receiveSomeCtor(::Some<caret>)
}

View File

@@ -96,6 +96,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile(fileName);
}
@TestMetadata("constructorReference.before.Main.kt")
public void testConstructorReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/constructorReference.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("delegateExtensionBoth.test")
public void testDelegateExtensionBoth() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionBoth.test");
@@ -474,6 +480,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
doTestWithExtraFile(fileName);
}
@TestMetadata("noImportInterfaceRefAsConstructor.before.Main.kt")
public void testNoImportInterfaceRefAsConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noImportInterfaceRefAsConstructor.before.Main.kt");
doTestWithExtraFile(fileName);
}
@TestMetadata("noImportsForClassInExcludedPackage.before.Main.kt")
public void testNoImportsForClassInExcludedPackage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noImportsForClassInExcludedPackage.before.Main.kt");