KT-13266 Non-imported type aliases do not appear in completion

#KT-13266 Fixed
This commit is contained in:
Valentin Kipyatkov
2016-08-31 20:15:57 +03:00
parent fcce1e3838
commit 50a0f9a4a7
14 changed files with 124 additions and 40 deletions
@@ -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)
}
@@ -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)
@@ -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)
}
}
@@ -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) }
@@ -0,0 +1,4 @@
package test
class MyClass
typealias MyAlias = MyClass
@@ -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: "" }
@@ -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
@@ -0,0 +1,3 @@
import test.MyAlias
fun foo(): MyAlias<caret>
@@ -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);
}
}
@@ -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()
}
}
}