From 51fba03253bf3346031979922d2ea15c3fa123ff Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 8 May 2018 14:59:01 +0300 Subject: [PATCH] Experimental fixes: introduce "Add @UseExperimental" #KT-22760 Fixed --- .../checkers/ExperimentalUsageChecker.kt | 2 +- .../kotlin/idea/quickfix/AddAnnotationFix.kt | 10 +++++-- .../idea/quickfix/ExperimentalFixesFactory.kt | 28 +++++++++++++++---- .../basicFunctionNotApplicable.kt | 1 + .../experimental/basicUseExperimental.kt | 20 +++++++++++++ .../basicUseExperimental.kt.after | 21 ++++++++++++++ .../experimental/classUseExperimental.kt | 18 ++++++++++++ .../classUseExperimental.kt.after | 19 +++++++++++++ .../quickfix/experimental/override.kt | 2 ++ .../idea/quickfix/QuickFixTestGenerated.java | 10 +++++++ 10 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 idea/testData/quickfix/experimental/basicUseExperimental.kt create mode 100644 idea/testData/quickfix/experimental/basicUseExperimental.kt.after create mode 100644 idea/testData/quickfix/experimental/classUseExperimental.kt create mode 100644 idea/testData/quickfix/experimental/classUseExperimental.kt.after diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt index c485a8b5405..a9ee6a499e0 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt @@ -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") diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationFix.kt index b14fb63cbb1..e50c4b9e879 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddAnnotationFix.kt @@ -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(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" }) } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt index e3aa38d935a..830378210d7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ExperimentalFixesFactory.kt @@ -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() - 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 + ) + ) + } } } diff --git a/idea/testData/quickfix/experimental/basicFunctionNotApplicable.kt b/idea/testData/quickfix/experimental/basicFunctionNotApplicable.kt index c58e4d17e2a..d8c49612bde 100644 --- a/idea/testData/quickfix/experimental/basicFunctionNotApplicable.kt +++ b/idea/testData/quickfix/experimental/basicFunctionNotApplicable.kt @@ -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)' diff --git a/idea/testData/quickfix/experimental/basicUseExperimental.kt b/idea/testData/quickfix/experimental/basicUseExperimental.kt new file mode 100644 index 00000000000..23cd4c4838d --- /dev/null +++ b/idea/testData/quickfix/experimental/basicUseExperimental.kt @@ -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() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/experimental/basicUseExperimental.kt.after b/idea/testData/quickfix/experimental/basicUseExperimental.kt.after new file mode 100644 index 00000000000..4ddddedf975 --- /dev/null +++ b/idea/testData/quickfix/experimental/basicUseExperimental.kt.after @@ -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() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/experimental/classUseExperimental.kt b/idea/testData/quickfix/experimental/classUseExperimental.kt new file mode 100644 index 00000000000..8be0c6e3b0c --- /dev/null +++ b/idea/testData/quickfix/experimental/classUseExperimental.kt @@ -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() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/experimental/classUseExperimental.kt.after b/idea/testData/quickfix/experimental/classUseExperimental.kt.after new file mode 100644 index 00000000000..217c445c4cb --- /dev/null +++ b/idea/testData/quickfix/experimental/classUseExperimental.kt.after @@ -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() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/experimental/override.kt b/idea/testData/quickfix/experimental/override.kt index 9bd3122e33b..a85723b5ad9 100644 --- a/idea/testData/quickfix/experimental/override.kt +++ b/idea/testData/quickfix/experimental/override.kt @@ -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 diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 25c2595a3a1..aaa61967f2e 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -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");