KT-4893 Code completion should not show multiple functions with the same signature

#KT-4893 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-06-09 23:10:47 +03:00
parent 4c0e7e32e7
commit 483da9607c
43 changed files with 737 additions and 106 deletions
@@ -1,4 +1,5 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -16,34 +17,37 @@
package org.jetbrains.kotlin.idea.codeInsight
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import java.util.*
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.idea.util.*
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo
import org.jetbrains.kotlin.resolve.calls.smartcasts.SmartCastUtils
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindExclude
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.types.JetType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.checker.JetTypeChecker
import org.jetbrains.kotlin.lexer.JetTokens
import org.jetbrains.kotlin.idea.util.CallType
import org.jetbrains.kotlin.idea.util.substituteExtensionIfCallable
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance
import java.util.*
public class ReferenceVariantsHelper(
private val context: BindingContext,
private val moduleDescriptor: ModuleDescriptor,
private val project: Project,
private val visibilityFilter: (DeclarationDescriptor) -> Boolean
) {
public data class ReceiversData(
public val receivers: Collection<ReceiverValue>,
public val callType: CallType
@@ -59,7 +63,10 @@ public class ReferenceVariantsHelper(
useRuntimeReceiverType: Boolean,
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
return getReferenceVariantsNoVisibilityFilter(expression, kindFilter, useRuntimeReceiverType, nameFilter).filter(visibilityFilter)
val variants = getReferenceVariantsNoVisibilityFilter(expression, kindFilter, useRuntimeReceiverType, nameFilter)
.filter(visibilityFilter)
return ShadowedDeclarationsFilter(context, moduleDescriptor, project).filter(variants, expression)
//TODO: if visibility filter is empty, filter out shadowed can work incorrectly!
}
private fun getReferenceVariantsNoVisibilityFilter(
@@ -69,7 +76,7 @@ public class ReferenceVariantsHelper(
nameFilter: (Name) -> Boolean
): Collection<DeclarationDescriptor> {
val parent = expression.getParent()
val resolutionScope = context[BindingContext.RESOLUTION_SCOPE, expression] ?: return listOf()
val resolutionScope = context.correctedResolutionScope(expression) ?: return listOf()
val containingDeclaration = resolutionScope.getContainingDeclaration()
if (parent is JetImportDirective || parent is JetPackageDirective) {
@@ -80,13 +87,12 @@ public class ReferenceVariantsHelper(
return resolutionScope.getDescriptorsFiltered(kindFilter.restrictedToKinds(DescriptorKindFilter.CLASSIFIERS_MASK or DescriptorKindFilter.PACKAGES_MASK), nameFilter)
}
val descriptors = LinkedHashSet<DeclarationDescriptor>()
val pair = getExplicitReceiverData(expression)
if (pair != null) {
val (receiverExpression, callType) = pair
// Process as call expression
val descriptors = HashSet<DeclarationDescriptor>()
val qualifier = context[BindingContext.QUALIFIER, receiverExpression]
if (qualifier != null) {
// It's impossible to add extension function for package or class (if it's companion object, expression type is not null)
@@ -107,20 +113,16 @@ public class ReferenceVariantsHelper(
descriptors.addCallableExtensions(resolutionScope, receiverValue, dataFlowInfo, callType, kindFilter, nameFilter)
}
return descriptors
}
else {
val dataFlowInfo = context.getDataFlowInfo(expression)
val descriptorsSet = HashSet<DeclarationDescriptor>()
// process instance members that can be called via implicit receiver's instances
val receivers = resolutionScope.getImplicitReceiversWithInstance()
val receiverValues = receivers.map { it.getValue() }
for (receiverValue in receiverValues) {
for (variant in SmartCastUtils.getSmartCastVariantsWithLessSpecificExcluded(receiverValue, context, containingDeclaration, dataFlowInfo)) {
descriptorsSet.addMembersFromReceiver(variant, CallType.NORMAL, kindFilter, nameFilter)
descriptors.addMembersFromReceiver(variant, CallType.NORMAL, kindFilter, nameFilter)
}
}
@@ -129,17 +131,17 @@ public class ReferenceVariantsHelper(
if (descriptor is CallableDescriptor && descriptor.getExtensionReceiverParameter() != null) {
val dispatchReceiver = descriptor.getDispatchReceiverParameter()
if (dispatchReceiver == null || dispatchReceiver in receivers) {
descriptorsSet.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, CallType.NORMAL, containingDeclaration))
descriptors.addAll(descriptor.substituteExtensionIfCallable(receiverValues, context, dataFlowInfo, CallType.NORMAL, containingDeclaration))
}
}
else {
if (descriptor is CallableDescriptor && descriptor.getDispatchReceiverParameter() != null) continue // should already be processed via implicit receivers
descriptorsSet.add(descriptor)
descriptors.add(descriptor)
}
}
return descriptorsSet
}
return descriptors
}
private fun MutableCollection<DeclarationDescriptor>.addMembersFromReceiver(
@@ -0,0 +1,178 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.util
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.di.InjectorForMacros
import org.jetbrains.kotlin.idea.imports.canBeReferencedViaImport
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DelegatingBindingTrace
import org.jetbrains.kotlin.resolve.bindingContextUtil.getDataFlowInfo
import org.jetbrains.kotlin.resolve.calls.callUtil.getCall
import org.jetbrains.kotlin.resolve.calls.checkers.AdditionalTypeChecker
import org.jetbrains.kotlin.resolve.calls.checkers.CompositeChecker
import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext
import org.jetbrains.kotlin.resolve.calls.context.CheckValueArgumentsMode
import org.jetbrains.kotlin.resolve.calls.context.ContextDependency
import org.jetbrains.kotlin.resolve.calls.util.DelegatingCall
import org.jetbrains.kotlin.resolve.scopes.ChainedScope
import org.jetbrains.kotlin.resolve.scopes.ExplicitImportsScope
import org.jetbrains.kotlin.resolve.validation.SymbolUsageValidator
import org.jetbrains.kotlin.types.TypeUtils
import java.util.ArrayList
public class ShadowedDeclarationsFilter(
private val bindingContext: BindingContext,
private val moduleDescriptor: ModuleDescriptor,
private val project: Project,
private val importDeclarations: Boolean = false
) {
private val psiFactory = JetPsiFactory(project)
private val dummyExpressionFactory = DummyExpressionFactory(psiFactory)
public fun <TDescriptor : DeclarationDescriptor> filter(declarations: Collection<TDescriptor>, expression: JetSimpleNameExpression): Collection<TDescriptor> {
val call = expression.getCall(bindingContext) ?: return declarations
return declarations
.groupBy { signature(it) }
.flatMap { filterEqualSignatureGroup(it.value, call) }
}
private fun signature(descriptor: DeclarationDescriptor): Any {
return when (descriptor) {
is SimpleFunctionDescriptor -> FunctionSignature(descriptor)
is VariableDescriptor -> descriptor.getName()
else -> descriptor
}
}
private fun <TDescriptor : DeclarationDescriptor> filterEqualSignatureGroup(descriptors: Collection<TDescriptor>, call: Call): Collection<TDescriptor> {
if (descriptors.size() == 1) return descriptors
val first = descriptors.first()
val isFunction = first is FunctionDescriptor
val name = first.getName()
val parameters = (first as CallableDescriptor).getValueParameters()
val dummyArgumentExpressions = dummyExpressionFactory.createDummyExpressions(parameters.size())
val bindingTrace = DelegatingBindingTrace(bindingContext, "Temporary trace for filtering shadowed declarations")
for ((expression, parameter) in dummyArgumentExpressions.zip(parameters)) {
bindingTrace.recordType(expression, parameter.getVarargElementType() ?: parameter.getType())
bindingTrace.record(BindingContext.PROCESSED, expression, true)
}
val firstVarargIndex = parameters.withIndex().firstOrNull { it.value.getVarargElementType() != null }?.index
val useNamedFromIndex = if (firstVarargIndex != null && firstVarargIndex != parameters.lastIndex) firstVarargIndex else parameters.size()
class DummyArgument(val index: Int) : ValueArgument {
private val expression = dummyArgumentExpressions[index]
private val argumentName: ValueArgumentName? = if (isNamed()) {
object : ValueArgumentName {
override val asName = parameters[index].getName()
override val referenceExpression = null
}
}
else {
null
}
override fun getArgumentExpression() = expression
override fun isNamed() = index >= useNamedFromIndex
override fun getArgumentName() = argumentName
override fun asElement() = expression
override fun getSpreadElement() = null
override fun isExternal() = false
}
val arguments = ArrayList<DummyArgument>()
for (i in parameters.indices) {
arguments.add(DummyArgument(i))
}
val newCall = object : DelegatingCall(call) {
//TODO: compiler crash (KT-8011)
//val arguments = parameters.indices.map { DummyArgument(it) }
val callee = psiFactory.createExpressionByPattern("$0", name)
override fun getCalleeExpression() = callee
override fun getValueArgumentList() = null
override fun getValueArguments() = arguments
override fun getFunctionLiteralArguments() = emptyList<FunctionLiteralArgument>()
override fun getTypeArguments() = emptyList<JetTypeProjection>()
override fun getTypeArgumentList() = null
}
val calleeExpression = call.getCalleeExpression() ?: return descriptors
var resolutionScope = bindingContext.correctedResolutionScope(calleeExpression) ?: return descriptors
if (importDeclarations) {
val importableDescriptors = descriptors.filter { it.canBeReferencedViaImport() }
resolutionScope = ChainedScope(resolutionScope.getContainingDeclaration(), "Scope with explicitly imported descriptors",
ExplicitImportsScope(importableDescriptors), resolutionScope)
}
val dataFlowInfo = bindingContext.getDataFlowInfo(calleeExpression)
val context = BasicCallResolutionContext.create(bindingTrace, resolutionScope, newCall, TypeUtils.NO_EXPECTED_TYPE, dataFlowInfo,
ContextDependency.INDEPENDENT, CheckValueArgumentsMode.ENABLED,
CompositeChecker(listOf()), SymbolUsageValidator.Empty, AdditionalTypeChecker.Composite(listOf()), false)
val callResolver = InjectorForMacros(project, moduleDescriptor).getCallResolver()
val results = if (isFunction) callResolver.resolveFunctionCall(context) else callResolver.resolveSimpleProperty(context)
val resultingDescriptors = results.getResultingCalls().map { it.getResultingDescriptor() }.toSet()
val filtered = descriptors.filter { it in resultingDescriptors }
return if (filtered.isNotEmpty()) filtered else descriptors /* something went wrong, none of our declarations among resolve candidates, let's not filter anything */
}
private class DummyExpressionFactory(val factory: JetPsiFactory) {
private val expressions = ArrayList<JetExpression>()
fun createDummyExpressions(count: Int): List<JetExpression> {
while (expressions.size() < count) {
expressions.add(factory.createExpression("dummy"))
}
return expressions.take(count)
}
}
private class FunctionSignature(val function: FunctionDescriptor) {
override fun equals(other: Any?): Boolean {
if (other === this) return true
if (other !is FunctionSignature) return false
if (function.getName() != other.function.getName()) return false
val parameters1 = function.getValueParameters()
val parameters2 = other.function.getValueParameters()
if (parameters1.size() != parameters2.size()) return false
for (i in parameters1.indices) {
val p1 = parameters1[i]
val p2 = parameters2[i]
if (p1.getVarargElementType() != p2.getVarargElementType()) return false // both should be vararg or or both not
if (p1.getType() != p2.getType()) return false
}
return true
}
override fun hashCode() = function.getName().hashCode() * 17 + function.getValueParameters().size()
}
}
@@ -16,8 +16,15 @@
package org.jetbrains.kotlin.idea.util
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.JetScope
import org.jetbrains.kotlin.utils.addIfNotNull
public fun JetFunctionLiteral.findLabelAndCall(): Pair<Name?, JetCallExpression?> {
val literalParent = (this.getParent() as JetFunctionLiteralExpression).getParent()
@@ -44,3 +51,34 @@ public fun JetFunctionLiteral.findLabelAndCall(): Pair<Name?, JetCallExpression?
}
}
}
// returns corrected resolution scope excluding variable inside its own initializer
// will not be needed after correcting JetScope stored BindingContext (see KT-4822 Wrong scope is used for local variable name completion)
public fun BindingContext.correctedResolutionScope(expression: JetExpression): JetScope? {
val scope = get(BindingContext.RESOLUTION_SCOPE, expression) ?: return null
val variablesToExclude = hashSetOf<VariableDescriptor>()
for (element in expression.parentsWithSelf) {
if (element is JetExpression) {
val declaration = element.getParent() as? JetVariableDeclaration ?: continue
if (element == declaration.getInitializer()) {
variablesToExclude.addIfNotNull(get(BindingContext.VARIABLE, declaration))
}
}
}
if (variablesToExclude.isEmpty()) return scope
return object : JetScope by scope {
override fun getDescriptors(kindFilter: DescriptorKindFilter, nameFilter: (Name) -> Boolean)
= scope.getDescriptors(kindFilter, nameFilter).filter { it !in variablesToExclude }
//TODO: it's not correct!
override fun getLocalVariable(name: Name): VariableDescriptor? {
val variable = scope.getLocalVariable(name) ?: return null
return if (variable in variablesToExclude) null else variable
}
override fun getProperties(name: Name) = scope.getProperties(name).filter { it !in variablesToExclude }
}
}