From d8d00a83bbce5899252bec1a1338b262ef78f357 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Fri, 10 Jul 2015 16:50:03 +0300 Subject: [PATCH] Separated references to get/set methods on synthetic extension usage into separate PsiReference's to avoid putting usages like "x++" into "Dynamic usages" group --- .../kotlin/idea/references/JetReference.kt | 2 + .../references/JetReferenceContributor.kt | 60 ++++++++-- .../idea/references/JetSimpleNameReference.kt | 104 +++--------------- .../SyntheticPropertyAccessorReference.kt | 69 ++++++++++++ 4 files changed, 137 insertions(+), 98 deletions(-) create mode 100644 idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReference.kt index f56408f4947..ce1a9eb59b5 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReference.kt @@ -114,6 +114,8 @@ public abstract class AbstractJetReference(element: T) } return context[BindingContext.AMBIGUOUS_LABEL_TARGET, reference] } + + override fun toString() = javaClass.getSimpleName() + ": " + expression.getText() } public abstract class JetSimpleReference(expression: T) : AbstractJetReference(expression) { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReferenceContributor.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReferenceContributor.kt index ba9e1b958c8..e123f54f596 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReferenceContributor.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetReferenceContributor.kt @@ -16,12 +16,16 @@ package org.jetbrains.kotlin.idea.references -import com.intellij.psi.* -import org.jetbrains.kotlin.psi.* -import com.intellij.util.ProcessingContext import com.intellij.patterns.PlatformPatterns +import com.intellij.psi.* +import com.intellij.util.ProcessingContext import org.jetbrains.kotlin.idea.kdoc.KDocReference import org.jetbrains.kotlin.kdoc.psi.impl.KDocName +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getAssignmentByLHS +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis +import org.jetbrains.kotlin.utils.addToStdlib.constant public class JetReferenceContributor() : PsiReferenceContributor() { public override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) { @@ -29,38 +33,78 @@ public class JetReferenceContributor() : PsiReferenceContributor() { registerProvider(javaClass()) { JetSimpleNameReference(it) } + + registerMultiProvider(javaClass()) { + if (it.getReferencedNameElementType() != JetTokens.IDENTIFIER) return@registerMultiProvider emptyArray() + + when (it.access()) { + Access.READ -> arrayOf(SyntheticPropertyAccessorReference(it, true)) + Access.WRITE -> arrayOf(SyntheticPropertyAccessorReference(it, false)) + Access.READ_WRITE -> arrayOf(SyntheticPropertyAccessorReference(it, true), SyntheticPropertyAccessorReference(it, false)) + } + } + registerProvider(javaClass()) { JetConstructorDelegationReference(it) } + registerProvider(javaClass()) { JetInvokeFunctionReference(it) } + registerProvider(javaClass()) { JetArrayAccessReference(it) } + registerProvider(javaClass()) { JetForLoopInReference(it) } + registerProvider(javaClass()) { JetPropertyDelegationMethodsReference(it) } + registerProvider(javaClass()) { JetMultiDeclarationReference(it) } + registerProvider(javaClass()) { KDocReference(it) } } } - private fun > PsiReferenceRegistrar.registerProvider( - elementClass: Class, - factory: (E) -> R - ) { + //TODO: there should be some common util for that + private enum class Access { + READ, WRITE, READ_WRITE + } + + private fun JetSimpleNameExpression.access(): Access { + var expression = getQualifiedExpressionForSelectorOrThis() + while (expression.getParent() is JetParenthesizedExpression) { + expression = expression.getParent() as JetParenthesizedExpression + } + + val assignment = expression.getAssignmentByLHS() + if (assignment != null) { + return if (assignment.getOperationToken() == JetTokens.EQ) Access.WRITE else Access.READ_WRITE + } + + return if ((expression.getParent() as? JetUnaryExpression)?.getOperationToken() in constant { setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) }) + Access.READ_WRITE + else + Access.READ + } + + private fun PsiReferenceRegistrar.registerProvider(elementClass: Class, factory: (E) -> JetReference) { + registerMultiProvider(elementClass, { arrayOf(factory(it)) }) + } + + private fun PsiReferenceRegistrar.registerMultiProvider(elementClass: Class, factory: (E) -> Array) { registerReferenceProvider(PlatformPatterns.psiElement(elementClass), object: PsiReferenceProvider() { override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array { @suppress("UNCHECKED_CAST") - return arrayOf(factory(element as E)) + return factory(element as E) } }) } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt index 8daa638292c..b4453c21d1f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/JetSimpleNameReference.kt @@ -21,8 +21,6 @@ import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiTreeUtil import com.intellij.util.IncorrectOperationException -import com.intellij.util.SmartList -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet import org.jetbrains.kotlin.idea.intentions.OperatorToFunctionIntention @@ -35,69 +33,21 @@ import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector +import org.jetbrains.kotlin.psi.psiUtil.startOffset import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode -import org.jetbrains.kotlin.synthetic.SyntheticExtensionPropertyDescriptor import org.jetbrains.kotlin.types.expressions.OperatorConventions -import org.jetbrains.kotlin.utils.addIfNotNull -import org.jetbrains.kotlin.utils.addToStdlib.constant - -public class JetSimpleNameReference( - jetSimpleNameExpression: JetSimpleNameExpression -) : JetSimpleReference(jetSimpleNameExpression) { - - override fun getTargetDescriptors(context: BindingContext): Collection { - val targets = super.getTargetDescriptors(context) - if (targets.none { it is SyntheticExtensionPropertyDescriptor }) return targets - - val newTargets = SmartList() - for (target in targets) { - if (!(target !is SyntheticExtensionPropertyDescriptor)) { - val access = access() - if (access == Access.READ || access == Access.READ_WRITE) { - newTargets.add(target.getMethod) - } - if (access == Access.WRITE || access == Access.READ_WRITE) { - newTargets.addIfNotNull(target.setMethod) - } - } - else { - newTargets.add(target) - } - } - return newTargets - } - - //TODO: there should be some common util for that - private enum class Access { - READ, WRITE, READ_WRITE - } - - private fun access(): Access { - var expression = myElement.getQualifiedExpressionForSelectorOrThis() - while (expression.getParent() is JetParenthesizedExpression) { - expression = expression.getParent() as JetParenthesizedExpression - } - - val assignment = expression.getAssignmentByLHS() - if (assignment != null) { - return if (assignment.getOperationToken() == JetTokens.EQ) Access.WRITE else Access.READ_WRITE - } - - return if ((expression.getParent() as? JetUnaryExpression)?.getOperationToken() in constant { setOf(JetTokens.PLUSPLUS, JetTokens.MINUSMINUS) }) - Access.READ_WRITE - else - Access.READ - } +class JetSimpleNameReference(expression: JetSimpleNameExpression) : JetSimpleReference(expression) { override fun isReferenceTo(element: PsiElement?): Boolean { if (element != null) { if (!canBeReferenceTo(element)) return false - val extensions = Extensions.getArea(element.getProject()).getExtensionPoint( - SimpleNameReferenceExtension.EP_NAME).getExtensions() + val extensions = Extensions.getArea(element.getProject()).getExtensionPoint(SimpleNameReferenceExtension.EP_NAME).getExtensions() for (extension in extensions) { val value = extension.isReferenceTo(this, element) if (value != null) { @@ -124,7 +74,7 @@ public class JetSimpleNameReference( return true } - public override fun handleElementRename(newElementName: String?): PsiElement? { + override fun handleElementRename(newElementName: String?): PsiElement { if (!canRename()) throw IncorrectOperationException() if (newElementName == null) return expression; @@ -136,38 +86,15 @@ public class JetSimpleNameReference( } } - @suppress("NAME_SHADOWING") - var newElementName = newElementName!! - - val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) - if (bindingContext[BindingContext.REFERENCE_TARGET, expression] is SyntheticExtensionPropertyDescriptor) { - if (Name.isValidIdentifier(newElementName)) { - val newNameAsName = Name.identifier(newElementName) - val newName = when (access()) { - Access.READ -> SyntheticExtensionPropertyDescriptor.propertyNameByGetMethodName(newNameAsName) - Access.WRITE -> SyntheticExtensionPropertyDescriptor.propertyNameBySetMethodName(newNameAsName) - Access.READ_WRITE -> SyntheticExtensionPropertyDescriptor.propertyNameByGetMethodName(newNameAsName) - ?: SyntheticExtensionPropertyDescriptor.propertyNameBySetMethodName(newNameAsName) - } ?: return expression //TODO: handle the case when get/set becomes ordinary method - newElementName = newName.getIdentifier() - } - } - val psiFactory = JetPsiFactory(expression) val element = when (expression.getReferencedNameElementType()) { JetTokens.FIELD_IDENTIFIER -> psiFactory.createFieldIdentifier(newElementName) - else -> { - val extensions = Extensions.getArea(expression.getProject()).getExtensionPoint( - SimpleNameReferenceExtension.EP_NAME).getExtensions() - var handled: PsiElement? = null - for (extension in extensions) { - handled = extension.handleElementRename(this, psiFactory, newElementName) - if (handled != null) { - break - } - } - handled ?: psiFactory.createNameIdentifier(newElementName) + else -> { + Extensions.getArea(expression.getProject()).getExtensionPoint(SimpleNameReferenceExtension.EP_NAME).getExtensions() + .asSequence() + .map { it.handleElementRename(this, psiFactory, newElementName) } + .firstOrNull { it != null } ?: psiFactory.createNameIdentifier(newElementName) } } @@ -176,6 +103,7 @@ public class JetSimpleNameReference( val elementType = nameElement.getNode()?.getElementType() val opExpression = PsiTreeUtil.getParentOfType(expression, javaClass(), javaClass()) if (elementType is JetToken && OperatorConventions.getNameForOperationSymbol(elementType) != null && opExpression != null) { + val bindingContext = expression.analyze(BodyResolveMode.PARTIAL) val oldDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, expression] val newExpression = OperatorToFunctionIntention.convert(opExpression) newExpression.accept(object : JetTreeVisitorVoid() { @@ -209,7 +137,7 @@ public class JetSimpleNameReference( fun bindToElement(element: PsiElement, shorteningMode: ShorteningMode): PsiElement = element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode) } ?: expression - public fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING): PsiElement { + fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING): PsiElement { if (fqName.isRoot()) return expression val newExpression = expression.changeQualifiedName(fqName).getQualifiedElementSelector() as JetSimpleNameExpression @@ -231,9 +159,5 @@ public class JetSimpleNameReference( return newExpression } - override fun toString(): String { - return javaClass().getSimpleName() + ": " + expression.getText() - } - override fun getCanonicalText(): String = expression.getText() } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt new file mode 100644 index 00000000000..04194df0f62 --- /dev/null +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt @@ -0,0 +1,69 @@ +/* + * 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.references + +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import com.intellij.util.SmartList +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.JetNameReferenceExpression +import org.jetbrains.kotlin.psi.JetPsiFactory +import org.jetbrains.kotlin.psi.psiUtil.startOffset +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.synthetic.SyntheticExtensionPropertyDescriptor +import org.jetbrains.kotlin.utils.addIfNotNull + +class SyntheticPropertyAccessorReference(expression: JetNameReferenceExpression, val getter: Boolean) : JetSimpleReference(expression) { + override fun getTargetDescriptors(context: BindingContext): Collection { + val descriptors = super.getTargetDescriptors(context) + if (descriptors.none { it is SyntheticExtensionPropertyDescriptor }) return emptyList() + + val result = SmartList() + for (descriptor in descriptors) { + if (descriptor is SyntheticExtensionPropertyDescriptor) { + if (getter) { + result.add(descriptor.getMethod) + } + else { + result.addIfNotNull(descriptor.setMethod) + } + } + } + return result + } + + override fun getRangeInElement() = TextRange(0, expression.getTextLength()) + + override fun canRename() = true + + override fun handleElementRename(newElementName: String?): PsiElement? { + if (!Name.isValidIdentifier(newElementName!!)) return expression + + val newNameAsName = Name.identifier(newElementName) + val newName = if (getter) + SyntheticExtensionPropertyDescriptor.propertyNameByGetMethodName(newNameAsName) + else + SyntheticExtensionPropertyDescriptor.propertyNameBySetMethodName(newNameAsName) + if (newName == null) return expression //TODO: handle the case when get/set becomes ordinary method + + val nameIdentifier = JetPsiFactory(expression).createNameIdentifier(newName.getIdentifier()) + expression.getReferencedNameElement().replace(nameIdentifier) + return expression + } +} \ No newline at end of file