Redundant override inspection: don't report for ambiguous derivation

#KT-24405 Fixed
#KT-25883 Fixed
This commit is contained in:
Mikhail Glukhikh
2018-09-04 15:45:26 +03:00
parent 607392a2ab
commit dead2516c2
7 changed files with 105 additions and 17 deletions
@@ -8,13 +8,16 @@ package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.*
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLocalInspectionTool {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) =
@@ -49,7 +52,7 @@ class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLoc
val superCallElement = qualifiedExpression.selectorExpression as? KtCallElement ?: return
if (!isSameFunctionName(superCallElement, function)) return
if (!isSameArguments(superCallElement, function)) return
if (function.isDefinedInDelegatedSuperType(qualifiedExpression)) return
if (function.isAmbiguouslyDerived()) return
val descriptor = holder.manager.createProblemDescriptor(
function,
@@ -90,23 +93,31 @@ class KotlinRedundantOverrideInspection : AbstractKotlinInspection(), CleanupLoc
}
}
private fun KtNamedFunction.isDefinedInDelegatedSuperType(superQualifiedExpression: KtDotQualifiedExpression): Boolean {
private fun KtNamedFunction.isAmbiguouslyDerived(): Boolean {
val context = analyze()
val original = context[BindingContext.FUNCTION, this]?.original
val overriddenDescriptors = original?.overriddenDescriptors ?: return false
if (overriddenDescriptors.size < 2) return false
// Two+ functions
// At least one default in interface or abstract in class, or just something from Java
if (overriddenDescriptors.any { overriddenFunction ->
overriddenFunction is JavaMethodDescriptor || when ((overriddenFunction.containingDeclaration as? ClassDescriptor)?.kind) {
ClassKind.CLASS -> overriddenFunction.modality == Modality.ABSTRACT
ClassKind.INTERFACE -> overriddenFunction.modality != Modality.ABSTRACT
else -> false
}
}
) return true
val delegatedSuperTypeEntries =
containingClassOrObject?.superTypeListEntries?.filterIsInstance<KtDelegatedSuperTypeEntry>() ?: return false
if (delegatedSuperTypeEntries.isEmpty()) return false
val context = superQualifiedExpression.analyze()
val delegatedSuperTypes = delegatedSuperTypeEntries.mapNotNull { entry ->
context[BindingContext.TYPE, entry.typeReference]
val delegatedSuperDeclarations = delegatedSuperTypeEntries.mapNotNull { entry ->
context[BindingContext.TYPE, entry.typeReference]?.constructor?.declarationDescriptor
}
val superResolvedCall = superQualifiedExpression.getResolvedCall(context) ?: return false
val superCallResolvedDescriptor = superResolvedCall.resultingDescriptor
val superCallResolvedReceiverTypes = superCallResolvedDescriptor.overriddenDescriptors.mapNotNull { it.dispatchReceiverParameter?.type }
return delegatedSuperTypes.any { delegatedSuperType ->
superCallResolvedReceiverTypes.any { superCallReceiverType ->
delegatedSuperType.isSubtypeOf(superCallReceiverType)
}
return overriddenDescriptors.any {
it.containingDeclaration in delegatedSuperDeclarations
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
abstract class AbstractClass {
abstract fun foo(): Int
}
interface Interface {
fun foo(): Int = 3
}
class ChildClass : AbstractClass(), Interface {
override <caret>fun foo() = super.foo()
}
@@ -0,0 +1,10 @@
// PROBLEM: none
// WITH_RUNTIME
interface Foo {
fun add(i: Int): Boolean
}
class Bar: ArrayList<Int>(), Foo {
override <caret>fun add(i: Int) = super.add(i)
}
@@ -0,0 +1,11 @@
open class Class {
open fun foo(): Int = 4
}
interface Interface {
fun foo(): Int
}
class ChildClass : Class(), Interface {
override <caret>fun foo() = super.foo()
}
@@ -0,0 +1,10 @@
open class Class {
open fun foo(): Int = 4
}
interface Interface {
fun foo(): Int
}
class ChildClass : Class(), Interface {
}
@@ -0,0 +1,13 @@
// PROBLEM: none
interface Foo {
fun test()
}
interface Gav {
fun test() {}
}
class TwoInterfaces : Foo, Gav {
override <caret>fun test() = super.test()
}
@@ -4027,6 +4027,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
}
@TestMetadata("abstractClassAndInterface.kt")
public void testAbstractClassAndInterface() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/abstractClassAndInterface.kt");
}
public void testAllFilesPresentInRedundantOverride() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantOverride"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
}
@@ -4051,11 +4056,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/redundantOverride/basic.kt");
}
@TestMetadata("boxedParameters.kt")
public void testBoxedParameters() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/boxedParameters.kt");
}
@TestMetadata("callDifferentSuperMethod.kt")
public void testCallDifferentSuperMethod() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/callDifferentSuperMethod.kt");
}
@TestMetadata("classAndInterface.kt")
public void testClassAndInterface() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/classAndInterface.kt");
}
@TestMetadata("dataClass.kt")
public void testDataClass() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/dataClass.kt");
@@ -4101,6 +4116,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
runTest("idea/testData/inspectionsLocal/redundantOverride/singleExpressionFunction.kt");
}
@TestMetadata("twoInterfaces.kt")
public void testTwoInterfaces() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/twoInterfaces.kt");
}
@TestMetadata("useGenericsSuper.kt")
public void testUseGenericsSuper() throws Exception {
runTest("idea/testData/inspectionsLocal/redundantOverride/useGenericsSuper.kt");