mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[Kotlin] fix false positives in the "redundant override" inspection
Cover cases with interface implementation by delegation: — Do not require two overridden symbols: both the delegate and the explicit override can refer to the same interface method with a default implementation. — Fix subtyping check: delegates override members from the implemented interface and its supertypes, not subtypes. KTIJ-16228 GitOrigin-RevId: 93dde1f05141098c359e3e7d4f15a5a63083f44c
This commit is contained in:
committed by
intellij-monorepo-bot
parent
92f5232120
commit
c94b90dfe8
+28
-19
@@ -45,8 +45,7 @@ internal class KotlinRedundantOverrideInspection : KotlinApplicableInspectionBas
|
||||
|
||||
override fun isApplicableByPsi(element: KtNamedFunction): Boolean {
|
||||
val modifierList = element.modifierList ?: return false
|
||||
val overrideKeyword = modifierList.getModifier(OVERRIDE_KEYWORD)
|
||||
if (overrideKeyword == null) return false
|
||||
if (modifierList.getModifier(OVERRIDE_KEYWORD) == null) return false
|
||||
if (MODIFIER_EXCLUDE_OVERRIDE.any { modifierList.hasModifier(it) }) return false
|
||||
if (KotlinPsiHeuristics.hasNonSuppressAnnotations(element)) return false
|
||||
|
||||
@@ -111,7 +110,11 @@ internal class KotlinRedundantOverrideInspection : KotlinApplicableInspectionBas
|
||||
return null
|
||||
}
|
||||
|
||||
if (isAmbiguouslyDerived(element, allFunctionOverriddenSymbols)) {
|
||||
if (isAmbiguouslyDerived(allFunctionOverriddenSymbols)) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (isOverridingDelegatedImplementation(element, allFunctionOverriddenSymbols)) {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -177,37 +180,43 @@ internal class KotlinRedundantOverrideInspection : KotlinApplicableInspectionBas
|
||||
}
|
||||
|
||||
private fun KaSession.isAmbiguouslyDerived(
|
||||
function: KtNamedFunction,
|
||||
allFunctionOverriddenSymbols: Sequence<KaCallableSymbol>,
|
||||
): Boolean {
|
||||
// less than 2 functions
|
||||
if (allFunctionOverriddenSymbols.take(2).count() < 2) return false
|
||||
|
||||
// Two+ functions
|
||||
// 2+ functions
|
||||
// At least one default in interface or abstract in class, or just something from Java
|
||||
if (allFunctionOverriddenSymbols.any { overriddenSymbol ->
|
||||
val javaSourceOrLibrary = overriddenSymbol.origin.isJavaSourceOrLibrary()
|
||||
|
||||
val kind = (overriddenSymbol.containingDeclaration as? KaNamedClassSymbol)?.classKind
|
||||
javaSourceOrLibrary || when (kind) {
|
||||
KaClassKind.CLASS -> overriddenSymbol.modality == KaSymbolModality.ABSTRACT
|
||||
KaClassKind.INTERFACE -> overriddenSymbol.modality != KaSymbolModality.ABSTRACT
|
||||
else -> false
|
||||
}
|
||||
return allFunctionOverriddenSymbols.any { overriddenSymbol ->
|
||||
val javaSourceOrLibrary = overriddenSymbol.origin.isJavaSourceOrLibrary()
|
||||
|
||||
val kind = (overriddenSymbol.containingDeclaration as? KaNamedClassSymbol)?.classKind
|
||||
javaSourceOrLibrary || when (kind) {
|
||||
KaClassKind.CLASS -> overriddenSymbol.modality == KaSymbolModality.ABSTRACT
|
||||
KaClassKind.INTERFACE -> overriddenSymbol.modality != KaSymbolModality.ABSTRACT
|
||||
else -> false
|
||||
}
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Don't mark an override unused if it overrides an implementation by delegation.
|
||||
*
|
||||
* Members from interfaces implemented by delegation and their super interfaces are affected.
|
||||
* Explicit overrides in this case replace the overrides from delegation and are not unused.
|
||||
*/
|
||||
private fun KaSession.isOverridingDelegatedImplementation(
|
||||
function: KtNamedFunction,
|
||||
allFunctionOverriddenSymbols: Sequence<KaCallableSymbol>,
|
||||
): Boolean {
|
||||
val superTypeListEntries = function.containingClassOrObject?.superTypeListEntries
|
||||
val delegatedSuperTypeEntries =
|
||||
superTypeListEntries?.filterIsInstance<KtDelegatedSuperTypeEntry>()?.ifEmpty { return false } ?: return false
|
||||
superTypeListEntries.orEmpty().filterIsInstance<KtDelegatedSuperTypeEntry>().ifEmpty { return false }
|
||||
val delegatedSuperDeclarationTypes =
|
||||
delegatedSuperTypeEntries.mapNotNull { it.typeReference?.type }
|
||||
return allFunctionOverriddenSymbols.any { overriddenSymbol ->
|
||||
val type = (overriddenSymbol.containingSymbol as? KaNamedClassSymbol)?.defaultType ?: return@any false
|
||||
delegatedSuperDeclarationTypes.any { type.isSubtypeOf(it) }
|
||||
val containingSymbolType = (overriddenSymbol.containingSymbol as? KaNamedClassSymbol)?.defaultType ?: return@any false
|
||||
delegatedSuperDeclarationTypes.any { delegatedIFaceType -> delegatedIFaceType.isSubtypeOf(containingSymbolType) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -1879,6 +1879,21 @@ public abstract class SharedK1LocalInspectionTestGenerated extends AbstractShare
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedMemberHidesSuperTypeOverride5.kt")
|
||||
public void testDelegatedMemberHidesSuperTypeOverride5() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedMemberHidesSuperTypeOverride6.kt")
|
||||
public void testDelegatedMemberHidesSuperTypeOverride6() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride6.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedMemberHidesSuperTypeOverride7.kt")
|
||||
public void testDelegatedMemberHidesSuperTypeOverride7() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride7.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/equals.kt");
|
||||
@@ -1939,6 +1954,16 @@ public abstract class SharedK1LocalInspectionTestGenerated extends AbstractShare
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/notCallSuper.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideInterfaceDelegation.kt")
|
||||
public void testOverrideInterfaceDelegation() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/overrideInterfaceDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideInterfaceDelegation2.kt")
|
||||
public void testOverrideInterfaceDelegation2() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/overrideInterfaceDelegation2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideModifireFinal.kt")
|
||||
public void testOverrideModifireFinal() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/overrideModifireFinal.kt");
|
||||
|
||||
+25
@@ -1879,6 +1879,21 @@ public abstract class SharedK2LocalInspectionTestGenerated extends AbstractShare
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride4.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedMemberHidesSuperTypeOverride5.kt")
|
||||
public void testDelegatedMemberHidesSuperTypeOverride5() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride5.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedMemberHidesSuperTypeOverride6.kt")
|
||||
public void testDelegatedMemberHidesSuperTypeOverride6() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride6.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("delegatedMemberHidesSuperTypeOverride7.kt")
|
||||
public void testDelegatedMemberHidesSuperTypeOverride7() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/delegatedMemberHidesSuperTypeOverride7.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("equals.kt")
|
||||
public void testEquals() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/equals.kt");
|
||||
@@ -1939,6 +1954,16 @@ public abstract class SharedK2LocalInspectionTestGenerated extends AbstractShare
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/notCallSuper.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideInterfaceDelegation.kt")
|
||||
public void testOverrideInterfaceDelegation() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/overrideInterfaceDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideInterfaceDelegation2.kt")
|
||||
public void testOverrideInterfaceDelegation2() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/overrideInterfaceDelegation2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("overrideModifireFinal.kt")
|
||||
public void testOverrideModifireFinal() throws Exception {
|
||||
runTest("../testData/inspectionsLocal/redundantOverride/overrideModifireFinal.kt");
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// PROBLEM: none
|
||||
// WITH_STDLIB
|
||||
|
||||
interface Foo {
|
||||
val message: String
|
||||
fun test() { println(message) }
|
||||
}
|
||||
|
||||
class FooImpl : Foo {
|
||||
override val message = "FooImpl"
|
||||
}
|
||||
|
||||
class Bar(foo: Foo) : Foo by foo {
|
||||
override val message = "Bar"
|
||||
<caret>override fun test() {
|
||||
super.test()
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// PROBLEM: none
|
||||
// WITH_STDLIB
|
||||
|
||||
interface Foo {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
interface Bar : Foo {
|
||||
fun bar()
|
||||
}
|
||||
|
||||
abstract class Baz : Bar {
|
||||
override fun bar() {
|
||||
println("baz")
|
||||
}
|
||||
}
|
||||
|
||||
class Boo(bar: Bar) : Baz(), Bar by bar {
|
||||
<caret>override fun bar() {
|
||||
super.bar()
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
interface Foo {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
interface Bar : Foo {
|
||||
fun bar()
|
||||
}
|
||||
|
||||
abstract class Baz : Bar {
|
||||
override fun bar() {
|
||||
println("baz")
|
||||
}
|
||||
}
|
||||
|
||||
class Boo(bar: Bar) : Baz(), Foo by bar {
|
||||
<caret>override fun bar() {
|
||||
super.bar()
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
interface Foo {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
interface Bar : Foo {
|
||||
fun bar()
|
||||
}
|
||||
|
||||
abstract class Baz : Bar {
|
||||
override fun bar() {
|
||||
println("baz")
|
||||
}
|
||||
}
|
||||
|
||||
class Boo(bar: Bar) : Baz(), Foo by bar {
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// PROBLEM: none
|
||||
// WITH_STDLIB
|
||||
|
||||
interface Base {
|
||||
val message: String
|
||||
|
||||
fun print() {
|
||||
println(message)
|
||||
}
|
||||
}
|
||||
|
||||
class BaseImpl : Base {
|
||||
override val message = "BaseImpl"
|
||||
}
|
||||
|
||||
class DerivedWithOverride(b: Base) : Base by b {
|
||||
override val message = "DerivedWithOverride"
|
||||
|
||||
<caret>override fun print() {
|
||||
super.print()
|
||||
}
|
||||
}
|
||||
|
||||
class DerivedNoOverride(b: Base) : Base by b {
|
||||
override val message = "DerivedNoOverride"
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
// PROBLEM: none
|
||||
// WITH_STDLIB
|
||||
interface Foo {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
open class C1 : Foo {
|
||||
override fun foo() {
|
||||
println("C1")
|
||||
}
|
||||
}
|
||||
|
||||
class C2(delegate: Foo) : C1(), Foo by delegate {
|
||||
<caret>override fun foo() {
|
||||
super.foo()
|
||||
}
|
||||
}
|
||||
|
||||
object FooImpl : Foo {
|
||||
override fun foo() {
|
||||
println("Impl")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user