diff --git a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt index d54ff27467d..1fc0c36df79 100644 --- a/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt +++ b/idea/ide-common/src/org/jetbrains/kotlin/idea/codeInsight/ReferenceVariantsHelper.kt @@ -24,7 +24,6 @@ import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtTypeReference @@ -43,8 +42,6 @@ import org.jetbrains.kotlin.resolve.scopes.utils.collectSyntheticExtensionProper import org.jetbrains.kotlin.resolve.scopes.utils.memberScopeAsImportingScope import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.types.KotlinType -import org.jetbrains.kotlin.types.TypeUtils -import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.utils.addIfNotNull import java.util.* @@ -59,10 +56,10 @@ class ReferenceVariantsHelper( nameFilter: (Name) -> Boolean, filterOutJavaGettersAndSetters: Boolean = false, filterOutShadowed: Boolean = true, - useRuntimeReceiverType: Boolean = false + useReceiverType: KotlinType? = null ): Collection = getReferenceVariants(expression, CallTypeAndReceiver.detect(expression), - kindFilter, nameFilter, filterOutJavaGettersAndSetters, filterOutShadowed, useRuntimeReceiverType) + kindFilter, nameFilter, filterOutJavaGettersAndSetters, filterOutShadowed, useReceiverType) fun getReferenceVariants( contextElement: PsiElement, @@ -71,10 +68,10 @@ class ReferenceVariantsHelper( nameFilter: (Name) -> Boolean, filterOutJavaGettersAndSetters: Boolean = false, filterOutShadowed: Boolean = true, - useRuntimeReceiverType: Boolean = false + useReceiverType: KotlinType? = null ): Collection { var variants: Collection - = getReferenceVariantsNoVisibilityFilter(contextElement, kindFilter, nameFilter, callTypeAndReceiver, useRuntimeReceiverType) + = getReferenceVariantsNoVisibilityFilter(contextElement, kindFilter, nameFilter, callTypeAndReceiver, useReceiverType) .filter { !it.isAnnotatedAsHidden() && visibilityFilter(it) } if (filterOutShadowed) { @@ -108,7 +105,7 @@ class ReferenceVariantsHelper( kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean, callTypeAndReceiver: CallTypeAndReceiver<*, *>, - useRuntimeReceiverType: Boolean + useReceiverType: KotlinType? ): Collection { val callType = callTypeAndReceiver.callType @@ -164,19 +161,26 @@ class ReferenceVariantsHelper( descriptors.addAll(qualifier.scope.getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter)) } - val expressionType = if (useRuntimeReceiverType) - getQualifierRuntimeType(receiverExpression) - else - bindingContext.getType(receiverExpression) - if (expressionType != null && !expressionType.isError()) { - val receiverValue = ExpressionReceiver(receiverExpression, expressionType) - val explicitReceiverTypes = smartCastManager - .getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, containingDeclaration, dataFlowInfo) - - descriptors.processAll(implicitReceiverTypes, explicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter) + val explicitReceiverTypes = if (useReceiverType != null) { + listOf(useReceiverType) } + else { + val expressionType = bindingContext.getType(receiverExpression) + if (expressionType != null && !expressionType.isError()) { + val receiverValue = ExpressionReceiver(receiverExpression, expressionType) + smartCastManager.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, bindingContext, containingDeclaration, dataFlowInfo) + + } + else { + emptyList() + } + } + + descriptors.processAll(implicitReceiverTypes, explicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter) } else { + assert(useReceiverType == null) { "'useReceiverType' parameter is not supported for implicit receiver" } + descriptors.processAll(implicitReceiverTypes, implicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter) // add non-instance members @@ -310,6 +314,7 @@ class ReferenceVariantsHelper( nameFilter: (Name) -> Boolean ) { if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return + if (receiverTypes.isEmpty()) return fun process(extension: CallableDescriptor) { if (kindFilter.accepts(extension) && nameFilter(extension.name)) { @@ -334,15 +339,4 @@ class ReferenceVariantsHelper( } } } - - private fun getQualifierRuntimeType(receiver: KtExpression): KotlinType? { - val type = bindingContext.getType(receiver) - if (type != null && TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type)) { - val evaluator = receiver.getContainingFile().getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) - val runtimeType = evaluator?.invoke(receiver) - if (runtimeType == type) return null - return runtimeType - } - return type - } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt index 67897834e88..781967c7e7b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionSession.kt @@ -49,6 +49,8 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils +import org.jetbrains.kotlin.types.checker.KotlinTypeChecker import org.jetbrains.kotlin.types.typeUtil.makeNotNullable import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance @@ -268,14 +270,17 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC data class ReferenceVariants(val imported: Collection, val notImportedExtensions: Collection) protected val referenceVariants: ReferenceVariants? by lazy { - if (descriptorKindFilter == null) return@lazy null + descriptorKindFilter?.let { collectReferenceVariants(it) } + } + private fun collectReferenceVariants(descriptorKindFilter: DescriptorKindFilter, runtimeReceiverType: KotlinType? = null): ReferenceVariants { var variants = referenceVariantsHelper.getReferenceVariants( nameExpression!!, - descriptorKindFilter!!, + descriptorKindFilter, descriptorNameFilter, filterOutJavaGettersAndSetters = false, - filterOutShadowed = false) + filterOutShadowed = false, + useReceiverType = runtimeReceiverType) variants = variants.excludeNonInitializedVariable(nameExpression) @@ -283,7 +288,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC var notImportedExtensions: Collection = emptyList() if (callTypeAndReceiver.shouldCompleteCallableExtensions()) { - val extensions = indicesHelper.getCallableTopLevelExtensions({ prefixMatcher.prefixMatches(it) }, callTypeAndReceiver, expression!!, bindingContext) + val nameFilter: (String) -> Boolean = { prefixMatcher.prefixMatches(it) } + val extensions = if (runtimeReceiverType != null) + indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, listOf(runtimeReceiverType), nameFilter) + else + indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, expression!!, bindingContext, nameFilter) + val pair = extensions.partition { isImportableDescriptorImported(it) } variants += pair.first notImportedExtensions = pair.second @@ -298,7 +308,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants) } - ReferenceVariants(variants, notImportedExtensions) + return ReferenceVariants(variants, notImportedExtensions) } // filters out variable inside its initializer @@ -315,14 +325,15 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC } protected fun getRuntimeReceiverTypeReferenceVariants(): Collection { - val descriptors = referenceVariantsHelper.getReferenceVariants( - nameExpression!!, - descriptorKindFilter!!, - descriptorNameFilter, - useRuntimeReceiverType = true, - filterOutJavaGettersAndSetters = configuration.filterOutJavaGettersAndSetters - ).excludeNonInitializedVariable(nameExpression) - return descriptors.filter { descriptor -> + val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return emptyList() + val type = bindingContext.getType(explicitReceiver) ?: return emptyList() + if (!TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type)) return emptyList() + val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return emptyList() + val runtimeType = evaluator(explicitReceiver) + if (runtimeType == null || runtimeType == type) return emptyList() + + val (variants, notImportedExtensions/*TODO: use them*/) = collectReferenceVariants(descriptorKindFilter!!, runtimeType) + return variants.filter { descriptor -> referenceVariants!!.imported.none { compareDescriptors(project, it, descriptor) } } } 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 1573d2468ef..c6729b43b04 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 @@ -90,13 +90,22 @@ public class KotlinIndicesHelper( } public fun getCallableTopLevelExtensions( - nameFilter: (String) -> Boolean, callTypeAndReceiver: CallTypeAndReceiver<*, *>, position: KtExpression, - bindingContext: BindingContext + bindingContext: BindingContext, + nameFilter: (String) -> Boolean ): Collection { val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, position, moduleDescriptor, resolutionFacade, predictableSmartCastsOnly = false) - if (receiverTypes == null || receiverTypes.isEmpty()) return emptyList() + ?: return emptyList() + return getCallableTopLevelExtensions(callTypeAndReceiver, receiverTypes, nameFilter) + } + + public fun getCallableTopLevelExtensions( + callTypeAndReceiver: CallTypeAndReceiver<*, *>, + receiverTypes: Collection, + nameFilter: (String) -> Boolean + ): Collection { + if (receiverTypes.isEmpty()) return emptyList() val receiverTypeNames = HashSet() receiverTypes.forEach { receiverTypeNames.addTypeNames(it) } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt index 063ff67c669..d875848f77e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AutoImportFix.kt @@ -33,16 +33,18 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.idea.JetBundle -import org.jetbrains.kotlin.idea.actions.* +import org.jetbrains.kotlin.idea.actions.KotlinAddImportAction +import org.jetbrains.kotlin.idea.actions.createGroupedImportsAction +import org.jetbrains.kotlin.idea.actions.createSingleImportAction import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper -import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.idea.core.isVisible import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver +import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression @@ -166,7 +168,7 @@ internal abstract class AutoImportFixBase(expression: T, val di } } - result.addAll(indicesHelper.getCallableTopLevelExtensions({ it == nameStr }, callTypeAndReceiver, element, bindingContext)) + result.addAll(indicesHelper.getCallableTopLevelExtensions(callTypeAndReceiver, element, bindingContext) { it == nameStr }) return if (result.size > 1) reduceCandidatesBasedOnDependencyRuleViolation(result, file)