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:
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
|
||||
+95
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+60
@@ -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))
|
||||
}
|
||||
}
|
||||
Vendored
+13
@@ -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() { }
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
@JAnn("abc")
|
||||
fun test1() {}
|
||||
|
||||
@JAnn(value = "abc")
|
||||
fun test2() {}
|
||||
Vendored
+4
@@ -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")
|
||||
Vendored
+9
@@ -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() {}
|
||||
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
class Test {
|
||||
@KAnn("abc")
|
||||
void test1() { }
|
||||
|
||||
@KAnn(value = "abc")
|
||||
void test2() { }
|
||||
}
|
||||
+4
@@ -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")
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package test;
|
||||
|
||||
public @interface JAnn {
|
||||
String valueNew();
|
||||
}
|
||||
|
||||
class Test {
|
||||
@JAnn(valueNew = "abc")
|
||||
void test1() { }
|
||||
|
||||
@JAnn(valueNew = "abc")
|
||||
void test2() { }
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
@JAnn(valueNew = "abc")
|
||||
fun test1() {}
|
||||
|
||||
@JAnn(valueNew = "abc")
|
||||
fun test2() {}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package test;
|
||||
|
||||
public @interface JAnn {
|
||||
String /*rename*/value();
|
||||
}
|
||||
|
||||
class Test {
|
||||
@JAnn("abc")
|
||||
void test1() { }
|
||||
|
||||
@JAnn(value = "abc")
|
||||
void test2() { }
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package test
|
||||
|
||||
@JAnn("abc")
|
||||
fun test1() {}
|
||||
|
||||
@JAnn(value = "abc")
|
||||
fun test2() {}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"type": "MARKED_ELEMENT",
|
||||
"mainFile": "test/JAnn.java",
|
||||
"newName": "valueNew",
|
||||
"withRuntime": "true"
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package test;
|
||||
|
||||
class Test {
|
||||
@KAnn(valueNew = "abc")
|
||||
void test1() { }
|
||||
|
||||
@KAnn(valueNew = "abc")
|
||||
void test2() { }
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
annotation class KAnn(val valueNew: String)
|
||||
|
||||
@KAnn(valueNew = "abc")
|
||||
fun test1() {}
|
||||
|
||||
@KAnn(valueNew = "abc")
|
||||
fun test2() {}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package test;
|
||||
|
||||
class Test {
|
||||
@KAnn("abc")
|
||||
void test1() { }
|
||||
|
||||
@KAnn(value = "abc")
|
||||
void test2() { }
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package test
|
||||
|
||||
annotation class KAnn(val /*rename*/value: String)
|
||||
|
||||
@KAnn("abc")
|
||||
fun test1() {}
|
||||
|
||||
@KAnn(value = "abc")
|
||||
fun test2() {}
|
||||
+6
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user