diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 22e27de2804..c3b0e141012 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -1240,6 +1240,14 @@ cleanupTool="true" level="WARNING"/> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedObjectInstanceFieldReference.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedObjectInstanceFieldReference.kt new file mode 100644 index 00000000000..62e551c9725 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/DeprecatedObjectInstanceFieldReference.kt @@ -0,0 +1,65 @@ +/* + * 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 com.intellij.psi.codeStyle.JavaCodeStyleManager +import org.jetbrains.kotlin.asJava.KotlinLightClass +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.psi.JetObjectDeclaration + +public class DeprecatedObjectInstanceFieldReferenceInspection : 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() + if (!(resolvedTo is PsiField && resolvedTo.name == JvmAbi.DEPRECATED_INSTANCE_FIELD)) return + + val containingClass = resolvedTo.containingClass + if (containingClass !is KotlinLightClass || containingClass.getOrigin() !is JetObjectDeclaration) return + + holder.registerProblem( + expression, "Use of deprecated '${JvmAbi.DEPRECATED_INSTANCE_FIELD}' field", + ProblemHighlightType.LIKE_DEPRECATED, + DeprecatedObjectInstanceFieldReferenceFix() + ) + } + } + } +} + +public class DeprecatedObjectInstanceFieldReferenceFix : LocalQuickFix { + override fun getName(): String = "Replace with reference to '${JvmAbi.INSTANCE_FIELD}' field" + override fun getFamilyName(): String = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val reference = descriptor.psiElement.reference ?: return + val deprecatedField = reference.resolve() as? PsiField ?: return + + val lightClassForObject = deprecatedField.containingClass as? KotlinLightClass ?: return + val correctField = lightClassForObject.findFieldByName(JvmAbi.INSTANCE_FIELD, false) ?: return + + val newReference = reference.bindToElement(correctField) + + val codeStyleManager = JavaCodeStyleManager.getInstance(project) + codeStyleManager.shortenClassReferences(newReference) + val javaFile = newReference.containingFile as? PsiJavaFile ?: return + codeStyleManager.removeRedundantImports(javaFile) + } +} diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/.inspection b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/.inspection new file mode 100644 index 00000000000..4b92563b76a --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.DeprecatedObjectInstanceFieldReferenceInspection \ No newline at end of file diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.after.java b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.after.java new file mode 100644 index 00000000000..b7869a56468 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.after.java @@ -0,0 +1,8 @@ +// "Replace with reference to 'INSTANCE' field" "true" +import a.A; + +class B { + void bar() { + A.INSTANCE.getName(); + } +} diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.Main.java b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.Main.java new file mode 100644 index 00000000000..c4a54dde6bf --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.Main.java @@ -0,0 +1,8 @@ +// "Replace with reference to 'INSTANCE' field" "true" +import a.A; + +class B { + void bar() { + A.INSTANCE$.getName(); + } +} diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.data.kt b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.data.kt new file mode 100644 index 00000000000..c8a4097ad47 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.data.kt @@ -0,0 +1,4 @@ +package a + +object A { +} diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.after.java b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.after.java new file mode 100644 index 00000000000..b7869a56468 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.after.java @@ -0,0 +1,8 @@ +// "Replace with reference to 'INSTANCE' field" "true" +import a.A; + +class B { + void bar() { + A.INSTANCE.getName(); + } +} diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.Main.java b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.Main.java new file mode 100644 index 00000000000..bdf53ad11a0 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.Main.java @@ -0,0 +1,8 @@ +// "Replace with reference to 'INSTANCE' field" "true" +import static a.A.INSTANCE$; + +class B { + void bar() { + INSTANCE$.getName(); + } +} diff --git a/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.data.kt b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.data.kt new file mode 100644 index 00000000000..a95463e2b95 --- /dev/null +++ b/idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.data.kt @@ -0,0 +1,5 @@ +package a + +object A { + val name: String = "" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 7d969251c8d..bae93a28c39 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1063,6 +1063,27 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes } } + @TestMetadata("idea/testData/quickfix/migration/deprecatedObjectInstanceField") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class DeprecatedObjectInstanceField extends AbstractQuickFixMultiFileTest { + public void testAllFilesPresentInDeprecatedObjectInstanceField() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/migration/deprecatedObjectInstanceField"), Pattern.compile("^(\\w+)\\.before\\.Main\\.\\w+$"), true); + } + + @TestMetadata("basic.before.Main.java") + public void testBasic() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedObjectInstanceField/basic.before.Main.java"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("staticImport.before.Main.java") + public void testStaticImport() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/quickfix/migration/deprecatedObjectInstanceField/staticImport.before.Main.java"); + doTestWithExtraFile(fileName); + } + } + @TestMetadata("idea/testData/quickfix/migration/deprecatedPackageFacade") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)