Experimental fixes: introduce "Add @UseExperimental" #KT-22760 Fixed
This commit is contained in:
+1
-1
@@ -61,7 +61,7 @@ class ExperimentalUsageChecker(project: Project) : CallChecker {
|
||||
|
||||
companion object {
|
||||
val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental")
|
||||
internal val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
|
||||
val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental")
|
||||
internal val WAS_EXPERIMENTAL_FQ_NAME = FqName("kotlin.WasExperimental")
|
||||
internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
|
||||
internal val WAS_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass")
|
||||
|
||||
@@ -15,13 +15,17 @@ import org.jetbrains.kotlin.psi.KtFile
|
||||
open class AddAnnotationFix(
|
||||
element: KtDeclaration,
|
||||
private val annotationFqName: FqName,
|
||||
private val suffix: String = ""
|
||||
private val suffix: String = "",
|
||||
private val argumentClassFqName: FqName? = null
|
||||
) : KotlinQuickFixAction<KtDeclaration>(element) {
|
||||
override fun getText(): String = "Add '@${annotationFqName.shortName()}' annotation$suffix"
|
||||
override fun getText(): String {
|
||||
val argumentsAsString = argumentClassFqName?.shortName()?.let { "($it::class)" } ?: ""
|
||||
return "Add '@${annotationFqName.shortName()}$argumentsAsString' annotation$suffix"
|
||||
}
|
||||
|
||||
override fun getFamilyName(): String = "Add annotation"
|
||||
|
||||
override fun invoke(project: Project, editor: Editor?, file: KtFile) {
|
||||
element?.addAnnotation(annotationFqName)
|
||||
element?.addAnnotation(annotationFqName, annotationInnerText = argumentClassFqName?.let { "$it::class" })
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.AnnotationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.ExperimentalUsageChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
|
||||
object ExperimentalFixesFactory : KotlinIntentionActionsFactory() {
|
||||
@@ -56,15 +57,30 @@ object ExperimentalFixesFactory : KotlinIntentionActionsFactory() {
|
||||
}
|
||||
|
||||
val result = mutableListOf<IntentionAction>()
|
||||
if (isApplicableTo(containingDeclaration, applicableTargets)) {
|
||||
result.add(AddAnnotationFix(containingDeclaration, annotationFqName, " to '${containingDeclaration.name}'"))
|
||||
run {
|
||||
val suffix = " to '${containingDeclaration.name}'"
|
||||
if (isApplicableTo(containingDeclaration, applicableTargets)) {
|
||||
result.add(AddAnnotationFix(containingDeclaration, annotationFqName, suffix))
|
||||
}
|
||||
result.add(
|
||||
AddAnnotationFix(
|
||||
containingDeclaration, ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME, suffix, annotationFqName
|
||||
)
|
||||
)
|
||||
}
|
||||
if (containingDeclaration is KtCallableDeclaration) {
|
||||
val containingClassOrObject = containingDeclaration.containingClassOrObject
|
||||
if (containingClassOrObject != null && isApplicableTo(containingClassOrObject, applicableTargets)) {
|
||||
result.add(
|
||||
AddAnnotationFix(containingClassOrObject, annotationFqName, " to containing class '${containingClassOrObject.name}'")
|
||||
)
|
||||
if (containingClassOrObject != null) {
|
||||
val suffix = " to containing class '${containingClassOrObject.name}'"
|
||||
if (isApplicableTo(containingClassOrObject, applicableTargets)) {
|
||||
result.add(AddAnnotationFix(containingClassOrObject, annotationFqName, suffix))
|
||||
} else {
|
||||
result.add(
|
||||
AddAnnotationFix(
|
||||
containingClassOrObject, ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME, suffix, annotationFqName
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.Experimental
|
||||
// WITH_RUNTIME
|
||||
// ACTION: Add '@MyExperimentalAPI' annotation to containing class 'Bar'
|
||||
// ACTION: Add '@UseExperimental(MyExperimentalAPI::class)' annotation to 'bar'
|
||||
// ERROR: This declaration is experimental and its usage must be marked with '@MyExperimentalAPI' or '@UseExperimental(MyExperimentalAPI::class)'
|
||||
// ERROR: This declaration is experimental and its usage must be marked with '@MyExperimentalAPI' or '@UseExperimental(MyExperimentalAPI::class)'
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
// "Add '@UseExperimental(MyExperimentalAPI::class)' annotation to 'bar'" "true"
|
||||
// COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.Experimental
|
||||
// WITH_RUNTIME
|
||||
|
||||
package a.b
|
||||
|
||||
@Experimental
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class MyExperimentalAPI
|
||||
|
||||
@MyExperimentalAPI
|
||||
class Some {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Bar {
|
||||
fun bar() {
|
||||
Some().foo<caret>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
// "Add '@UseExperimental(MyExperimentalAPI::class)' annotation to 'bar'" "true"
|
||||
// COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.Experimental
|
||||
// WITH_RUNTIME
|
||||
|
||||
package a.b
|
||||
|
||||
@Experimental
|
||||
@Target(AnnotationTarget.CLASS)
|
||||
annotation class MyExperimentalAPI
|
||||
|
||||
@MyExperimentalAPI
|
||||
class Some {
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
class Bar {
|
||||
@UseExperimental(MyExperimentalAPI::class)
|
||||
fun bar() {
|
||||
Some().foo()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// "Add '@UseExperimental(MyExperimentalAPI::class)' annotation to containing class 'Bar'" "true"
|
||||
// COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.Experimental
|
||||
// WITH_RUNTIME
|
||||
|
||||
package a.b
|
||||
|
||||
@Experimental
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class MyExperimentalAPI
|
||||
|
||||
@MyExperimentalAPI
|
||||
fun foo() {}
|
||||
|
||||
class Bar {
|
||||
fun bar() {
|
||||
foo<caret>()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// "Add '@UseExperimental(MyExperimentalAPI::class)' annotation to containing class 'Bar'" "true"
|
||||
// COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.Experimental
|
||||
// WITH_RUNTIME
|
||||
|
||||
package a.b
|
||||
|
||||
@Experimental
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
annotation class MyExperimentalAPI
|
||||
|
||||
@MyExperimentalAPI
|
||||
fun foo() {}
|
||||
|
||||
@UseExperimental(MyExperimentalAPI::class)
|
||||
class Bar {
|
||||
fun bar() {
|
||||
foo()
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
// COMPILER_ARGUMENTS: -Xuse-experimental=kotlin.Experimental
|
||||
// WITH_RUNTIME
|
||||
// ACTION: Add '@MyExperimentalAPI' annotation to 'foo'
|
||||
// ACTION: Add '@UseExperimental(MyExperimentalAPI::class)' annotation to 'foo'
|
||||
// ACTION: Add '@UseExperimental(MyExperimentalAPI::class)' annotation to containing class 'Derived'
|
||||
// ERROR: This declaration overrides experimental member of supertype 'Base' and must be annotated with '@MyExperimentalAPI'
|
||||
|
||||
@Experimental
|
||||
|
||||
@@ -6053,6 +6053,16 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest {
|
||||
runTest("idea/testData/quickfix/experimental/basicFunctionNotApplicable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("basicUseExperimental.kt")
|
||||
public void testBasicUseExperimental() throws Exception {
|
||||
runTest("idea/testData/quickfix/experimental/basicUseExperimental.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("classUseExperimental.kt")
|
||||
public void testClassUseExperimental() throws Exception {
|
||||
runTest("idea/testData/quickfix/experimental/classUseExperimental.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("override.kt")
|
||||
public void testOverride() throws Exception {
|
||||
runTest("idea/testData/quickfix/experimental/override.kt");
|
||||
|
||||
Reference in New Issue
Block a user