From b3b83e344b487cac417591e1cd04fcbc43ce8b57 Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Thu, 11 Aug 2016 16:30:02 +0300 Subject: [PATCH] Added fake override search in unused symbol inspection #KT-13288 Fixed --- .../inspections/UnusedSymbolInspection.kt | 44 +++++++++++++++++-- .../unusedSymbol/property/triangle.kt | 17 +++++++ .../quickfix/removeUnused/deledage.kt | 15 +++++++ .../javaTriangle.before.Dependency.java | 3 ++ .../removeUnused/javaTriangle.before.Main.kt | 13 ++++++ .../javaTriangle2.before.Dependency.java | 7 +++ .../removeUnused/javaTriangle2.before.Main.kt | 10 +++++ .../javaTriangle3.before.Dependency.java | 5 +++ .../removeUnused/javaTriangle3.before.Main.kt | 11 +++++ .../removeUnused/javaTriangleUnused.after.kt | 4 ++ .../javaTriangleUnused.before.Dependency.java | 9 ++++ .../javaTriangleUnused.before.Main.kt | 5 +++ .../removeUnused/javaTriangleUnused2.after.kt | 4 ++ ...javaTriangleUnused2.before.Dependency.java | 2 + .../javaTriangleUnused2.before.Main.kt | 5 +++ .../quickfix/removeUnused/notTriangle.kt | 8 ++++ .../removeUnused/notTriangle.kt.after | 7 +++ .../quickfix/removeUnused/triangle.kt | 17 +++++++ .../quickfix/removeUnused/triangle2.kt | 18 ++++++++ .../QuickFixMultiFileTestGenerated.java | 30 +++++++++++++ .../idea/quickfix/QuickFixTestGenerated.java | 24 ++++++++++ 21 files changed, 255 insertions(+), 3 deletions(-) create mode 100644 idea/testData/inspections/unusedSymbol/property/triangle.kt create mode 100644 idea/testData/quickfix/removeUnused/deledage.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangle.before.Dependency.java create mode 100644 idea/testData/quickfix/removeUnused/javaTriangle.before.Main.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangle2.before.Dependency.java create mode 100644 idea/testData/quickfix/removeUnused/javaTriangle2.before.Main.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangle3.before.Dependency.java create mode 100644 idea/testData/quickfix/removeUnused/javaTriangle3.before.Main.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangleUnused.after.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Dependency.java create mode 100644 idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Main.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangleUnused2.after.kt create mode 100644 idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Dependency.java create mode 100644 idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Main.kt create mode 100644 idea/testData/quickfix/removeUnused/notTriangle.kt create mode 100644 idea/testData/quickfix/removeUnused/notTriangle.kt.after create mode 100644 idea/testData/quickfix/removeUnused/triangle.kt create mode 100644 idea/testData/quickfix/removeUnused/triangle2.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt index a65f1702848..e73beb7f484 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/UnusedSymbolInspection.kt @@ -29,6 +29,7 @@ import com.intellij.codeInspection.ex.EntryPointsManagerBase import com.intellij.codeInspection.ex.EntryPointsManagerImpl import com.intellij.openapi.project.Project import com.intellij.openapi.util.text.StringUtil +import com.intellij.psi.PsiClass import com.intellij.psi.PsiElement import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiReference @@ -40,11 +41,16 @@ import com.intellij.psi.search.searches.DefinitionsScopedSearch import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.safeDelete.SafeDeleteHandler import org.jetbrains.kotlin.asJava.LightClassUtil +import org.jetbrains.kotlin.asJava.classes.KtLightClass import org.jetbrains.kotlin.asJava.toLightClass import org.jetbrains.kotlin.asJava.toLightMethods +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.annotations.Annotated import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.core.toDescriptor import org.jetbrains.kotlin.idea.findUsages.KotlinFindUsagesHandlerFactory import org.jetbrains.kotlin.idea.findUsages.handlers.KotlinFindClassUsagesHandler import org.jetbrains.kotlin.idea.imports.importableFqName @@ -57,6 +63,7 @@ import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.util.OperatorNameConventions +import org.jetbrains.kotlin.util.findCallableMemberBySignature import org.jetbrains.kotlin.utils.singletonOrEmptyList import java.awt.GridBagConstraints import java.awt.GridBagLayout @@ -154,7 +161,7 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { if (declaration is KtNamedFunction && isConventionalName(declaration)) return // More expensive, resolve-based checks - if (declaration.resolveToDescriptorIfAny() == null) return + declaration.resolveToDescriptorIfAny() ?: return if (isEntryPoint(declaration)) return if (declaration is KtProperty && declaration.isSerializationImplicitlyUsedField()) return if (declaration is KtNamedFunction && declaration.isSerializationImplicitlyUsedMethod()) return @@ -232,8 +239,8 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { return (declaration is KtObjectDeclaration && declaration.isCompanion() && declaration.getBody()?.declarations?.isNotEmpty() == true) || hasReferences(declaration, useScope) || - hasOverrides(declaration, useScope) - + hasOverrides(declaration, useScope) || + hasFakeOverrides(declaration, useScope) } private fun hasReferences(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean { @@ -272,6 +279,37 @@ class UnusedSymbolInspection : AbstractKotlinInspection() { return DefinitionsScopedSearch.search(declaration, useScope).findFirst() != null } + private fun hasFakeOverrides(declaration: KtNamedDeclaration, useScope: SearchScope): Boolean { + val ownerClass = declaration.containingClassOrObject as? KtClass ?: return false + if (!ownerClass.isInheritable()) return false + val descriptor = declaration.toDescriptor() as? CallableMemberDescriptor ?: return false + if (descriptor.modality == Modality.ABSTRACT) return false + val lightMethods = declaration.toLightMethods() + return DefinitionsScopedSearch.search(ownerClass, useScope).any { + element: PsiElement -> + + when (element) { + is KtLightClass -> { + val memberBySignature = + (element.kotlinOrigin?.toDescriptor() as? ClassDescriptor)?.findCallableMemberBySignature(descriptor) + memberBySignature != null && + !memberBySignature.kind.isReal && + memberBySignature.overriddenDescriptors.any { it != descriptor } + } + is PsiClass -> + lightMethods.any { + lightMethod -> + + val sameMethods = element.findMethodsBySignature(lightMethod, true) + sameMethods.all { it.containingClass != element } && + sameMethods.any { it.containingClass != lightMethod.containingClass } + } + else -> + false + } + } + } + override fun createOptionsPanel(): JComponent? { val panel = JPanel(GridBagLayout()) panel.add( diff --git a/idea/testData/inspections/unusedSymbol/property/triangle.kt b/idea/testData/inspections/unusedSymbol/property/triangle.kt new file mode 100644 index 00000000000..f1ac26f5909 --- /dev/null +++ b/idea/testData/inspections/unusedSymbol/property/triangle.kt @@ -0,0 +1,17 @@ +// See KT-13288 + +interface Inter { + fun something(): String +} + +abstract class Abstract { + fun something() = "hi" +} + +class Test: Abstract(), Inter { +} + +fun main(args: Array) { + val x: Inter = Test() + x.something() +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/deledage.kt b/idea/testData/quickfix/removeUnused/deledage.kt new file mode 100644 index 00000000000..925c0a93cb9 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/deledage.kt @@ -0,0 +1,15 @@ +// "Safe delete 'something'" "false" +// ACTION: Convert function to property +// ACTION: Convert to block body +// ACTION: Specify return type explicitly + +interface Inter { + fun something(): String +} + +class Impl : Inter { + override fun something() = "hi" +} + +class Test: Inter by Impl() { +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangle.before.Dependency.java b/idea/testData/quickfix/removeUnused/javaTriangle.before.Dependency.java new file mode 100644 index 00000000000..59f5a3f0dbf --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangle.before.Dependency.java @@ -0,0 +1,3 @@ +interface Inter { + String something(); +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/javaTriangle.before.Main.kt b/idea/testData/quickfix/removeUnused/javaTriangle.before.Main.kt new file mode 100644 index 00000000000..8c5b4e8d2d3 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangle.before.Main.kt @@ -0,0 +1,13 @@ +// "Safe delete 'something'" "false" +// ACTION: Convert function to property +// ACTION: Convert member to extension +// ACTION: Convert to block body +// ACTION: Move to companion object +// ACTION: Specify return type explicitly + +abstract class Abstract { + fun something() = "hi" +} + +class Test: Abstract(), Inter { +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangle2.before.Dependency.java b/idea/testData/quickfix/removeUnused/javaTriangle2.before.Dependency.java new file mode 100644 index 00000000000..c8c2c459093 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangle2.before.Dependency.java @@ -0,0 +1,7 @@ +interface Inter { + String something(); +} + +class Test extends Abstract implements Inter { + +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/javaTriangle2.before.Main.kt b/idea/testData/quickfix/removeUnused/javaTriangle2.before.Main.kt new file mode 100644 index 00000000000..4461d91d226 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangle2.before.Main.kt @@ -0,0 +1,10 @@ +// "Safe delete 'something'" "false" +// ACTION: Convert function to property +// ACTION: Convert member to extension +// ACTION: Convert to block body +// ACTION: Move to companion object +// ACTION: Specify return type explicitly + +abstract class Abstract { + fun something() = "hi" +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangle3.before.Dependency.java b/idea/testData/quickfix/removeUnused/javaTriangle3.before.Dependency.java new file mode 100644 index 00000000000..b240c4378b1 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangle3.before.Dependency.java @@ -0,0 +1,5 @@ +class Test extends Abstract { + String something() { + return "123"; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/javaTriangle3.before.Main.kt b/idea/testData/quickfix/removeUnused/javaTriangle3.before.Main.kt new file mode 100644 index 00000000000..99f12371f97 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangle3.before.Main.kt @@ -0,0 +1,11 @@ +// "Safe delete 'something'" "false" +// ACTION: Convert function to property +// ACTION: Convert member to extension +// ACTION: Convert to block body +// ACTION: Move to companion object +// ACTION: Specify return type explicitly + + +abstract class Abstract { + open fun something() = "hi" +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangleUnused.after.kt b/idea/testData/quickfix/removeUnused/javaTriangleUnused.after.kt new file mode 100644 index 00000000000..a4e061a57a8 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangleUnused.after.kt @@ -0,0 +1,4 @@ +// "Safe delete 'something'" "true" + +abstract class Abstract { +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Dependency.java b/idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Dependency.java new file mode 100644 index 00000000000..9f9368cb880 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Dependency.java @@ -0,0 +1,9 @@ +interface Inter { + String something(); +} + +class Test extends Abstract implements Inter { + String something() { + return "123"; + } +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Main.kt b/idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Main.kt new file mode 100644 index 00000000000..9294533c9dd --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Main.kt @@ -0,0 +1,5 @@ +// "Safe delete 'something'" "true" + +abstract class Abstract { + fun something() = "hi" +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangleUnused2.after.kt b/idea/testData/quickfix/removeUnused/javaTriangleUnused2.after.kt new file mode 100644 index 00000000000..a4e061a57a8 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangleUnused2.after.kt @@ -0,0 +1,4 @@ +// "Safe delete 'something'" "true" + +abstract class Abstract { +} diff --git a/idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Dependency.java b/idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Dependency.java new file mode 100644 index 00000000000..2e56e74711a --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Dependency.java @@ -0,0 +1,2 @@ +class Test extends Abstract { +} \ No newline at end of file diff --git a/idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Main.kt b/idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Main.kt new file mode 100644 index 00000000000..9294533c9dd --- /dev/null +++ b/idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Main.kt @@ -0,0 +1,5 @@ +// "Safe delete 'something'" "true" + +abstract class Abstract { + fun something() = "hi" +} diff --git a/idea/testData/quickfix/removeUnused/notTriangle.kt b/idea/testData/quickfix/removeUnused/notTriangle.kt new file mode 100644 index 00000000000..ead1dd4b1f3 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/notTriangle.kt @@ -0,0 +1,8 @@ +// "Safe delete 'something'" "true" + +abstract class Abstract { + open fun something() = "hi" +} + +class Test: Abstract() { +} diff --git a/idea/testData/quickfix/removeUnused/notTriangle.kt.after b/idea/testData/quickfix/removeUnused/notTriangle.kt.after new file mode 100644 index 00000000000..4d6f33549fd --- /dev/null +++ b/idea/testData/quickfix/removeUnused/notTriangle.kt.after @@ -0,0 +1,7 @@ +// "Safe delete 'something'" "true" + +abstract class Abstract { +} + +class Test: Abstract() { +} diff --git a/idea/testData/quickfix/removeUnused/triangle.kt b/idea/testData/quickfix/removeUnused/triangle.kt new file mode 100644 index 00000000000..e8d47943938 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/triangle.kt @@ -0,0 +1,17 @@ +// "Safe delete 'something'" "false" +// ACTION: Convert function to property +// ACTION: Convert member to extension +// ACTION: Convert to block body +// ACTION: Move to companion object +// ACTION: Specify return type explicitly + +interface Inter { + fun something(): String +} + +abstract class Abstract { + fun something() = "hi" +} + +class Test: Abstract(), Inter { +} diff --git a/idea/testData/quickfix/removeUnused/triangle2.kt b/idea/testData/quickfix/removeUnused/triangle2.kt new file mode 100644 index 00000000000..1a60578eaa6 --- /dev/null +++ b/idea/testData/quickfix/removeUnused/triangle2.kt @@ -0,0 +1,18 @@ +// "Safe delete 'something'" "false" +// ACTION: Convert function to property +// ACTION: Convert member to extension +// ACTION: Convert to block body +// ACTION: Move to companion object +// ACTION: Specify return type explicitly + +interface Inter { + fun something(): String +} + +abstract class Abstract { + open fun something() = "hi" +} + +class Test: Abstract(), Inter { + override fun something() = "bye" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java index 68a82071ad6..16d44b110b6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiFileTestGenerated.java @@ -1537,6 +1537,36 @@ public class QuickFixMultiFileTestGenerated extends AbstractQuickFixMultiFileTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/removeUnused"), Pattern.compile("^(\\w+)\\.((before\\.Main\\.\\w+)|(test))$"), true); } + @TestMetadata("javaTriangle.before.Main.kt") + public void testJavaTriangle() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/javaTriangle.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("javaTriangle2.before.Main.kt") + public void testJavaTriangle2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/javaTriangle2.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("javaTriangle3.before.Main.kt") + public void testJavaTriangle3() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/javaTriangle3.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("javaTriangleUnused.before.Main.kt") + public void testJavaTriangleUnused() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/javaTriangleUnused.before.Main.kt"); + doTestWithExtraFile(fileName); + } + + @TestMetadata("javaTriangleUnused2.before.Main.kt") + public void testJavaTriangleUnused2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/javaTriangleUnused2.before.Main.kt"); + doTestWithExtraFile(fileName); + } + @TestMetadata("usedObjectAsAliasMulti.before.Main.kt") public void testUsedObjectAsAliasMulti() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/usedObjectAsAliasMulti.before.Main.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java index 7545722a5e9..8247d06b1be 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixTestGenerated.java @@ -6901,6 +6901,30 @@ public class QuickFixTestGenerated extends AbstractQuickFixTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/quickfix/removeUnused"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true); } + @TestMetadata("deledage.kt") + public void testDeledage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/deledage.kt"); + doTest(fileName); + } + + @TestMetadata("notTriangle.kt") + public void testNotTriangle() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/notTriangle.kt"); + doTest(fileName); + } + + @TestMetadata("triangle.kt") + public void testTriangle() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/triangle.kt"); + doTest(fileName); + } + + @TestMetadata("triangle2.kt") + public void testTriangle2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/triangle2.kt"); + doTest(fileName); + } + @TestMetadata("unusedClass.kt") public void testUnusedClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/quickfix/removeUnused/unusedClass.kt");