Annotations supported

#KT-8912 Fixed
This commit is contained in:
Valentin Kipyatkov
2015-09-01 23:22:39 +03:00
parent 366204da85
commit 6575fb8535
15 changed files with 160 additions and 16 deletions
@@ -22,7 +22,6 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.quickfix.CleanupFix
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
@@ -60,9 +59,7 @@ public class DeprecatedSymbolUsageFix(
companion object : JetSingleIntentionActionFactory() {
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val nameExpression = diagnostic.psiElement as? JetSimpleNameExpression ?: return null
val descriptor = Errors.DEPRECATED_SYMBOL_WITH_MESSAGE.cast(diagnostic).a
val replacement = DeprecatedSymbolUsageFixBase.replaceWithPattern(descriptor, nameExpression.project) ?: return null
val (nameExpression, replacement) = DeprecatedSymbolUsageFixBase.extractDataFromDiagnostic(diagnostic) ?: return null
return DeprecatedSymbolUsageFix(nameExpression, replacement)
}
@@ -22,8 +22,11 @@ import com.intellij.psi.PsiFile
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.OptionalParametersHelper
import org.jetbrains.kotlin.idea.quickfix.JetIntentionAction
import org.jetbrains.kotlin.psi.JetConstructorCalleeExpression
import org.jetbrains.kotlin.psi.JetFile
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.resolve.DescriptorUtils
@@ -56,7 +59,7 @@ public abstract class DeprecatedSymbolUsageFixBase(
editor: Editor?)
companion object {
public fun replaceWithPattern(descriptor: DeclarationDescriptor, project: Project): ReplaceWith? {
fun replaceWithPattern(descriptor: DeclarationDescriptor, project: Project): ReplaceWith? {
val annotationClass = descriptor.builtIns.deprecatedAnnotation
val annotation = descriptor.annotations.findAnnotation(DescriptorUtils.getFqNameSafe(annotationClass)) ?: return null
val replaceWithValue = annotation.argumentValue(kotlin.deprecated::replaceWith.name) as? AnnotationDescriptor ?: return null
@@ -72,5 +75,34 @@ public abstract class DeprecatedSymbolUsageFixBase(
return ReplaceWith(pattern, *imports.toTypedArray())
}
data class Data(
val nameExpression: JetSimpleNameExpression,
val replaceWith: ReplaceWith,
val descriptor: DeclarationDescriptor
)
fun extractDataFromDiagnostic(deprecatedDiagnostic: Diagnostic): Data? {
val psiElement = deprecatedDiagnostic.psiElement
//TODO: compiler crash here
/*
val nameExpression: JetSimpleNameExpression = when (psiElement) {
is JetSimpleNameExpression -> psiElement
is JetConstructorCalleeExpression -> psiElement.constructorReferenceExpression
else -> null
} ?: return null
*/
val nameExpression: JetSimpleNameExpression = (if (psiElement is JetSimpleNameExpression)
psiElement
else if (psiElement is JetConstructorCalleeExpression)
psiElement.constructorReferenceExpression
else
null) ?: return null
val descriptor = Errors.DEPRECATED_SYMBOL_WITH_MESSAGE.cast(deprecatedDiagnostic).a
val replacement = DeprecatedSymbolUsageFixBase.replaceWithPattern(descriptor, nameExpression.project) ?: return null
return Data(nameExpression, replacement, descriptor)
}
}
}
@@ -28,7 +28,6 @@ import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.util.ui.UIUtil
import org.jetbrains.kotlin.diagnostics.Diagnostic
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.core.targetDescriptors
import org.jetbrains.kotlin.idea.quickfix.JetSingleIntentionActionFactory
import org.jetbrains.kotlin.idea.references.JetSimpleNameReference
@@ -36,10 +35,7 @@ import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.stubindex.JetSourceFilterScope
import org.jetbrains.kotlin.idea.util.application.executeWriteCommand
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.psi.JetImportDirective
import org.jetbrains.kotlin.psi.JetNamedFunction
import org.jetbrains.kotlin.psi.JetProperty
import org.jetbrains.kotlin.psi.JetSimpleNameExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.renderer.NameShortness
@@ -61,12 +57,11 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
override fun isAvailable(project: Project, editor: Editor?, file: PsiFile): Boolean {
if (!super.isAvailable(project, editor, file)) return false
val targetPsiElement = element.mainReference.resolve()
return targetPsiElement is JetNamedFunction || targetPsiElement is JetProperty //TODO
return targetPsiElement() != null
}
override fun invoke(replacementStrategy: UsageReplacementStrategy, project: Project, editor: Editor?) {
val psiElement = element.mainReference.resolve()!!
val psiElement = targetPsiElement()!!
ProgressManager.getInstance().run(
object : Task.Modal(project, "Applying '$text'", true) {
@@ -82,6 +77,16 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
})
}
private fun targetPsiElement(): JetDeclaration? {
val referenceTarget = element.mainReference.resolve()
return when (referenceTarget) {
is JetNamedFunction -> referenceTarget
is JetProperty -> referenceTarget
is JetConstructor<*> -> referenceTarget.getContainingClassOrObject() //TODO: constructor can be deprecated itself
else -> null
}
}
private fun replaceUsages(project: Project, usages: Collection<JetSimpleNameExpression>, replacementStrategy: UsageReplacementStrategy) {
UIUtil.invokeLaterIfNeeded {
project.executeWriteCommand(text) {
@@ -127,9 +132,7 @@ public class DeprecatedSymbolUsageInWholeProjectFix(
}
override fun createAction(diagnostic: Diagnostic): IntentionAction? {
val nameExpression = diagnostic.psiElement as? JetSimpleNameExpression ?: return null
val descriptor = Errors.DEPRECATED_SYMBOL_WITH_MESSAGE.cast(diagnostic).a
val replacement = DeprecatedSymbolUsageFixBase.replaceWithPattern(descriptor, nameExpression.project) ?: return null
val (nameExpression, replacement, descriptor) = DeprecatedSymbolUsageFixBase.extractDataFromDiagnostic(diagnostic) ?: return null
val descriptorName = RENDERER.render(descriptor)
return DeprecatedSymbolUsageInWholeProjectFix(nameExpression, replacement, "Replace usages of '$descriptorName' in whole project")
}
@@ -0,0 +1,10 @@
// "Replace with 'test.Bar'" "true"
package test
@deprecated("Replace with bar", ReplaceWith("test.Bar"))
annotation class Foo
annotation class Bar
@Foo<caret> class C {}
@@ -0,0 +1,10 @@
// "Replace with 'test.Bar'" "true"
package test
@deprecated("Replace with bar", ReplaceWith("test.Bar"))
annotation class Foo
annotation class Bar
@Bar class C {}
@@ -0,0 +1,10 @@
// "Replace with 'test.Bar'" "true"
package test
@deprecated("Replace with bar", ReplaceWith("test.Bar"))
annotation class Foo(val p1: String, val p2: Int)
annotation class Bar(val p1: String, val p2: Int)
@Foo<caret>("", 1) class C {}
@@ -0,0 +1,10 @@
// "Replace with 'test.Bar'" "true"
package test
@deprecated("Replace with bar", ReplaceWith("test.Bar"))
annotation class Foo(val p1: String, val p2: Int)
annotation class Bar(val p1: String, val p2: Int)
@Bar("", 1) class C {}
@@ -0,0 +1,8 @@
package dependency
@deprecated("", ReplaceWith("dependency.NewAnnotation"))
annotation class OldAnnotation(val p: Int = 0)
annotation class NewAnnotation(val p: Int = 0, val newP: String = "")
@NewAnnotation class C
@@ -0,0 +1,9 @@
// "Replace with 'test.Bar'" "true"
package x
import dependency.*
annotation class A(val a: NewAnnotation)
@A(NewAnnotation()) class Y
@@ -0,0 +1,10 @@
// "Replace usages of 'OldAnnotation' in whole project" "true"
package test
import dependency.NewAnnotation
fun foo(a: NewAnnotation) {
}
@NewAnnotation(1) class X
@@ -0,0 +1,8 @@
package dependency
@deprecated("", ReplaceWith("dependency.NewAnnotation"))
annotation class OldAnnotation(val p: Int = 0)
annotation class NewAnnotation(val p: Int = 0, val newP: String = "")
@OldAnnotation class C
@@ -0,0 +1,10 @@
// "Replace usages of 'OldAnnotation' in whole project" "true"
package test
import dependency.OldAnnotation
fun foo(a: OldAnnotation) {
}
@<caret>OldAnnotation(1) class X
@@ -0,0 +1,9 @@
// "Replace with 'test.Bar'" "true"
package x
import dependency.*
annotation class A(val a: OldAnnotation)
@A(OldAnnotation()) class Y
@@ -919,6 +919,12 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes
public void testAllFilesPresentInClassUsages() throws Exception {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/classUsages"), Pattern.compile("^(\\w+)\\.before\\.Main\\.kt$"), true);
}
@TestMetadata("inWholeProject.before.Main.kt")
public void testInWholeProject() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/inWholeProject.before.Main.kt");
doTestWithExtraFile(fileName);
}
}
@TestMetadata("idea/testData/quickfix/deprecatedSymbolUsage/imports")
@@ -3312,6 +3312,18 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/deprecatedSymbolUsage/classUsages"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
}
@TestMetadata("annotation1.kt")
public void testAnnotation1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/annotation1.kt");
doTest(fileName);
}
@TestMetadata("annotation2.kt")
public void testAnnotation2() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/annotation2.kt");
doTest(fileName);
}
@TestMetadata("constructorUsage1.kt")
public void testConstructorUsage1() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/deprecatedSymbolUsage/classUsages/constructorUsage1.kt");