diff --git a/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt b/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt index 6bfe3d22295..728e82f93ed 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/util/descriptorUtils.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.util import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.OverridingUtil.OverrideCompatibilityInfo.Result.OVERRIDABLE +import org.jetbrains.kotlin.resolve.calls.tower.getTypeAliasConstructors import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeConstructor @@ -76,4 +77,18 @@ fun TypeConstructor.supertypesWithAny(): Collection { .map { it.constructor.declarationDescriptor as? ClassDescriptor } .all { it == null || it.kind == ClassKind.INTERFACE } return if (noSuperClass) supertypes + builtIns.anyType else supertypes -} \ No newline at end of file +} + +val ClassifierDescriptorWithTypeParameters.constructors: Collection + get() = when (this) { + is TypeAliasDescriptor -> getTypeAliasConstructors() + is ClassDescriptor -> this.constructors + else -> emptyList() + } + +val ClassifierDescriptorWithTypeParameters.kind: ClassKind? + get() = when (this) { + is TypeAliasDescriptor -> classDescriptor?.kind + is ClassDescriptor -> kind + else -> null + } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt index b2ee7f72541..2f3f4bc62d4 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletion.kt @@ -210,7 +210,7 @@ class SmartCompletion( } if (callTypeAndReceiver is CallTypeAndReceiver.DEFAULT) { - TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion) + TypeInstantiationItems(resolutionFacade, bindingContext, visibilityFilter, toFromOriginalFileMapper, inheritorSearchScope, lookupElementFactory, forBasicCompletion, indicesHelper) .addTo(items, inheritanceSearchers, expectedInfos) if (expression is KtSimpleNameExpression) { diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt index 99bd216f91b..3cf275bfbac 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt @@ -33,9 +33,7 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.completion.* import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler -import org.jetbrains.kotlin.idea.core.ExpectedInfo -import org.jetbrains.kotlin.idea.core.Tail -import org.jetbrains.kotlin.idea.core.multipleFuzzyTypes +import org.jetbrains.kotlin.idea.core.* import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.* @@ -44,11 +42,12 @@ import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor import org.jetbrains.kotlin.platform.JavaToKotlinClassMap import org.jetbrains.kotlin.psi.KtClassOrObject import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.renderer.DescriptorRenderer import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.util.constructors +import org.jetbrains.kotlin.util.kind import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -59,7 +58,8 @@ class TypeInstantiationItems( val toFromOriginalFileMapper: ToFromOriginalFileMapper, val inheritorSearchScope: GlobalSearchScope, val lookupElementFactory: LookupElementFactory, - val forOrdinaryCompletion: Boolean + val forOrdinaryCompletion: Boolean, + val indicesHelper: KotlinIndicesHelper ) { fun addTo( items: MutableCollection, @@ -87,22 +87,31 @@ class TypeInstantiationItems( ) { if (fuzzyType.type.isFunctionType) return // do not show "object: ..." for function types - val classifier = fuzzyType.type.constructor.declarationDescriptor - if (classifier !is ClassDescriptor) return - - addSamConstructorItem(items, classifier, tail) + val classifier = fuzzyType.type.constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters ?: return + val classDescriptor = when (classifier) { + is ClassDescriptor -> classifier + is TypeAliasDescriptor -> classifier.classDescriptor + else -> null + } + addSamConstructorItem(items, classifier, classDescriptor, tail) items.addIfNotNull(createTypeInstantiationItem(fuzzyType, tail)) - if (!forOrdinaryCompletion && !KotlinBuiltIns.isAny(classifier)) { // do not search inheritors of Any + indicesHelper.resolveTypeAliasesUsingIndex(fuzzyType.type, classifier.name.asString()).forEach { + addSamConstructorItem(items, it, classDescriptor, tail) + val typeAliasFuzzyType = it.defaultType.toFuzzyType(fuzzyType.freeParameters) + items.addIfNotNull(createTypeInstantiationItem(typeAliasFuzzyType, tail)) + } + + if (classDescriptor != null && !forOrdinaryCompletion && !KotlinBuiltIns.isAny(classDescriptor)) { // do not search inheritors of Any val typeArgs = fuzzyType.type.arguments - inheritanceSearchers.addInheritorSearcher(classifier, classifier, typeArgs, fuzzyType.freeParameters, tail) + inheritanceSearchers.addInheritorSearcher(classDescriptor, classDescriptor, typeArgs, fuzzyType.freeParameters, tail) val javaClassId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(DescriptorUtils.getFqName(classifier)) if (javaClassId != null) { val javaAnalog = resolutionFacade.moduleDescriptor.resolveTopLevelClass(javaClassId.asSingleFqName(), NoLookupLocation.FROM_IDE) if (javaAnalog != null) { - inheritanceSearchers.addInheritorSearcher(javaAnalog, classifier, typeArgs, fuzzyType.freeParameters, tail) + inheritanceSearchers.addInheritorSearcher(javaAnalog, classDescriptor, typeArgs, fuzzyType.freeParameters, tail) } } } @@ -126,7 +135,7 @@ class TypeInstantiationItems( } private fun createTypeInstantiationItem(fuzzyType: FuzzyType, tail: Tail?): LookupElement? { - val classifier = fuzzyType.type.constructor.declarationDescriptor as? ClassDescriptor ?: return null + val classifier = fuzzyType.type.constructor.declarationDescriptor as? ClassifierDescriptorWithTypeParameters ?: return null var lookupElement = lookupElementFactory.createLookupElement(classifier, useReceiverTypes = false) @@ -286,15 +295,18 @@ class TypeInstantiationItems( return FuzzyType(this, freeParameters).freeParameters.isNotEmpty() } - private fun addSamConstructorItem(collection: MutableCollection, `class`: ClassDescriptor, tail: Tail?) { - if (`class`.kind == ClassKind.INTERFACE) { - val container = `class`.containingDeclaration + private fun addSamConstructorItem(collection: MutableCollection, + classifier: ClassifierDescriptorWithTypeParameters, + classDescriptor: ClassDescriptor?, + tail: Tail?) { + if (classDescriptor?.kind == ClassKind.INTERFACE) { + val container = classifier.containingDeclaration val scope = when (container) { is PackageFragmentDescriptor -> container.getMemberScope() is ClassDescriptor -> container.staticScope else -> return } - val samConstructor = scope.getContributedFunctions(`class`.name, NoLookupLocation.FROM_IDE) + val samConstructor = scope.getContributedFunctions(classifier.name, NoLookupLocation.FROM_IDE) .filterIsInstance() .singleOrNull() ?: return lookupElementFactory.createStandardLookupElementsForDescriptor(samConstructor, useReceiverTypes = false) diff --git a/idea/idea-completion/testData/smart/SAMConstructorForTypeAlias.kt b/idea/idea-completion/testData/smart/SAMConstructorForTypeAlias.kt new file mode 100644 index 00000000000..a3958fedc93 --- /dev/null +++ b/idea/idea-completion/testData/smart/SAMConstructorForTypeAlias.kt @@ -0,0 +1,13 @@ + +typealias TaRunnable = Runnable + + +fun usesRunnable(runnable: Runnable) { + +} + +fun usage() { + usesRunnable() +} + +// EXIST: {"lookupString":"TaRunnable","tailText":"() ()","typeText":"Runnable"} \ No newline at end of file diff --git a/idea/idea-completion/testData/smart/constructor/ConstructorViaTypeAlias.kt b/idea/idea-completion/testData/smart/constructor/ConstructorViaTypeAlias.kt new file mode 100644 index 00000000000..8d239e695af --- /dev/null +++ b/idea/idea-completion/testData/smart/constructor/ConstructorViaTypeAlias.kt @@ -0,0 +1,13 @@ +class SomeClass +typealias TaSomeClass = SomeClass + +fun usesSomeClass(p: SomeClass) { + +} + + +fun usage() { + usesSomeClass() +} + +// EXIST: {"lookupString":"TaSomeClass","tailText":"() ()"} \ No newline at end of file diff --git a/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution1ViaTypeAlias.kt b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution1ViaTypeAlias.kt new file mode 100644 index 00000000000..502d5fc03d1 --- /dev/null +++ b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution1ViaTypeAlias.kt @@ -0,0 +1,13 @@ +class SomeClass +typealias TaSomeClass = SomeClass + +fun usesSomeClass(p: SomeClass) { + +} + + +fun usage() { + usesSomeClass() +} + +// EXIST: {"lookupString":"TaSomeClass","tailText":"() ()","typeText":"SomeClass"} \ No newline at end of file diff --git a/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution2ViaTypeAlias.kt b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution2ViaTypeAlias.kt new file mode 100644 index 00000000000..b47317a0c0a --- /dev/null +++ b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution2ViaTypeAlias.kt @@ -0,0 +1,13 @@ +class SomeClass +typealias TaSomeClass = SomeClass + +fun usesSomeClass(p: SomeClass<*, *>) { + +} + + +fun usage() { + usesSomeClass() +} + +// EXIST: {"lookupString":"TaSomeClass","tailText":"() ()","typeText":"SomeClass"} \ No newline at end of file diff --git a/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsSubstitutionViaTypeAlias.kt b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsSubstitutionViaTypeAlias.kt new file mode 100644 index 00000000000..8d7531a95eb --- /dev/null +++ b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsSubstitutionViaTypeAlias.kt @@ -0,0 +1,13 @@ +class SomeClass +typealias TaSomeClass = SomeClass + +fun usesSomeClass(p: SomeClass<*, *>) { + +} + + +fun usage() { + usesSomeClass() +} + +// EXIST: {"lookupString":"TaSomeClass","tailText":"() ()","typeText":"SomeClass"} \ No newline at end of file diff --git a/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsViaTypeAlias.kt b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsViaTypeAlias.kt new file mode 100644 index 00000000000..722560399f9 --- /dev/null +++ b/idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsViaTypeAlias.kt @@ -0,0 +1,13 @@ +class SomeClass +typealias TaSomeClass = SomeClass + +fun usesSomeClass(p: SomeClass<*, *>) { + +} + + +fun usage() { + usesSomeClass() +} + +// EXIST: {"lookupString":"TaSomeClass","tailText":"() ()","typeText":"SomeClass"} \ No newline at end of file diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java index 4f67fcdec53..b6a6cb1bdd4 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/JvmSmartCompletionTestGenerated.java @@ -498,6 +498,12 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT doTest(fileName); } + @TestMetadata("SAMConstructorForTypeAlias.kt") + public void testSAMConstructorForTypeAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SAMConstructorForTypeAlias.kt"); + doTest(fileName); + } + @TestMetadata("SAMExpected1.kt") public void testSAMExpected1() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/SAMExpected1.kt"); @@ -827,6 +833,36 @@ public class JvmSmartCompletionTestGenerated extends AbstractJvmSmartCompletionT KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/idea-completion/testData/smart/constructor"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("ConstructorViaTypeAlias.kt") + public void testConstructorViaTypeAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/constructor/ConstructorViaTypeAlias.kt"); + doTest(fileName); + } + + @TestMetadata("ConstructorWithTypeParamsPartialSubstitution1ViaTypeAlias.kt") + public void testConstructorWithTypeParamsPartialSubstitution1ViaTypeAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution1ViaTypeAlias.kt"); + doTest(fileName); + } + + @TestMetadata("ConstructorWithTypeParamsPartialSubstitution2ViaTypeAlias.kt") + public void testConstructorWithTypeParamsPartialSubstitution2ViaTypeAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsPartialSubstitution2ViaTypeAlias.kt"); + doTest(fileName); + } + + @TestMetadata("ConstructorWithTypeParamsSubstitutionViaTypeAlias.kt") + public void testConstructorWithTypeParamsSubstitutionViaTypeAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsSubstitutionViaTypeAlias.kt"); + doTest(fileName); + } + + @TestMetadata("ConstructorWithTypeParamsViaTypeAlias.kt") + public void testConstructorWithTypeParamsViaTypeAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/constructor/ConstructorWithTypeParamsViaTypeAlias.kt"); + doTest(fileName); + } + @TestMetadata("GenericJavaClass.kt") public void testGenericJavaClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/smart/constructor/GenericJavaClass.kt"); diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt index 6b22c10939b..e5414184096 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/KotlinIndicesHelper.kt @@ -49,6 +49,7 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.isHiddenInResolution import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.asSimpleType import org.jetbrains.kotlin.utils.addToStdlib.singletonOrEmptyList import java.lang.reflect.Field import java.lang.reflect.Modifier @@ -175,29 +176,33 @@ class KotlinIndicesHelper( } - private fun resolveTypeAliasesUsingIndex(type: KotlinType, originalTypeName: String, out: MutableCollection) { + fun resolveTypeAliasesUsingIndex(type: KotlinType, originalTypeName: String): Set { + val typeConstructor = type.constructor + val index = KotlinTypeAliasByExpansionShortNameIndex.INSTANCE + val out = mutableSetOf() fun searchRecursively(typeName: String) { ProgressManager.checkCanceled() index[typeName, project, scope].asSequence() .map { it.resolveToDescriptorIfAny() as? TypeAliasDescriptor } .filterNotNull() - .filter { it.expandedType == type } - .map { it.name.asString() } + .filter { it.expandedType.constructor == typeConstructor } .filter { it !in out } - .onEach(::searchRecursively) - .toCollection(out) + .onEach { out.add(it) } + .map { it.name.asString() } + .forEach(::searchRecursively) } searchRecursively(originalTypeName) + return out } private fun MutableCollection.addTypeNames(type: KotlinType) { val constructor = type.constructor constructor.declarationDescriptor?.name?.asString()?.let { typeName -> add(typeName) - resolveTypeAliasesUsingIndex(type, typeName, this) + resolveTypeAliasesUsingIndex(type, typeName).mapTo(this, { it.name.asString() }) } constructor.supertypes.forEach { addTypeNames(it) } }