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
+1
View File
@@ -227,6 +227,7 @@
- [`KT-7851`](https://youtrack.jetbrains.com/issue/KT-7851) Respect naming conventions in automatic variable rename
- [`KT-8044`](https://youtrack.jetbrains.com/issue/KT-8044), [`KT-9432`](https://youtrack.jetbrains.com/issue/KT-9432) Support @JvmName annotation in rename refactoring
- [`KT-8512`](https://youtrack.jetbrains.com/issue/KT-8512) Support "Rename tests" options in Rename dialog
- [`KT-12657`](https://youtrack.jetbrains.com/issue/KT-12657) Rename implicit usages of annotation method 'value'
- [`KT-12759`](https://youtrack.jetbrains.com/issue/KT-12759) Suggest renaming both property accessors with matching @JvmName when renaming one of them from Java
###### Issues fixed
@@ -197,11 +197,7 @@ object LightClassUtil {
var setterWrapper = specialSetter
val additionalAccessors = arrayListOf<PsiMethod>()
val wrappers = getPsiMethodWrappers(ktDeclaration, true).filter {
JvmAbi.isGetterName(it.name) || JvmAbi.isSetterName(it.name)
}
for (wrapper in wrappers) {
for (wrapper in getPsiMethodWrappers(ktDeclaration, true)) {
if (JvmAbi.isSetterName(wrapper.name)) {
if (setterWrapper == null || setterWrapper === specialSetter) {
setterWrapper = wrapper
+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))
}
}
@@ -0,0 +1,13 @@
// PSI_ELEMENT: com.intellij.psi.PsiMethod
// OPTIONS: usages
public @interface JAnn {
String <caret>value();
}
class Test {
@JAnn("abc")
void test1() { }
@JAnn(value = "abc")
void test2() { }
}
@@ -0,0 +1,5 @@
@JAnn("abc")
fun test1() {}
@JAnn(value = "abc")
fun test2() {}
@@ -0,0 +1,4 @@
[defaultJavaAnnotationMethodUsages.0.java] Unclassified usage 11 @JAnn(value = "abc")
[defaultJavaAnnotationMethodUsages.0.java] Unclassified usage 8 @JAnn("abc")
[defaultJavaAnnotationMethodUsages.1.kt] Named argument 4 @JAnn(value = "abc")
[defaultJavaAnnotationMethodUsages.1.kt] Unclassified usage 1 @JAnn("abc")
@@ -0,0 +1,9 @@
// PSI_ELEMENT: org.jetbrains.kotlin.psi.KtParameter
// OPTIONS: usages
annotation class KAnn(val <caret>value: String)
@KAnn("abc")
fun test1() {}
@KAnn(value = "abc")
fun test2() {}
@@ -0,0 +1,7 @@
class Test {
@KAnn("abc")
void test1() { }
@KAnn(value = "abc")
void test2() { }
}
@@ -0,0 +1,4 @@
[kotlinDefaultAnnotationParameterUsages.0.kt] Named argument 8 @KAnn(value = "abc")
[kotlinDefaultAnnotationParameterUsages.0.kt] Unclassified usage 5 @KAnn("abc")
[kotlinDefaultAnnotationParameterUsages.1.java] Unclassified usage 2 @KAnn("abc")
[kotlinDefaultAnnotationParameterUsages.1.java] Unclassified usage 5 @KAnn(value = "abc")
@@ -0,0 +1,13 @@
package test;
public @interface JAnn {
String valueNew();
}
class Test {
@JAnn(valueNew = "abc")
void test1() { }
@JAnn(valueNew = "abc")
void test2() { }
}
@@ -0,0 +1,7 @@
package test
@JAnn(valueNew = "abc")
fun test1() {}
@JAnn(valueNew = "abc")
fun test2() {}
@@ -0,0 +1,13 @@
package test;
public @interface JAnn {
String /*rename*/value();
}
class Test {
@JAnn("abc")
void test1() { }
@JAnn(value = "abc")
void test2() { }
}
@@ -0,0 +1,7 @@
package test
@JAnn("abc")
fun test1() {}
@JAnn(value = "abc")
fun test2() {}
@@ -0,0 +1,6 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test/JAnn.java",
"newName": "valueNew",
"withRuntime": "true"
}
@@ -0,0 +1,9 @@
package test;
class Test {
@KAnn(valueNew = "abc")
void test1() { }
@KAnn(valueNew = "abc")
void test2() { }
}
@@ -0,0 +1,9 @@
package test
annotation class KAnn(val valueNew: String)
@KAnn(valueNew = "abc")
fun test1() {}
@KAnn(valueNew = "abc")
fun test2() {}
@@ -0,0 +1,9 @@
package test;
class Test {
@KAnn("abc")
void test1() { }
@KAnn(value = "abc")
void test2() { }
}
@@ -0,0 +1,9 @@
package test
annotation class KAnn(val /*rename*/value: String)
@KAnn("abc")
fun test1() {}
@KAnn(value = "abc")
fun test2() {}
@@ -0,0 +1,6 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test/KAnn.kt",
"newName": "valueNew",
"withRuntime": "true"
}
@@ -822,6 +822,12 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
doTest(fileName);
}
@TestMetadata("kotlinDefaultAnnotationParameterUsages.0.kt")
public void testKotlinDefaultAnnotationParameterUsages() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findParameterUsages/kotlinDefaultAnnotationParameterUsages.0.kt");
doTest(fileName);
}
@TestMetadata("kotlinFunctionParameterUsages.0.kt")
public void testKotlinFunctionParameterUsages() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/kotlin/findParameterUsages/kotlinFunctionParameterUsages.0.kt");
@@ -1428,6 +1434,12 @@ public class FindUsagesTestGenerated extends AbstractFindUsagesTest {
doTest(fileName);
}
@TestMetadata("defaultJavaAnnotationMethodUsages.0.java")
public void testDefaultJavaAnnotationMethodUsages() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/defaultJavaAnnotationMethodUsages.0.java");
doTest(fileName);
}
@TestMetadata("JKMethodOverrides.0.java")
public void testJKMethodOverrides() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/findUsages/java/findJavaMethodUsages/JKMethodOverrides.0.java");
@@ -311,6 +311,18 @@ public class RenameTestGenerated extends AbstractRenameTest {
doTest(fileName);
}
@TestMetadata("renameDefaultJavaAnnotationMethod/renameDefaultJavaAnnotationMethod.test")
public void testRenameDefaultJavaAnnotationMethod_RenameDefaultJavaAnnotationMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameDefaultJavaAnnotationMethod/renameDefaultJavaAnnotationMethod.test");
doTest(fileName);
}
@TestMetadata("renameDefaultKotlinAnnotationMethod/renameDefaultKotlinAnnotationMethod.test")
public void testRenameDefaultKotlinAnnotationMethod_RenameDefaultKotlinAnnotationMethod() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameDefaultKotlinAnnotationMethod/renameDefaultKotlinAnnotationMethod.test");
doTest(fileName);
}
@TestMetadata("renameEmptyKotlinFile/renameFile.test")
public void testRenameEmptyKotlinFile_RenameFile() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameEmptyKotlinFile/renameFile.test");