KT-14815 alt + enter -> "import" over a constructor reference is not working
#KT-14815 fixed
This commit is contained in:
committed by
Simon Ogorodnik
parent
f9b2929bca
commit
db8edb01c3
@@ -59,8 +59,8 @@ internal fun createSingleImportAction(
|
|||||||
element: KtElement,
|
element: KtElement,
|
||||||
fqNames: Collection<FqName>
|
fqNames: Collection<FqName>
|
||||||
): KotlinAddImportAction {
|
): KotlinAddImportAction {
|
||||||
val file = element.getContainingKtFile()
|
val file = element.containingKtFile
|
||||||
val prioritizer = Prioritizer(element.getContainingKtFile())
|
val prioritizer = Prioritizer(element.containingKtFile)
|
||||||
val variants = fqNames
|
val variants = fqNames
|
||||||
.map { fqName ->
|
.map { fqName ->
|
||||||
val sameFqNameDescriptors = file.resolveImportReference(fqName)
|
val sameFqNameDescriptors = file.resolveImportReference(fqName)
|
||||||
@@ -73,6 +73,29 @@ internal fun createSingleImportAction(
|
|||||||
return KotlinAddImportAction(project, editor, element, variants)
|
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(
|
internal fun createGroupedImportsAction(
|
||||||
project: Project,
|
project: Project,
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import org.jetbrains.kotlin.idea.KotlinBundle
|
|||||||
import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction
|
import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction
|
||||||
import org.jetbrains.kotlin.idea.actions.createGroupedImportsAction
|
import org.jetbrains.kotlin.idea.actions.createGroupedImportsAction
|
||||||
import org.jetbrains.kotlin.idea.actions.createSingleImportAction
|
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.analysis.analyzeInContext
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
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 is KtSimpleNameExpression) {
|
||||||
if (!expression.isImportDirectiveExpression() && !isSelectorInQualified(expression)) {
|
if (!expression.isImportDirectiveExpression() && !isSelectorInQualified(expression)) {
|
||||||
val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
|
val filterByCallType = callTypeAndReceiver.toFilter()
|
||||||
|
|
||||||
when (TargetPlatformDetector.getPlatform(expression.getContainingKtFile())) {
|
indicesHelper.getClassesByName(expression, name).filterTo(result, filterByCallType)
|
||||||
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.getTopLevelTypeAliases { it == 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) {
|
internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase<KtExpression>(expression, MyFactory) {
|
||||||
override val importNames = OperatorNameConventions.INVOKE.singletonList()
|
override val importNames = OperatorNameConventions.INVOKE.singletonList()
|
||||||
|
|
||||||
@@ -279,7 +297,7 @@ internal class InvokeImportFix(expression: KtExpression) : OrdinaryImportFixBase
|
|||||||
|
|
||||||
companion object MyFactory : Factory() {
|
companion object MyFactory : Factory() {
|
||||||
override fun createAction(diagnostic: Diagnostic) =
|
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 result = ArrayList<DeclarationDescriptor>()
|
||||||
|
|
||||||
val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
|
val filterByCallType = callTypeAndReceiver.toFilter()
|
||||||
|
|
||||||
indicesHelper.getKotlinEnumsByName(name).filterTo(result, filterByCallType)
|
indicesHelper.getKotlinEnumsByName(name).filterTo(result, filterByCallType)
|
||||||
|
|
||||||
@@ -553,3 +571,16 @@ object ImportForMissingOperatorFactory : KotlinSingleIntentionActionFactory() {
|
|||||||
return null
|
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) }
|
||||||
@@ -140,6 +140,7 @@ class QuickFixRegistrar : QuickFixContributor {
|
|||||||
|
|
||||||
UNRESOLVED_REFERENCE.registerFactory(ImportMemberFix)
|
UNRESOLVED_REFERENCE.registerFactory(ImportMemberFix)
|
||||||
UNRESOLVED_REFERENCE.registerFactory(ImportFix)
|
UNRESOLVED_REFERENCE.registerFactory(ImportFix)
|
||||||
|
UNRESOLVED_REFERENCE.registerFactory(ImportConstructorReferenceFix)
|
||||||
|
|
||||||
TOO_MANY_ARGUMENTS.registerFactory(ImportForMismatchingArgumentsFix)
|
TOO_MANY_ARGUMENTS.registerFactory(ImportForMismatchingArgumentsFix)
|
||||||
NO_VALUE_FOR_PARAMETER.registerFactory(ImportForMismatchingArgumentsFix)
|
NO_VALUE_FOR_PARAMETER.registerFactory(ImportForMismatchingArgumentsFix)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
// "Import" "true"
|
||||||
|
// ERROR: Unresolved reference: Some
|
||||||
|
package p1
|
||||||
|
|
||||||
|
import p2.Some
|
||||||
|
import p2.receiveSomeCtor
|
||||||
|
|
||||||
|
fun a() {
|
||||||
|
receiveSomeCtor(::Some<caret>)
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package p2
|
||||||
|
|
||||||
|
class Some {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun receiveSomeCtor(ctor: () -> Some) {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// "Import" "true"
|
||||||
|
// ERROR: Unresolved reference: Some
|
||||||
|
package p1
|
||||||
|
|
||||||
|
import p2.receiveSomeCtor
|
||||||
|
|
||||||
|
fun a() {
|
||||||
|
receiveSomeCtor(::Some<caret>)
|
||||||
|
}
|
||||||
+14
@@ -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>)
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package p2
|
||||||
|
|
||||||
|
interface Some
|
||||||
|
|
||||||
|
fun receiveSomeCtor(ctor: () -> Some) {
|
||||||
|
|
||||||
|
}
|
||||||
+14
@@ -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>)
|
||||||
|
}
|
||||||
@@ -96,6 +96,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
doTestWithExtraFile(fileName);
|
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")
|
@TestMetadata("delegateExtensionBoth.test")
|
||||||
public void testDelegateExtensionBoth() throws Exception {
|
public void testDelegateExtensionBoth() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionBoth.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/delegateExtensionBoth.test");
|
||||||
@@ -474,6 +480,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
|
|||||||
doTestWithExtraFile(fileName);
|
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")
|
@TestMetadata("noImportsForClassInExcludedPackage.before.Main.kt")
|
||||||
public void testNoImportsForClassInExcludedPackage() throws Exception {
|
public void testNoImportsForClassInExcludedPackage() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noImportsForClassInExcludedPackage.before.Main.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/noImportsForClassInExcludedPackage.before.Main.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user