diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index de658286fca..072a510db5b 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -612,6 +612,11 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter>("baseDeclaration") } + val VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION by error(PositioningStrategy.DECLARATION_NAME) { + parameter>("delegateDeclaration") + parameter>("baseDeclaration") + } + val ABSTRACT_MEMBER_NOT_IMPLEMENTED by error(PositioningStrategy.DECLARATION_NAME) { parameter("classOrObject") parameter("missingDeclaration") diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index ce9de435345..7d1eade95a3 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -352,6 +352,7 @@ object FirErrors { val VAR_TYPE_MISMATCH_ON_INHERITANCE by error2, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) val RETURN_TYPE_MISMATCH_BY_DELEGATION by error2, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) val PROPERTY_TYPE_MISMATCH_BY_DELEGATION by error2, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) + val VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION by error2, FirCallableDeclaration<*>>(SourceElementPositioningStrategies.DECLARATION_NAME) val ABSTRACT_MEMBER_NOT_IMPLEMENTED by error2(SourceElementPositioningStrategies.DECLARATION_NAME) val ABSTRACT_CLASS_MEMBER_NOT_IMPLEMENTED by error2(SourceElementPositioningStrategies.DECLARATION_NAME) val INVISIBLE_ABSTRACT_MEMBER_FROM_SUPER by error2(SourceElementPositioningStrategies.DECLARATION_NAME) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplementationMismatchChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplementationMismatchChecker.kt index 90fddf7fb9c..0f908b57c0e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplementationMismatchChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirImplementationMismatchChecker.kt @@ -15,11 +15,11 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.impl.deduplicating import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.scopes.impl.delegatedWrapperData -import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol -import org.jetbrains.kotlin.fir.symbols.impl.FirIntersectionCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.* import org.jetbrains.kotlin.fir.typeContext import org.jetbrains.kotlin.fir.types.ConeKotlinErrorType import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.ConeTypeCheckerContext import org.jetbrains.kotlin.fir.types.coneType import org.jetbrains.kotlin.types.AbstractTypeChecker @@ -40,6 +40,22 @@ object FirImplementationMismatchChecker : FirClassChecker() { val classScope = declaration.unsubstitutedScope(context) val dedupReporter = reporter.deduplicating() + for (name in classScope.getCallableNames()) { + classScope.processFunctionsByName(name) { checkInheritanceClash(declaration, context, dedupReporter, typeCheckerContext, it) } + classScope.processPropertiesByName(name) { + checkInheritanceClash(declaration, context, dedupReporter, typeCheckerContext, it) + checkValOverrideVar(declaration, context, dedupReporter, it) + } + } + } + + private fun checkInheritanceClash( + containingClass: FirClass<*>, + context: CheckerContext, + reporter: DiagnosticReporter, + typeCheckerContext: ConeTypeCheckerContext, + symbol: FirCallableSymbol<*> + ) { fun reportTypeMismatch(member1: FirCallableDeclaration<*>, member2: FirCallableDeclaration<*>, isDelegation: Boolean) { val error = if (member1 is FirProperty && member2 is FirProperty) { if (member1.isVar || member2.isVar) { @@ -52,7 +68,7 @@ object FirImplementationMismatchChecker : FirClassChecker() { if (isDelegation) FirErrors.RETURN_TYPE_MISMATCH_BY_DELEGATION else FirErrors.RETURN_TYPE_MISMATCH_ON_INHERITANCE } - dedupReporter.reportOn(source, error, member1, member2, context) + reporter.reportOn(containingClass.source, error, member1, member2, context) } fun canOverride( @@ -64,61 +80,78 @@ object FirImplementationMismatchChecker : FirClassChecker() { else AbstractTypeChecker.isSubtypeOf(typeCheckerContext, inheritedType, baseType) - fun checkSymbol(symbol: FirCallableSymbol<*>) { - if (symbol.callableId.classId != declaration.classId) return - if (symbol !is FirIntersectionCallableSymbol) return - val withTypes = symbol.intersections.map { - it.fir to context.returnTypeCalculator.tryCalculateReturnType(it.fir).coneType + if (symbol.callableId.classId != containingClass.classId) return + if (symbol !is FirIntersectionCallableSymbol) return + val withTypes = symbol.intersections.map { + it.fir to context.returnTypeCalculator.tryCalculateReturnType(it.fir).coneType + } + + if (withTypes.any { it.second is ConeKotlinErrorType }) return + + var delegation: FirCallableDeclaration<*>? = null + val implementations = mutableListOf>() + + for (intSymbol in symbol.intersections) { + val fir = intSymbol.fir + if (fir.delegatedWrapperData?.containingClass?.classId == containingClass.classId) { + delegation = fir + break } - - if (withTypes.any { it.second is ConeKotlinErrorType }) return - - var delegation: FirCallableDeclaration<*>? = null - val implementations = mutableListOf>() - - for (intSymbol in symbol.intersections) { - val fir = intSymbol.fir - if (fir.delegatedWrapperData?.containingClass?.classId == declaration.classId) { - delegation = fir - break - } - if (!(fir as FirCallableMemberDeclaration<*>).isAbstract) { - implementations.add(fir) - } - } - - run { - var clash: Pair, FirCallableDeclaration<*>>? = null - val compatible = withTypes.any { (m1, type1) -> - withTypes.all { (m2, type2) -> - val result = canOverride(m2, type1, type2) - if (!result && clash == null && !canOverride(m1, type2, type1)) { - clash = m1 to m2 - } - result - } - } - clash?.takeIf { !compatible }?.let { (m1, m2) -> - reportTypeMismatch(m1, m2, false) - return@checkSymbol - } - } - - if (delegation != null || implementations.isNotEmpty()) { - //if there are more than one implementation we report nothing because it will be reported differently - val implementationMember = delegation ?: implementations.singleOrNull() ?: return - val methodType = context.returnTypeCalculator.tryCalculateReturnType(implementationMember).coneType - val (conflict, _) = withTypes.find { (baseMember, baseType) -> - !canOverride(baseMember, methodType, baseType) - } ?: return - - reportTypeMismatch(implementationMember, conflict, delegation != null) + if (!(fir as FirCallableMemberDeclaration<*>).isAbstract) { + implementations.add(fir) } } - for (name in classScope.getCallableNames()) { - classScope.processFunctionsByName(name, ::checkSymbol) - classScope.processPropertiesByName(name, ::checkSymbol) + run { + var clash: Pair, FirCallableDeclaration<*>>? = null + val compatible = withTypes.any { (m1, type1) -> + withTypes.all { (m2, type2) -> + val result = canOverride(m2, type1, type2) + if (!result && clash == null && !canOverride(m1, type2, type1)) { + clash = m1 to m2 + } + result + } + } + clash?.takeIf { !compatible }?.let { (m1, m2) -> + reportTypeMismatch(m1, m2, false) + return@checkInheritanceClash + } + } + + if (delegation != null || implementations.isNotEmpty()) { + //if there are more than one implementation we report nothing because it will be reported differently + val implementationMember = delegation ?: implementations.singleOrNull() ?: return + val methodType = context.returnTypeCalculator.tryCalculateReturnType(implementationMember).coneType + val (conflict, _) = withTypes.find { (baseMember, baseType) -> + !canOverride(baseMember, methodType, baseType) + } ?: return + + reportTypeMismatch(implementationMember, conflict, delegation != null) + } + } + + private fun checkValOverrideVar( + containingClass: FirClass<*>, + context: CheckerContext, + reporter: DiagnosticReporter, + symbol: FirVariableSymbol<*> + ) { + if (symbol.callableId.classId != containingClass.classId) return + if (symbol !is FirIntersectionOverridePropertySymbol) return + + val (delegates, others) = symbol.intersections.partition { + val fir = it.fir as? FirProperty ?: return@partition false + fir.isVal && fir.delegatedWrapperData?.containingClass?.classId == containingClass.classId + } + + val delegatedVal = delegates.singleOrNull() ?: return + val baseVar = others.find { + it is FirPropertySymbol && it.fir.isVar + } + + if (baseVar != null) { + reporter.reportOn(containingClass.source, FirErrors.VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION, delegatedVal.fir, baseVar.fir, context) } } } \ No newline at end of file diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index dc8ee5e71e1..0494ab590a2 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -386,6 +386,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_NEVER_RE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_ANNOTATION_PARAMETER import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_OVERRIDDEN_BY_VAL +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_TYPE_MISMATCH_ON_INHERITANCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_TYPE_MISMATCH_ON_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VIRTUAL_MEMBER_HIDDEN @@ -975,6 +976,13 @@ class FirDefaultErrorMessages { NAME ) + map.put( + VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION, + "Val-property ''{0}'' implicitly overrides a var-property ''{1}'' by delegation", + NAME, + NAME + ) + // Redeclarations map.put(MANY_COMPANION_OBJECTS, "Only one companion object is allowed per class") map.put(CONFLICTING_OVERLOADS, "Conflicting overloads: {0}", SYMBOLS) // * diff --git a/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.fir.kt b/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.fir.kt deleted file mode 100644 index 364ec0c5290..00000000000 --- a/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.fir.kt +++ /dev/null @@ -1,55 +0,0 @@ -open class Final { - fun foo() {} - val bar: Int = 0 - var qux: Int = 0 -} - -open class Derived : Final() - -interface IFoo { - fun foo() -} - -class CFoo : IFoo { - override fun foo() {} -} - -interface IBar { - val bar: Int -} - -class CBar : IBar { - override val bar: Int get() = 0 -} - -interface IQux { - val qux: Int -} - -class CQux : IQux { - override val qux: Int get() = 0 -} - -interface IBarT { - val bar: T -} - -class CBarT : IBarT { - override val bar: T get() = null!! -} - -class Test1 : Final(), IFoo by CFoo() - -class Test2 : Final(), IBar by CBar() - -class Test3 : Final(), IQux by CQux() - -class Test4 : Derived(), IFoo by CFoo() - -class Test5 : Derived(), IBar by CBar() - -class Test6 : Derived(), IQux by CQux() - -class Test7 : Final(), IBarT by CBarT() - -class Test8 : Final(), IBarT by CBar() diff --git a/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.kt b/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.kt index 5a99e5f74f4..36416cea52c 100644 --- a/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.kt +++ b/compiler/testData/diagnostics/tests/delegation/clashes/finalMemberOverridden.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL open class Final { fun foo() {} val bar: Int = 0 diff --git a/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.fir.kt b/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.fir.kt deleted file mode 100644 index 77def9382cc..00000000000 --- a/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.fir.kt +++ /dev/null @@ -1,29 +0,0 @@ -interface IVar { - var foo: Int -} - -interface IDerived : IVar - -interface IVal { - val foo: Int -} - -class CVal : IVal { - override val foo: Int get() = 42 -} - -interface IValT { - val foo: T -} - -class CValT : IValT { - override val foo: T get() = null!! -} - -abstract class Test1 : IVar, IVal by CVal() - -abstract class Test2 : IVar, IValT by CValT() - -abstract class Test3 : IDerived, IVal by CVal() - -abstract class Test4 : IDerived, IValT by CValT() diff --git a/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.kt b/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.kt index 910d0b369e2..a868064bb21 100644 --- a/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.kt +++ b/compiler/testData/diagnostics/tests/delegation/clashes/varOverriddenByVal.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL interface IVar { var foo: Int } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 57e86d87350..6b186432e0f 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -1692,6 +1692,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.VAR_OVERRIDDEN_BY_VAL_BY_DELEGATION) { firDiagnostic -> + VarOverriddenByValByDelegationImpl( + firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.a as FirCallableDeclaration), + firSymbolBuilder.callableBuilder.buildCallableSymbol(firDiagnostic.b as FirCallableDeclaration), + firDiagnostic as FirPsiDiagnostic<*>, + token, + ) + } add(FirErrors.ABSTRACT_MEMBER_NOT_IMPLEMENTED) { firDiagnostic -> AbstractMemberNotImplementedImpl( firSymbolBuilder.classifierBuilder.buildClassLikeSymbol(firDiagnostic.a), diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index b53fea508bf..0abc02d2311 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -1201,6 +1201,12 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { abstract val baseDeclaration: KtCallableSymbol } + abstract class VarOverriddenByValByDelegation : KtFirDiagnostic() { + override val diagnosticClass get() = VarOverriddenByValByDelegation::class + abstract val delegateDeclaration: KtCallableSymbol + abstract val baseDeclaration: KtCallableSymbol + } + abstract class AbstractMemberNotImplemented : KtFirDiagnostic() { override val diagnosticClass get() = AbstractMemberNotImplemented::class abstract val classOrObject: KtClassLikeSymbol diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index b96d76cfbe2..ef2f850d24c 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -1928,6 +1928,15 @@ internal class PropertyTypeMismatchByDelegationImpl( override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) } +internal class VarOverriddenByValByDelegationImpl( + override val delegateDeclaration: KtCallableSymbol, + override val baseDeclaration: KtCallableSymbol, + firDiagnostic: FirPsiDiagnostic<*>, + override val token: ValidityToken, +) : KtFirDiagnostic.VarOverriddenByValByDelegation(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic<*> by weakRef(firDiagnostic) +} + internal class AbstractMemberNotImplementedImpl( override val classOrObject: KtClassLikeSymbol, override val missingDeclaration: KtCallableSymbol,