From 0edc5c15e242467a1fa9b18bfe1213f90f00f9d4 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 25 Mar 2016 12:57:42 +0300 Subject: [PATCH] Complete type instantiation items after "by" --- .../idea/completion/BasicCompletionSession.kt | 8 ++--- .../idea/completion/CompletionSession.kt | 16 ++++----- .../completion/KDocCompletionContributor.kt | 11 ++++--- .../completion/KotlinCompletionContributor.kt | 7 ++-- .../completion/ToFromOriginalFileMapper.kt | 11 ++++++- .../smart/SmartCompletionSession.kt | 4 +-- .../smart/TypeInstantiationItems.kt | 12 +++++-- .../ExtensionSubstitution1.kt | 7 ++-- .../ExtensionSubstitution2.kt | 3 +- .../ExtensionSubstitution3.kt | 3 +- .../ExtensionSubstitution4.kt | 3 +- .../smart/propertyDelegate/ExtensionVal.kt | 9 ++++- .../smart/propertyDelegate/ExtensionVar.kt | 7 ++++ .../smart/propertyDelegate/ValInClass.kt | 9 +++-- .../smart/propertyDelegate/VarInClass.kt | 14 ++++++++ .../kotlin/idea/core/ExpectedInfos.kt | 33 +++++++++++++++++-- .../kotlin/idea/core/KotlinIndicesHelper.kt | 20 +++++++---- .../idea/core/TypesWithOperatorDetector.kt | 26 +++++++++++---- 18 files changed, 153 insertions(+), 50 deletions(-) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt index 430947f5bef..4f5f62490ed 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/BasicCompletionSession.kt @@ -57,9 +57,9 @@ import java.util.* class BasicCompletionSession( configuration: CompletionSessionConfiguration, parameters: CompletionParameters, - private val toFromOriginalFileMapper: ToFromOriginalFileMapper, + toFromOriginalFileMapper: ToFromOriginalFileMapper, resultSet: CompletionResultSet -) : CompletionSession(configuration, parameters, resultSet) { +) : CompletionSession(configuration, parameters, toFromOriginalFileMapper, resultSet) { private interface CompletionKind { val descriptorKindFilter: DescriptorKindFilter? @@ -197,11 +197,11 @@ class BasicCompletionSession( // getting root packages from scope is very slow so we do this in alternative way if (callTypeAndReceiver.receiver == null && callTypeAndReceiver.callType.descriptorKindFilter.kindMask.and(DescriptorKindFilter.PACKAGES_MASK) != 0) { //TODO: move this code somewhere else? - val packageNames = PackageIndexUtil.getSubPackageFqNames(FqName.ROOT, originalSearchScope, project, prefixMatcher.asNameFilter()) + val packageNames = PackageIndexUtil.getSubPackageFqNames(FqName.ROOT, searchScope, project, prefixMatcher.asNameFilter()) .toMutableSet() if (!ProjectStructureUtil.isJsKotlinModule(parameters.originalFile as KtFile)) { - JavaPsiFacade.getInstance(project).findPackage("")?.getSubPackages(originalSearchScope)?.forEach { psiPackage -> + JavaPsiFacade.getInstance(project).findPackage("")?.getSubPackages(searchScope)?.forEach { psiPackage -> val name = psiPackage.name if (Name.isValidIdentifier(name!!)) { packageNames.add(FqName(name)) 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 7f77dfd7431..6090925a152 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 @@ -22,9 +22,7 @@ import com.intellij.codeInsight.completion.CompletionSorter import com.intellij.codeInsight.completion.CompletionUtil import com.intellij.codeInsight.completion.impl.CamelHumpMatcher import com.intellij.codeInsight.lookup.LookupElement -import com.intellij.openapi.vfs.VirtualFile import com.intellij.patterns.StandardPatterns -import com.intellij.psi.search.DelegatingGlobalSearchScope import com.intellij.psi.search.GlobalSearchScope import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.caches.resolve.* @@ -70,6 +68,7 @@ fun CompletionSessionConfiguration(parameters: CompletionParameters) = Completio abstract class CompletionSession( protected val configuration: CompletionSessionConfiguration, protected val parameters: CompletionParameters, + protected val toFromOriginalFileMapper: ToFromOriginalFileMapper, resultSet: CompletionResultSet ) { protected val position = parameters.position @@ -148,16 +147,15 @@ abstract class CompletionSession( LookupElementsCollector(prefixMatcher, parameters, resultSet, createSorter()) } - protected val originalSearchScope: GlobalSearchScope = getResolveScope(parameters.getOriginalFile() as KtFile) - - // we need to exclude the original file from scope because our resolve session is built with this file replaced by synthetic one - protected val searchScope: GlobalSearchScope = object : DelegatingGlobalSearchScope(originalSearchScope) { - override fun contains(file: VirtualFile) = super.contains(file) && file != parameters.originalFile.virtualFile - } + protected val searchScope: GlobalSearchScope = getResolveScope(parameters.originalFile as KtFile) protected fun indicesHelper(mayIncludeInaccessible: Boolean): KotlinIndicesHelper { val filter = if (mayIncludeInaccessible) isVisibleFilter else isVisibleFilterCheckAlways - return KotlinIndicesHelper(resolutionFacade, searchScope, filter, filterOutPrivate = !mayIncludeInaccessible) + return KotlinIndicesHelper(resolutionFacade, + searchScope, + filter, + filterOutPrivate = !mayIncludeInaccessible, + declarationTranslator = { toFromOriginalFileMapper.toSyntheticFile(it) }) } // excludes top-level extensions except for ones declared in the current file - those that are fetched from indices diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt index 496b26c73a9..936e719cd75 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KDocCompletionContributor.kt @@ -59,12 +59,15 @@ class KDocCompletionContributor(): CompletionContributor() { object KDocNameCompletionProvider: CompletionProvider() { override fun addCompletions(parameters: CompletionParameters, context: ProcessingContext, result: CompletionResultSet) { - KDocNameCompletionSession(parameters, result).complete() + KDocNameCompletionSession(parameters, ToFromOriginalFileMapper.create(parameters), result).complete() } } -class KDocNameCompletionSession(parameters: CompletionParameters, - resultSet: CompletionResultSet): CompletionSession(CompletionSessionConfiguration(parameters), parameters, resultSet) { +class KDocNameCompletionSession( + parameters: CompletionParameters, + toFromOriginalFileMapper: ToFromOriginalFileMapper, + resultSet: CompletionResultSet +): CompletionSession(CompletionSessionConfiguration(parameters), parameters, toFromOriginalFileMapper, resultSet) { override val descriptorKindFilter: DescriptorKindFilter? get() = null override val expectedInfos: Collection get() = emptyList() @@ -85,7 +88,7 @@ class KDocNameCompletionSession(parameters: CompletionParameters, val section = position.getContainingSection() val documentedParameters = section.findTagsByName("param").map { it.getSubjectName() }.toSet() val descriptors = getParamDescriptors(declarationDescriptor) - .filter { it.getName().asString() !in documentedParameters } + .filter { it.name.asString() !in documentedParameters } descriptors.forEach { collector.addElement(basicLookupElementFactory.createLookupElement(it, parametersAndTypeGrayed = true)) diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt index e2711a12604..5f60e322182 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/KotlinCompletionContributor.kt @@ -240,11 +240,10 @@ class KotlinCompletionContributor : CompletionContributor() { private fun performCompletion(parameters: CompletionParameters, result: CompletionResultSet) { val position = parameters.position - val positionFile = position.containingFile as? KtFile ?: return - val originalFile = parameters.originalFile as KtFile - if (originalFile.doNotComplete ?: false) return + if (position.containingFile !is KtFile) return + if ((parameters.originalFile as KtFile).doNotComplete ?: false) return - val toFromOriginalFileMapper = ToFromOriginalFileMapper(originalFile, positionFile, parameters.offset) + val toFromOriginalFileMapper = ToFromOriginalFileMapper.create(parameters) if (position.node.elementType == KtTokens.LONG_TEMPLATE_ENTRY_START) { val expression = (position.parent as KtBlockStringTemplateEntry).expression diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ToFromOriginalFileMapper.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ToFromOriginalFileMapper.kt index d18eedea167..3385f2c7b9b 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ToFromOriginalFileMapper.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/ToFromOriginalFileMapper.kt @@ -16,16 +16,25 @@ package org.jetbrains.kotlin.idea.completion +import com.intellij.codeInsight.completion.CompletionParameters import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.psiUtil.startOffset -class ToFromOriginalFileMapper( +class ToFromOriginalFileMapper private constructor( val originalFile: KtFile, val syntheticFile: KtFile, val completionOffset: Int ) { + companion object { + fun create(parameters: CompletionParameters): ToFromOriginalFileMapper { + val originalFile = parameters.originalFile as KtFile + val syntheticFile = parameters.position.containingFile as KtFile + return ToFromOriginalFileMapper(originalFile, syntheticFile, parameters.offset) + } + } + private val syntheticLength: Int private val originalLength: Int private val tailLength: Int diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt index 9617088e22f..fb63bd0213e 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/SmartCompletionSession.kt @@ -41,7 +41,7 @@ class SmartCompletionSession( parameters: CompletionParameters, toFromOriginalFileMapper: ToFromOriginalFileMapper, resultSet: CompletionResultSet -) : CompletionSession(configuration, parameters, resultSet) { +) : CompletionSession(configuration, parameters, toFromOriginalFileMapper, resultSet) { override val descriptorKindFilter: DescriptorKindFilter by lazy { // we do not include SAM-constructors because they are handled separately and adding them requires iterating of java classes @@ -60,7 +60,7 @@ class SmartCompletionSession( private val smartCompletion by lazy(LazyThreadSafetyMode.NONE) { expression?.let { SmartCompletion(it, resolutionFacade, bindingContext, moduleDescriptor, isVisibleFilter, indicesHelper(false), - prefixMatcher, originalSearchScope, toFromOriginalFileMapper, + prefixMatcher, searchScope, toFromOriginalFileMapper, callTypeAndReceiver, isJvmModule) } } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt index a7ce22ea273..9939b42c712 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/smart/TypeInstantiationItems.kt @@ -35,7 +35,7 @@ import org.jetbrains.kotlin.idea.completion.* import org.jetbrains.kotlin.idea.completion.handlers.KotlinFunctionInsertHandler import org.jetbrains.kotlin.idea.core.ExpectedInfo import org.jetbrains.kotlin.idea.core.Tail -import org.jetbrains.kotlin.idea.core.fuzzyType +import org.jetbrains.kotlin.idea.core.multipleFuzzyTypes import org.jetbrains.kotlin.idea.core.overrideImplement.ImplementMembersHandler import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.FuzzyType @@ -52,6 +52,7 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.resolveTopLevelClass import org.jetbrains.kotlin.types.* import org.jetbrains.kotlin.utils.addIfNotNull +import java.util.* class TypeInstantiationItems( val resolutionFacade: ResolutionFacade, @@ -67,9 +68,14 @@ class TypeInstantiationItems( inheritanceSearchers: MutableCollection, expectedInfos: Collection ) { - val expectedInfosGrouped: Map> = expectedInfos.groupBy { it.fuzzyType?.makeNotNullable() } + val expectedInfosGrouped = LinkedHashMap>() + for (expectedInfo in expectedInfos) { + for (fuzzyType in expectedInfo.multipleFuzzyTypes) { + expectedInfosGrouped.getOrPut(fuzzyType.makeNotNullable()) { ArrayList() }.add(expectedInfo) + } + } + for ((type, infos) in expectedInfosGrouped) { - if (type == null) continue val tail = mergeTails(infos.map { it.tail }) addTo(items, inheritanceSearchers, type, tail) } diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution1.kt b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution1.kt index b6bf6e1ca89..7b70b75fdcc 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution1.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution1.kt @@ -1,15 +1,16 @@ import kotlin.reflect.KProperty -class Property +class Property(owner: TOwner, value: TValue) operator fun Property.getValue(thisRef: TOwner, property: KProperty<*>): TValue { throw Exception() } -fun createProperty(): Property = Property() +fun createProperty(owner: TOwner, value: TValue): Property = Property(owner, value) class C { - val v by create + val v by } // EXIST: { itemText: "createProperty", typeText: "Property" } +// EXIST: Property diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution2.kt b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution2.kt index bafa3808357..a14dacd9222 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution2.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution2.kt @@ -9,7 +9,8 @@ operator fun Property.getValue(thisRef: TOwner, fun createProperty(): Property = Property() class C { - val v: Int by create + val v: Int by } // EXIST: { itemText: "createProperty", typeText: "Property" } +// EXIST: Property diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution3.kt b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution3.kt index b267d638fa3..bdc654f8e4e 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution3.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution3.kt @@ -13,7 +13,8 @@ operator fun Property.setValue(thisRef: TOw fun createProperty(): Property = Property() class C { - var v by create + var v by } // EXIST: { itemText: "createProperty", typeText: "Property" } +// EXIST: Property diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution4.kt b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution4.kt index d0623b9d00d..0a22828cfe2 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution4.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionSubstitution4.kt @@ -13,7 +13,8 @@ operator fun Property.setValue(thisRef: TOw fun createProperty(): Property = Property() class C { - var v: Int by create + var v: Int by } // EXIST: { itemText: "createProperty", typeText: "Property" } +// EXIST: Property diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVal.kt b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVal.kt index 9336efe9a47..6002bd09fc2 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVal.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVal.kt @@ -30,10 +30,17 @@ class C val C.property by // EXIST: lazy + // EXIST: createX1 // ABSENT: createX2 // EXIST: createX3 // EXIST: createY1 // ABSENT: createY2 // EXIST: createY3 -/*TODO: add constructors*/ + +// EXIST: X1 +// ABSENT: X2 +// EXIST: X3 +// EXIST: Y1 +// ABSENT: Y2 +// EXIST: Y3 diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVar.kt b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVar.kt index 816ebb3950b..75527310979 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVar.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ExtensionVar.kt @@ -35,8 +35,15 @@ class C var C.property by // ABSENT: lazy + // EXIST: createX1 // ABSENT: createX2 // ABSENT: createX3 // EXIST: createX4 // ABSENT: createX5 + +// EXIST: X1 +// ABSENT: X2 +// ABSENT: X3 +// EXIST: X4 +// ABSENT: X5 diff --git a/idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt b/idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt index 81cb5a3d521..5fc0c4d8d25 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/ValInClass.kt @@ -12,7 +12,7 @@ class X3 { class Y1 class Y2 -class Y3 +abstract class Y3 operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = "" operator fun Y2.getValue(thisRef: String, property: KProperty<*>): String = "" @@ -36,4 +36,9 @@ class C { // EXIST: createY1 // ABSENT: createY2 // EXIST: createY3 -/*TODO: add constructors*/ +// EXIST: X1 +// ABSENT: X2 +// EXIST: X3 +// EXIST: Y1 +// ABSENT: Y2 +// ABSENT: Y3 diff --git a/idea/idea-completion/testData/smart/propertyDelegate/VarInClass.kt b/idea/idea-completion/testData/smart/propertyDelegate/VarInClass.kt index a6f6c1513f5..e619a048fa8 100644 --- a/idea/idea-completion/testData/smart/propertyDelegate/VarInClass.kt +++ b/idea/idea-completion/testData/smart/propertyDelegate/VarInClass.kt @@ -24,6 +24,13 @@ class X5 { operator fun setValue(thisRef: C, property: KProperty<*>, value: String) {} } +class Y1 +class Y2 + +operator fun Y1.getValue(thisRef: C, property: KProperty<*>): String = "" +operator fun Y1.setValue(thisRef: C, property: KProperty<*>, value: String) {} +operator fun Y2.getValue(thisRef: C, property: KProperty<*>): String = "" + fun createX1() = X1() fun createX2() = X2() fun createX3() = X3() @@ -40,3 +47,10 @@ class C { // ABSENT: createX3 // EXIST: createX4 // ABSENT: createX5 +// EXIST: X1 +// ABSENT: X2 +// ABSENT: X3 +// EXIST: X4 +// ABSENT: X5 +// EXIST: Y1 +// ABSENT: Y2 diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ExpectedInfos.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ExpectedInfos.kt index face8bc8fed..60a4f6bfde1 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ExpectedInfos.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ExpectedInfos.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ideService import org.jetbrains.kotlin.idea.util.FuzzyType +import org.jetbrains.kotlin.idea.util.fuzzyExtensionReceiverType import org.jetbrains.kotlin.idea.util.fuzzyReturnType import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.lexer.KtTokens @@ -63,6 +64,12 @@ data class ItemOptions(val starPrefix: Boolean) { interface ByTypeFilter { fun matchingSubstitutor(descriptorType: FuzzyType): TypeSubstitutor? + val fuzzyType: FuzzyType? + get() = null + + val multipleFuzzyTypes: Collection + get() = fuzzyType.singletonOrEmptyList() + object All : ByTypeFilter { override fun matchingSubstitutor(descriptorType: FuzzyType) = TypeSubstitutor.EMPTY } @@ -72,7 +79,7 @@ interface ByTypeFilter { } } -class ByExpectedTypeFilter(val fuzzyType: FuzzyType) : ByTypeFilter { +class ByExpectedTypeFilter(override val fuzzyType: FuzzyType) : ByTypeFilter { override fun matchingSubstitutor(descriptorType: FuzzyType) = descriptorType.checkIsSubtypeOf(fuzzyType) override fun equals(other: Any?) = other is ByExpectedTypeFilter && fuzzyType == other.fuzzyType @@ -118,7 +125,10 @@ class ExpectedInfo( } val ExpectedInfo.fuzzyType: FuzzyType? - get() = (this.filter as? ByExpectedTypeFilter)?.fuzzyType + get() = filter.fuzzyType + +val ExpectedInfo.multipleFuzzyTypes: Collection + get() = filter.multipleFuzzyTypes sealed class ArgumentPositionData(val function: FunctionDescriptor, val callType: Call.CallType) : ExpectedInfo.AdditionalData { class Positional( @@ -632,6 +642,25 @@ class ExpectedInfos( TypeSubstitutor.createChainedSubstitutor(setOperatorSubstitutor.substitution, setParamTypeSubstitutor.substitution).substitution) } + + override val multipleFuzzyTypes: Collection by lazy { + val result = ArrayList() + + for (classDescriptor in typesWithGetDetector.classesWithMemberOperators) { + val type = classDescriptor.defaultType + val typeParameters = classDescriptor.declaredTypeParameters + val substitutor = matchingSubstitutor(FuzzyType(type, typeParameters)) ?: continue + result.add(FuzzyType(substitutor.substitute(type, Variance.INVARIANT)!!, typeParameters)) + } + + for (extensionOperator in typesWithGetDetector.extensionOperators) { + val receiverType = extensionOperator.fuzzyExtensionReceiverType()!! + val substitutor = matchingSubstitutor(receiverType) ?: continue + result.add(FuzzyType(substitutor.substitute(receiverType.type, Variance.INVARIANT)!!, receiverType.freeParameters)) + } + + result + } } return listOf(ExpectedInfo(byTypeFilter, null, null)) //TODO: special items for "Delegates...." 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 7e116f5bfe3..6b806d77578 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 @@ -42,10 +42,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.descriptors.SamAdapterDescriptor import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.KtCallableDeclaration -import org.jetbrains.kotlin.psi.KtExpression -import org.jetbrains.kotlin.psi.KtFile -import org.jetbrains.kotlin.psi.KtObjectDeclaration +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.isHiddenInResolution import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils @@ -59,6 +56,7 @@ class KotlinIndicesHelper( private val resolutionFacade: ResolutionFacade, private val scope: GlobalSearchScope, visibilityFilter: (DeclarationDescriptor) -> Boolean, + private val declarationTranslator: (KtDeclaration) -> KtDeclaration? = { it }, applyExcludeSettings: Boolean = true, private val filterOutPrivate: Boolean = true ) { @@ -94,7 +92,16 @@ class KotlinIndicesHelper( .filter { it.parent is KtFile && it.receiverTypeReference != null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } .flatMap { it.resolveToDescriptorsWithHack() } .filterIsInstance() - .filter { descriptorFilter(it) } + .filter { descriptorFilter(it) && it.extensionReceiverParameter != null } + .distinct() + } + + fun getMemberOperatorsByName(name: String): Collection { + return KotlinFunctionShortNameIndex.getInstance().get(name, project, scope) + .filter { it.parent is KtClassBody && it.receiverTypeReference == null && it.hasModifier(KtTokens.OPERATOR_KEYWORD) } + .flatMap { it.resolveToDescriptorsWithHack() } + .filterIsInstance() + .filter { descriptorFilter(it) && it.extensionReceiverParameter == null } .distinct() } @@ -316,7 +323,8 @@ class KotlinIndicesHelper( return resolutionFacade.resolveImportReference(moduleDescriptor, fqName!!).filterIsInstance() } else { - return (resolutionFacade.resolveToDescriptor(this) as? CallableDescriptor).singletonOrEmptyList() + val translatedDeclaration = declarationTranslator(this) ?: return emptyList() + return (resolutionFacade.resolveToDescriptor(translatedDeclaration) as? CallableDescriptor).singletonOrEmptyList() } } } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/TypesWithOperatorDetector.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/TypesWithOperatorDetector.kt index d66d91d1f17..dd926ae8812 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/TypesWithOperatorDetector.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/TypesWithOperatorDetector.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.core +import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor import org.jetbrains.kotlin.idea.util.FuzzyType @@ -41,19 +42,32 @@ abstract class TypesWithOperatorDetector( private val cache = HashMap?>() - private val extensionOperators: Collection by lazy { + val extensionOperators: Collection by lazy { val result = ArrayList() - collectExtensionOperators(scope.collectFunctions(name, NoLookupLocation.FROM_IDE), result) - indicesHelper?.getTopLevelExtensionOperatorsByName(name.asString())?.let { collectExtensionOperators(it, result) } + + val extensionsFromScope = scope + .collectFunctions(name, NoLookupLocation.FROM_IDE) + .filter { it.extensionReceiverParameter != null } + result.addSuitableOperators(extensionsFromScope) + + indicesHelper?.getTopLevelExtensionOperatorsByName(name.asString())?.let { result.addSuitableOperators(it) } + result.distinctBy { it.original } } - private fun collectExtensionOperators(functions: Collection, result: MutableCollection) { + val classesWithMemberOperators: Collection by lazy { + if (indicesHelper == null) return@lazy emptyList() + val operators = ArrayList().addSuitableOperators(indicesHelper.getMemberOperatorsByName(name.asString())) + operators.map { it.containingDeclaration as ClassDescriptor }.distinct() + } + + private fun MutableCollection.addSuitableOperators(functions: Collection): MutableCollection { for (function in functions) { - if (function.extensionReceiverParameter == null || !function.isValidOperator()) continue + if (!function.isValidOperator()) continue val substitutor = checkIsSuitableByType(function, function.typeParameters) ?: continue - result.add(function.substitute(substitutor)) + add(function.substitute(substitutor)) } + return this } fun findOperator(type: FuzzyType): Pair? {