diff --git a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt index 8bcdee014e2..33708cc8479 100644 --- a/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt +++ b/idea/idea-test-framework/src/org/jetbrains/kotlin/idea/test/DirectiveBasedActionUtils.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.idea.test import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.psi.PsiFile import com.intellij.testFramework.UsefulTestCase import org.jetbrains.kotlin.diagnostics.Severity import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully @@ -42,7 +43,7 @@ public object DirectiveBasedActionUtils { expectedErrors) } - public fun checkAvailableActionsAreExpected(file: JetFile, availableActions: Collection) { + public fun checkAvailableActionsAreExpected(file: PsiFile, availableActions: Collection) { val expectedActions = InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.getText(), "// ACTION:").sorted() UsefulTestCase.assertEmpty("Irrelevant actions should not be specified in ACTION directive for they are not checked anyway", @@ -70,6 +71,11 @@ public object DirectiveBasedActionUtils { "Inject language or reference", "Suppress '", "Run inspection on", - "Inspection '" + "Inspection '", + "Suppress for ", + "Suppress all ", + "Edit cleanup profile settings", + "Fix all '", + "Cleanup code" ) } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 8291c9b3bca..f190b1d6813 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1260,6 +1260,14 @@ cleanupTool="true" level="WARNING"/> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedUsageOfStaticField.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedUsageOfStaticField.kt new file mode 100644 index 00000000000..e63287ff1ba --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedUsageOfStaticField.kt @@ -0,0 +1,156 @@ +/* + * Copyright 2010-2015 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.inspections + +import com.intellij.codeInspection.* +import com.intellij.openapi.project.Project +import com.intellij.psi.* +import org.jetbrains.kotlin.asJava.KotlinLightClass +import org.jetbrains.kotlin.asJava.KotlinLightFieldForDeclaration +import org.jetbrains.kotlin.idea.quickfix.AddConstModifierFix +import org.jetbrains.kotlin.idea.quickfix.AddConstModifierIntention +import org.jetbrains.kotlin.idea.quickfix.replaceReferencesToGetterByReferenceToField +import org.jetbrains.kotlin.lexer.JetTokens +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.psi.JetClass +import org.jetbrains.kotlin.psi.JetObjectDeclaration +import org.jetbrains.kotlin.psi.JetProperty +import org.jetbrains.kotlin.psi.JetPsiFactory +import java.util.* + +class DeprecatedUsageOfStaticFieldInspection : LocalInspectionTool(), CleanupLocalInspectionTool { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor { + return object : JavaElementVisitor() { + override fun visitReferenceExpression(expression: PsiReferenceExpression) { + val resolvedTo = expression.reference?.resolve() as? PsiField ?: return + if (!resolvedTo.hasModifierProperty(PsiModifier.STATIC) || !resolvedTo.isDeprecated) return + + val kotlinProperty = (resolvedTo as? KotlinLightFieldForDeclaration)?.getOrigin() as? JetProperty + + // NOTE: this is hack to avoid test failing with "action is still available" error + if (kotlinProperty?.hasJvmFieldAnnotationOrConstModifier() ?: false) return + + val kotlinClassOrObject = (resolvedTo.containingClass as? KotlinLightClass)?.getOrigin() ?: return + + val containingObject = when (kotlinClassOrObject) { + is JetObjectDeclaration -> kotlinClassOrObject as JetObjectDeclaration // KT-9578 + is JetClass -> kotlinClassOrObject.getCompanionObjects().singleOrNull() ?: return + else -> return + } + holder.registerProblem( + expression, "This field will not be generated in future versions of Kotlin. Use 'const' modifier, '@JvmField' annotation or access data through corresponding object.", + ProblemHighlightType.LIKE_DEPRECATED, + *createFixes(containingObject, kotlinProperty).toTypedArray() + ) + } + } + } + + + private fun createFixes(containingObject: JetObjectDeclaration, property: JetProperty?): List { + if (containingObject.getContainingJetFile().isCompiled) return listOf(ReplaceWithGetterInvocationFix()) + + // order matters here, 'cleanup' applies fixes in this order + val fixes = ArrayList() + if (property != null && AddConstModifierIntention.isApplicableTo(property)) { + fixes.add(AddConstModifierLocalFix()) + } + + if (containingObject.isCompanion()) { + val classWithCompanion = containingObject.parent?.parent as? JetClass ?: return listOf() + if (!classWithCompanion.isInterface()) { + fixes.add(AddJvmFieldAnnotationFix()) + } + } else { + fixes.add(AddJvmFieldAnnotationFix()) + } + + fixes.add(ReplaceWithGetterInvocationFix()) + + return fixes + } +} + +abstract class StaticFieldUsageFix: LocalQuickFix { + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val deprecatedField = descriptor.psiElement.reference?.resolve() as? PsiField ?: return + val kotlinProperty = (deprecatedField as? KotlinLightFieldForDeclaration)?.getOrigin() as? JetProperty + + if (kotlinProperty != null && kotlinProperty.hasJvmFieldAnnotationOrConstModifier()) return + + doFix(deprecatedField, kotlinProperty, descriptor) + } + + abstract fun doFix(deprecatedField: PsiField, property: JetProperty?, problemDescriptor: ProblemDescriptor) +} + +class AddJvmFieldAnnotationFix : StaticFieldUsageFix() { + override fun doFix(deprecatedField: PsiField, property: JetProperty?, problemDescriptor: ProblemDescriptor) { + replaceReferencesToGetterByReferenceToField(property ?: return) + + property.addAnnotationEntry(JetPsiFactory(property).createAnnotationEntry("@JvmField")) + } + + override fun getName(): String = "Annotate property with @JvmField" + override fun getFamilyName(): String = name +} + +class AddConstModifierLocalFix : StaticFieldUsageFix() { + override fun getName(): String = "Add 'const' modifier to a property" + override fun getFamilyName(): String = name + + override fun doFix(deprecatedField: PsiField, property: JetProperty?, problemDescriptor: ProblemDescriptor) { + AddConstModifierFix.addConstModifier(property ?: return) + } +} + +class ReplaceWithGetterInvocationFix : StaticFieldUsageFix() { + override fun getName(): String = "Replace with getter invocation" + override fun getFamilyName(): String = name + + override fun doFix(deprecatedField: PsiField, property: JetProperty?, problemDescriptor: ProblemDescriptor) { + val lightClass = deprecatedField.containingClass as? KotlinLightClass ?: return + + fun replaceWithGetterInvocation(objectField: PsiField) { + val factory = PsiElementFactory.SERVICE.getInstance(deprecatedField.project) + val elementToReplace = problemDescriptor.psiElement + + val getterInvocation = factory.createExpressionFromText( + objectField.containingClass!!.qualifiedName + "." + objectField.name + "." + JvmAbi.getterName(deprecatedField.name!!) + "()", + elementToReplace + ) + elementToReplace.replace(getterInvocation) + } + + val kotlinClass = lightClass.getOrigin() + when (kotlinClass) { + is JetObjectDeclaration -> { + val instanceField = lightClass.findFieldByName(JvmAbi.INSTANCE_FIELD, false) ?: return + replaceWithGetterInvocation(instanceField) + } + is JetClass -> { + val companionObjectName = kotlinClass.getCompanionObjects().singleOrNull()?.name ?: return + val companionObjectField = lightClass.findFieldByName(companionObjectName, false) ?: return + replaceWithGetterInvocation(companionObjectField) + } + } + } +} + +private fun JetProperty.hasJvmFieldAnnotationOrConstModifier(): Boolean { + return hasModifier(JetTokens.CONST_KEYWORD) || annotationEntries.any { it.text == "@JvmField" } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt index 059049d3a50..3100cd54f79 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/AddConstModifierFix.kt @@ -48,30 +48,8 @@ public class AddConstModifierFix(val property: JetProperty) : AddModifierFix(pro companion object { fun addConstModifier(property: JetProperty) { - val project = property.project - val getter = LightClassUtil.getLightClassPropertyMethods(property).getter - - val javaScope = GlobalSearchScope.getScopeRestrictedByFileTypes(project.allScope(), JavaFileType.INSTANCE) - val getterUsages = if (getter != null) - ReferencesSearch.search(getter, javaScope).findAll() - else - emptyList() - + replaceReferencesToGetterByReferenceToField(property) property.addModifier(JetTokens.CONST_KEYWORD) - - val backingField = LightClassUtil.getLightClassPropertyMethods(property).backingField - if (backingField != null) { - val factory = PsiElementFactory.SERVICE.getInstance(project) - val fieldFQName = backingField.containingClass!!.qualifiedName + "." + backingField.name - - getterUsages.forEach { - val call = it.element.getNonStrictParentOfType() - if (call != null && it.element == call.methodExpression) { - val fieldRef = factory.createExpressionFromText(fieldFQName, it.element) - call.replace(fieldRef) - } - } - } } } } @@ -82,12 +60,18 @@ public class AddConstModifierIntention : JetSelfTargetingIntention( } override fun isApplicableTo(element: JetProperty, caretOffset: Int): Boolean { - if (element.isLocal || element.isVar || element.hasDelegate() || element.initializer == null || element.getter?.hasBody() == true || - element.receiverTypeReference != null) { - return false + return isApplicableTo(element) + } + + companion object { + fun isApplicableTo(element: JetProperty): Boolean { + if (element.isLocal || element.isVar || element.hasDelegate() || element.initializer == null + || element.getter?.hasBody() == true || element.receiverTypeReference != null) { + return false + } + val propertyDescriptor = element.descriptor as? VariableDescriptor ?: return false + return ConstModifierChecker.checkCanBeConst(element, element, propertyDescriptor) == null } - val propertyDescriptor = element.descriptor as? VariableDescriptor ?: return false - return ConstModifierChecker.checkCanBeConst(element, element, propertyDescriptor) == null } } @@ -104,3 +88,29 @@ public object ConstFixFactory : JetSingleIntentionActionFactory() { return null } } + +fun replaceReferencesToGetterByReferenceToField(property: JetProperty) { + val project = property.project + val getter = LightClassUtil.getLightClassPropertyMethods(property).getter + + val javaScope = GlobalSearchScope.getScopeRestrictedByFileTypes(project.allScope(), JavaFileType.INSTANCE) + val getterUsages = if (getter != null) + ReferencesSearch.search(getter, javaScope).findAll() + else + emptyList() + + val backingField = LightClassUtil.getLightClassPropertyMethods(property).backingField + if (backingField != null) { + val factory = PsiElementFactory.SERVICE.getInstance(project) + val fieldFQName = backingField.containingClass!!.qualifiedName + "." + backingField.name + + getterUsages.forEach { + val call = it.element.getNonStrictParentOfType() + if (call != null && it.element == call.methodExpression) { + val fieldRef = factory.createExpressionFromText(fieldFQName, it.element) + call.replace(fieldRef) + } + } + } +} + diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/.inspection b/idea/testData/quickfix/migration/deprecatedStaticField/.inspection new file mode 100644 index 00000000000..7b9ce4b0978 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.DeprecatedUsageOfStaticFieldInspection \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.after.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.after.data.kt new file mode 100644 index 00000000000..039e7e9ac11 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.after.data.kt @@ -0,0 +1,22 @@ +package a + +class A + +class Cl { + companion object { + @JvmField val property1 = A() + const val property2 = 2 + } +} + +interface Int { + companion object { + val property1 = A() + const val property2 = 2 + } +} + +object Obj { + @JvmField val property1 = A() + const val property2 = 2 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.after.java new file mode 100644 index 00000000000..638753da50a --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.after.java @@ -0,0 +1,19 @@ +// "Cleanup code" "true" +import a.*; + +class B { + void bar() { + Cl.property1; + Cl.property2; + Cl.property1; + Cl.property2; + Int.Companion.getProperty1(); + Int.property2; + Int.Companion.getProperty1(); + Int.property2; + Obj.property1; + Obj.property2; + Obj.property1; + Obj.property2; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.Main.java new file mode 100644 index 00000000000..88e0c707b27 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.Main.java @@ -0,0 +1,19 @@ +// "Cleanup code" "true" +import a.*; + +class B { + void bar() { + Cl.property1; + Cl.property2; + Cl.Companion.getProperty1(); + Cl.Companion.getProperty2(); + Int.property1; + Int.property2; + Int.Companion.getProperty1(); + Int.Companion.getProperty2(); + Obj.property1; + Obj.property2; + Obj.INSTANCE.getProperty1(); + Obj.INSTANCE.getProperty2(); + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.data.kt new file mode 100644 index 00000000000..0df46d23c48 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.data.kt @@ -0,0 +1,22 @@ +package a + +class A + +class Cl { + companion object { + val property1 = A() + val property2 = 2 + } +} + +interface Int { + companion object { + val property1 = A() + val property2 = 2 + } +} + +object Obj { + val property1 = A() + val property2 = 2 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.after.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.after.data.kt new file mode 100644 index 00000000000..abdc80401df --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.after.data.kt @@ -0,0 +1,7 @@ +package a + +class A { + companion object Named { + const val property = 1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.after.java new file mode 100644 index 00000000000..b80b5299c59 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.after.java @@ -0,0 +1,10 @@ +// "Add 'const' modifier to a property" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = a.A.property; + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.Main.java new file mode 100644 index 00000000000..b1433964f88 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.Main.java @@ -0,0 +1,10 @@ +// "Add 'const' modifier to a property" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = A.Named.getProperty(); + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.data.kt new file mode 100644 index 00000000000..a6cb926bd42 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.data.kt @@ -0,0 +1,7 @@ +package a + +class A { + companion object Named { + val property = 1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.after.java new file mode 100644 index 00000000000..665f4bafab8 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.after.java @@ -0,0 +1,10 @@ +// "Replace with getter invocation" "true" +import a.A; + +class B { + void bar() { + A a = a.A.Named.getProperty(); + A a2 = A.Named.getProperty(); + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.Main.java new file mode 100644 index 00000000000..42563954469 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.Main.java @@ -0,0 +1,10 @@ +// "Replace with getter invocation" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = A.Named.getProperty(); + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.data.kt new file mode 100644 index 00000000000..a620f752ee1 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.data.kt @@ -0,0 +1,7 @@ +package a + +class A { + companion object Named { + val property = A() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.data.kt new file mode 100644 index 00000000000..126b7623d00 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.data.kt @@ -0,0 +1,7 @@ +package a + +class A { + companion object { + @JvmField val property = A() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.java new file mode 100644 index 00000000000..aae1ac3a27a --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.java @@ -0,0 +1,10 @@ +// "Annotate property with @JvmField" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = a.A.property; + A a3 = A.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.Main.java new file mode 100644 index 00000000000..88795bf772b --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.Main.java @@ -0,0 +1,10 @@ +// "Annotate property with @JvmField" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = A.Companion.getProperty(); + A a3 = A.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.data.kt new file mode 100644 index 00000000000..74198529b9a --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.data.kt @@ -0,0 +1,7 @@ +package a + +class A { + companion object { + val property = A() + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.after.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.after.data.kt new file mode 100644 index 00000000000..b1751a529ec --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.after.data.kt @@ -0,0 +1,7 @@ +package a + +interface A { + companion object Named { + const val property = 1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.after.java new file mode 100644 index 00000000000..b80b5299c59 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.after.java @@ -0,0 +1,10 @@ +// "Add 'const' modifier to a property" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = a.A.property; + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.Main.java new file mode 100644 index 00000000000..253e13d3a18 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.Main.java @@ -0,0 +1,10 @@ +// "Add 'const' modifier to a property" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = A.Named.getProperty(); + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.data.kt new file mode 100644 index 00000000000..25e2160304f --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.data.kt @@ -0,0 +1,7 @@ +package a + +interface A { + companion object Named { + val property = 1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.after.java new file mode 100644 index 00000000000..16a61a54591 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.after.java @@ -0,0 +1,10 @@ +// "Replace with getter invocation" "true" +import a.A; + +class B { + void bar() { + A a = a.A.Companion.getProperty(); + A a2 = A.Companion.getProperty(); + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.Main.java new file mode 100644 index 00000000000..0996002f61d --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.Main.java @@ -0,0 +1,10 @@ +// "Replace with getter invocation" "true" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = A.Companion.getProperty(); + A a3 = A.property; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.data.kt new file mode 100644 index 00000000000..4ba8a34f39e --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.data.kt @@ -0,0 +1,7 @@ +package a + +interface A { + companion object { + val property = 1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.Main.java new file mode 100644 index 00000000000..28d251affa5 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.Main.java @@ -0,0 +1,20 @@ +// "Annotate property with @JvmField" "false" +import a.A; + +class B { + void bar() { + A a = A.property; + A a2 = A.Named.getProperty(); + A a3 = A.property; + } +} + +// ACTION: Add 'const' modifier to a property +// ACTION: Add static import for 'a.A.property' +// ACTION: Annotate 'property' as @Deprecated +// ACTION: Annotate 'property' as @NotNull +// ACTION: Annotate 'property' as @Nullable +// ACTION: Change variable 'a' type to 'int' +// ACTION: Migrate 'a' type to 'int' +// ACTION: Replace with getter invocation +// ACTION: Split into declaration and assignment \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.data.kt new file mode 100644 index 00000000000..25e2160304f --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.data.kt @@ -0,0 +1,7 @@ +package a + +interface A { + companion object Named { + val property = 1 + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_const.after.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.after.data.kt new file mode 100644 index 00000000000..fd59288a85f --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.after.data.kt @@ -0,0 +1,5 @@ +package a + +object Obj { + const val property = 1 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_const.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.after.java new file mode 100644 index 00000000000..798a8cc707d --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.after.java @@ -0,0 +1,11 @@ +// "Add 'const' modifier to a property" "true" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = Obj.property; + A a2 = a.Obj.property; + A a3 = Obj.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.Main.java new file mode 100644 index 00000000000..65e5c43bd8f --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.Main.java @@ -0,0 +1,11 @@ +// "Add 'const' modifier to a property" "true" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = Obj.property; + A a2 = Obj.INSTANCE.getProperty(); + A a3 = Obj.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.data.kt new file mode 100644 index 00000000000..621f7813639 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.data.kt @@ -0,0 +1,5 @@ +package a + +object Obj { + val property = 1 +} \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.Main.java new file mode 100644 index 00000000000..8a93a5ec08e --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.Main.java @@ -0,0 +1,16 @@ +// "Add 'const' modifier to a property" "false" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = Obj.property; + Obj.INSTANCE.getProperty(); + } +} + +// ACTION: Annotate 'property' as @Deprecated +// ACTION: Add static import for 'a.Obj.property' +// ACTION: Annotate property with @JvmField +// ACTION: Split into declaration and assignment +// ACTION: Replace with getter invocation \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.data.kt new file mode 100644 index 00000000000..17fa8e5b676 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.data.kt @@ -0,0 +1,7 @@ +package a + +object Obj { + val property = A() +} + +class A \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.after.java new file mode 100644 index 00000000000..51ec25914c6 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.after.java @@ -0,0 +1,11 @@ +// "Replace with getter invocation" "true" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = a.Obj.INSTANCE.getProperty(); + A a2 = Obj.getProperty(); + A a3 = Obj.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.Main.java new file mode 100644 index 00000000000..806aa1d0eb9 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.Main.java @@ -0,0 +1,11 @@ +// "Replace with getter invocation" "true" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = Obj.property; + A a2 = Obj.getProperty(); + A a3 = Obj.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.data.kt new file mode 100644 index 00000000000..17fa8e5b676 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.data.kt @@ -0,0 +1,7 @@ +package a + +object Obj { + val property = A() +} + +class A \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.after.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.after.data.kt new file mode 100644 index 00000000000..a2cbc3fa642 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.after.data.kt @@ -0,0 +1,7 @@ +package a + +object Obj { + @JvmField val property = A() +} + +class A \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.after.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.after.java new file mode 100644 index 00000000000..4d2db5e6cc9 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.after.java @@ -0,0 +1,11 @@ +// "Annotate property with @JvmField" "true" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = Obj.property; + A a2 = a.Obj.property; + A a3 = Obj.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.Main.java b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.Main.java new file mode 100644 index 00000000000..6c9f2ae7c09 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.Main.java @@ -0,0 +1,11 @@ +// "Annotate property with @JvmField" "true" +import a.Obj; +import a.A; + +class B { + void bar() { + A a = Obj.property; + A a2 = Obj.getProperty(); + A a3 = Obj.property; + } +} diff --git a/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.data.kt b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.data.kt new file mode 100644 index 00000000000..17fa8e5b676 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.data.kt @@ -0,0 +1,7 @@ +package a + +object Obj { + val property = A() +} + +class A \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java index c39b8131f53..55ff31c72f3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/AbstractQuickFixMultiFileTest.java @@ -129,12 +129,11 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer assert mainFileDir != null; final String mainFileName = mainFile.getName(); - final String extraFileNamePrefix = mainFileName.replace(".Main.kt", ".").replace(".Main.java", "."); File[] extraFiles = mainFileDir.listFiles( new FilenameFilter() { @Override public boolean accept(@NotNull File dir, @NotNull String name) { - return name.startsWith(extraFileNamePrefix) && !name.equals(mainFileName); + return name.startsWith(extraFileNamePrefix(mainFileName)) && !name.equals(mainFileName); } } ); @@ -209,7 +208,7 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer "Infos:" + infos); } else { - DirectiveBasedActionUtils.INSTANCE$.checkAvailableActionsAreExpected((JetFile) getFile(), availableActions); + DirectiveBasedActionUtils.INSTANCE$.checkAvailableActionsAreExpected(getFile(), availableActions); } } else { @@ -233,10 +232,9 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer PsiFile mainFile = myFile; String mainFileName = mainFile.getName(); - String extraFileNamePrefix = mainFileName.replace(".Main.kt", "."); for (PsiFile file : mainFile.getContainingDirectory().getFiles()) { String fileName = file.getName(); - if (fileName.equals(mainFileName) || !fileName.startsWith(extraFileNamePrefix)) continue; + if (fileName.equals(mainFileName) || !fileName.startsWith(extraFileNamePrefix(myFile.getName()))) continue; myFile = file; String extraFileFullPath = testFullPath.replace(mainFileName, fileName); @@ -267,4 +265,9 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer protected String getTestDataPath() { return JetTestUtils.getHomeDirectory() + "/"; } + + @NotNull + private static String extraFileNamePrefix(@NotNull String mainFileName) { + return mainFileName.replace(".Main.kt", ".").replace(".Main.java", "."); + } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 9010b97fbbc..9e14f2f3f51 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1111,6 +1111,81 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/migration/deprecatedStaticField") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DeprecatedStaticField extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInDeprecatedStaticField() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/deprecatedStaticField"), Pattern.compile("^(\\w+)\\.before\\.Main\\.\\w+$"), true); + } + + @TestMetadata("cleanUp.before.Main.java") + public void testCleanUp() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/cleanUp.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionObjectOfClass_const.before.Main.java") + public void testCompanionObjectOfClass_const() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionObjectOfClass_getterReference.before.Main.java") + public void testCompanionObjectOfClass_getterReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_getterReference.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionObjectOfClass_jvmField.before.Main.java") + public void testCompanionObjectOfClass_jvmField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionObjectOfInterface_const.before.Main.java") + public void testCompanionObjectOfInterface_const() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_const.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionObjectOfInterface_getterReference.before.Main.java") + public void testCompanionObjectOfInterface_getterReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_getterReference.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("companionObjectOfInterface_jvmField_unavailable.before.Main.java") + public void testCompanionObjectOfInterface_jvmField_unavailable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfInterface_jvmField_unavailable.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("object_const.before.Main.java") + public void testObject_const() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/object_const.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("object_const_unavailable.before.Main.java") + public void testObject_const_unavailable() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/object_const_unavailable.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("object_getterReference.before.Main.java") + public void testObject_getterReference() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/object_getterReference.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("object_jvmField.before.Main.java") + public void testObject_jvmField() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedStaticField/object_jvmField.before.Main.java"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)