Added fake override search in unused symbol inspection #KT-13288 Fixed

This commit is contained in:
Mikhail Glukhikh
2016-08-11 16:30:02 +03:00
parent 8de68e3e87
commit b3b83e344b
21 changed files with 255 additions and 3 deletions
@@ -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(
@@ -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<String>) {
val x: Inter = Test()
x.something()
}
+15
View File
@@ -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 <caret>something() = "hi"
}
class Test: Inter by Impl() {
}
@@ -0,0 +1,3 @@
interface Inter {
String something();
}
@@ -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 <caret>something() = "hi"
}
class Test: Abstract(), Inter {
}
@@ -0,0 +1,7 @@
interface Inter {
String something();
}
class Test extends Abstract implements Inter {
}
@@ -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 <caret>something() = "hi"
}
@@ -0,0 +1,5 @@
class Test extends Abstract {
String something() {
return "123";
}
}
@@ -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 <caret>something() = "hi"
}
@@ -0,0 +1,4 @@
// "Safe delete 'something'" "true"
abstract class Abstract {
}
@@ -0,0 +1,9 @@
interface Inter {
String something();
}
class Test extends Abstract implements Inter {
String something() {
return "123";
}
}
@@ -0,0 +1,5 @@
// "Safe delete 'something'" "true"
abstract class Abstract {
fun <caret>something() = "hi"
}
@@ -0,0 +1,4 @@
// "Safe delete 'something'" "true"
abstract class Abstract {
}
@@ -0,0 +1,2 @@
class Test extends Abstract {
}
@@ -0,0 +1,5 @@
// "Safe delete 'something'" "true"
abstract class Abstract {
fun <caret>something() = "hi"
}
+8
View File
@@ -0,0 +1,8 @@
// "Safe delete 'something'" "true"
abstract class Abstract {
open fun <caret>something() = "hi"
}
class Test: Abstract() {
}
@@ -0,0 +1,7 @@
// "Safe delete 'something'" "true"
abstract class Abstract {
}
class Test: Abstract() {
}
+17
View File
@@ -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 <caret>something() = "hi"
}
class Test: Abstract(), Inter {
}
+18
View File
@@ -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 <caret>something() = "hi"
}
class Test: Abstract(), Inter {
override fun something() = "bye"
}
@@ -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");
@@ -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");