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.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<DeclarationDescriptor>
= 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<DeclarationDescriptor> {
var variants: Collection<DeclarationDescriptor>
= 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<DeclarationDescriptor> {
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
}
}
@@ -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<DeclarationDescriptor>, val notImportedExtensions: Collection<CallableDescriptor>)
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<CallableDescriptor> = 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<DeclarationDescriptor> {
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) }
}
}
@@ -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<CallableDescriptor> {
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>()
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.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<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)
reduceCandidatesBasedOnDependencyRuleViolation(result, file)