Provide inspection and quickfixes for usages of static fields which will be no longer accessible in future versions
For this inspection three kinds of fixes are potentially available: - Add 'const' modifier to kotlin property - Add '@JvmField' annotation to kotlin property - Change field reference to getter invocation In case user chooses to 'cleanup code', these fixes are prioritized in this order
This commit is contained in:
+8
-2
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.idea.test
|
package org.jetbrains.kotlin.idea.test
|
||||||
|
|
||||||
import com.intellij.codeInsight.intention.IntentionAction
|
import com.intellij.codeInsight.intention.IntentionAction
|
||||||
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.testFramework.UsefulTestCase
|
import com.intellij.testFramework.UsefulTestCase
|
||||||
import org.jetbrains.kotlin.diagnostics.Severity
|
import org.jetbrains.kotlin.diagnostics.Severity
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully
|
||||||
@@ -42,7 +43,7 @@ public object DirectiveBasedActionUtils {
|
|||||||
expectedErrors)
|
expectedErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun checkAvailableActionsAreExpected(file: JetFile, availableActions: Collection<IntentionAction>) {
|
public fun checkAvailableActionsAreExpected(file: PsiFile, availableActions: Collection<IntentionAction>) {
|
||||||
val expectedActions = InTextDirectivesUtils.findLinesWithPrefixesRemoved(file.getText(), "// ACTION:").sorted()
|
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",
|
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",
|
"Inject language or reference",
|
||||||
"Suppress '",
|
"Suppress '",
|
||||||
"Run inspection on",
|
"Run inspection on",
|
||||||
"Inspection '"
|
"Inspection '",
|
||||||
|
"Suppress for ",
|
||||||
|
"Suppress all ",
|
||||||
|
"Edit cleanup profile settings",
|
||||||
|
"Fix all '",
|
||||||
|
"Cleanup code"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1260,6 +1260,14 @@
|
|||||||
cleanupTool="true"
|
cleanupTool="true"
|
||||||
level="WARNING"/>
|
level="WARNING"/>
|
||||||
|
|
||||||
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.DeprecatedUsageOfStaticFieldInspection"
|
||||||
|
displayName="This field will not be generated in future versions of Kotlin. Use 'const' modifier, '@JvmField' annotation or access data through corresponding object."
|
||||||
|
groupName="Kotlin"
|
||||||
|
language="JAVA"
|
||||||
|
enabledByDefault="true"
|
||||||
|
cleanupTool="true"
|
||||||
|
level="WARNING"/>
|
||||||
|
|
||||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||||
|
|
||||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||||
|
|||||||
@@ -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<LocalQuickFix> {
|
||||||
|
if (containingObject.getContainingJetFile().isCompiled) return listOf(ReplaceWithGetterInvocationFix())
|
||||||
|
|
||||||
|
// order matters here, 'cleanup' applies fixes in this order
|
||||||
|
val fixes = ArrayList<LocalQuickFix>()
|
||||||
|
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" }
|
||||||
|
}
|
||||||
@@ -48,30 +48,8 @@ public class AddConstModifierFix(val property: JetProperty) : AddModifierFix(pro
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun addConstModifier(property: JetProperty) {
|
fun addConstModifier(property: JetProperty) {
|
||||||
val project = property.project
|
replaceReferencesToGetterByReferenceToField(property)
|
||||||
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()
|
|
||||||
|
|
||||||
property.addModifier(JetTokens.CONST_KEYWORD)
|
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<PsiMethodCallExpression>()
|
|
||||||
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<JetProperty>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun isApplicableTo(element: JetProperty, caretOffset: Int): Boolean {
|
override fun isApplicableTo(element: JetProperty, caretOffset: Int): Boolean {
|
||||||
if (element.isLocal || element.isVar || element.hasDelegate() || element.initializer == null || element.getter?.hasBody() == true ||
|
return isApplicableTo(element)
|
||||||
element.receiverTypeReference != null) {
|
}
|
||||||
return false
|
|
||||||
|
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
|
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<PsiMethodCallExpression>()
|
||||||
|
if (call != null && it.element == call.methodExpression) {
|
||||||
|
val fieldRef = factory.createExpressionFromText(fieldFQName, it.element)
|
||||||
|
call.replace(fieldRef)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.inspections.DeprecatedUsageOfStaticFieldInspection
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// "Cleanup code" "true"
|
||||||
|
import a.*;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
Cl.prope<caret>rty1;
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
@@ -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
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object Named {
|
||||||
|
const val property = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+10
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_const.before.Main.java
Vendored
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Add 'const' modifier to a property" "true"
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = A.pro<caret>perty;
|
||||||
|
A a2 = A.Named.getProperty();
|
||||||
|
A a3 = A.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object Named {
|
||||||
|
val property = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Replace with getter invocation" "true"
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = A.pro<caret>perty;
|
||||||
|
A a2 = A.Named.getProperty();
|
||||||
|
A a3 = A.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object Named {
|
||||||
|
val property = A()
|
||||||
|
}
|
||||||
|
}
|
||||||
idea/testData/quickfix/migration/deprecatedStaticField/companionObjectOfClass_jvmField.after.data.kt
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
@JvmField val property = A()
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+10
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Annotate property with @JvmField" "true"
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = A.pro<caret>perty;
|
||||||
|
A a2 = A.Companion.getProperty();
|
||||||
|
A a3 = A.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
class A {
|
||||||
|
companion object {
|
||||||
|
val property = A()
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
companion object Named {
|
||||||
|
const val property = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+10
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Add 'const' modifier to a property" "true"
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = A.prop<caret>erty;
|
||||||
|
A a2 = A.Named.getProperty();
|
||||||
|
A a3 = A.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
companion object Named {
|
||||||
|
val property = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// "Replace with getter invocation" "true"
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = A.prop<caret>erty;
|
||||||
|
A a2 = A.Companion.getProperty();
|
||||||
|
A a3 = A.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
companion object {
|
||||||
|
val property = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
// "Annotate property with @JvmField" "false"
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = A.prop<caret>erty;
|
||||||
|
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
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
interface A {
|
||||||
|
companion object Named {
|
||||||
|
val property = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
object Obj {
|
||||||
|
const val property = 1
|
||||||
|
}
|
||||||
+11
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// "Add 'const' modifier to a property" "true"
|
||||||
|
import a.Obj;
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = Obj.pro<caret>perty;
|
||||||
|
A a2 = Obj.INSTANCE.getProperty();
|
||||||
|
A a3 = Obj.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
object Obj {
|
||||||
|
val property = 1
|
||||||
|
}
|
||||||
Vendored
+16
@@ -0,0 +1,16 @@
|
|||||||
|
// "Add 'const' modifier to a property" "false"
|
||||||
|
import a.Obj;
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = Obj.pro<caret>perty;
|
||||||
|
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
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
object Obj {
|
||||||
|
val property = A()
|
||||||
|
}
|
||||||
|
|
||||||
|
class A
|
||||||
+11
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// "Replace with getter invocation" "true"
|
||||||
|
import a.Obj;
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = Obj.pro<caret>perty;
|
||||||
|
A a2 = Obj.getProperty();
|
||||||
|
A a3 = Obj.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
object Obj {
|
||||||
|
val property = A()
|
||||||
|
}
|
||||||
|
|
||||||
|
class A
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
object Obj {
|
||||||
|
@JvmField val property = A()
|
||||||
|
}
|
||||||
|
|
||||||
|
class A
|
||||||
+11
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
// "Annotate property with @JvmField" "true"
|
||||||
|
import a.Obj;
|
||||||
|
import a.A;
|
||||||
|
|
||||||
|
class B {
|
||||||
|
void bar() {
|
||||||
|
A a = Obj.pro<caret>perty;
|
||||||
|
A a2 = Obj.getProperty();
|
||||||
|
A a3 = Obj.property;
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package a
|
||||||
|
|
||||||
|
object Obj {
|
||||||
|
val property = A()
|
||||||
|
}
|
||||||
|
|
||||||
|
class A
|
||||||
@@ -129,12 +129,11 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer
|
|||||||
assert mainFileDir != null;
|
assert mainFileDir != null;
|
||||||
|
|
||||||
final String mainFileName = mainFile.getName();
|
final String mainFileName = mainFile.getName();
|
||||||
final String extraFileNamePrefix = mainFileName.replace(".Main.kt", ".").replace(".Main.java", ".");
|
|
||||||
File[] extraFiles = mainFileDir.listFiles(
|
File[] extraFiles = mainFileDir.listFiles(
|
||||||
new FilenameFilter() {
|
new FilenameFilter() {
|
||||||
@Override
|
@Override
|
||||||
public boolean accept(@NotNull File dir, @NotNull String name) {
|
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);
|
"Infos:" + infos);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
DirectiveBasedActionUtils.INSTANCE$.checkAvailableActionsAreExpected((JetFile) getFile(), availableActions);
|
DirectiveBasedActionUtils.INSTANCE$.checkAvailableActionsAreExpected(getFile(), availableActions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -233,10 +232,9 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer
|
|||||||
|
|
||||||
PsiFile mainFile = myFile;
|
PsiFile mainFile = myFile;
|
||||||
String mainFileName = mainFile.getName();
|
String mainFileName = mainFile.getName();
|
||||||
String extraFileNamePrefix = mainFileName.replace(".Main.kt", ".");
|
|
||||||
for (PsiFile file : mainFile.getContainingDirectory().getFiles()) {
|
for (PsiFile file : mainFile.getContainingDirectory().getFiles()) {
|
||||||
String fileName = file.getName();
|
String fileName = file.getName();
|
||||||
if (fileName.equals(mainFileName) || !fileName.startsWith(extraFileNamePrefix)) continue;
|
if (fileName.equals(mainFileName) || !fileName.startsWith(extraFileNamePrefix(myFile.getName()))) continue;
|
||||||
|
|
||||||
myFile = file;
|
myFile = file;
|
||||||
String extraFileFullPath = testFullPath.replace(mainFileName, fileName);
|
String extraFileFullPath = testFullPath.replace(mainFileName, fileName);
|
||||||
@@ -267,4 +265,9 @@ public abstract class AbstractQuickFixMultiFileTest extends KotlinDaemonAnalyzer
|
|||||||
protected String getTestDataPath() {
|
protected String getTestDataPath() {
|
||||||
return JetTestUtils.getHomeDirectory() + "/";
|
return JetTestUtils.getHomeDirectory() + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String extraFileNamePrefix(@NotNull String mainFileName) {
|
||||||
|
return mainFileName.replace(".Main.kt", ".").replace(".Main.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")
|
@TestMetadata("idea/testData/quickfix/migration/javaAnnotationPositionedArguments")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user