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
|
||||
|
||||
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<IntentionAction>) {
|
||||
public fun checkAvailableActionsAreExpected(file: PsiFile, availableActions: Collection<IntentionAction>) {
|
||||
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"
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1260,6 +1260,14 @@
|
||||
cleanupTool="true"
|
||||
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"/>
|
||||
|
||||
<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 {
|
||||
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<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 {
|
||||
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<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;
|
||||
|
||||
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", ".");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user