Fixed extensions completion for runtime receiver type in debugger (broken by previos changes in extensions completion)

This commit is contained in:
Valentin Kipyatkov
2015-10-28 19:07:07 +03:00
parent a26f6da12d
commit c9048266c8
4 changed files with 64 additions and 48 deletions
@@ -24,7 +24,6 @@ import org.jetbrains.kotlin.idea.resolve.frontendService
import org.jetbrains.kotlin.idea.util.* import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtExpression import org.jetbrains.kotlin.psi.KtExpression
import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtTypeReference 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.resolve.scopes.utils.memberScopeAsImportingScope
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.types.KotlinType 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 org.jetbrains.kotlin.utils.addIfNotNull
import java.util.* import java.util.*
@@ -59,10 +56,10 @@ class ReferenceVariantsHelper(
nameFilter: (Name) -> Boolean, nameFilter: (Name) -> Boolean,
filterOutJavaGettersAndSetters: Boolean = false, filterOutJavaGettersAndSetters: Boolean = false,
filterOutShadowed: Boolean = true, filterOutShadowed: Boolean = true,
useRuntimeReceiverType: Boolean = false useReceiverType: KotlinType? = null
): Collection<DeclarationDescriptor> ): Collection<DeclarationDescriptor>
= getReferenceVariants(expression, CallTypeAndReceiver.detect(expression), = getReferenceVariants(expression, CallTypeAndReceiver.detect(expression),
kindFilter, nameFilter, filterOutJavaGettersAndSetters, filterOutShadowed, useRuntimeReceiverType) kindFilter, nameFilter, filterOutJavaGettersAndSetters, filterOutShadowed, useReceiverType)
fun getReferenceVariants( fun getReferenceVariants(
contextElement: PsiElement, contextElement: PsiElement,
@@ -71,10 +68,10 @@ class ReferenceVariantsHelper(
nameFilter: (Name) -> Boolean, nameFilter: (Name) -> Boolean,
filterOutJavaGettersAndSetters: Boolean = false, filterOutJavaGettersAndSetters: Boolean = false,
filterOutShadowed: Boolean = true, filterOutShadowed: Boolean = true,
useRuntimeReceiverType: Boolean = false useReceiverType: KotlinType? = null
): Collection<DeclarationDescriptor> { ): Collection<DeclarationDescriptor> {
var variants: Collection<DeclarationDescriptor> var variants: Collection<DeclarationDescriptor>
= getReferenceVariantsNoVisibilityFilter(contextElement, kindFilter, nameFilter, callTypeAndReceiver, useRuntimeReceiverType) = getReferenceVariantsNoVisibilityFilter(contextElement, kindFilter, nameFilter, callTypeAndReceiver, useReceiverType)
.filter { !it.isAnnotatedAsHidden() && visibilityFilter(it) } .filter { !it.isAnnotatedAsHidden() && visibilityFilter(it) }
if (filterOutShadowed) { if (filterOutShadowed) {
@@ -108,7 +105,7 @@ class ReferenceVariantsHelper(
kindFilter: DescriptorKindFilter, kindFilter: DescriptorKindFilter,
nameFilter: (Name) -> Boolean, nameFilter: (Name) -> Boolean,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
useRuntimeReceiverType: Boolean useReceiverType: KotlinType?
): Collection<DeclarationDescriptor> { ): Collection<DeclarationDescriptor> {
val callType = callTypeAndReceiver.callType val callType = callTypeAndReceiver.callType
@@ -164,19 +161,26 @@ class ReferenceVariantsHelper(
descriptors.addAll(qualifier.scope.getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter)) descriptors.addAll(qualifier.scope.getDescriptorsFiltered(kindFilter exclude DescriptorKindExclude.Extensions, nameFilter))
} }
val expressionType = if (useRuntimeReceiverType) val explicitReceiverTypes = if (useReceiverType != null) {
getQualifierRuntimeType(receiverExpression) listOf(useReceiverType)
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)
} }
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 { else {
assert(useReceiverType == null) { "'useReceiverType' parameter is not supported for implicit receiver" }
descriptors.processAll(implicitReceiverTypes, implicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter) descriptors.processAll(implicitReceiverTypes, implicitReceiverTypes, resolutionScope, callType, kindFilter, nameFilter)
// add non-instance members // add non-instance members
@@ -310,6 +314,7 @@ class ReferenceVariantsHelper(
nameFilter: (Name) -> Boolean nameFilter: (Name) -> Boolean
) { ) {
if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return if (kindFilter.excludes.contains(DescriptorKindExclude.Extensions)) return
if (receiverTypes.isEmpty()) return
fun process(extension: CallableDescriptor) { fun process(extension: CallableDescriptor) {
if (kindFilter.accepts(extension) && nameFilter(extension.name)) { 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
}
} }
@@ -49,6 +49,8 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.types.KotlinType 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.types.typeUtil.makeNotNullable
import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart import org.jetbrains.kotlin.util.capitalizeDecapitalize.decapitalizeSmart
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstance
@@ -268,14 +270,17 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>) data class ReferenceVariants(val imported: Collection<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
protected val referenceVariants: ReferenceVariants? by lazy { 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( var variants = referenceVariantsHelper.getReferenceVariants(
nameExpression!!, nameExpression!!,
descriptorKindFilter!!, descriptorKindFilter,
descriptorNameFilter, descriptorNameFilter,
filterOutJavaGettersAndSetters = false, filterOutJavaGettersAndSetters = false,
filterOutShadowed = false) filterOutShadowed = false,
useReceiverType = runtimeReceiverType)
variants = variants.excludeNonInitializedVariable(nameExpression) variants = variants.excludeNonInitializedVariable(nameExpression)
@@ -283,7 +288,12 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
var notImportedExtensions: Collection<CallableDescriptor> = emptyList() var notImportedExtensions: Collection<CallableDescriptor> = emptyList()
if (callTypeAndReceiver.shouldCompleteCallableExtensions()) { 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) } val pair = extensions.partition { isImportableDescriptorImported(it) }
variants += pair.first variants += pair.first
notImportedExtensions = pair.second notImportedExtensions = pair.second
@@ -298,7 +308,7 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants) variants = referenceVariantsHelper.filterOutJavaGettersAndSetters(variants)
} }
ReferenceVariants(variants, notImportedExtensions) return ReferenceVariants(variants, notImportedExtensions)
} }
// filters out variable inside its initializer // filters out variable inside its initializer
@@ -315,14 +325,15 @@ abstract class CompletionSession(protected val configuration: CompletionSessionC
} }
protected fun getRuntimeReceiverTypeReferenceVariants(): Collection<DeclarationDescriptor> { protected fun getRuntimeReceiverTypeReferenceVariants(): Collection<DeclarationDescriptor> {
val descriptors = referenceVariantsHelper.getReferenceVariants( val explicitReceiver = callTypeAndReceiver.receiver as? KtExpression ?: return emptyList()
nameExpression!!, val type = bindingContext.getType(explicitReceiver) ?: return emptyList()
descriptorKindFilter!!, if (!TypeUtils.canHaveSubtypes(KotlinTypeChecker.DEFAULT, type)) return emptyList()
descriptorNameFilter, val evaluator = file.getCopyableUserData(KtCodeFragment.RUNTIME_TYPE_EVALUATOR) ?: return emptyList()
useRuntimeReceiverType = true, val runtimeType = evaluator(explicitReceiver)
filterOutJavaGettersAndSetters = configuration.filterOutJavaGettersAndSetters if (runtimeType == null || runtimeType == type) return emptyList()
).excludeNonInitializedVariable(nameExpression)
return descriptors.filter { descriptor -> val (variants, notImportedExtensions/*TODO: use them*/) = collectReferenceVariants(descriptorKindFilter!!, runtimeType)
return variants.filter { descriptor ->
referenceVariants!!.imported.none { compareDescriptors(project, it, descriptor) } referenceVariants!!.imported.none { compareDescriptors(project, it, descriptor) }
} }
} }
@@ -90,13 +90,22 @@ public class KotlinIndicesHelper(
} }
public fun getCallableTopLevelExtensions( public fun getCallableTopLevelExtensions(
nameFilter: (String) -> Boolean,
callTypeAndReceiver: CallTypeAndReceiver<*, *>, callTypeAndReceiver: CallTypeAndReceiver<*, *>,
position: KtExpression, position: KtExpression,
bindingContext: BindingContext bindingContext: BindingContext,
nameFilter: (String) -> Boolean
): Collection<CallableDescriptor> { ): Collection<CallableDescriptor> {
val receiverTypes = callTypeAndReceiver.receiverTypes(bindingContext, position, moduleDescriptor, resolutionFacade, predictableSmartCastsOnly = false) 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<KotlinType>,
nameFilter: (String) -> Boolean
): Collection<CallableDescriptor> {
if (receiverTypes.isEmpty()) return emptyList()
val receiverTypeNames = HashSet<String>() val receiverTypeNames = HashSet<String>()
receiverTypes.forEach { receiverTypeNames.addTypeNames(it) } receiverTypes.forEach { receiverTypeNames.addTypeNames(it) }
@@ -33,16 +33,18 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory import org.jetbrains.kotlin.diagnostics.DiagnosticFactory
import org.jetbrains.kotlin.diagnostics.Errors import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.JetBundle 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.analyze
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope import org.jetbrains.kotlin.idea.caches.resolve.getResolveScope
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.KotlinIndicesHelper 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.core.isVisible
import org.jetbrains.kotlin.idea.project.ProjectStructureUtil import org.jetbrains.kotlin.idea.project.ProjectStructureUtil
import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver import org.jetbrains.kotlin.idea.util.CallTypeAndReceiver
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression import org.jetbrains.kotlin.psi.psiUtil.isImportDirectiveExpression
@@ -166,7 +168,7 @@ internal abstract class AutoImportFixBase<T: KtExpression>(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) return if (result.size > 1)
reduceCandidatesBasedOnDependencyRuleViolation(result, file) reduceCandidatesBasedOnDependencyRuleViolation(result, file)