From b332867b25c31d56a7f0d30db91826c048063163 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Mon, 29 Aug 2016 23:07:48 +0300 Subject: [PATCH] Use one KtDestructuringDeclarationReference per each entry - better highlight usages and other actions --- .../kotlin/idea/findUsages/UsageTypeUtils.kt | 5 ++- .../references/KotlinReferenceContributor.kt | 32 +++++-------------- .../KtDestructuringDeclarationReference.kt | 16 +++------- .../KotlinRequestResultProcessor.kt | 23 +++++++++---- .../destructuringDeclarationUsages.kt | 26 +++++++-------- .../KotlinChangeSignatureUsageProcessor.kt | 4 +-- .../KotlinComponentUsageInDestructuring.kt | 12 ++++--- .../KotlinTargetElementEvaluator.kt | 11 ++++++- .../multiDeclarationWithTypeParameters.kt | 3 -- .../references/MultiDeclarationExtension.kt | 4 +-- .../references/MultiDeclarationMember.kt | 4 +-- 11 files changed, 66 insertions(+), 74 deletions(-) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt index 648ba65c11f..64a37027f76 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/findUsages/UsageTypeUtils.kt @@ -34,13 +34,12 @@ object UsageTypeUtils { fun getUsageType(element: PsiElement?): UsageTypeEnum? { when (element) { is KtForExpression -> return IMPLICIT_ITERATION - is KtDestructuringDeclaration -> return READ + is KtDestructuringDeclarationEntry -> return READ is KtPropertyDelegate -> return PROPERTY_DELEGATION is KtStringTemplateExpression -> return USAGE_IN_STRING_LITERAL } - val refExpr = element?.getNonStrictParentOfType() - if (refExpr == null) return null + val refExpr = element?.getNonStrictParentOfType() ?: return null val context = refExpr.analyze() diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KotlinReferenceContributor.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KotlinReferenceContributor.kt index cf05d938f90..c1ecd2b2af5 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KotlinReferenceContributor.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KotlinReferenceContributor.kt @@ -26,9 +26,7 @@ import org.jetbrains.kotlin.psi.* class KotlinReferenceContributor() : AbstractKotlinReferenceContributor() { override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { with(registrar) { - registerProvider { - KtSimpleNameReference(it) - } + registerProvider(factory = ::KtSimpleNameReference) registerMultiProvider { if (it.getReferencedNameElementType() != KtTokens.IDENTIFIER) return@registerMultiProvider emptyArray() @@ -43,33 +41,19 @@ class KotlinReferenceContributor() : AbstractKotlinReferenceContributor() { } } - registerProvider { - KtConstructorDelegationReference(it) - } + registerProvider(factory = ::KtConstructorDelegationReference) - registerProvider { - KtInvokeFunctionReference(it) - } + registerProvider(factory = ::KtInvokeFunctionReference) - registerProvider { - KtArrayAccessReference(it) - } + registerProvider(factory = ::KtArrayAccessReference) - registerProvider { - KtForLoopInReference(it) - } + registerProvider(factory = ::KtForLoopInReference) - registerProvider { - KtPropertyDelegationMethodsReference(it) - } + registerProvider(factory = ::KtPropertyDelegationMethodsReference) - registerProvider { - KtDestructuringDeclarationReference(it) - } + registerProvider(factory = ::KtDestructuringDeclarationReference) - registerProvider { - KDocReference(it) - } + registerProvider(factory = ::KDocReference) } } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtDestructuringDeclarationReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtDestructuringDeclarationReference.kt index ce3d7a41909..d99ac119352 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtDestructuringDeclarationReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/KtDestructuringDeclarationReference.kt @@ -22,22 +22,16 @@ import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze -import org.jetbrains.kotlin.psi.KtDestructuringDeclaration +import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.utils.singletonOrEmptyList -class KtDestructuringDeclarationReference(element: KtDestructuringDeclaration) : KtMultiReference(element) { +class KtDestructuringDeclarationReference(element: KtDestructuringDeclarationEntry) : AbstractKtReference(element) { override fun getTargetDescriptors(context: BindingContext): Collection { - return expression.entries.mapNotNull { entry -> - context.get(BindingContext.COMPONENT_RESOLVED_CALL, entry)?.candidateDescriptor - } + return context[BindingContext.COMPONENT_RESOLVED_CALL, element]?.candidateDescriptor.singletonOrEmptyList() } - override fun getRangeInElement(): TextRange? { - val start = expression.lPar - val end = expression.rPar - if (start == null || end == null) return TextRange.EMPTY_RANGE - return TextRange(start.startOffsetInParent, end.startOffsetInParent) - } + override fun getRangeInElement() = TextRange(0, element.textLength) override fun canRename(): Boolean { val bindingContext = expression.analyze() //TODO: should it use full body resolve? diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinRequestResultProcessor.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinRequestResultProcessor.kt index 35cda4f2c0e..fb0a57c9738 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinRequestResultProcessor.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinRequestResultProcessor.kt @@ -23,9 +23,11 @@ import com.intellij.psi.PsiReferenceService import com.intellij.psi.ReferenceRange import com.intellij.psi.search.RequestResultProcessor import com.intellij.util.Processor +import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference import org.jetbrains.kotlin.idea.search.usagesSearch.isCallableOverrideUsage import org.jetbrains.kotlin.idea.search.usagesSearch.isExtensionOfDeclarationClassUsage import org.jetbrains.kotlin.idea.search.usagesSearch.isUsageInContainingDeclaration +import org.jetbrains.kotlin.psi.KtDestructuringDeclaration import org.jetbrains.kotlin.psi.KtNamedDeclaration class KotlinRequestResultProcessor( @@ -37,18 +39,27 @@ class KotlinRequestResultProcessor( private val referenceService = PsiReferenceService.getService() override fun processTextOccurrence(element: PsiElement, offsetInElement: Int, consumer: Processor): Boolean { - return referenceService.getReferences(element, PsiReferenceService.Hints.NO_HINTS).all { ref -> + val references = if (element is KtDestructuringDeclaration) + element.entries.flatMap { referenceService.getReferences(it, PsiReferenceService.Hints.NO_HINTS) } + else + referenceService.getReferences(element, PsiReferenceService.Hints.NO_HINTS) + return references.all { ref -> ProgressManager.checkCanceled() - when { - !filter(ref) -> true - !ReferenceRange.containsOffsetInElement(ref, offsetInElement) -> true - !ref.isReferenceToTarget(unwrappedElement) -> true - else -> consumer.process(ref) + if (filter(ref) && ref.containsOffsetInElement(offsetInElement) && ref.isReferenceToTarget(unwrappedElement)) { + consumer.process(ref) + } + else { + true } } } + private fun PsiReference.containsOffsetInElement(offsetInElement: Int): Boolean { + if (this is KtDestructuringDeclarationReference) return true + return ReferenceRange.containsOffsetInElement(this, offsetInElement) + } + private fun PsiReference.isReferenceToTarget(element: PsiElement): Boolean { if (isReferenceTo(element)) { return true diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt index 9bd1bd0bf38..b4f9e504484 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/search/usagesSearch/destructuringDeclarationUsages.kt @@ -118,8 +118,16 @@ fun findDestructuringDeclarationUsages( classDescriptor.defaultType.toFuzzyType(classDescriptor.typeConstructor.parameters) } + val componentIndex = when (ktDeclaration) { + is KtParameter -> ktDeclaration.dataClassComponentFunction()?.name?.asString()?.let { getComponentIndex(it) } + is KtFunction -> ktDeclaration.name?.let { getComponentIndex(it) } + //TODO: java component functions (see KT-13605) + else -> null + } ?: return + Processor(dataType, ktDeclaration, + componentIndex, scope, consumer, plainSearchHandler = { searchScope -> doPlainSearch(ktDeclaration, searchScope, optimizer) }, @@ -141,6 +149,7 @@ private fun doPlainSearch(ktDeclaration: KtDeclaration, scope: SearchScope, opti private class Processor( private val dataType: FuzzyType, private val target: KtDeclaration, + private val componentIndex: Int, private val searchScope: SearchScope, private val consumer: Processor, plainSearchHandler: (SearchScope) -> Unit, @@ -248,17 +257,8 @@ private class Processor( when (kind) { CallableToProcessKind.HAS_DATA_CLASS_TYPE -> { if (reference is KtDestructuringDeclarationReference) { - // declaration usage in form of destructuring declaration - val entries = reference.element.entries - val componentIndex = when (declaration) { - is KtParameter -> declaration.dataClassComponentFunction()?.name?.asString()?.let { getComponentIndex(it) } - is KtFunction -> declaration.name?.let { getComponentIndex(it) } - //TODO: java component functions (see KT-13605) - else -> null - } - if (componentIndex != null && componentIndex <= entries.size) { - addCallableDeclarationToProcess(entries[componentIndex - 1], CallableToProcessKind.HAS_DATA_CLASS_TYPE) - } + // declaration usage in form of destructuring declaration entry + addCallableDeclarationToProcess(reference.element, CallableToProcessKind.HAS_DATA_CLASS_TYPE) } else { (reference.element as? KtReferenceExpression)?.let { processSuspiciousExpression(it) } @@ -552,10 +552,10 @@ private class Processor( */ private fun processSuspiciousDeclaration(declaration: KtDeclaration) { if (declaration is KtDestructuringDeclaration) { - if (searchScope.contains(declaration)) { + if (searchScope.contains(declaration) && componentIndex <= declaration.entries.size) { testLog?.add("Checked type of ${declaration.logPresentation()}") - val declarationReference = declaration.references.firstIsInstance() + val declarationReference = declaration.entries[componentIndex - 1].references.firstIsInstance() if (declarationReference.isReferenceTo(target)) { consumer.process(declarationReference) } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt index f928f370e07..a85a9f3c050 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/KotlinChangeSignatureUsageProcessor.kt @@ -293,8 +293,8 @@ class KotlinChangeSignatureUsageProcessor : ChangeSignatureUsageProcessor { if (isDataClass) { (functionPsi as KtPrimaryConstructor).valueParameters.firstOrNull()?.let { ReferencesSearch.search(it).mapNotNullTo(result) { - val destructuringDeclaration = it.element as? KtDestructuringDeclaration ?: return@mapNotNullTo null - KotlinComponentUsageInDestructuring(destructuringDeclaration) + val destructuringEntry = it.element as? KtDestructuringDeclarationEntry ?: return@mapNotNullTo null + KotlinComponentUsageInDestructuring(destructuringEntry) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinComponentUsageInDestructuring.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinComponentUsageInDestructuring.kt index ab29959b02f..56b4a5229b7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinComponentUsageInDestructuring.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/changeSignature/usages/KotlinComponentUsageInDestructuring.kt @@ -24,21 +24,23 @@ import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator.Target import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinChangeInfo import org.jetbrains.kotlin.idea.refactoring.replaceListPsiAndKeepDelimiters import org.jetbrains.kotlin.psi.KtDestructuringDeclaration +import org.jetbrains.kotlin.psi.KtDestructuringDeclarationEntry import org.jetbrains.kotlin.psi.KtPsiFactory import org.jetbrains.kotlin.psi.buildDestructuringDeclaration import org.jetbrains.kotlin.psi.psiUtil.PsiChildRange import org.jetbrains.kotlin.utils.ifEmpty -class KotlinComponentUsageInDestructuring(element: KtDestructuringDeclaration) : KotlinUsageInfo(element) { - override fun processUsage(changeInfo: KotlinChangeInfo, element: KtDestructuringDeclaration, allUsages: Array): Boolean { +class KotlinComponentUsageInDestructuring(element: KtDestructuringDeclarationEntry) : KotlinUsageInfo(element) { + override fun processUsage(changeInfo: KotlinChangeInfo, element: KtDestructuringDeclarationEntry, allUsages: Array): Boolean { if (!changeInfo.isParameterSetOrOrderChanged) return true - val currentEntries = element.entries + val declaration = element.parent as KtDestructuringDeclaration + val currentEntries = declaration.entries val newParameterInfos = changeInfo.getNonReceiverParameters() val newDestructuring = KtPsiFactory(element).buildDestructuringDeclaration { val lastIndex = newParameterInfos.indexOfLast { it.oldIndex in currentEntries.indices } - val nameValidator = CollectingNameValidator(filter = NewDeclarationNameValidator(element.parent, null, Target.VARIABLES)) + val nameValidator = CollectingNameValidator(filter = NewDeclarationNameValidator(declaration.parent, null, Target.VARIABLES)) appendFixedText("val (") for (i in 0..lastIndex) { @@ -58,7 +60,7 @@ class KotlinComponentUsageInDestructuring(element: KtDestructuringDeclaration) : appendFixedText(")") } replaceListPsiAndKeepDelimiters( - element, + declaration, newDestructuring, { apply { diff --git a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt index dca94bc1d5c..98b67c80f74 100644 --- a/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt +++ b/idea/src/org/jetbrains/kotlin/idea/search/ideaExtensions/KotlinTargetElementEvaluator.kt @@ -17,9 +17,12 @@ package org.jetbrains.kotlin.idea.search.ideaExtensions import com.intellij.codeInsight.TargetElementEvaluatorEx +import com.intellij.codeInsight.TargetElementUtil +import com.intellij.codeInsight.TargetElementUtilExtender import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile import com.intellij.psi.PsiReference +import org.jetbrains.kotlin.idea.references.KtDestructuringDeclarationReference import org.jetbrains.kotlin.psi.KtClass import org.jetbrains.kotlin.psi.KtPrimaryConstructor import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType @@ -28,7 +31,13 @@ import org.jetbrains.kotlin.psi.psiUtil.isAbstract class KotlinTargetElementEvaluator : TargetElementEvaluatorEx { override fun includeSelfInGotoImplementation(element: PsiElement): Boolean = !(element is KtClass && element.isAbstract()) - override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? = null + override fun getElementByReference(ref: PsiReference, flags: Int): PsiElement? { + // prefer destructing declaration entry to its target if element name is accepted + if (ref is KtDestructuringDeclarationReference && flags.and(TargetElementUtil.ELEMENT_NAME_ACCEPTED) != 0) { + return ref.element + } + return null + } override fun isIdentifierPart(file: PsiFile, text: CharSequence?, offset: Int): Boolean { // '(' is considered identifier part if it belongs to primary constructor without 'constructor' keyword diff --git a/idea/testData/resolve/referenceWithLib/multiDeclarationWithTypeParameters.kt b/idea/testData/resolve/referenceWithLib/multiDeclarationWithTypeParameters.kt index 84155f3048c..468e734389a 100644 --- a/idea/testData/resolve/referenceWithLib/multiDeclarationWithTypeParameters.kt +++ b/idea/testData/resolve/referenceWithLib/multiDeclarationWithTypeParameters.kt @@ -6,8 +6,5 @@ fun f(l: List) { val (el1, el2, el3) = l } -// MULTIRESOLVE // REF: (for kotlin.collections.List in dependency).component1() -// REF: (for kotlin.collections.List in dependency).component2() -// REF: (for kotlin.collections.List in dependency).component3() diff --git a/idea/testData/resolve/references/MultiDeclarationExtension.kt b/idea/testData/resolve/references/MultiDeclarationExtension.kt index d834fbc5f86..0984a175ade 100644 --- a/idea/testData/resolve/references/MultiDeclarationExtension.kt +++ b/idea/testData/resolve/references/MultiDeclarationExtension.kt @@ -7,9 +7,7 @@ fun A.component1() = 1 fun A.component2() = 1 fun main(args: Array) { - val (a, b) = A() + val (a, b) = A() } -// MULTIRESOLVE -// REF: (for A in a).component1() // REF: (for A in a).component2() diff --git a/idea/testData/resolve/references/MultiDeclarationMember.kt b/idea/testData/resolve/references/MultiDeclarationMember.kt index e42459fe3c7..de5262e052b 100644 --- a/idea/testData/resolve/references/MultiDeclarationMember.kt +++ b/idea/testData/resolve/references/MultiDeclarationMember.kt @@ -7,9 +7,7 @@ class MyPair { fun main(args: Array) { val p = MyPair() - val (a, b) = p + val (a, b) = p } -// MULTIRESOLVE -// REF: (in a.MyPair).component1() // REF: (in a.MyPair).component2() \ No newline at end of file