Migrate RetentionPolicy arguments in deprecated Java quick-fix
#KT-29666 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
db5396c1c2
commit
39f576d75c
@@ -1,64 +1,97 @@
|
|||||||
package org.jetbrains.kotlin.idea.quickfix
|
package org.jetbrains.kotlin.idea.quickfix
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.actions.OptimizeImportsProcessor
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
import org.jetbrains.kotlin.diagnostics.Diagnostic
|
||||||
import org.jetbrains.kotlin.load.java.components.JavaAnnotationMapper
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.idea.core.ShortenReferences
|
||||||
|
import org.jetbrains.kotlin.idea.core.replaced
|
||||||
|
import org.jetbrains.kotlin.idea.util.application.runWriteAction
|
||||||
|
import org.jetbrains.kotlin.name.ClassId
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.KtPsiFactory
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.psi.KtValueArgument
|
|
||||||
import org.jetbrains.kotlin.resolve.ImportPath
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm
|
||||||
|
|
||||||
internal class DeprecatedJavaAnnotationFix(element: KtAnnotationEntry,
|
internal class DeprecatedJavaAnnotationFix(
|
||||||
private val annotationFqName: FqName,
|
element: KtAnnotationEntry,
|
||||||
private val arguments: List<KtValueArgument>) : KotlinQuickFixAction<KtAnnotationEntry>(element) {
|
private val annotationFqName: FqName
|
||||||
|
) : KotlinQuickFixAction<KtAnnotationEntry>(element) {
|
||||||
override fun getFamilyName() = "Replace Annotation"
|
override fun getFamilyName() = "Replace Annotation"
|
||||||
override fun getText(): String = "Replace annotation with ${annotationFqName.asString()}"
|
override fun getText(): String = "Replace annotation with ${annotationFqName.asString()}"
|
||||||
|
|
||||||
|
override fun startInWriteAction(): Boolean = false
|
||||||
|
|
||||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||||
val element = element ?: return
|
val element = element ?: return
|
||||||
|
|
||||||
val psiFactory = KtPsiFactory(project)
|
val psiFactory = KtPsiFactory(project)
|
||||||
|
|
||||||
val argumentString = if(arguments.isEmpty()) {
|
val arguments = updateAnnotation(psiFactory)
|
||||||
""
|
val replacementAnnotation = psiFactory.createAnnotationEntry("@$annotationFqName")
|
||||||
} else {
|
val valueArgumentList = psiFactory.buildValueArgumentList {
|
||||||
"("+ arguments.joinToString(",") { it.text } + ")"
|
appendFixedText("(")
|
||||||
|
arguments.forEach { argument ->
|
||||||
|
appendExpression(argument.getArgumentExpression())
|
||||||
|
}
|
||||||
|
appendFixedText(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
element.replace(psiFactory.createAnnotationEntry("@" + annotationFqName.shortName() + argumentString))
|
if (arguments.isNotEmpty()) {
|
||||||
|
replacementAnnotation.add(valueArgumentList)
|
||||||
|
}
|
||||||
|
|
||||||
for ((java, kotlin) in JavaAnnotationMapper.javaToKotlinNameMap) {
|
val replaced = runWriteAction {
|
||||||
if (kotlin == annotationFqName) {
|
element.replaced(replacementAnnotation)
|
||||||
val oldImport = file.importDirectives.find { it -> it.importedFqName == java } ?: return
|
}
|
||||||
oldImport.delete()
|
OptimizeImportsProcessor(project, file).run()
|
||||||
break
|
runWriteAction {
|
||||||
|
ShortenReferences.DEFAULT.process(replaced)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateAnnotation(psiFactory: KtPsiFactory): List<KtValueArgument> {
|
||||||
|
val bindingContext = element?.analyze() ?: return emptyList()
|
||||||
|
|
||||||
|
val descriptor = bindingContext[BindingContext.ANNOTATION, element]
|
||||||
|
val name = descriptor?.fqName ?: return emptyList()
|
||||||
|
|
||||||
|
val argumentOutput = mutableListOf<KtValueArgument>()
|
||||||
|
if (name == RETENTION_FQ_NAME) {
|
||||||
|
for (arg in descriptor.allValueArguments.values) {
|
||||||
|
val typeAndValue = arg.value as? Pair<*, *>
|
||||||
|
val classId = typeAndValue?.first as? ClassId
|
||||||
|
val value = typeAndValue?.second
|
||||||
|
|
||||||
|
if (classId == RETENTION_POLICY_ID) {
|
||||||
|
val argument = when ((value as? Name)?.asString()) {
|
||||||
|
"SOURCE" -> psiFactory.createArgument("kotlin.annotation.AnnotationRetention.SOURCE")
|
||||||
|
"CLASS" -> psiFactory.createArgument("kotlin.annotation.AnnotationRetention.BINARY")
|
||||||
|
"RUNTIME" -> psiFactory.createArgument("kotlin.annotation.AnnotationRetention.RUNTIME")
|
||||||
|
else -> psiFactory.createArgument("${classId.shortClassName}.$value")
|
||||||
|
}
|
||||||
|
argumentOutput.add(argument)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file.importList?.add(psiFactory.createImportDirective(ImportPath(annotationFqName, false, null)))
|
return argumentOutput
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object Factory : KotlinSingleIntentionActionFactory() {
|
companion object Factory : KotlinSingleIntentionActionFactory() {
|
||||||
|
private val RETENTION_FQ_NAME = FqName("java.lang.annotation.Retention")
|
||||||
|
|
||||||
|
private val RETENTION_POLICY_ID = ClassId(FqName("java.lang.annotation"), FqName("RetentionPolicy"), false)
|
||||||
|
|
||||||
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
|
||||||
val castedDiagnostic = ErrorsJvm.DEPRECATED_JAVA_ANNOTATION.cast(diagnostic)
|
val castedDiagnostic = ErrorsJvm.DEPRECATED_JAVA_ANNOTATION.cast(diagnostic)
|
||||||
|
|
||||||
val updatedAnnotation = castedDiagnostic.a as? FqName ?: return null
|
val updatedAnnotation = castedDiagnostic.a as? FqName ?: return null
|
||||||
|
|
||||||
val entry = diagnostic.psiElement as? KtAnnotationEntry ?: return null
|
val entry = diagnostic.psiElement as? KtAnnotationEntry ?: return null
|
||||||
|
|
||||||
val arguments = mutableListOf<KtValueArgument>()
|
return DeprecatedJavaAnnotationFix(entry, updatedAnnotation)
|
||||||
entry.valueArguments.forEach {
|
|
||||||
(it as KtValueArgument).children.forEach { child ->
|
|
||||||
arguments.add(child.context as KtValueArgument)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return DeprecatedJavaAnnotationFix(entry, updatedAnnotation, arguments)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// "Replace annotation with kotlin.annotation.Retention" "true"
|
// "Replace annotation with kotlin.annotation.Retention" "true"
|
||||||
// ERROR: Type mismatch: inferred type is RetentionPolicy but AnnotationRetention was expected
|
|
||||||
|
|
||||||
import java.lang.annotation.RetentionPolicy
|
import java.lang.annotation.RetentionPolicy
|
||||||
import java.lang.annotation.Retention
|
import java.lang.annotation.Retention
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
// "Replace annotation with kotlin.annotation.Retention" "true"
|
// "Replace annotation with kotlin.annotation.Retention" "true"
|
||||||
// ERROR: Type mismatch: inferred type is RetentionPolicy but AnnotationRetention was expected
|
|
||||||
|
|
||||||
import java.lang.annotation.RetentionPolicy
|
@<caret>Retention(AnnotationRetention.SOURCE)
|
||||||
import kotlin.annotation.Retention
|
|
||||||
|
|
||||||
@Retention<caret>(RetentionPolicy.SOURCE)
|
|
||||||
annotation class Foo
|
annotation class Foo
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace annotation with kotlin.annotation.Retention" "true"
|
||||||
|
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
|
||||||
|
@Retention<caret>(RetentionPolicy.CLASS)
|
||||||
|
annotation class Foo
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// "Replace annotation with kotlin.annotation.Retention" "true"
|
||||||
|
|
||||||
|
@<caret>Retention(AnnotationRetention.BINARY)
|
||||||
|
annotation class Foo
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
// "Replace annotation with kotlin.annotation.Retention" "true"
|
||||||
|
|
||||||
|
import java.lang.annotation.RetentionPolicy
|
||||||
|
import java.lang.annotation.Retention
|
||||||
|
|
||||||
|
@Retention<caret>(RetentionPolicy.RUNTIME)
|
||||||
|
annotation class Foo
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// "Replace annotation with kotlin.annotation.Retention" "true"
|
||||||
|
|
||||||
|
@<caret>Retention(AnnotationRetention.RUNTIME)
|
||||||
|
annotation class Foo
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
// "Replace annotation with kotlin.annotation.MustBeDocumented" "true"
|
// "Replace annotation with kotlin.annotation.MustBeDocumented" "true"
|
||||||
|
|
||||||
import kotlin.annotation.MustBeDocumented
|
|
||||||
|
|
||||||
@MustBeDocumented<caret>
|
@MustBeDocumented<caret>
|
||||||
annotation class Foo
|
annotation class Foo
|
||||||
@@ -5656,6 +5656,16 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
|||||||
runTest("idea/testData/quickfix/deprecatedJavaAnnotation/withArgument.kt");
|
runTest("idea/testData/quickfix/deprecatedJavaAnnotation/withArgument.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("withClassRetention.kt")
|
||||||
|
public void testWithClassRetention() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/deprecatedJavaAnnotation/withClassRetention.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("withRuntimeRetention.kt")
|
||||||
|
public void testWithRuntimeRetention() throws Exception {
|
||||||
|
runTest("idea/testData/quickfix/deprecatedJavaAnnotation/withRuntimeRetention.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("withoutArguments.kt")
|
@TestMetadata("withoutArguments.kt")
|
||||||
public void testWithoutArguments() throws Exception {
|
public void testWithoutArguments() throws Exception {
|
||||||
runTest("idea/testData/quickfix/deprecatedJavaAnnotation/withoutArguments.kt");
|
runTest("idea/testData/quickfix/deprecatedJavaAnnotation/withoutArguments.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user