Auto-import does not suggest enum constant (KT-14100)

#KT-14100 Fixed

(cherry picked from commit 61730ee)
This commit is contained in:
Nikolay Krasko
2016-11-29 15:43:07 +03:00
committed by Nikolay Krasko
parent cbebb06574
commit 6a9c368532
9 changed files with 148 additions and 37 deletions
@@ -59,7 +59,7 @@ class AllClassesCompletion(private val parameters: CompletionParameters,
} }
kotlinIndicesHelper kotlinIndicesHelper
.getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter) .getKotlinClasses({ prefixMatcher.prefixMatches(it) }, kindFilter = kindFilter)
.forEach { classifierDescriptorCollector(it) } .forEach { classifierDescriptorCollector(it) }
if (includeTypeAliases) { if (includeTypeAliases) {
@@ -78,7 +78,7 @@ class KotlinIndicesHelper(
declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name) declarations.addTopLevelNonExtensionCallablesByName(KotlinFunctionShortNameIndex.getInstance(), name)
declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name) declarations.addTopLevelNonExtensionCallablesByName(KotlinPropertyShortNameIndex.getInstance(), name)
return declarations return declarations
.flatMap { it.resolveToDescriptorsWithHack<CallableDescriptor>() } .flatMap { it.resolveToDescriptors<CallableDescriptor>() }
.filter { descriptorFilter(it) } .filter { descriptorFilter(it) }
} }
@@ -92,7 +92,7 @@ class KotlinIndicesHelper(
fun getTopLevelExtensionOperatorsByName(name: String): Collection<FunctionDescriptor> { fun getTopLevelExtensionOperatorsByName(name: String): Collection<FunctionDescriptor> {
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
.filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } .filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
.flatMap { it.resolveToDescriptorsWithHack<FunctionDescriptor>() } .flatMap { it.resolveToDescriptors<FunctionDescriptor>() }
.filter { descriptorFilter(it) && it.extensionReceiverParameter != null } .filter { descriptorFilter(it) && it.extensionReceiverParameter != null }
.distinct() .distinct()
} }
@@ -100,7 +100,7 @@ class KotlinIndicesHelper(
fun getMemberOperatorsByName(name: String): Collection<FunctionDescriptor> { fun getMemberOperatorsByName(name: String): Collection<FunctionDescriptor> {
return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope)
.filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } .filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) }
.flatMap { it.resolveToDescriptorsWithHack<FunctionDescriptor>() } .flatMap { it.resolveToDescriptors<FunctionDescriptor>() }
.filter { descriptorFilter(it) && it.extensionReceiverParameter == null } .filter { descriptorFilter(it) && it.extensionReceiverParameter == null }
.distinct() .distinct()
} }
@@ -115,7 +115,7 @@ class KotlinIndicesHelper(
if (declaration.receiverTypeReference != null) continue if (declaration.receiverTypeReference != null) continue
if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue
for (descriptor in declaration.resolveToDescriptorsWithHack<CallableDescriptor>()) { for (descriptor in declaration.resolveToDescriptors<CallableDescriptor>()) {
if (descriptorFilter(descriptor)) { if (descriptorFilter(descriptor)) {
processor(descriptor) processor(descriptor)
} }
@@ -195,7 +195,7 @@ class KotlinIndicesHelper(
} }
} }
declarations.forEach { it.resolveToDescriptorsWithHack<CallableDescriptor>().forEach(::processDescriptor) } declarations.forEach { it.resolveToDescriptors<CallableDescriptor>().forEach(::processDescriptor) }
return result return result
} }
@@ -208,6 +208,14 @@ class KotlinIndicesHelper(
.toSet() .toSet()
} }
fun getKotlinEnumsByName(name: String): Collection<DeclarationDescriptor> {
return KotlinClassShortNameIndex.getInstance()[name, project, scope]
.filter { it is KtEnumEntry && it in scope }
.mapNotNull { it.resolveToDescriptor() }
.filter(descriptorFilter)
.toSet()
}
fun processJvmCallablesByName( fun processJvmCallablesByName(
name: String, name: String,
filter: (PsiMember) -> Boolean, filter: (PsiMember) -> Boolean,
@@ -245,18 +253,21 @@ class KotlinIndicesHelper(
} }
} }
fun getKotlinClasses(nameFilter: (String) -> Boolean, kindFilter: (ClassKind) -> Boolean): Collection<ClassDescriptor> { fun getKotlinClasses(
nameFilter: (String) -> Boolean,
psiFilter: (KtDeclaration) -> Boolean = { true },
kindFilter: (ClassKind) -> Boolean = { true }): Collection<ClassDescriptor> {
val index = KotlinFullClassNameIndex.getInstance() val index = KotlinFullClassNameIndex.getInstance()
return index.getAllKeys(project).asSequence() return index.getAllKeys(project).asSequence()
.filter { .filter { fqName ->
ProgressManager.checkCanceled() ProgressManager.checkCanceled()
nameFilter(it.substringAfterLast('.')) nameFilter(fqName.substringAfterLast('.'))
} }
.toList() .toList()
.flatMap { fqName -> .flatMap { fqName ->
index[fqName, project, scope] index[fqName, project, scope].flatMap { classOrObject ->
.flatMap { it.resolveToDescriptorsWithHack<ClassDescriptor>() } classOrObject.resolveToDescriptorsWithHack(psiFilter).filterIsInstance<ClassDescriptor>()
}
} }
.filter { kindFilter(it.kind) && descriptorFilter(it) } .filter { kindFilter(it.kind) && descriptorFilter(it) }
} }
@@ -271,7 +282,7 @@ class KotlinIndicesHelper(
.toList() .toList()
.flatMap { fqName -> .flatMap { fqName ->
index[fqName, project, scope] index[fqName, project, scope]
.flatMap { it.resolveToDescriptorsWithHack<TypeAliasDescriptor>() } .flatMap { it.resolveToDescriptors<TypeAliasDescriptor>() }
} }
.filter(descriptorFilter) .filter(descriptorFilter)
@@ -293,7 +304,7 @@ class KotlinIndicesHelper(
if (objectDeclaration.isObjectLiteral()) continue if (objectDeclaration.isObjectLiteral()) continue
if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue if (filterOutPrivate && declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) continue
if (!filter(declaration, objectDeclaration)) continue if (!filter(declaration, objectDeclaration)) continue
for (descriptor in declaration.resolveToDescriptorsWithHack<CallableDescriptor>()) { for (descriptor in declaration.resolveToDescriptors<CallableDescriptor>()) {
if (descriptorKindFilter.accepts(descriptor) && descriptorFilter(descriptor)) { if (descriptorKindFilter.accepts(descriptor) && descriptorFilter(descriptor)) {
processor(descriptor) processor(descriptor)
} }
@@ -362,13 +373,20 @@ class KotlinIndicesHelper(
return JavaProjectCodeInsightSettings.getSettings(project).isExcluded(fqName) return JavaProjectCodeInsightSettings.getSettings(project).isExcluded(fqName)
} }
private inline fun <reified TDescriptor : Any> KtNamedDeclaration.resolveToDescriptorsWithHack(): Collection<TDescriptor> { private inline fun <reified TDescriptor : Any> KtNamedDeclaration.resolveToDescriptors(): Collection<TDescriptor> {
return resolveToDescriptorsWithHack({ true }).filterIsInstance<TDescriptor>()
}
private fun KtNamedDeclaration.resolveToDescriptorsWithHack(
psiFilter: (KtDeclaration) -> Boolean): Collection<DeclarationDescriptor> {
if (getContainingKtFile().isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations if (getContainingKtFile().isCompiled) { //TODO: it's temporary while resolveToDescriptor does not work for compiled declarations
return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<TDescriptor>() return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance<DeclarationDescriptor>()
} }
else { else {
val translatedDeclaration = declarationTranslator(this) ?: return emptyList() val translatedDeclaration = declarationTranslator(this) ?: return emptyList()
return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? TDescriptor).singletonOrEmptyList() if (!psiFilter(translatedDeclaration)) return emptyList()
return (resolutionFacade.resolveToDescriptor(translatedDeclaration)).singletonOrEmptyList()
} }
} }
} }
@@ -30,6 +30,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.PsiErrorElement import com.intellij.psi.PsiErrorElement
import com.intellij.psi.PsiFile import com.intellij.psi.PsiFile
import com.intellij.psi.PsiModifier import com.intellij.psi.PsiModifier
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import org.jetbrains.kotlin.descriptors.CallableDescriptor import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
@@ -244,7 +245,9 @@ internal abstract class OrdinaryImportFixBase<T : KtExpression>(expression: T, f
val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) } val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
when (TargetPlatformDetector.getPlatform(expression.getContainingKtFile())) { when (TargetPlatformDetector.getPlatform(expression.getContainingKtFile())) {
JsPlatform -> indicesHelper.getKotlinClasses({ it == name }, { true }).filterTo(result, filterByCallType) JsPlatform -> indicesHelper
.getKotlinClasses({ it == name }, psiFilter = { ktDeclaration -> ktDeclaration !is KtEnumEntry })
.filterTo(result, filterByCallType)
JvmPlatform -> indicesHelper.getJvmClassesByName(name).filterTo(result, filterByCallType) JvmPlatform -> indicesHelper.getJvmClassesByName(name).filterTo(result, filterByCallType)
} }
@@ -391,7 +394,6 @@ internal class ComponentsImportFix(
} }
internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) { internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBase<KtSimpleNameExpression>(expression, MyFactory) {
override fun getText() = "Import member" override fun getText() = "Import member"
override fun fillCandidates( override fun fillCandidates(
@@ -401,31 +403,34 @@ internal class ImportMemberFix(expression: KtSimpleNameExpression) : ImportFixBa
indicesHelper: KotlinIndicesHelper indicesHelper: KotlinIndicesHelper
): List<DeclarationDescriptor> { ): List<DeclarationDescriptor> {
val element = element ?: return emptyList() val element = element ?: return emptyList()
if (element.isImportDirectiveExpression() || isSelectorInQualified(element)) return emptyList()
val result = ArrayList<DeclarationDescriptor>() val result = ArrayList<DeclarationDescriptor>()
if (!element.isImportDirectiveExpression() && !isSelectorInQualified(element)) {
val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
val processor = { descriptor: CallableDescriptor -> val filterByCallType = { descriptor: DeclarationDescriptor -> callTypeAndReceiver.callType.descriptorKindFilter.accepts(descriptor) }
if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)) {
result.add(descriptor)
}
}
indicesHelper.processKotlinCallablesByName( indicesHelper.getKotlinEnumsByName(name).filterTo(result, filterByCallType)
name,
filter = { declaration -> (declaration.parent as? KtClassBody)?.parent is KtObjectDeclaration },
processor = processor
)
if (TargetPlatformDetector.getPlatform(element.getContainingKtFile()) == JvmPlatform) { val processor = { descriptor: CallableDescriptor ->
indicesHelper.processJvmCallablesByName( if (descriptor.canBeReferencedViaImport() && filterByCallType(descriptor)) {
name, result.add(descriptor)
filter = { it.hasModifierProperty(PsiModifier.STATIC) },
processor = processor
)
} }
} }
indicesHelper.processKotlinCallablesByName(
name,
filter = { declaration -> (declaration.parent as? KtClassBody)?.parent is KtObjectDeclaration },
processor = processor
)
if (TargetPlatformDetector.getPlatform(element.getContainingKtFile()) == JvmPlatform) {
indicesHelper.processJvmCallablesByName(
name,
filter = { it.hasModifierProperty(PsiModifier.STATIC) },
processor = processor
)
}
return result return result
} }
+8
View File
@@ -0,0 +1,8 @@
// "Import member" "true"
package e
enum class ImportEnum {
RED, GREEN, BLUE
}
val v5 = <caret>BLUE
@@ -0,0 +1,8 @@
// "Import member" "true"
package e
enum class ImportEnum {
RED, GREEN, BLUE
}
val v5 = ImportEnum.BLUE
@@ -0,0 +1,24 @@
// "Import" "false"
// ACTION: Convert property initializer to getter
// ACTION: Create object 'BLUE'
// ACTION: Create property 'BLUE'
// ACTION: Import member
// ACTION: Rename reference
// ERROR: Unresolved reference: BLUE
// Import should be present only in "Import member" action
package e
enum class ImportEnum {
RED, GREEN, BLUE
}
class ImportClass {
companion object {
val BLUE = 0
}
}
val v5 = B<caret>LUE
@@ -0,0 +1,14 @@
// "Import member" "true"
package e
enum class ImportEnum {
RED, GREEN, BLUE
}
class ImportClass {
companion object {
val BLUE = 0
}
}
val v5 = <caret>BLUE
@@ -0,0 +1,16 @@
// "Import member" "true"
package e
import e.ImportClass.Companion.BLUE
enum class ImportEnum {
RED, GREEN, BLUE
}
class ImportClass {
companion object {
val BLUE = 0
}
}
val v5 = BLUE
@@ -716,6 +716,24 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("enumEntries.kt")
public void testEnumEntries() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/enumEntries.kt");
doTest(fileName);
}
@TestMetadata("enumEntriesImportAbsentJsRuntime.kt")
public void testEnumEntriesImportAbsentJsRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/enumEntriesImportAbsentJsRuntime.kt");
doTest(fileName);
}
@TestMetadata("enumEntriesJsRuntime.kt")
public void testEnumEntriesJsRuntime() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/enumEntriesJsRuntime.kt");
doTest(fileName);
}
@TestMetadata("infixCallAndObject.kt") @TestMetadata("infixCallAndObject.kt")
public void testInfixCallAndObject() throws Exception { public void testInfixCallAndObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/infixCallAndObject.kt"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/autoImports/infixCallAndObject.kt");