Smart completion: no duplicates among static members

This commit is contained in:
Valentin Kipyatkov
2014-04-11 17:40:08 +04:00
parent 4b5d175aa7
commit 7d5b86e000
6 changed files with 131 additions and 46 deletions
@@ -32,6 +32,7 @@ import org.jetbrains.jet.lang.resolve.calls.util.CallMaker
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue
import com.intellij.lang.ASTNode
import org.jetbrains.jet.lang.resolve.scopes.JetScope
trait SmartCompletionData{
fun toElement(descriptor: DeclarationDescriptor): LookupElement?
@@ -69,14 +70,11 @@ fun buildSmartCompletionData(expression: JetSimpleNameExpression, resolveSession
val additionalElements = ArrayList<LookupElement>()
if (receiver == null) {
for (expectedType in expectedTypes) {
//TODO: there can be duplicates here for multiple expected types
staticMembers(expressionWithType, expectedType, resolveSession, bindingContext).toCollection(additionalElements)
}
additionalElements.addTypeInstantiationItems(expectedTypes, resolveSession, bindingContext)
thisItems(expressionWithType, expectedTypes, bindingContext).toCollection(additionalElements)
additionalElements.addStaticMembers(expressionWithType, expectedTypes, resolveSession, bindingContext)
additionalElements.addThisItems(expressionWithType, expectedTypes, bindingContext)
}
val dataFlowInfo = bindingContext[BindingContext.EXPRESSION_DATA_FLOW_INFO, expressionWithType]
@@ -301,12 +299,11 @@ private fun MutableCollection<LookupElement>.addTypeInstantiationItems(jetType:
}
}
private fun thisItems(context: JetExpression, expectedTypes: Collection<ExpectedTypeInfo>, bindingContext: BindingContext): Iterable<LookupElement> {
private fun MutableCollection<LookupElement>.addThisItems(context: JetExpression, expectedTypes: Collection<ExpectedTypeInfo>, bindingContext: BindingContext) {
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
if (scope == null) return listOf()
if (scope == null) return
val receivers: List<ReceiverParameterDescriptor> = scope.getImplicitReceiversHierarchy()
val result = ArrayList<LookupElement>()
for (i in 0..receivers.size - 1) {
val receiver = receivers[i]
val thisType = receiver.getType()
@@ -318,10 +315,9 @@ private fun thisItems(context: JetExpression, expectedTypes: Collection<Expected
val expressionText = if (qualifier == null) "this" else "this@" + qualifier
val lookupElement = LookupElementBuilder.create(expressionText).withTypeText(DescriptorRenderer.TEXT.renderType(thisType))
val tailType = mergeTails(matchedExpectedTypes.map { it.tail })
result.add(decorateLookupElement(lookupElement, tailType))
add(decorateLookupElement(lookupElement, tailType))
}
}
return result
}
private fun thisQualifierName(receiver: ReceiverParameterDescriptor, bindingContext: BindingContext): String? {
@@ -389,37 +385,61 @@ private fun processDataFlowInfo(dataFlowInfo: DataFlowInfo?, receiver: JetExpres
}
// adds java static members, enum members and members from class object
private fun staticMembers(context: JetExpression, expectedType: ExpectedTypeInfo, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext): Iterable<LookupElement> {
val classDescriptor = TypeUtils.getClassDescriptor(expectedType.`type`)
if (classDescriptor == null) return listOf()
if (classDescriptor.getName().isSpecial()) return listOf()
private fun MutableCollection<LookupElement>.addStaticMembers(context: JetExpression, expectedTypes: Collection<ExpectedTypeInfo>,
resolveSession: ResolveSessionForBodies, bindingContext: BindingContext) {
val scope = bindingContext[BindingContext.RESOLUTION_SCOPE, context]
if (scope == null) return listOf()
if (scope == null) return
val descriptors = ArrayList<DeclarationDescriptor>()
val isSuitableCallable: (DeclarationDescriptor) -> Boolean = {
it is CallableDescriptor && it.getReturnType()?.let { isSubtypeOf(it, expectedType.`type`) } ?: false
val expectedTypesByClass = expectedTypes.groupBy { TypeUtils.getClassDescriptor(it.`type`) }
for ((classDescriptor, expectedTypesForClass) in expectedTypesByClass) {
if (classDescriptor != null && !classDescriptor.getName().isSpecial()) {
addStaticMembers(classDescriptor, expectedTypesForClass, scope, resolveSession, bindingContext)
}
}
}
if (classDescriptor is JavaClassDescriptor) {
val pseudoPackage = classDescriptor.getCorrespondingPackageFragment()
if (pseudoPackage != null) {
pseudoPackage.getMemberScope().getAllDescriptors().filterTo(descriptors, isSuitableCallable)
private fun MutableCollection<LookupElement>.addStaticMembers(classDescriptor: ClassDescriptor, expectedTypes: Collection<ExpectedTypeInfo>,
scope: JetScope, resolveSession: ResolveSessionForBodies, bindingContext: BindingContext) {
val memberDescriptors = HashMap<DeclarationDescriptor, MutableList<ExpectedTypeInfo>>()
for (expectedType in expectedTypes) {
fun addMemberDescriptor(descriptor: DeclarationDescriptor) {
val list = memberDescriptors[descriptor]
if (list != null) {
list.add(expectedType)
}
else{
if (descriptor is DeclarationDescriptorWithVisibility && !Visibilities.isVisible(descriptor, scope.getContainingDeclaration())) return
val newList = ArrayList<ExpectedTypeInfo>()
newList.add(expectedType)
memberDescriptors[descriptor] = newList
}
}
fun isSuitableCallable(descriptor: DeclarationDescriptor)
= descriptor is CallableDescriptor && descriptor.getReturnType()?.let { isSubtypeOf(it, expectedType.`type`) } ?: false
if (classDescriptor is JavaClassDescriptor) {
val pseudoPackage = classDescriptor.getCorrespondingPackageFragment()
if (pseudoPackage != null) {
pseudoPackage.getMemberScope().getAllDescriptors().filter(::isSuitableCallable).forEach(::addMemberDescriptor)
}
}
val classObject = classDescriptor.getClassObjectDescriptor()
if (classObject != null) {
classObject.getDefaultType().getMemberScope().getAllDescriptors().filter(::isSuitableCallable).forEach(::addMemberDescriptor)
}
if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()
.filter{ it is ClassDescriptor && it.getKind() == ClassKind.ENUM_ENTRY }.forEach(::addMemberDescriptor)
}
}
val classObject = classDescriptor.getClassObjectDescriptor()
if (classObject != null) {
classObject.getDefaultType().getMemberScope().getAllDescriptors().filterTo(descriptors, isSuitableCallable)
}
if (classDescriptor.getKind() == ClassKind.ENUM_CLASS) {
classDescriptor.getDefaultType().getMemberScope().getAllDescriptors()
.filterTo(descriptors) { it is ClassDescriptor && it.getKind() == ClassKind.ENUM_ENTRY }
}
fun toLookupElement(descriptor: DeclarationDescriptor): LookupElement {
for ((descriptor, descriptorExpectedTypes) in memberDescriptors) {
val lookupElement = DescriptorLookupConverter.createLookupElement(resolveSession, bindingContext, descriptor)
val qualifierPresentation = classDescriptor.getName().asString()
val lookupString = qualifierPresentation + "." + lookupElement.getLookupString()
@@ -480,12 +500,9 @@ private fun staticMembers(context: JetExpression, expectedType: ExpectedTypeInfo
}
}
return decorateLookupElement(lookupElementDecorated, expectedType.tail)
val tail = mergeTails(descriptorExpectedTypes.map { it.tail })
add(decorateLookupElement(lookupElementDecorated, tail))
}
return descriptors
.filter { !(it is DeclarationDescriptorWithVisibility) || Visibilities.isVisible(it, scope.getContainingDeclaration()) }
.map(::toLookupElement)
}
private fun mergeTails(tails: Collection<Tail?>): Tail? {
@@ -517,10 +534,4 @@ private fun isSubtypeOf(t: JetType, expectedType: JetType): Boolean{
private fun <T : Any> T?.toList(): List<T> = if (this != null) listOf(this) else listOf()
private fun <T> MutableCollection<T>.addAll(iterator: Iterator<T>) {
for (item in iterator) {
add(item)
}
}
private fun String?.isNullOrEmpty() = this == null || this.isEmpty()
@@ -0,0 +1,16 @@
class C {
class object {
fun f1(): C = C()
fun f2(): C = C()
fun f3(): C? = C()
}
}
fun foo(c: C){}
fun foo(c: C?, i: Int){}
fun foo() {
foo(<caret>
}
// ELEMENT: C.f1
@@ -0,0 +1,16 @@
class C {
class object {
fun f1(): C = C()
fun f2(): C = C()
fun f3(): C? = C()
}
}
fun foo(c: C){}
fun foo(c: C?, i: Int){}
fun foo() {
foo(C.f1()<caret>
}
// ELEMENT: C.f1
@@ -0,0 +1,16 @@
class C {
class object {
fun f1(): C = C()
fun f2(): C = C()
fun f3(): C? = C()
}
}
fun foo(c: C){}
fun foo(c: C?, i: Int){}
fun foo() {
foo(<caret>
}
// ELEMENT: C.f3
@@ -0,0 +1,16 @@
class C {
class object {
fun f1(): C = C()
fun f2(): C = C()
fun f3(): C? = C()
}
}
fun foo(c: C){}
fun foo(c: C?, i: Int){}
fun foo() {
foo(C.f3(), <caret>
}
// ELEMENT: C.f3
@@ -196,6 +196,16 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion
doTest("idea/testData/completion/handlers/smart/MergeTail2.kt");
}
@TestMetadata("MergeTail3.kt")
public void testMergeTail3() throws Exception {
doTest("idea/testData/completion/handlers/smart/MergeTail3.kt");
}
@TestMetadata("MergeTail4.kt")
public void testMergeTail4() throws Exception {
doTest("idea/testData/completion/handlers/smart/MergeTail4.kt");
}
@TestMetadata("TabReplaceComma1.kt")
public void testTabReplaceComma1() throws Exception {
doTest("idea/testData/completion/handlers/smart/TabReplaceComma1.kt");