References: Support implicit references to annotation method 'value'. Support Find Usages/Rename on these references

#KT-12657 Fixed
(cherry picked from commit 68d0d84)
This commit is contained in:
Alexey Sedunov
2016-06-21 18:03:21 +03:00
parent efa27e6048
commit 89816c5f12
23 changed files with 316 additions and 5 deletions
+5
View File
@@ -413,6 +413,10 @@
id="kotlinFilePathReferenceContributor"
language="kotlin"
implementation="org.jetbrains.kotlin.idea.references.KotlinFilePathReferenceContributor"/>
<psi.referenceContributor
id="kotlinDefaultAnnotationMethodImplicitReferenceContributor"
language="kotlin"
implementation="org.jetbrains.kotlin.idea.references.KotlinDefaultAnnotationMethodImplicitReferenceContributor"/>
<renamePsiElementProcessor id="KotlinClass"
implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinClassProcessor"
@@ -633,6 +637,7 @@
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinConstructorDelegationCallReferenceSearcher"/>
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinOverridingMethodReferenceSearcher"/>
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinConventionMethodReferencesSearcher"/>
<methodReferencesSearch implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.DefaultAnnotationMethodKotlinImplicitReferenceSearcher"/>
<readWriteAccessDetector implementation="org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector" id="kotlin"/>
@@ -0,0 +1,95 @@
/*
* Copyright 2010-2016 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.*
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.util.ArrayUtil
import com.intellij.util.IncorrectOperationException
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class KotlinDefaultAnnotationMethodImplicitReferenceContributor : AbstractKotlinReferenceContributor() {
class ReferenceImpl(private val argument: KtValueArgument) : PsiReference {
private fun resolveAnnotationCallee(): PsiElement? = argument.getStrictParentOfType<KtAnnotationEntry>()
?.calleeExpression
?.constructorReferenceExpression
?.mainReference
?.resolve()
override fun getElement() = argument
override fun getRangeInElement() = TextRange.EMPTY_RANGE
override fun resolve(): PsiElement? {
val annotationPsi = resolveAnnotationCallee() ?: return null
val name = PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME
return when (annotationPsi) {
is PsiClass -> {
val signature = MethodSignatureUtil.createMethodSignature(
name, PsiType.EMPTY_ARRAY, PsiTypeParameter.EMPTY_ARRAY, PsiSubstitutor.EMPTY
)
MethodSignatureUtil.findMethodBySignature(annotationPsi, signature, false)
}
is KtPrimaryConstructor -> annotationPsi.containingClassOrObject?.findPropertyByName(name) as? KtParameter
else -> null
}
}
override fun getCanonicalText() = PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME
override fun handleElementRename(newElementName: String): PsiElement {
val psiFactory = KtPsiFactory(argument)
val newArgument = psiFactory.createArgument(
argument.getArgumentExpression(),
Name.identifier(newElementName.quoteIfNeeded()),
argument.getSpreadElement() != null
)
return argument.replaced(newArgument)
}
override fun bindToElement(element: PsiElement) = throw IncorrectOperationException("Not implemented")
override fun isReferenceTo(element: PsiElement): Boolean {
val unwrapped = element.unwrapped
return (unwrapped is PsiMethod || unwrapped is KtParameter) && unwrapped == resolve()
}
override fun getVariants() = ArrayUtil.EMPTY_OBJECT_ARRAY
override fun isSoft() = false
}
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerProvider<KtValueArgument> {
if (it.isNamed()) return@registerProvider null
val annotationEntry = it.getParentOfTypeAndBranch<KtAnnotationEntry> { valueArgumentList }
?: return@registerProvider null
if (annotationEntry.valueArguments.size != 1) return@registerProvider null
ReferenceImpl(it)
}
}
}
@@ -0,0 +1,60 @@
/*
* Copyright 2010-2016 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.search.ideaExtensions
import com.intellij.openapi.application.QueryExecutorBase
import com.intellij.openapi.application.ReadActionProcessor
import com.intellij.openapi.application.runReadAction
import com.intellij.psi.PsiAnnotation
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiUtil
import com.intellij.util.Processor
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.psi.KtAnnotationEntry
import org.jetbrains.kotlin.psi.KtValueArgument
import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull
import org.jetbrains.kotlin.idea.references.KotlinDefaultAnnotationMethodImplicitReferenceContributor.ReferenceImpl as ImplicitReference
class DefaultAnnotationMethodKotlinImplicitReferenceSearcher : QueryExecutorBase<PsiReference, MethodReferencesSearch.SearchParameters>(true) {
private val PsiMethod.isDefaultAnnotationMethod: Boolean
get() = PsiUtil.isAnnotationMethod(this) && name == PsiAnnotation.DEFAULT_REFERENCED_METHOD_NAME && parameterList.parametersCount == 0
private fun createReferenceProcessor(consumer: Processor<PsiReference>) = object : ReadActionProcessor<PsiReference>() {
override fun processInReadAction(reference: PsiReference): Boolean {
if (reference !is KtSimpleNameReference) return true
val annotationEntry = reference.expression.getParentOfTypeAndBranch<KtAnnotationEntry> { typeReference } ?: return true
val argument = annotationEntry.valueArguments.singleOrNull() as? KtValueArgument ?: return true
val implicitRef = argument.references.firstIsInstanceOrNull<ImplicitReference>() ?: return true
return consumer.process(implicitRef)
}
}
override fun processQuery(queryParameters: MethodReferencesSearch.SearchParameters, consumer: Processor<PsiReference>) {
runReadAction {
val method = queryParameters.method
if (!method.isDefaultAnnotationMethod) return@runReadAction null
val annotationClass = method.containingClass ?: return@runReadAction null
val searchScope = queryParameters.effectiveSearchScope.restrictToKotlinSources()
ReferencesSearch.search(annotationClass, searchScope)
}?.forEach(createReferenceProcessor(consumer))
}
}