FIR: introduce MANY_*_MEMBER_NOT_IMPLEMENTED diagnostic
This commit is contained in:
@@ -11,7 +11,7 @@ open class B {
|
|||||||
open fun baz() {}
|
open fun baz() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class C : A, B() {
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class C<!> : A, B() {
|
||||||
override fun foo() {
|
override fun foo() {
|
||||||
super.foo()
|
super.foo()
|
||||||
|
|
||||||
|
|||||||
+47
-5
@@ -16,11 +16,21 @@ import org.jetbrains.kotlin.fir.analysis.checkers.getContainingClass
|
|||||||
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
import org.jetbrains.kotlin.fir.analysis.checkers.modality
|
||||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_IMPL_MEMBER_NOT_IMPLEMENTED
|
||||||
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED
|
||||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn
|
||||||
|
import org.jetbrains.kotlin.fir.containingClass
|
||||||
import org.jetbrains.kotlin.fir.declarations.*
|
import org.jetbrains.kotlin.fir.declarations.*
|
||||||
import org.jetbrains.kotlin.fir.languageVersionSettings
|
import org.jetbrains.kotlin.fir.languageVersionSettings
|
||||||
|
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverrideFunctionSymbol
|
||||||
|
import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionOverridePropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.types.coneType
|
import org.jetbrains.kotlin.fir.types.coneType
|
||||||
import org.jetbrains.kotlin.fir.types.isNullableAny
|
import org.jetbrains.kotlin.fir.types.isNullableAny
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
@@ -40,6 +50,7 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
|||||||
val classScope = declaration.unsubstitutedScope(context)
|
val classScope = declaration.unsubstitutedScope(context)
|
||||||
|
|
||||||
val notImplementedSymbols = mutableListOf<FirCallableSymbol<*>>()
|
val notImplementedSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||||
|
val notImplementedIntersectionSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||||
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
|
val invisibleSymbols = mutableListOf<FirCallableSymbol<*>>()
|
||||||
val classPackage = declaration.symbol.classId.packageFqName
|
val classPackage = declaration.symbol.classId.packageFqName
|
||||||
|
|
||||||
@@ -64,6 +75,15 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
|||||||
for (name in classScope.getCallableNames()) {
|
for (name in classScope.getCallableNames()) {
|
||||||
classScope.processFunctionsByName(name) { namedFunctionSymbol ->
|
classScope.processFunctionsByName(name) { namedFunctionSymbol ->
|
||||||
val simpleFunction = namedFunctionSymbol.fir
|
val simpleFunction = namedFunctionSymbol.fir
|
||||||
|
if (namedFunctionSymbol is FirIntersectionOverrideFunctionSymbol) {
|
||||||
|
if (namedFunctionSymbol.intersections.count {
|
||||||
|
(it.fir as FirCallableMemberDeclaration).modality != Modality.ABSTRACT
|
||||||
|
} > 1 && simpleFunction.getContainingClass(context) === declaration
|
||||||
|
) {
|
||||||
|
notImplementedIntersectionSymbols += namedFunctionSymbol
|
||||||
|
return@processFunctionsByName
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!simpleFunction.shouldBeImplemented()) return@processFunctionsByName
|
if (!simpleFunction.shouldBeImplemented()) return@processFunctionsByName
|
||||||
if (declaration is FirRegularClass && declaration.isData && simpleFunction.matchesDataClassSyntheticMemberSignatures) {
|
if (declaration is FirRegularClass && declaration.isData && simpleFunction.matchesDataClassSyntheticMemberSignatures) {
|
||||||
return@processFunctionsByName
|
return@processFunctionsByName
|
||||||
@@ -79,6 +99,15 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
|||||||
}
|
}
|
||||||
classScope.processPropertiesByName(name) { propertySymbol ->
|
classScope.processPropertiesByName(name) { propertySymbol ->
|
||||||
val property = propertySymbol.fir as? FirProperty ?: return@processPropertiesByName
|
val property = propertySymbol.fir as? FirProperty ?: return@processPropertiesByName
|
||||||
|
if (propertySymbol is FirIntersectionOverridePropertySymbol) {
|
||||||
|
if (propertySymbol.intersections.count {
|
||||||
|
(it.fir as FirCallableMemberDeclaration).modality != Modality.ABSTRACT
|
||||||
|
} > 1 && property.getContainingClass(context) === declaration
|
||||||
|
) {
|
||||||
|
notImplementedIntersectionSymbols += propertySymbol
|
||||||
|
return@processPropertiesByName
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!property.shouldBeImplemented()) return@processPropertiesByName
|
if (!property.shouldBeImplemented()) return@processPropertiesByName
|
||||||
|
|
||||||
if (property.isInvisible()) {
|
if (property.isInvisible()) {
|
||||||
@@ -92,17 +121,30 @@ object FirNotImplementedOverrideChecker : FirClassChecker() {
|
|||||||
if (notImplementedSymbols.isNotEmpty()) {
|
if (notImplementedSymbols.isNotEmpty()) {
|
||||||
val notImplemented = notImplementedSymbols.first().fir
|
val notImplemented = notImplementedSymbols.first().fir
|
||||||
if (notImplemented.isFromInterfaceOrEnum(context)) {
|
if (notImplemented.isFromInterfaceOrEnum(context)) {
|
||||||
reporter.reportOn(source, FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED, declaration, notImplemented, context)
|
reporter.reportOn(source, ABSTRACT_MEMBER_NOT_IMPLEMENTED, declaration, notImplemented, context)
|
||||||
} else {
|
} else {
|
||||||
reporter.reportOn(source, FirErrors.ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED, declaration, notImplemented, context)
|
reporter.reportOn(source, ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED, declaration, notImplemented, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (invisibleSymbols.isNotEmpty()) {
|
if (invisibleSymbols.isNotEmpty()) {
|
||||||
val invisible = invisibleSymbols.first().fir
|
val invisible = invisibleSymbols.first().fir
|
||||||
if (context.session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitInvisibleAbstractMethodsInSuperclasses)) {
|
if (context.session.languageVersionSettings.supportsFeature(LanguageFeature.ProhibitInvisibleAbstractMethodsInSuperclasses)) {
|
||||||
reporter.reportOn(source, FirErrors.INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER, declaration, invisible, context)
|
reporter.reportOn(source, INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER, declaration, invisible, context)
|
||||||
} else {
|
} else {
|
||||||
reporter.reportOn(source, FirErrors.INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING, declaration, invisible, context)
|
reporter.reportOn(source, INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER_WARNING, declaration, invisible, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (notImplementedIntersectionSymbols.isNotEmpty()) {
|
||||||
|
val notImplementedIntersectionSymbol = notImplementedIntersectionSymbols.first()
|
||||||
|
val notImplementedIntersection = notImplementedIntersectionSymbol.fir
|
||||||
|
val intersections = (notImplementedIntersectionSymbol as FirIntersectionCallableSymbol).intersections
|
||||||
|
if (intersections.any {
|
||||||
|
(it.containingClass()?.toSymbol(context.session)?.fir as? FirRegularClass)?.classKind == ClassKind.CLASS
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
reporter.reportOn(source, MANY_IMPL_MEMBER_NOT_IMPLEMENTED, declaration, notImplementedIntersection, context)
|
||||||
|
} else {
|
||||||
|
reporter.reportOn(source, MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED, declaration, notImplementedIntersection, context)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,14 @@ open class FirNamedFunctionSymbol(
|
|||||||
callableId: CallableId,
|
callableId: CallableId,
|
||||||
) : FirFunctionSymbol<FirSimpleFunction>(callableId)
|
) : FirFunctionSymbol<FirSimpleFunction>(callableId)
|
||||||
|
|
||||||
|
interface FirIntersectionCallableSymbol {
|
||||||
|
val intersections: Collection<FirCallableSymbol<*>>
|
||||||
|
}
|
||||||
|
|
||||||
class FirIntersectionOverrideFunctionSymbol(
|
class FirIntersectionOverrideFunctionSymbol(
|
||||||
callableId: CallableId,
|
callableId: CallableId,
|
||||||
val intersections: Collection<FirCallableSymbol<*>>
|
override val intersections: Collection<FirCallableSymbol<*>>
|
||||||
) : FirNamedFunctionSymbol(callableId)
|
) : FirNamedFunctionSymbol(callableId), FirIntersectionCallableSymbol
|
||||||
|
|
||||||
class FirConstructorSymbol(
|
class FirConstructorSymbol(
|
||||||
callableId: CallableId
|
callableId: CallableId
|
||||||
|
|||||||
@@ -29,8 +29,8 @@ open class FirPropertySymbol(
|
|||||||
|
|
||||||
class FirIntersectionOverridePropertySymbol(
|
class FirIntersectionOverridePropertySymbol(
|
||||||
callableId: CallableId,
|
callableId: CallableId,
|
||||||
val intersections: Collection<FirCallableSymbol<*>>
|
override val intersections: Collection<FirCallableSymbol<*>>
|
||||||
) : FirPropertySymbol(callableId)
|
) : FirPropertySymbol(callableId), FirIntersectionCallableSymbol
|
||||||
|
|
||||||
class FirBackingFieldSymbol(callableId: CallableId) : FirVariableSymbol<FirProperty>(callableId)
|
class FirBackingFieldSymbol(callableId: CallableId) : FirVariableSymbol<FirProperty>(callableId)
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -44,5 +44,5 @@ interface GI : G {
|
|||||||
override fun a(@An arg: @An Int) {}
|
override fun a(@An arg: @An Int) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AG1(val a: A, val g: G) : A by a, G by g
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class AG1<!>(val a: A, val g: G) : A by a, G by g
|
||||||
class AG2() : AI, GI
|
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class AG2<!>() : AI, GI
|
||||||
|
|||||||
+6
-6
@@ -10,9 +10,9 @@ interface Three {
|
|||||||
public fun foo(): String
|
public fun foo(): String
|
||||||
}
|
}
|
||||||
|
|
||||||
class Test123(val v1: One, val v2: Two, val v3: Three) : One by v1, Two by v2, Three by v3 { }
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test123<!>(val v1: One, val v2: Two, val v3: Three) : One by v1, Two by v2, Three by v3 { }
|
||||||
class Test132(val v1: One, val v2: Two, val v3: Three) : One by v1, Three by v3, Two by v2 { }
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test132<!>(val v1: One, val v2: Two, val v3: Three) : One by v1, Three by v3, Two by v2 { }
|
||||||
class Test312(val v1: One, val v2: Two, val v3: Three) : Three by v3, One by v1, Two by v2 { }
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test312<!>(val v1: One, val v2: Two, val v3: Three) : Three by v3, One by v1, Two by v2 { }
|
||||||
class Test321(val v1: One, val v2: Two, val v3: Three) : Three by v3, Two by v2, One by v1 { }
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test321<!>(val v1: One, val v2: Two, val v3: Three) : Three by v3, Two by v2, One by v1 { }
|
||||||
class Test231(val v1: One, val v2: Two, val v3: Three) : Two by v2, Three by v3, One by v1 { }
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test231<!>(val v1: One, val v2: Two, val v3: Three) : Two by v2, Three by v3, One by v1 { }
|
||||||
class Test213(val v1: One, val v2: Two, val v3: Three) : Two by v2, One by v1, Three by v3 { }
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class Test213<!>(val v1: One, val v2: Two, val v3: Three) : Two by v2, One by v1, Three by v3 { }
|
||||||
|
|||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
// !DIAGNOSTICS: -CONFLICTING_JVM_DECLARATIONS
|
|
||||||
interface One {
|
|
||||||
public <!REDUNDANT_OPEN_IN_INTERFACE!>open<!> fun foo() : Int
|
|
||||||
private fun boo() = 10
|
|
||||||
}
|
|
||||||
interface Two {
|
|
||||||
public <!REDUNDANT_OPEN_IN_INTERFACE!>open<!> fun foo() : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
interface OneImpl : One {
|
|
||||||
public override fun foo() = 1
|
|
||||||
}
|
|
||||||
interface TwoImpl : Two {
|
|
||||||
public override fun foo() = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
class Test1() : TwoImpl, OneImpl {}
|
|
||||||
class Test2(a : One) : One by a, Two {}
|
|
||||||
class Test3(a : One, b : Two) : Two by b, One by a {}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// !DIAGNOSTICS: -CONFLICTING_JVM_DECLARATIONS
|
// !DIAGNOSTICS: -CONFLICTING_JVM_DECLARATIONS
|
||||||
interface One {
|
interface One {
|
||||||
public <!REDUNDANT_OPEN_IN_INTERFACE!>open<!> fun foo() : Int
|
public <!REDUNDANT_OPEN_IN_INTERFACE!>open<!> fun foo() : Int
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ class Delegate : Base {
|
|||||||
public open class MyClass : Base by Delegate()
|
public open class MyClass : Base by Delegate()
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
object : MyClass(), Base by Delegate() {
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>object<!> : MyClass(), Base by Delegate() {
|
||||||
}
|
}
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -77,7 +77,7 @@ open class NWEH: NotDeprecated, WarningDeprecated, ErrorDeprecated, HiddenDeprec
|
|||||||
|
|
||||||
class WE2: WE()
|
class WE2: WE()
|
||||||
|
|
||||||
class NWE2: WE(), NotDeprecated
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class NWE2<!>: WE(), NotDeprecated
|
||||||
|
|
||||||
class NWE3: WE(), NotDeprecated {
|
class NWE3: WE(), NotDeprecated {
|
||||||
override fun f() {
|
override fun f() {
|
||||||
|
|||||||
Vendored
-11
@@ -1,11 +0,0 @@
|
|||||||
interface A {
|
|
||||||
fun f(): String = "string"
|
|
||||||
}
|
|
||||||
|
|
||||||
open class B {
|
|
||||||
open fun f(): CharSequence = "charSequence"
|
|
||||||
}
|
|
||||||
|
|
||||||
class C : B(), A
|
|
||||||
|
|
||||||
val obj: A = object : B(), A {}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
interface A {
|
interface A {
|
||||||
fun f(): String = "string"
|
fun f(): String = "string"
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -10,4 +10,4 @@ object Impl : D, E {
|
|||||||
override fun foo() {}
|
override fun foo() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
val obj: D = object : D by Impl, E by Impl {}
|
val obj: D = <!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>object<!> : D by Impl, E by Impl {}
|
||||||
|
|||||||
-17
@@ -1,17 +0,0 @@
|
|||||||
package d
|
|
||||||
|
|
||||||
interface A {
|
|
||||||
fun foo() = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
interface B {
|
|
||||||
fun foo() = 2
|
|
||||||
}
|
|
||||||
|
|
||||||
open class C : A, B {}
|
|
||||||
|
|
||||||
interface E {
|
|
||||||
fun foo(): Int
|
|
||||||
}
|
|
||||||
|
|
||||||
class D : C() {}
|
|
||||||
+1
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
package d
|
package d
|
||||||
|
|
||||||
interface A {
|
interface A {
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
// KT-302 Report an error when inheriting many implementations of the same member
|
|
||||||
|
|
||||||
package kt302
|
|
||||||
|
|
||||||
interface A {
|
|
||||||
open fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface B {
|
|
||||||
open fun foo() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
class C : A, B {} //should be error here
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// FIR_IDENTICAL
|
||||||
// KT-302 Report an error when inheriting many implementations of the same member
|
// KT-302 Report an error when inheriting many implementations of the same member
|
||||||
|
|
||||||
package kt302
|
package kt302
|
||||||
|
|||||||
+1
-1
@@ -53,7 +53,7 @@ class ManySupers2: Foo2(), C {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ManySupers3: Bar2(), C {
|
<!MANY_IMPL_MEMBER_NOT_IMPLEMENTED!>class ManySupers3<!>: Bar2(), C {
|
||||||
fun foo() {
|
fun foo() {
|
||||||
super<Bar2>.test()
|
super<Bar2>.test()
|
||||||
super<C>.test()
|
super<C>.test()
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
interface AsyncVal { suspend fun getVal(): Int = 1}
|
interface AsyncVal { suspend fun getVal(): Int = 1}
|
||||||
interface SyncVal { fun getVal(): Int = 1 }
|
interface SyncVal { fun getVal(): Int = 1 }
|
||||||
|
|
||||||
class MixSuspend : AsyncVal, SyncVal {
|
<!MANY_INTERFACES_MEMBER_NOT_IMPLEMENTED!>class MixSuspend<!> : AsyncVal, SyncVal {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user