KT-13266 Non-imported type aliases do not appear in completion
#KT-13266 Fixed
This commit is contained in:
@@ -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<KotlinTypeAliasStub>, KtNamedDe
|
||||
findChildByType(KtTokens.TYPE_ALIAS_KEYWORD)
|
||||
|
||||
@IfNotParsed
|
||||
fun getTypeReference(): KtTypeReference? =
|
||||
findChildByType(KtNodeTypes.TYPE_REFERENCE)
|
||||
fun getTypeReference(): KtTypeReference? {
|
||||
if (stub != null) {
|
||||
val typeReferences = getStubOrPsiChildrenAsList<KtTypeReference, KotlinPlaceHolderStub<KtTypeReference>>(KtStubElementTypes.TYPE_REFERENCE)
|
||||
return typeReferences[0]
|
||||
}
|
||||
else {
|
||||
return findChildByType(KtNodeTypes.TYPE_REFERENCE)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPresentation() = ItemPresentationProviders.getItemPresentation(this)
|
||||
}
|
||||
|
||||
+11
-3
@@ -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)
|
||||
|
||||
+23
-8
@@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -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) }
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package test
|
||||
|
||||
class MyClass
|
||||
typealias MyAlias = MyClass
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package test2
|
||||
|
||||
fun foo(ali<caret>)
|
||||
|
||||
// EXIST: { lookupString: "alias : MyAlias", itemText: "alias: MyAlias", typeText: "public typealias MyAlias = MyClass defined in test", attributes: "" }
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package dependency
|
||||
|
||||
class MyClass
|
||||
typealias MyAlias = MyClass
|
||||
@@ -0,0 +1,10 @@
|
||||
package test
|
||||
|
||||
typealias MySameFileAlias = (String) -> Int
|
||||
private typealias MyPrivateAlias = (String, Char) -> Unit
|
||||
|
||||
val test: My<caret>
|
||||
|
||||
// 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: "" }
|
||||
@@ -0,0 +1 @@
|
||||
fun foo(): MyAl<caret>
|
||||
@@ -0,0 +1,5 @@
|
||||
package test
|
||||
|
||||
class MyClass
|
||||
|
||||
typealias MyAlias = MyClass
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import test.MyAlias
|
||||
|
||||
fun foo(): MyAlias<caret>
|
||||
+12
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+4
@@ -101,6 +101,10 @@ class CompletionMultiFileHandlerTest : KotlinCompletionTestCase() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun testNotImportedTypeAlias() {
|
||||
doTest()
|
||||
}
|
||||
|
||||
fun doTest(completionChar: Char = '\n', vararg extraFileNames: String) {
|
||||
val fileName = getTestName(false)
|
||||
|
||||
|
||||
@@ -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<CallableDescriptor>() }
|
||||
.filter { descriptorFilter(it) }
|
||||
}
|
||||
|
||||
@@ -88,8 +86,7 @@ class KotlinIndicesHelper(
|
||||
fun getTopLevelExtensionOperatorsByName(name: String): Collection<FunctionDescriptor> {
|
||||
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
|
||||
.filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
|
||||
.flatMap { it.resolveToDescriptorsWithHack() }
|
||||
.filterIsInstance<FunctionDescriptor>()
|
||||
.flatMap { it.resolveToDescriptorsWithHack<FunctionDescriptor>() }
|
||||
.filter { descriptorFilter(it) && it.extensionReceiverParameter != null }
|
||||
.distinct()
|
||||
}
|
||||
@@ -97,8 +94,7 @@ class KotlinIndicesHelper(
|
||||
fun getMemberOperatorsByName(name: String): Collection<FunctionDescriptor> {
|
||||
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
|
||||
.filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
|
||||
.flatMap { it.resolveToDescriptorsWithHack() }
|
||||
.filterIsInstance<FunctionDescriptor>()
|
||||
.flatMap { it.resolveToDescriptorsWithHack<FunctionDescriptor>() }
|
||||
.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<CallableDescriptor>()) {
|
||||
if (descriptorFilter(descriptor)) {
|
||||
processor(descriptor)
|
||||
}
|
||||
@@ -193,7 +189,7 @@ class KotlinIndicesHelper(
|
||||
}
|
||||
}
|
||||
|
||||
declarations.forEach { it.resolveToDescriptorsWithHack().forEach(::processDescriptor) }
|
||||
declarations.forEach { it.resolveToDescriptorsWithHack<CallableDescriptor>().forEach(::processDescriptor) }
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -229,26 +225,34 @@ class KotlinIndicesHelper(
|
||||
}
|
||||
|
||||
fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
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<ClassDescriptor>() }
|
||||
|
||||
}
|
||||
.filter { kindFilter(it.kind) && descriptorFilter(it) }
|
||||
}
|
||||
|
||||
private fun getClassDescriptorsByFQName(classFQName: FqName, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> {
|
||||
val declarations = KotlinFullClassNameIndex.getInstance()[classFQName.asString(), project, scope]
|
||||
fun getTopLevelTypeAliases(nameFilter: (String) -> Boolean): Collection<TypeAliasDescriptor> {
|
||||
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<TypeAliasDescriptor>() }
|
||||
|
||||
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<CallableDescriptor>()) {
|
||||
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<CallableDescriptor> {
|
||||
private inline fun <reified TDescriptor : Any> KtNamedDeclaration.resolveToDescriptorsWithHack(): Collection<TDescriptor> {
|
||||
if (getContainingKtFile().isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
|
||||
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<CallableDescriptor>()
|
||||
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<TDescriptor>()
|
||||
}
|
||||
else {
|
||||
val translatedDeclaration = declarationTranslator(this) ?: return emptyList()
|
||||
return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? CallableDescriptor).singletonOrEmptyList()
|
||||
return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? TDescriptor).singletonOrEmptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user