diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeAlias.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeAlias.kt index 70c8c1271f8..438697de8d5 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeAlias.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtTypeAlias.kt @@ -22,6 +22,7 @@ import com.intellij.navigation.ItemPresentationProviders import com.intellij.psi.PsiElement import org.jetbrains.kotlin.KtNodeTypes import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.psi.stubs.KotlinPlaceHolderStub import org.jetbrains.kotlin.psi.stubs.KotlinTypeAliasStub import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes @@ -40,8 +41,15 @@ class KtTypeAlias : KtTypeParameterListOwnerStub, KtNamedDe findChildByType(KtTokens.TYPE_ALIAS_KEYWORD) @IfNotParsed - fun getTypeReference(): KtTypeReference? = - findChildByType(KtNodeTypes.TYPE_REFERENCE) + fun getTypeReference(): KtTypeReference? { + if (stub != null) { + val typeReferences = getStubOrPsiChildrenAsList>(KtStubElementTypes.TYPE_REFERENCE) + return typeReferences[0] + } + else { + return findChildByType(KtNodeTypes.TYPE_REFERENCE) + } + } override fun getPresentation() = ItemPresentationProviders.getItemPresentation(this) } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt index b0c7a366be5..980bb20a75f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/AllClassesCompletion.kt @@ -24,6 +24,7 @@ import com.intellij.psi.PsiLiteral import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper import org.jetbrains.kotlin.idea.core.isJavaClassNotToBeUsedInKotlin import org.jetbrains.kotlin.idea.project.ProjectStructureUtil @@ -41,23 +42,30 @@ class AllClassesCompletion(private val parameters: CompletionParameters, private val prefixMatcher: PrefixMatcher, private val resolutionFacade: ResolutionFacade, private val kindFilter: (ClassKind) -> Boolean, + private val includeTypeAliases: Boolean, private val includeJavaClassesNotToBeUsed: Boolean ) { - fun collect(classDescriptorCollector: (ClassDescriptor) -> Unit, javaClassCollector: (PsiClass) -> Unit) { + fun collect(classifierDescriptorCollector: (ClassifierDescriptorWithTypeParameters) -> Unit, javaClassCollector: (PsiClass) -> Unit) { //TODO: this is a temporary solution until we have built-ins in indices // we need only nested classes because top-level built-ins are all added through default imports for (builtinPackage in resolutionFacade.moduleDescriptor.builtIns.builtInsPackageFragments) { collectClassesFromScope(builtinPackage.getMemberScope()) { if (it.containingDeclaration is ClassDescriptor) { - classDescriptorCollector(it) + classifierDescriptorCollector(it) } } } kotlinIndicesHelper .getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter) - .forEach { classDescriptorCollector(it) } + .forEach { classifierDescriptorCollector(it) } + + if (includeTypeAliases) { + kotlinIndicesHelper + .getTopLevelTypeAliases(prefixMatcher.asStringNameFilter()) + .forEach { classifierDescriptorCollector(it) } + } if (!ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as KtFile)) { addAdaptedJavaCompletion(javaClassCollector) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 028c8f871c5..10d89c03446 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -28,6 +28,7 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.util.ProcessingContext import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.ClassifierDescriptorWithTypeParameters import org.jetbrains.kotlin.idea.completion.handlers.createKeywordConstructLookupElement import org.jetbrains.kotlin.idea.completion.smart.ExpectedInfoMatch import org.jetbrains.kotlin.idea.completion.smart.SMART_COMPLETION_ITEM_PRIORITY_KEY @@ -291,17 +292,30 @@ class BasicCompletionSession( if (callTypeAndReceiver.receiver == null && prefix.isNotEmpty()) { val classKindFilter: ((ClassKind) -> Boolean)? + val includeTypeAliases: Boolean when (callTypeAndReceiver) { - is CallTypeAndReceiver.ANNOTATION -> classKindFilter = { it == ClassKind.ANNOTATION_CLASS } - is CallTypeAndReceiver.DEFAULT, is CallTypeAndReceiver.TYPE -> classKindFilter = { it != ClassKind.ENUM_ENTRY } - else -> classKindFilter = null + is CallTypeAndReceiver.ANNOTATION -> { + classKindFilter = { it == ClassKind.ANNOTATION_CLASS } + includeTypeAliases = false + } + + is CallTypeAndReceiver.DEFAULT, is CallTypeAndReceiver.TYPE -> { + classKindFilter = { it != ClassKind.ENUM_ENTRY } + includeTypeAliases = true + } + + else -> { + classKindFilter = null + includeTypeAliases = false + } } + if (classKindFilter != null) { val prefixMatcher = if (configuration.useBetterPrefixMatcherForNonImportedClasses) BetterPrefixMatcher(prefixMatcher, collector.bestMatchingDegree) else prefixMatcher - addClassesFromIndex(classKindFilter, prefixMatcher) + addClassesFromIndex(classKindFilter, includeTypeAliases, prefixMatcher) } } } @@ -572,15 +586,16 @@ class BasicCompletionSession( } } - private fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean, prefixMatcher: PrefixMatcher) { - val classDescriptorCollector = { descriptor: ClassDescriptor -> + private fun addClassesFromIndex(kindFilter: (ClassKind) -> Boolean, includeTypeAliases: Boolean, prefixMatcher: PrefixMatcher) { + val classifierDescriptorCollector = { descriptor: ClassifierDescriptorWithTypeParameters -> collector.addElement(basicLookupElementFactory.createLookupElement(descriptor), notImported = true) } val javaClassCollector = { javaClass: PsiClass -> collector.addElement(basicLookupElementFactory.createLookupElementForJavaClass(javaClass), notImported = true) } - AllClassesCompletion(parameters, indicesHelper(true), prefixMatcher, resolutionFacade, kindFilter, configuration.completeJavaClassesNotToBeUsed) - .collect(classDescriptorCollector, javaClassCollector) + AllClassesCompletion(parameters, indicesHelper(true), prefixMatcher, resolutionFacade, + kindFilter, includeTypeAliases, configuration.completeJavaClassesNotToBeUsed + ).collect(classifierDescriptorCollector, javaClassCollector) } } \ No newline at end of file diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt index 9227a68c032..f93cf80a99f 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ParameterNameAndTypeCompletion.kt @@ -89,7 +89,8 @@ class ParameterNameAndTypeCompletion( fun addFromAllClasses(parameters: CompletionParameters, indicesHelper: KotlinIndicesHelper) { for ((classNameMatcher, userPrefix) in classNamePrefixMatchers.zip(userPrefixes)) { AllClassesCompletion( - parameters, indicesHelper, classNameMatcher, resolutionFacade, { !it.isSingleton }, includeJavaClassesNotToBeUsed = false + parameters, indicesHelper, classNameMatcher, resolutionFacade, { !it.isSingleton }, + includeTypeAliases = true, includeJavaClassesNotToBeUsed = false ).collect( { addSuggestionsForClassifier(it, userPrefix, notImported = true) }, { addSuggestionsForJavaClass(it, userPrefix, notImported = true) } diff --git a/idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/ParameterNameAndTypeForNotImportedAlias.dependency.kt b/idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/ParameterNameAndTypeForNotImportedAlias.dependency.kt new file mode 100644 index 00000000000..3912fb3150e --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/ParameterNameAndTypeForNotImportedAlias.dependency.kt @@ -0,0 +1,4 @@ +package test + +class MyClass +typealias MyAlias = MyClass \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/ParameterNameAndTypeForNotImportedAlias.kt b/idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/ParameterNameAndTypeForNotImportedAlias.kt new file mode 100644 index 00000000000..d538f2e4ee4 --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/ParameterNameAndTypeForNotImportedAlias.kt @@ -0,0 +1,5 @@ +package test2 + +fun foo(ali) + +// EXIST: { lookupString: "alias : MyAlias", itemText: "alias: MyAlias", typeText: "public typealias MyAlias = MyClass defined in test", attributes: "" } diff --git a/idea/idea-completion/testData/basic/multifile/TypeAliases/TypeAliases.dependency.kt b/idea/idea-completion/testData/basic/multifile/TypeAliases/TypeAliases.dependency.kt new file mode 100644 index 00000000000..c1e8830e4fb --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/TypeAliases/TypeAliases.dependency.kt @@ -0,0 +1,4 @@ +package dependency + +class MyClass +typealias MyAlias = MyClass \ No newline at end of file diff --git a/idea/idea-completion/testData/basic/multifile/TypeAliases/TypeAliases.kt b/idea/idea-completion/testData/basic/multifile/TypeAliases/TypeAliases.kt new file mode 100644 index 00000000000..8e160bb6c78 --- /dev/null +++ b/idea/idea-completion/testData/basic/multifile/TypeAliases/TypeAliases.kt @@ -0,0 +1,10 @@ +package test + +typealias MySameFileAlias = (String) -> Int +private typealias MyPrivateAlias = (String, Char) -> Unit + +val test: My + +// EXIST: { lookupString: "MySameFileAlias", itemText: "MySameFileAlias", tailText: null, typeText: "public typealias MySameFileAlias = (String) -> Int defined in test", attributes: "" } +// EXIST: { lookupString: "MyPrivateAlias", itemText: "MyPrivateAlias", tailText: null, typeText: "private typealias MyPrivateAlias = (String, Char) -> Unit defined in test", attributes: "" } +// EXIST: { lookupString: "MyAlias", itemText: "MyAlias", tailText: null, typeText: "public typealias MyAlias = MyClass defined in dependency", attributes: "" } diff --git a/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias-1.kt b/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias-1.kt new file mode 100644 index 00000000000..2c0eb3156c0 --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias-1.kt @@ -0,0 +1 @@ +fun foo(): MyAl diff --git a/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias-2.kt b/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias-2.kt new file mode 100644 index 00000000000..80923426102 --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias-2.kt @@ -0,0 +1,5 @@ +package test + +class MyClass + +typealias MyAlias = MyClass \ No newline at end of file diff --git a/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias.kt.after b/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias.kt.after new file mode 100644 index 00000000000..a27c3f6b9e5 --- /dev/null +++ b/idea/idea-completion/testData/handlers/multifile/NotImportedTypeAlias.kt.after @@ -0,0 +1,3 @@ +import test.MyAlias + +fun foo(): MyAlias diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java index d2bec073cb0..e7a422edd92 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java @@ -323,6 +323,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ doTest(fileName); } + @TestMetadata("ParameterNameAndTypeForNotImportedAlias") + public void testParameterNameAndTypeForNotImportedAlias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeForNotImportedAlias/"); + doTest(fileName); + } + @TestMetadata("ParameterNameAndTypeNestedClasses") public void testParameterNameAndTypeNestedClasses() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/ParameterNameAndTypeNestedClasses/"); @@ -400,4 +406,10 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/TopLevelFunction/"); doTest(fileName); } + + @TestMetadata("TypeAliases") + public void testTypeAliases() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/TypeAliases/"); + doTest(fileName); + } } diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt index a0061c14bae..032fbc2e4f0 100644 --- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt +++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/handlers/CompletionMultifileHandlerTest.kt @@ -101,6 +101,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() { doTest() } + fun testNotImportedTypeAlias() { + doTest() + } + fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) { val fileName = getTestName(false) 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 8d63316ca2b..e0ee84d89ef 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 @@ -39,11 +39,9 @@ import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.isHiddenInResolution -import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.utils.addIfNotNull @@ -74,7 +72,7 @@ class KotlinIndicesHelper( declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name) declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name) return declarations - .flatMap { it.resolveToDescriptorsWithHack() } + .flatMap { it.resolveToDescriptorsWithHack() } .filter { descriptorFilter(it) } } @@ -88,8 +86,7 @@ class KotlinIndicesHelper( fun getTopLevelExtensionOperatorsByName(name: String): Collection { return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) .filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } - .flatMap { it.resolveToDescriptorsWithHack() } - .filterIsInstance() + .flatMap { it.resolveToDescriptorsWithHack() } .filter { descriptorFilter(it) && it.extensionReceiverParameter != null } .distinct() } @@ -97,8 +94,7 @@ class KotlinIndicesHelper( fun getMemberOperatorsByName(name: String): Collection { return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) .filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } - .flatMap { it.resolveToDescriptorsWithHack() } - .filterIsInstance() + .flatMap { it.resolveToDescriptorsWithHack() } .filter { descriptorFilter(it) && it.extensionReceiverParameter == null } .distinct() } @@ -113,7 +109,7 @@ class KotlinIndicesHelper( if (declaration.receiverTypeReference != null) continue if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue - for (descriptor in declaration.resolveToDescriptorsWithHack()) { + for (descriptor in declaration.resolveToDescriptorsWithHack()) { if (descriptorFilter(descriptor)) { processor(descriptor) } @@ -193,7 +189,7 @@ class KotlinIndicesHelper( } } - declarations.forEach { it.resolveToDescriptorsWithHack().forEach(::processDescriptor) } + declarations.forEach { it.resolveToDescriptorsWithHack().forEach(::processDescriptor) } return result } @@ -229,26 +225,34 @@ class KotlinIndicesHelper( } fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection { - return KotlinFullClassNameIndex.getInstance().getAllKeys(project).asSequence() - .map { FqName(it) } + val index = KotlinFullClassNameIndex.getInstance() + return index.getAllKeys(project).asSequence() .filter { ProgressManager.checkCanceled() - nameFilter(it.shortName().asString()) + nameFilter(it.substringAfterLast('.')) } .toList() - .flatMap { getClassDescriptorsByFQName(it, kindFilter) } + .flatMap { fqName -> + index[fqName, project, scope] + .flatMap { it.resolveToDescriptorsWithHack() } + + } + .filter { kindFilter(it.kind) && descriptorFilter(it) } } - private fun getClassDescriptorsByFQName(classFQName: FqName, kindFilter: (ClassKind) -> Boolean): Collection { - val declarations = KotlinFullClassNameIndex.getInstance()[classFQName.asString(), project, scope] + fun getTopLevelTypeAliases(nameFilter: (String) -> Boolean): Collection { + val index = KotlinTopLevelTypeAliasFqNameIndex.getInstance() + return index.getAllKeys(project).asSequence() + .filter { + ProgressManager.checkCanceled() + nameFilter(it.substringAfterLast('.')) + } + .toList() + .flatMap { fqName -> + index[fqName, project, scope] + .flatMap { it.resolveToDescriptorsWithHack() } - if (declarations.isEmpty()) { - // This fqn is absent in caches, dead or not in scope - return emptyList() - } - - // Note: Can't search with psi element as analyzer could be built over temp files - return ResolveSessionUtils.getClassOrObjectDescriptorsByFqName(moduleDescriptor, classFQName) { kindFilter(it!!.kind) } + } .filter(descriptorFilter) } @@ -268,7 +272,7 @@ class KotlinIndicesHelper( if (objectDeclaration.isObjectLiteral()) continue if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue if (!filter(declaration, objectDeclaration)) continue - for (descriptor in declaration.resolveToDescriptorsWithHack()) { + for (descriptor in declaration.resolveToDescriptorsWithHack()) { if (descriptorKindFilter.accepts(descriptor) && descriptorFilter(descriptor)) { processor(descriptor) } @@ -337,13 +341,13 @@ class KotlinIndicesHelper( return JavaProjectCodeInsightSettings.getSettings(project).isExcluded(fqName) } - private fun KtCallableDeclaration.resolveToDescriptorsWithHack(): Collection { + private inline fun KtNamedDeclaration.resolveToDescriptorsWithHack(): Collection { if (getContainingKtFile().isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations - return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance() + return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance() } else { val translatedDeclaration = declarationTranslator(this) ?: return emptyList() - return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? CallableDescriptor).singletonOrEmptyList() + return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? TDescriptor).singletonOrEmptyList() } } }