Use one KtDestructuringDeclarationReference per each entry - better highlight usages and other actions

This commit is contained in:
Valentin Kipyatkov
2016-08-29 23:07:48 +03:00
parent 9cd7e9d519
commit b332867b25
11 changed files with 66 additions and 74 deletions
@@ -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<KtReferenceExpression>()
if (refExpr == null) return null
val refExpr = element?.getNonStrictParentOfType<KtReferenceExpression>() ?: return null
val context = refExpr.analyze()
@@ -26,9 +26,7 @@ import org.jetbrains.kotlin.psi.*
class KotlinReferenceContributor() : AbstractKotlinReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
with(registrar) {
registerProvider<KtSimpleNameExpression> {
KtSimpleNameReference(it)
}
registerProvider<KtSimpleNameExpression>(factory = ::KtSimpleNameReference)
registerMultiProvider<KtNameReferenceExpression> {
if (it.getReferencedNameElementType() != KtTokens.IDENTIFIER) return@registerMultiProvider emptyArray()
@@ -43,33 +41,19 @@ class KotlinReferenceContributor() : AbstractKotlinReferenceContributor() {
}
}
registerProvider<KtConstructorDelegationReferenceExpression> {
KtConstructorDelegationReference(it)
}
registerProvider<KtConstructorDelegationReferenceExpression>(factory = ::KtConstructorDelegationReference)
registerProvider<KtCallExpression> {
KtInvokeFunctionReference(it)
}
registerProvider<KtCallExpression>(factory = ::KtInvokeFunctionReference)
registerProvider<KtArrayAccessExpression> {
KtArrayAccessReference(it)
}
registerProvider<KtArrayAccessExpression>(factory = ::KtArrayAccessReference)
registerProvider<KtForExpression> {
KtForLoopInReference(it)
}
registerProvider<KtForExpression>(factory = ::KtForLoopInReference)
registerProvider<KtPropertyDelegate> {
KtPropertyDelegationMethodsReference(it)
}
registerProvider<KtPropertyDelegate>(factory = ::KtPropertyDelegationMethodsReference)
registerProvider<KtDestructuringDeclaration> {
KtDestructuringDeclarationReference(it)
}
registerProvider<KtDestructuringDeclarationEntry>(factory = ::KtDestructuringDeclarationReference)
registerProvider<KDocName> {
KDocReference(it)
}
registerProvider<KDocName>(factory = ::KDocReference)
}
}
}
@@ -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<KtDestructuringDeclaration>(element) {
class KtDestructuringDeclarationReference(element: KtDestructuringDeclarationEntry) : AbstractKtReference<KtDestructuringDeclarationEntry>(element) {
override fun getTargetDescriptors(context: BindingContext): Collection<DeclarationDescriptor> {
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?
@@ -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<PsiReference>): 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
@@ -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<PsiReference>,
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<KtDestructuringDeclarationReference>()
val declarationReference = declaration.entries[componentIndex - 1].references.firstIsInstance<KtDestructuringDeclarationReference>()
if (declarationReference.isReferenceTo(target)) {
consumer.process(declarationReference)
}
@@ -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)
}
}
}
@@ -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<KtDestructuringDeclaration>(element) {
override fun processUsage(changeInfo: KotlinChangeInfo, element: KtDestructuringDeclaration, allUsages: Array<out UsageInfo>): Boolean {
class KotlinComponentUsageInDestructuring(element: KtDestructuringDeclarationEntry) : KotlinUsageInfo<KtDestructuringDeclarationEntry>(element) {
override fun processUsage(changeInfo: KotlinChangeInfo, element: KtDestructuringDeclarationEntry, allUsages: Array<out UsageInfo>): 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 {
@@ -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
@@ -6,8 +6,5 @@ fun <Int> f(l: List<Int>) {
val (e<caret>l1, el2, el3) = l
}
// MULTIRESOLVE
// REF: (for kotlin.collections.List<T> in dependency).component1()
// REF: (for kotlin.collections.List<T> in dependency).component2()
// REF: (for kotlin.collections.List<T> in dependency).component3()
@@ -7,9 +7,7 @@ fun A.component1() = 1
fun A.component2() = 1
fun main(args: Array<String>) {
val (a,<caret> b) = A()
val (a, <caret>b) = A()
}
// MULTIRESOLVE
// REF: (for A in a).component1()
// REF: (for A in a).component2()
+1 -3
View File
@@ -7,9 +7,7 @@ class MyPair {
fun main(args: Array<String>) {
val p = MyPair()
val (a, b<caret>) = p
val (a, <caret>b) = p
}
// MULTIRESOLVE
// REF: (in a.MyPair).component1()
// REF: (in a.MyPair).component2()