Redundant override inspection: don't report for ambiguous derivation
#KT-24405 Fixed #KT-25883 Fixed
This commit is contained in:
+28
-17
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
@@ -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()
|
||||
}
|
||||
+20
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user