diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt new file mode 100644 index 00000000000..1ad1c48926d --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt @@ -0,0 +1,35 @@ +open class A { + open var test: Number = 10 +} + +open class B : A { + override var test: Double = 20.0 +} + +class C() : A() { + override var test: String = "Test" +} + +open class D() : B() { + override var test: Char = '\n' +} + +class E(val value: T) : B() { + override var test: T = value +} + +open class F(val value: T) { + open var rest: T = value +} + +class G(val balue: E) : F(balue) { + override var rest: E = balue +} + +class H(val balue: E) : F(balue) { + override var rest: E = balue // no report because of INAPPLICABLE_CANDIDATE +} + +class M(val balue: E) : F(3.14) { + override var rest: E = balue +} diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.txt new file mode 100644 index 00000000000..10667bdb45b --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.txt @@ -0,0 +1,106 @@ +FILE: propertyTypeMismatchOnOverride.kt + public open class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public open var test: R|kotlin/Number| = Int(10) + public get(): R|kotlin/Number| + public set(value: R|kotlin/Number|): R|kotlin/Unit| + + } + public open class B : R|A| { + public constructor(): R|B| { + super() + } + + public open override var test: R|kotlin/Double| = Double(20.0) + public get(): R|kotlin/Double| + public set(value: R|kotlin/Double|): R|kotlin/Unit| + + } + public final class C : R|A| { + public constructor(): R|C| { + super() + } + + public final override var test: R|kotlin/String| = String(Test) + public get(): R|kotlin/String| + public set(value: R|kotlin/String|): R|kotlin/Unit| + + } + public open class D : R|B| { + public constructor(): R|D| { + super() + } + + public open override var test: R|kotlin/Char| = Char(10) + public get(): R|kotlin/Char| + public set(value: R|kotlin/Char|): R|kotlin/Unit| + + } + public final class E : R|B| { + public constructor(value: R|T|): R|E| { + super() + } + + public final val value: R|T| = R|/value| + public get(): R|T| + + public final override var test: R|T| = R|/value| + public get(): R|T| + public set(value: R|T|): R|kotlin/Unit| + + } + public open class F : R|kotlin/Any| { + public constructor(value: R|T|): R|F| { + super() + } + + public final val value: R|T| = R|/value| + public get(): R|T| + + public open var rest: R|T| = R|/value| + public get(): R|T| + public set(value: R|T|): R|kotlin/Unit| + + } + public final class G : R|F| { + public constructor(balue: R|E|): R|G| { + super|>(R|/balue|) + } + + public final val balue: R|E| = R|/balue| + public get(): R|E| + + public final override var rest: R|E| = R|/balue| + public get(): R|E| + public set(value: R|E|): R|kotlin/Unit| + + } + public final class H : R|F| { + public constructor(balue: R|E|): R|H| { + super|>(R|/balue|) + } + + public final val balue: R|E| = R|/balue| + public get(): R|E| + + public final override var rest: R|E| = R|/balue| + public get(): R|E| + public set(value: R|E|): R|kotlin/Unit| + + } + public final class M : R|F| { + public constructor(balue: R|E|): R|M| { + super|>(Double(3.14)) + } + + public final val balue: R|E| = R|/balue| + public get(): R|E| + + public final override var rest: R|E| = R|/balue| + public get(): R|E| + public set(value: R|E|): R|kotlin/Unit| + + } diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt new file mode 100644 index 00000000000..d5d6c933864 --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt @@ -0,0 +1,69 @@ +open class A { + open fun test(): Number = 10 +} + +open class B : A { + override fun test(): Double = 20.0 + fun test(x: Int) = x +} + +class C() : A() { + override fun test(): String = "Test" +} + +open class D() : B() { + override fun test(): Char = '\n' +} + +class E(val value: T) : B() { + override fun test(): T = value +} + +open class F(val value: T) { + open fun rest(): T = value +} + +class G(val balue: E) : F(balue) { + override fun rest(): E = balue +} + +class H(val balue: E) : F(balue) { + override fun rest(): E = balue // no report because of INAPPLICABLE_CANDIDATE +} + +class M(val balue: E) : F(3.14) { + override fun rest(): E = balue +} + +open class X +open class Y() : X() +open class Z() : Y() +open class W() : Z() + +open class V { + open fun hello(): X = X() +} + +open class L() : V() + +open class Q() : L() + +open class S() : Q() { + override fun hello(): W = W() +} + +open class J() : S() { + override fun hello(): Z = Z() +} + +open class Base { + open fun kek(): Z = Z() +} + +open class GoodDerrived : Base() { + override fun kek(): W = W() +} + +open class BadDerrived : Base() { + override fun kek(): String = "test" +} \ No newline at end of file diff --git a/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.txt b/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.txt new file mode 100644 index 00000000000..17a10ed869c --- /dev/null +++ b/compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.txt @@ -0,0 +1,206 @@ +FILE: returnTypeMismatchOnOverride.kt + public open class A : R|kotlin/Any| { + public constructor(): R|A| { + super() + } + + public open fun test(): R|kotlin/Number| { + ^test Int(10) + } + + } + public open class B : R|A| { + public constructor(): R|B| { + super() + } + + public open override fun test(): R|kotlin/Double| { + ^test Double(20.0) + } + + public final fun test(x: R|kotlin/Int|): R|kotlin/Int| { + ^test R|/x| + } + + } + public final class C : R|A| { + public constructor(): R|C| { + super() + } + + public final override fun test(): R|kotlin/String| { + ^test String(Test) + } + + } + public open class D : R|B| { + public constructor(): R|D| { + super() + } + + public open override fun test(): R|kotlin/Char| { + ^test Char(10) + } + + } + public final class E : R|B| { + public constructor(value: R|T|): R|E| { + super() + } + + public final val value: R|T| = R|/value| + public get(): R|T| + + public final override fun test(): R|T| { + ^test this@R|/E|.R|/E.value| + } + + } + public open class F : R|kotlin/Any| { + public constructor(value: R|T|): R|F| { + super() + } + + public final val value: R|T| = R|/value| + public get(): R|T| + + public open fun rest(): R|T| { + ^rest this@R|/F|.R|/F.value| + } + + } + public final class G : R|F| { + public constructor(balue: R|E|): R|G| { + super|>(R|/balue|) + } + + public final val balue: R|E| = R|/balue| + public get(): R|E| + + public final override fun rest(): R|E| { + ^rest this@R|/G|.R|/G.balue| + } + + } + public final class H : R|F| { + public constructor(balue: R|E|): R|H| { + super|>(R|/balue|) + } + + public final val balue: R|E| = R|/balue| + public get(): R|E| + + public final override fun rest(): R|E| { + ^rest this@R|/H|.R|/H.balue| + } + + } + public final class M : R|F| { + public constructor(balue: R|E|): R|M| { + super|>(Double(3.14)) + } + + public final val balue: R|E| = R|/balue| + public get(): R|E| + + public final override fun rest(): R|E| { + ^rest this@R|/M|.R|/M.balue| + } + + } + public open class X : R|kotlin/Any| { + public constructor(): R|X| { + super() + } + + } + public open class Y : R|X| { + public constructor(): R|Y| { + super() + } + + } + public open class Z : R|Y| { + public constructor(): R|Z| { + super() + } + + } + public open class W : R|Z| { + public constructor(): R|W| { + super() + } + + } + public open class V : R|kotlin/Any| { + public constructor(): R|V| { + super() + } + + public open fun hello(): R|X| { + ^hello R|/X.X|() + } + + } + public open class L : R|V| { + public constructor(): R|L| { + super() + } + + } + public open class Q : R|L| { + public constructor(): R|Q| { + super() + } + + } + public open class S : R|Q| { + public constructor(): R|S| { + super() + } + + public open override fun hello(): R|W| { + ^hello R|/W.W|() + } + + } + public open class J : R|S| { + public constructor(): R|J| { + super() + } + + public open override fun hello(): R|Z| { + ^hello R|/Z.Z|() + } + + } + public open class Base : R|kotlin/Any| { + public constructor(): R|Base| { + super() + } + + public open fun kek(): R|Z| { + ^kek R|/Z.Z|() + } + + } + public open class GoodDerrived : R|Base| { + public constructor(): R|GoodDerrived| { + super|>() + } + + public open override fun kek(): R|W| { + ^kek R|/W.W|() + } + + } + public open class BadDerrived : R|Base| { + public constructor(): R|BadDerrived| { + super|>() + } + + public open override fun kek(): R|kotlin/String| { + ^kek String(test) + } + + } diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java index 89e20d1f7cd..9f9947519fb 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsTestGenerated.java @@ -1021,6 +1021,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/projectionsOnNonClassTypeArguments.kt"); } + @TestMetadata("propertyTypeMismatchOnOverride.kt") + public void testPropertyTypeMismatchOnOverride() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt"); + } + @TestMetadata("qualifiedSupertypeExtendedByOtherSupertype.kt") public void testQualifiedSupertypeExtendedByOtherSupertype() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/qualifiedSupertypeExtendedByOtherSupertype.kt"); @@ -1046,6 +1051,11 @@ public class FirDiagnosticsTestGenerated extends AbstractFirDiagnosticsTest { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/sealedSupertype.kt"); } + @TestMetadata("returnTypeMismatchOnOverride.kt") + public void testReturnTypeMismatchOnOverride() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt"); + } + @TestMetadata("superIsNotAnExpression.kt") public void testSuperIsNotAnExpression() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superIsNotAnExpression.kt"); diff --git a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java index 694be47c9e0..1bd1eb87dd7 100644 --- a/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests/org/jetbrains/kotlin/fir/FirDiagnosticsWithLightTreeTestGenerated.java @@ -1021,6 +1021,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/projectionsOnNonClassTypeArguments.kt"); } + @TestMetadata("propertyTypeMismatchOnOverride.kt") + public void testPropertyTypeMismatchOnOverride() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/propertyTypeMismatchOnOverride.kt"); + } + @TestMetadata("qualifiedSupertypeExtendedByOtherSupertype.kt") public void testQualifiedSupertypeExtendedByOtherSupertype() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/qualifiedSupertypeExtendedByOtherSupertype.kt"); @@ -1046,6 +1051,11 @@ public class FirDiagnosticsWithLightTreeTestGenerated extends AbstractFirDiagnos runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/sealedSupertype.kt"); } + @TestMetadata("returnTypeMismatchOnOverride.kt") + public void testReturnTypeMismatchOnOverride() throws Exception { + runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/returnTypeMismatchOnOverride.kt"); + } + @TestMetadata("superIsNotAnExpression.kt") public void testSuperIsNotAnExpression() throws Exception { runTest("compiler/fir/analysis-tests/testData/resolve/diagnostics/superIsNotAnExpression.kt"); diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt index 5b4ddeb8194..78c4b3cffc7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/DefaultDeclarationCheckers.kt @@ -40,6 +40,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirEnumClassSimpleChecker, FirSealedSupertypeChecker, FirInapplicableLateinitChecker, + FirTypeMismatchOnOverrideChecker, ) override val regularClassCheckers: List = listOf( diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeMismatchOnOverrideChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeMismatchOnOverrideChecker.kt new file mode 100644 index 00000000000..fcd07b8b562 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirTypeMismatchOnOverrideChecker.kt @@ -0,0 +1,242 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.fir.analysis.checkers.declaration + +import org.jetbrains.kotlin.fir.FirSourceElement +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.resolve.substitution.ConeSubstitutor +import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.scopes.* +import org.jetbrains.kotlin.fir.scopes.impl.toConeType +import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.typeContext +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.AbstractTypeCheckerContext +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +object FirTypeMismatchOnOverrideChecker : FirMemberDeclarationChecker() { + override fun check(declaration: FirMemberDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (declaration !is FirRegularClass) { + return + } + + val typeCheckerContext = context.session.typeContext.newBaseTypeCheckerContext( + errorTypesEqualToAnything = false, + stubTypesEqualToAnything = false + ) + + val firTypeScope = declaration.unsubstitutedScope( + context.sessionHolder.session, + context.sessionHolder.scopeSession + ) + + for (it in declaration.declarations) { + when (it) { + is FirSimpleFunction -> checkFunction(declaration, it, context, reporter, typeCheckerContext, firTypeScope) + is FirProperty -> checkProperty(declaration, it, context, reporter, typeCheckerContext, firTypeScope) + } + } + } + + private fun FirTypeScope.getDirectOverridesOf(function: FirSimpleFunction): List> { + val overriddenFunctions = mutableSetOf>() + + processFunctionsByName(function.name) {} + processDirectlyOverriddenFunctions(function.symbol) { + overriddenFunctions.add(it) + ProcessorAction.NEXT + } + + return overriddenFunctions.toList() + } + + private fun FirTypeScope.getDirectOverridesOf(property: FirProperty): List { + val overriddenProperties = mutableSetOf() + + processPropertiesByName(property.name) {} + processDirectlyOverriddenProperties(property.symbol) { + overriddenProperties.add(it) + ProcessorAction.NEXT + } + + return overriddenProperties.toList() + } + + private fun FirRegularClass.substituteAllSupertypeParameters(context: CheckerContext): ConeSubstitutor { + val allSupertypesMapping = mutableMapOf() + val remapper = mutableMapOf() + + fun FirRegularClass.collectSupertypes() { + for (it in superTypeRefs) { + val fir = it.coneType.safeAs()?.lookupTag?.toSymbol(context.session) + ?.fir.safeAs() + ?: continue + + if (it.coneType.typeArguments.size != fir.typeParameters.size) { + continue + } + + for (that in fir.typeParameters.indices) { + val proto = fir.typeParameters[that].symbol + val actual = it.coneType.typeArguments[that].safeAs() + ?.lowerBoundIfFlexible() + ?: continue + val value = remapper.getOrDefault(actual, actual) + remapper[proto.fir.toConeType()] = value + allSupertypesMapping[proto] = value + } + + fir.collectSupertypes() + } + } + + collectSupertypes() + return substitutorByMap(allSupertypesMapping) + } + + private fun ConeKotlinType.substituteAllTypeParameters( + overrideDeclaration: FirCallableMemberDeclaration<*>, + baseDeclarationSymbol: FirCallableSymbol<*>, + ): ConeKotlinType { + if (overrideDeclaration.typeParameters.isEmpty()) { + return this + } + + val parametersOwner = baseDeclarationSymbol.fir.safeAs() + ?: return this + + val map = mutableMapOf() + + overrideDeclaration.typeParameters.zip(parametersOwner.typeParameters).forEach { (to, from) -> + map[from.symbol] = to.toConeType() + } + + return substitutorByMap(map).substituteOrSelf(this) + } + + private fun ConeKotlinType.substituteOrSelf(substitutor: ConeSubstitutor) = substitutor.substituteOrSelf(this) + + private fun FirCallableMemberDeclaration<*>.checkReturnType( + regularClass: FirRegularClass, + overriddenSymbols: List>, + context: CheckerContext, + typeCheckerContext: AbstractTypeCheckerContext, + ): FirMemberDeclaration? { + val returnType = returnTypeRef.safeAs()?.type + ?: return null + + val bounds = overriddenSymbols.map { it.fir.returnTypeRef.coneType.upperBoundIfFlexible() } + val supertypesParameterSubstitutor = regularClass.substituteAllSupertypeParameters(context) + + for (it in bounds.indices) { + val restriction = bounds[it] + .substituteAllTypeParameters(this, overriddenSymbols[it]) + .substituteOrSelf(supertypesParameterSubstitutor) + + if (!AbstractTypeChecker.isSubtypeOf(typeCheckerContext, returnType, restriction)) { + return overriddenSymbols[it].fir.safeAs() + } + } + + return null + } + + private fun checkFunction( + regularClass: FirRegularClass, + function: FirSimpleFunction, + context: CheckerContext, + reporter: DiagnosticReporter, + typeCheckerContext: AbstractTypeCheckerContext, + firTypeScope: FirTypeScope + ) { + if (!function.isOverride) { + return + } + + val overriddenFunctionSymbols = firTypeScope.getDirectOverridesOf(function) + + if (overriddenFunctionSymbols.isEmpty()) { + return + } + + val restriction = function.checkReturnType( + regularClass = regularClass, + overriddenSymbols = overriddenFunctionSymbols, + context = context, + typeCheckerContext = typeCheckerContext + ) + + restriction?.let { + reporter.reportMismatchOnFunction( + function.returnTypeRef.source, + function.returnTypeRef.coneType.toString(), + it + ) + } + } + + private fun checkProperty( + regularClass: FirRegularClass, + property: FirProperty, + context: CheckerContext, + reporter: DiagnosticReporter, + typeCheckerContext: AbstractTypeCheckerContext, + firTypeScope: FirTypeScope + ) { + if (!property.isOverride) { + return + } + + val overriddenPropertySymbols = firTypeScope.getDirectOverridesOf(property) + + if (overriddenPropertySymbols.isEmpty()) { + return + } + + val restriction = property.checkReturnType( + regularClass = regularClass, + overriddenSymbols = overriddenPropertySymbols, + context = context, + typeCheckerContext = typeCheckerContext + ) + + restriction?.let { + if (property.isVar) { + reporter.reportMismatchOnVariable( + property.returnTypeRef.source, + property.returnTypeRef.coneType.toString(), + it + ) + } else { + reporter.reportMismatchOnProperty( + property.returnTypeRef.source, + property.returnTypeRef.coneType.toString(), + it + ) + } + } + } + + private fun DiagnosticReporter.reportMismatchOnFunction(source: FirSourceElement?, type: String, declaration: FirMemberDeclaration) { + source?.let { report(FirErrors.RETURN_TYPE_MISMATCH_ON_OVERRIDE.on(it, type, declaration)) } + } + + private fun DiagnosticReporter.reportMismatchOnProperty(source: FirSourceElement?, type: String, declaration: FirMemberDeclaration) { + source?.let { report(FirErrors.PROPERTY_TYPE_MISMATCH_ON_OVERRIDE.on(it, type, declaration)) } + } + + private fun DiagnosticReporter.reportMismatchOnVariable(source: FirSourceElement?, type: String, declaration: FirMemberDeclaration) { + source?.let { report(FirErrors.VAR_TYPE_MISMATCH_ON_OVERRIDE.on(it, type, declaration)) } + } +} \ 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 250ceab62cb..61948e96430 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 @@ -75,6 +75,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.OTHER_ERROR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_DELEGATION_CALL_EXPECTED import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PRIMARY_CONSTRUCTOR_REQUIRED_FOR_DATA_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROJECTION_ON_NON_CLASS_TYPE_ARGUMENT +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.PROPERTY_TYPE_MISMATCH_ON_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.QUALIFIED_SUPERTYPE_EXTENDED_BY_OTHER_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_IMPLICIT_TYPES import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RECURSION_IN_SUPERTYPES @@ -92,6 +93,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_NOT_ALLOWE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_CLASS_CONSTRUCTOR_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SEALED_SUPERTYPE_IN_LOCAL_CLASS +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.RETURN_TYPE_MISMATCH_ON_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERCLASS_NOT_ACCESSIBLE_FROM_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_IN_INTERFACE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.SUPERTYPE_INITIALIZED_WITHOUT_PRIMARY_CONSTRUCTOR @@ -111,6 +113,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VARIABLE_EXPECTED 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.WRONG_INVOCATION_KIND +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.VAR_TYPE_MISMATCH_ON_OVERRIDE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.WRONG_NUMBER_OF_TYPE_ARGUMENTS @Suppress("unused") @@ -275,6 +278,24 @@ class FirDefaultErrorMessages : DefaultErrorMessages.Extension { VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED, "Variance annotations are only allowed for type parameters of classes and interfaces" ) + map.put( + RETURN_TYPE_MISMATCH_ON_OVERRIDE, + "Return type of ''{0}'' is not a subtype of the return type of the overridden member ''{1}''", + TO_STRING, + DECLARATION_NAME + ) // # + map.put( + PROPERTY_TYPE_MISMATCH_ON_OVERRIDE, + "Type of ''{0}'' is not a subtype of the overridden property ''{1}''", + TO_STRING, + DECLARATION_NAME + ) // # + map.put( + VAR_TYPE_MISMATCH_ON_OVERRIDE, + "Type of ''{0}'' doesn''t match the type of the overridden var-property ''{1}''", + TO_STRING, + DECLARATION_NAME + ) // # // Redeclarations map.put(MANY_COMPANION_OBJECTS, "Only one companion object is allowed per class") diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 0ea58e062b5..7e5833f27d9 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -130,6 +130,9 @@ object FirErrors { val TYPE_PARAMETERS_IN_ENUM by error0() val CONFLICTING_PROJECTION by error1() val VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED by error0() + val RETURN_TYPE_MISMATCH_ON_OVERRIDE by error2() + val PROPERTY_TYPE_MISMATCH_ON_OVERRIDE by error2() + val VAR_TYPE_MISMATCH_ON_OVERRIDE by error2() // Redeclarations val MANY_COMPANION_OBJECTS by error0() diff --git a/compiler/testData/diagnostics/tests/CovariantOverrideType.fir.kt b/compiler/testData/diagnostics/tests/CovariantOverrideType.fir.kt index bbfd7ba22a3..fa0be88d003 100644 --- a/compiler/testData/diagnostics/tests/CovariantOverrideType.fir.kt +++ b/compiler/testData/diagnostics/tests/CovariantOverrideType.fir.kt @@ -13,14 +13,14 @@ interface A { abstract class B() : A { override fun foo() { } - override fun foo2() : Unit { + override fun foo2() : Unit { } - override val a : Double = 1.toDouble() + override val a : Double = 1.toDouble() override val a1 = 1.toDouble() - abstract override fun g() : Int - abstract override fun g1() : List + abstract override fun g() : Int + abstract override fun g1() : List - abstract override val g : Iterator + abstract override val g : Iterator } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.fir.kt b/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.fir.kt deleted file mode 100644 index 79f4f16d1bf..00000000000 --- a/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -abstract class A { - abstract var x: Int; - abstract fun foo() : Int; -} - -abstract class C : A() { - override abstract var x: String = ? - override abstract fun foo(): String = ? -} diff --git a/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.kt b/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.kt index 2c935f8af4c..a639ed29813 100644 --- a/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.kt +++ b/compiler/testData/diagnostics/tests/TypeMismatchOnOverrideWithSyntaxErrors.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL abstract class A { abstract var x: Int; abstract fun foo() : Int; diff --git a/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverride.fir.kt b/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverride.fir.kt index 2a3d5566e23..a208725096c 100644 --- a/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverride.fir.kt +++ b/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverride.fir.kt @@ -15,12 +15,12 @@ interface A { @An interface B : A { - override val p1: Int + override val p1: Int @An override val p2: @An String - override fun test(arg: String): Int + override fun test(arg: String): Int } interface C : A { - override var p2: Int + override var p2: Int } \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverrideJavaNullable.fir.kt b/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverrideJavaNullable.fir.kt index e34b5de84f1..2641a81ae86 100644 --- a/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverrideJavaNullable.fir.kt +++ b/compiler/testData/diagnostics/tests/annotations/rendering/typeMismatchOnOverrideJavaNullable.fir.kt @@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull; annotation class An class B : A { - override fun foo(): String? = null + override fun foo(): String? = null } @An @@ -38,5 +38,5 @@ public interface C { } class D : C { - override fun foo(): String? = null + override fun foo(): String? = null } diff --git a/compiler/testData/diagnostics/tests/dataClasses/implementMethodsFromInterface.fir.kt b/compiler/testData/diagnostics/tests/dataClasses/implementMethodsFromInterface.fir.kt index be0e89007bf..bf3b2bc347a 100644 --- a/compiler/testData/diagnostics/tests/dataClasses/implementMethodsFromInterface.fir.kt +++ b/compiler/testData/diagnostics/tests/dataClasses/implementMethodsFromInterface.fir.kt @@ -21,5 +21,5 @@ data class FooImplAll(val num: Int) : Foo { data class WrongSignatures(val num: Int) : Foo { override fun equals(other: WrongSignatures) = false - override fun hashCode(): Boolean = true + override fun hashCode(): Boolean = true } diff --git a/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.fir.kt b/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.fir.kt index d23a9803b84..8e963287db2 100644 --- a/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.fir.kt +++ b/compiler/testData/diagnostics/tests/delegatedProperty/delegatedPropertyOverridedInTraitTypeMismatch.fir.kt @@ -15,7 +15,7 @@ fun foo() { } class Delegate { - operator fun getValue(t: Any?, p: KProperty<*>): String { + operator fun getValue(t: Any?, p: KProperty<*>): String { return "" } } diff --git a/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt b/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt index 127b8af6c69..353194cf6cd 100644 --- a/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt +++ b/compiler/testData/diagnostics/tests/delegation/covariantOverrides/kt13952.fir.kt @@ -13,5 +13,5 @@ object AImpl : IA { open class C : IA by AImpl, IB class D : C() { - override fun foo(): Double = 3.14 + override fun foo(): Double = 3.14 } diff --git a/compiler/testData/diagnostics/tests/j+k/collectionOverrides/removeAtInt.kt b/compiler/testData/diagnostics/tests/j+k/collectionOverrides/removeAtInt.kt index 10fb23dd26d..94bdc3eb1bf 100644 --- a/compiler/testData/diagnostics/tests/j+k/collectionOverrides/removeAtInt.kt +++ b/compiler/testData/diagnostics/tests/j+k/collectionOverrides/removeAtInt.kt @@ -11,7 +11,16 @@ import java.util.*; abstract class B : MutableList, AbstractList() { override fun removeAt(index: Int): Int = null!! - override fun remove(element: Int): Boolean = null!! + override fun remove(element: Int): Boolean = null!! +} + +abstract class D : AbstractList() { + // removeAt() doesn't exist in java/util/AbstractList, it's a + // fake override of the method from kotlin/collections/MutableList + override fun removeAt(index: Int): Int = null!! + // AbstractList::remove() should return Int here. No fake overrides created. + // This may be a bug because the old compiler doesn't report a diagnostic here. + override fun remove(element: Int): Boolean = null!! } fun main(a: A, b: B, c: ArrayList) { diff --git a/compiler/testData/diagnostics/tests/override/Delegation.fir.kt b/compiler/testData/diagnostics/tests/override/Delegation.fir.kt deleted file mode 100644 index f4820e64c54..00000000000 --- a/compiler/testData/diagnostics/tests/override/Delegation.fir.kt +++ /dev/null @@ -1,49 +0,0 @@ -package test - -interface X { - fun foo(): String? { - return null - } -} - -interface Y { - fun foo(): String { - return "foo" - } -} - -interface Incompatible { - fun foo(): Int { - return 3 - } -} - -class Test1(val x: X) : X by x, Y { - override fun foo(): String? { - return null - } -} - -class Test2(val x: X) : X by x, Y { - override fun foo(): String { - return "foo" - } -} - -class Test3(val y: Y) : X, Y by y { - override fun foo(): String? { - return null - } -} - -class Test4(val y: Y) : X, Y by y { - override fun foo(): String { - return "foo" - } -} - -class Test5(val y: Y, val x: X) : X by x, Y by y, Incompatible { - override fun foo(): Int { - return 3 - } -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/override/Delegation.kt b/compiler/testData/diagnostics/tests/override/Delegation.kt index 14558ca7464..9972e786f97 100644 --- a/compiler/testData/diagnostics/tests/override/Delegation.kt +++ b/compiler/testData/diagnostics/tests/override/Delegation.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL package test interface X { diff --git a/compiler/testData/diagnostics/tests/override/kt12482.fir.kt b/compiler/testData/diagnostics/tests/override/kt12482.fir.kt index db5999b06e5..1ae130d6152 100644 --- a/compiler/testData/diagnostics/tests/override/kt12482.fir.kt +++ b/compiler/testData/diagnostics/tests/override/kt12482.fir.kt @@ -3,7 +3,7 @@ interface A { } interface B : A { - override fun test(): Unit = "B" + override fun test(): Unit = "B" } open class C : A diff --git a/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.fir.kt deleted file mode 100644 index ab727c004be..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.fir.kt +++ /dev/null @@ -1,35 +0,0 @@ -// FILE: A.java -import org.jetbrains.annotations.*; - -public interface A { - @Nullable - String foo(); -} - -// FILE: B.java -import org.jetbrains.annotations.*; - -public interface B { - @NotNull - String foo(); -} - -// FILE: C.kt - -class C1 : A, B { - override fun foo(): String? = "" -} - -class C2 : A, B { - override fun foo(): String = "" -} - -interface I : A, B - -class C3 : I { - override fun foo(): String? = "" -} - -class C4 : I { - override fun foo(): String = "" -} diff --git a/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.kt b/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.kt index 1ed1ab66af4..43330823fbd 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/typeEnhancement/supertypeDifferentReturnNullability.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // FILE: A.java import org.jetbrains.annotations.*; diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCovarianJavaOverride.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCovarianJavaOverride.fir.kt index e4d376125b0..d24b143c660 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCovarianJavaOverride.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendCovarianJavaOverride.fir.kt @@ -23,7 +23,7 @@ class K2 : JavaClass() { } class K3 : JavaClass() { - override fun foo(x: Int, y: Continuation): Any? = null + override fun foo(x: Int, y: Continuation): Any? = null } fun builder(block: suspend () -> Unit) {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/lambdaInOverriddenValInitializer.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/lambdaInOverriddenValInitializer.fir.kt index dd982fe46f5..b5bd1cae554 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/lambdaInOverriddenValInitializer.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/lambdaInOverriddenValInitializer.fir.kt @@ -7,7 +7,7 @@ interface Bar { } class Test1 : Foo { - override val foo = {} + override val foo = {} } class Test2 : Foo { @@ -15,7 +15,7 @@ class Test2 : Foo { } class Test3 : Bar Unit> { - override val bar = {} + override val bar = {} } class Test4 : Bar Unit> { diff --git a/compiler/tests-spec/testData/diagnostics/linked/declarations/classifier-declaration/class-declaration/abstract-classes/p-2/neg/1.3.fir.kt b/compiler/tests-spec/testData/diagnostics/linked/declarations/classifier-declaration/class-declaration/abstract-classes/p-2/neg/1.3.fir.kt index 4b503aa351c..57af86eaa66 100644 --- a/compiler/tests-spec/testData/diagnostics/linked/declarations/classifier-declaration/class-declaration/abstract-classes/p-2/neg/1.3.fir.kt +++ b/compiler/tests-spec/testData/diagnostics/linked/declarations/classifier-declaration/class-declaration/abstract-classes/p-2/neg/1.3.fir.kt @@ -11,12 +11,12 @@ abstract class Base { } class Case1 : Base() { - override fun foo(): Any + override fun foo(): Any { return "" } - override val a: Any? + override val a: Any? get() = TODO() override var b: String get() = TODO() @@ -30,7 +30,7 @@ class Case1 : Base() { */ class Case2(override val a: String, override var b: String) : Base() { - override fun foo(): CharSequence? { + override fun foo(): CharSequence? { return "" } }