[FIR] Add 3 type mismatch diagnostics
This commit is contained in:
+35
@@ -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: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>String<!> = "Test"
|
||||
}
|
||||
|
||||
open class D() : B() {
|
||||
override var test: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>Char<!> = '\n'
|
||||
}
|
||||
|
||||
class E<T : Double>(val value: T) : B() {
|
||||
override var test: T = value
|
||||
}
|
||||
|
||||
open class F<T : Number>(val value: T) {
|
||||
open var rest: T = value
|
||||
}
|
||||
|
||||
class G<E : Double>(val balue: E) : F<E>(balue) {
|
||||
override var rest: E = balue
|
||||
}
|
||||
|
||||
class H<E : String>(val balue: E) : <!INAPPLICABLE_CANDIDATE!>F<E><!>(balue) {
|
||||
override var rest: E = balue // no report because of INAPPLICABLE_CANDIDATE
|
||||
}
|
||||
|
||||
class M<E : String>(val balue: E) : F<Double>(3.14) {
|
||||
override var rest: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>E<!> = balue
|
||||
}
|
||||
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
FILE: propertyTypeMismatchOnOverride.kt
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
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<R|A|>()
|
||||
}
|
||||
|
||||
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<R|A|>()
|
||||
}
|
||||
|
||||
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<R|B|>()
|
||||
}
|
||||
|
||||
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<T : R|kotlin/Double|> : R|B| {
|
||||
public constructor<T : R|kotlin/Double|>(value: R|T|): R|E<T>| {
|
||||
super<R|B|>()
|
||||
}
|
||||
|
||||
public final val value: R|T| = R|<local>/value|
|
||||
public get(): R|T|
|
||||
|
||||
public final override var test: R|T| = R|<local>/value|
|
||||
public get(): R|T|
|
||||
public set(value: R|T|): R|kotlin/Unit|
|
||||
|
||||
}
|
||||
public open class F<T : R|kotlin/Number|> : R|kotlin/Any| {
|
||||
public constructor<T : R|kotlin/Number|>(value: R|T|): R|F<T>| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val value: R|T| = R|<local>/value|
|
||||
public get(): R|T|
|
||||
|
||||
public open var rest: R|T| = R|<local>/value|
|
||||
public get(): R|T|
|
||||
public set(value: R|T|): R|kotlin/Unit|
|
||||
|
||||
}
|
||||
public final class G<E : R|kotlin/Double|> : R|F<E>| {
|
||||
public constructor<E : R|kotlin/Double|>(balue: R|E|): R|G<E>| {
|
||||
super<R|F<E>|>(R|<local>/balue|)
|
||||
}
|
||||
|
||||
public final val balue: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
|
||||
public final override var rest: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
public set(value: R|E|): R|kotlin/Unit|
|
||||
|
||||
}
|
||||
public final class H<E : R|kotlin/String|> : R|F<E>| {
|
||||
public constructor<E : R|kotlin/String|>(balue: R|E|): R|H<E>| {
|
||||
super<R|F<E>|>(R|<local>/balue|)
|
||||
}
|
||||
|
||||
public final val balue: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
|
||||
public final override var rest: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
public set(value: R|E|): R|kotlin/Unit|
|
||||
|
||||
}
|
||||
public final class M<E : R|kotlin/String|> : R|F<kotlin/Double>| {
|
||||
public constructor<E : R|kotlin/String|>(balue: R|E|): R|M<E>| {
|
||||
super<R|F<kotlin/Double>|>(Double(3.14))
|
||||
}
|
||||
|
||||
public final val balue: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
|
||||
public final override var rest: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
public set(value: R|E|): R|kotlin/Unit|
|
||||
|
||||
}
|
||||
+69
@@ -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(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>String<!> = "Test"
|
||||
}
|
||||
|
||||
open class D() : B() {
|
||||
override fun test(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Char<!> = '\n'
|
||||
}
|
||||
|
||||
class E<T : Double>(val value: T) : B() {
|
||||
override fun test(): T = value
|
||||
}
|
||||
|
||||
open class F<T : Number>(val value: T) {
|
||||
open fun rest(): T = value
|
||||
}
|
||||
|
||||
class G<E : Double>(val balue: E) : F<E>(balue) {
|
||||
override fun rest(): E = balue
|
||||
}
|
||||
|
||||
class H<E : String>(val balue: E) : <!INAPPLICABLE_CANDIDATE!>F<E><!>(balue) {
|
||||
override fun rest(): E = balue // no report because of INAPPLICABLE_CANDIDATE
|
||||
}
|
||||
|
||||
class M<E : String>(val balue: E) : F<Double>(3.14) {
|
||||
override fun rest(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>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(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Z<!> = Z()
|
||||
}
|
||||
|
||||
open class Base<T : X, Z : T> {
|
||||
open fun kek(): Z = Z()
|
||||
}
|
||||
|
||||
open class GoodDerrived : Base<Y, W>() {
|
||||
override fun kek(): W = W()
|
||||
}
|
||||
|
||||
open class BadDerrived : Base<Y, W>() {
|
||||
override fun kek(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>String<!> = "test"
|
||||
}
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
FILE: returnTypeMismatchOnOverride.kt
|
||||
public open class A : R|kotlin/Any| {
|
||||
public constructor(): R|A| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public open fun test(): R|kotlin/Number| {
|
||||
^test Int(10)
|
||||
}
|
||||
|
||||
}
|
||||
public open class B : R|A| {
|
||||
public constructor(): R|B| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
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|<local>/x|
|
||||
}
|
||||
|
||||
}
|
||||
public final class C : R|A| {
|
||||
public constructor(): R|C| {
|
||||
super<R|A|>()
|
||||
}
|
||||
|
||||
public final override fun test(): R|kotlin/String| {
|
||||
^test String(Test)
|
||||
}
|
||||
|
||||
}
|
||||
public open class D : R|B| {
|
||||
public constructor(): R|D| {
|
||||
super<R|B|>()
|
||||
}
|
||||
|
||||
public open override fun test(): R|kotlin/Char| {
|
||||
^test Char(10)
|
||||
}
|
||||
|
||||
}
|
||||
public final class E<T : R|kotlin/Double|> : R|B| {
|
||||
public constructor<T : R|kotlin/Double|>(value: R|T|): R|E<T>| {
|
||||
super<R|B|>()
|
||||
}
|
||||
|
||||
public final val value: R|T| = R|<local>/value|
|
||||
public get(): R|T|
|
||||
|
||||
public final override fun test(): R|T| {
|
||||
^test this@R|/E|.R|/E.value|
|
||||
}
|
||||
|
||||
}
|
||||
public open class F<T : R|kotlin/Number|> : R|kotlin/Any| {
|
||||
public constructor<T : R|kotlin/Number|>(value: R|T|): R|F<T>| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public final val value: R|T| = R|<local>/value|
|
||||
public get(): R|T|
|
||||
|
||||
public open fun rest(): R|T| {
|
||||
^rest this@R|/F|.R|/F.value|
|
||||
}
|
||||
|
||||
}
|
||||
public final class G<E : R|kotlin/Double|> : R|F<E>| {
|
||||
public constructor<E : R|kotlin/Double|>(balue: R|E|): R|G<E>| {
|
||||
super<R|F<E>|>(R|<local>/balue|)
|
||||
}
|
||||
|
||||
public final val balue: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
|
||||
public final override fun rest(): R|E| {
|
||||
^rest this@R|/G|.R|/G.balue|
|
||||
}
|
||||
|
||||
}
|
||||
public final class H<E : R|kotlin/String|> : R|F<E>| {
|
||||
public constructor<E : R|kotlin/String|>(balue: R|E|): R|H<E>| {
|
||||
super<R|F<E>|>(R|<local>/balue|)
|
||||
}
|
||||
|
||||
public final val balue: R|E| = R|<local>/balue|
|
||||
public get(): R|E|
|
||||
|
||||
public final override fun rest(): R|E| {
|
||||
^rest this@R|/H|.R|/H.balue|
|
||||
}
|
||||
|
||||
}
|
||||
public final class M<E : R|kotlin/String|> : R|F<kotlin/Double>| {
|
||||
public constructor<E : R|kotlin/String|>(balue: R|E|): R|M<E>| {
|
||||
super<R|F<kotlin/Double>|>(Double(3.14))
|
||||
}
|
||||
|
||||
public final val balue: R|E| = R|<local>/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<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class Y : R|X| {
|
||||
public constructor(): R|Y| {
|
||||
super<R|X|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class Z : R|Y| {
|
||||
public constructor(): R|Z| {
|
||||
super<R|Y|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class W : R|Z| {
|
||||
public constructor(): R|W| {
|
||||
super<R|Z|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class V : R|kotlin/Any| {
|
||||
public constructor(): R|V| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public open fun hello(): R|X| {
|
||||
^hello R|/X.X|()
|
||||
}
|
||||
|
||||
}
|
||||
public open class L : R|V| {
|
||||
public constructor(): R|L| {
|
||||
super<R|V|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class Q : R|L| {
|
||||
public constructor(): R|Q| {
|
||||
super<R|L|>()
|
||||
}
|
||||
|
||||
}
|
||||
public open class S : R|Q| {
|
||||
public constructor(): R|S| {
|
||||
super<R|Q|>()
|
||||
}
|
||||
|
||||
public open override fun hello(): R|W| {
|
||||
^hello R|/W.W|()
|
||||
}
|
||||
|
||||
}
|
||||
public open class J : R|S| {
|
||||
public constructor(): R|J| {
|
||||
super<R|S|>()
|
||||
}
|
||||
|
||||
public open override fun hello(): R|Z| {
|
||||
^hello R|/Z.Z|()
|
||||
}
|
||||
|
||||
}
|
||||
public open class Base<T : R|X|, Z : R|T|> : R|kotlin/Any| {
|
||||
public constructor<T : R|X|, Z : R|T|>(): R|Base<T, Z>| {
|
||||
super<R|kotlin/Any|>()
|
||||
}
|
||||
|
||||
public open fun kek(): R|Z| {
|
||||
^kek R|/Z.Z|()
|
||||
}
|
||||
|
||||
}
|
||||
public open class GoodDerrived : R|Base<Y, W>| {
|
||||
public constructor(): R|GoodDerrived| {
|
||||
super<R|Base<Y, W>|>()
|
||||
}
|
||||
|
||||
public open override fun kek(): R|W| {
|
||||
^kek R|/W.W|()
|
||||
}
|
||||
|
||||
}
|
||||
public open class BadDerrived : R|Base<Y, W>| {
|
||||
public constructor(): R|BadDerrived| {
|
||||
super<R|Base<Y, W>|>()
|
||||
}
|
||||
|
||||
public open override fun kek(): R|kotlin/String| {
|
||||
^kek String(test)
|
||||
}
|
||||
|
||||
}
|
||||
Generated
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
+1
@@ -40,6 +40,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() {
|
||||
FirEnumClassSimpleChecker,
|
||||
FirSealedSupertypeChecker,
|
||||
FirInapplicableLateinitChecker,
|
||||
FirTypeMismatchOnOverrideChecker,
|
||||
)
|
||||
|
||||
override val regularClassCheckers: List<FirRegularClassChecker> = listOf(
|
||||
|
||||
+242
@@ -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<FirFunctionSymbol<*>> {
|
||||
val overriddenFunctions = mutableSetOf<FirFunctionSymbol<*>>()
|
||||
|
||||
processFunctionsByName(function.name) {}
|
||||
processDirectlyOverriddenFunctions(function.symbol) {
|
||||
overriddenFunctions.add(it)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
return overriddenFunctions.toList()
|
||||
}
|
||||
|
||||
private fun FirTypeScope.getDirectOverridesOf(property: FirProperty): List<FirPropertySymbol> {
|
||||
val overriddenProperties = mutableSetOf<FirPropertySymbol>()
|
||||
|
||||
processPropertiesByName(property.name) {}
|
||||
processDirectlyOverriddenProperties(property.symbol) {
|
||||
overriddenProperties.add(it)
|
||||
ProcessorAction.NEXT
|
||||
}
|
||||
|
||||
return overriddenProperties.toList()
|
||||
}
|
||||
|
||||
private fun FirRegularClass.substituteAllSupertypeParameters(context: CheckerContext): ConeSubstitutor {
|
||||
val allSupertypesMapping = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
|
||||
val remapper = mutableMapOf<ConeKotlinType, ConeKotlinType>()
|
||||
|
||||
fun FirRegularClass.collectSupertypes() {
|
||||
for (it in superTypeRefs) {
|
||||
val fir = it.coneType.safeAs<ConeClassLikeType>()?.lookupTag?.toSymbol(context.session)
|
||||
?.fir.safeAs<FirRegularClass>()
|
||||
?: 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<ConeKotlinType>()
|
||||
?.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<FirTypeParametersOwner>()
|
||||
?: return this
|
||||
|
||||
val map = mutableMapOf<FirTypeParameterSymbol, ConeKotlinType>()
|
||||
|
||||
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<FirCallableSymbol<*>>,
|
||||
context: CheckerContext,
|
||||
typeCheckerContext: AbstractTypeCheckerContext,
|
||||
): FirMemberDeclaration? {
|
||||
val returnType = returnTypeRef.safeAs<FirResolvedTypeRef>()?.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)) }
|
||||
}
|
||||
}
|
||||
+21
@@ -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")
|
||||
|
||||
@@ -130,6 +130,9 @@ object FirErrors {
|
||||
val TYPE_PARAMETERS_IN_ENUM by error0<FirSourceElement, PsiElement>()
|
||||
val CONFLICTING_PROJECTION by error1<FirSourceElement, PsiElement, String>()
|
||||
val VARIANCE_ON_TYPE_PARAMETER_NOT_ALLOWED by error0<FirSourceElement, PsiElement>()
|
||||
val RETURN_TYPE_MISMATCH_ON_OVERRIDE by error2<FirSourceElement, PsiElement, String, FirMemberDeclaration>()
|
||||
val PROPERTY_TYPE_MISMATCH_ON_OVERRIDE by error2<FirSourceElement, PsiElement, String, FirMemberDeclaration>()
|
||||
val VAR_TYPE_MISMATCH_ON_OVERRIDE by error2<FirSourceElement, PsiElement, String, FirMemberDeclaration>()
|
||||
|
||||
// Redeclarations
|
||||
val MANY_COMPANION_OBJECTS by error0<FirSourceElement, PsiElement>()
|
||||
|
||||
@@ -13,14 +13,14 @@ interface A<H> {
|
||||
abstract class B<H>() : A<H> {
|
||||
override fun foo() {
|
||||
}
|
||||
override fun foo2() : Unit {
|
||||
override fun foo2() : <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Unit<!> {
|
||||
}
|
||||
|
||||
override val a : Double = 1.toDouble()
|
||||
override val a : <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>Double<!> = 1.toDouble()
|
||||
override val a1 = 1.toDouble()
|
||||
|
||||
abstract override fun <X> g() : Int
|
||||
abstract override fun <X> g1() : List<X>
|
||||
abstract override fun <X> g() : <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Int<!>
|
||||
abstract override fun <X> g1() : <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>List<X><!>
|
||||
|
||||
abstract override val g : Iterator<Int>
|
||||
abstract override val g : <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>Iterator<Int><!>
|
||||
}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
abstract class A {
|
||||
abstract var x: Int;
|
||||
abstract fun foo() : Int;
|
||||
}
|
||||
|
||||
abstract class C : A() {
|
||||
override abstract var x: String =<!SYNTAX!><!> <!SYNTAX!>?<!>
|
||||
override abstract fun foo(): String =<!SYNTAX!><!> <!SYNTAX!>?<!>
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
abstract class A {
|
||||
abstract var x: Int;
|
||||
abstract fun foo() : Int;
|
||||
|
||||
+3
-3
@@ -15,12 +15,12 @@ interface A {
|
||||
|
||||
@An
|
||||
interface B : A {
|
||||
override val p1: Int
|
||||
override val p1: <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>Int<!>
|
||||
@An
|
||||
override val p2: @An String
|
||||
override fun test(arg: String): Int
|
||||
override fun test(arg: String): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Int<!>
|
||||
}
|
||||
|
||||
interface C : A {
|
||||
override var p2: Int
|
||||
override var p2: <!VAR_TYPE_MISMATCH_ON_OVERRIDE!>Int<!>
|
||||
}
|
||||
Vendored
+2
-2
@@ -27,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
annotation class An
|
||||
|
||||
class B : A {
|
||||
override fun foo(): String? = null
|
||||
override fun foo(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>String?<!> = null
|
||||
}
|
||||
|
||||
@An
|
||||
@@ -38,5 +38,5 @@ public interface C {
|
||||
}
|
||||
|
||||
class D : C {
|
||||
override fun foo(): String? = null
|
||||
override fun foo(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>String?<!> = null
|
||||
}
|
||||
|
||||
+1
-1
@@ -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(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Boolean<!> = true
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ fun foo() {
|
||||
}
|
||||
|
||||
class Delegate {
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): String {
|
||||
operator fun getValue(t: Any?, p: KProperty<*>): <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>String<!> {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Double<!> = 3.14
|
||||
}
|
||||
|
||||
+10
-1
@@ -11,7 +11,16 @@ import java.util.*;
|
||||
|
||||
abstract class B : MutableList<Int>, AbstractList<Int>() {
|
||||
override fun removeAt(index: Int): Int = null!!
|
||||
override fun remove(element: Int): Boolean = null!!
|
||||
override fun remove(element: Int): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Boolean<!> = null!!
|
||||
}
|
||||
|
||||
abstract class D : AbstractList<Int>() {
|
||||
// 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): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Boolean<!> = null!!
|
||||
}
|
||||
|
||||
fun main(a: A, b: B, c: ArrayList<Int>) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
package test
|
||||
|
||||
interface X {
|
||||
|
||||
@@ -3,7 +3,7 @@ interface A {
|
||||
}
|
||||
|
||||
interface B : A {
|
||||
override fun test(): Unit = "B"
|
||||
override fun test(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Unit<!> = "B"
|
||||
}
|
||||
|
||||
open class C : A
|
||||
|
||||
-35
@@ -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 = ""
|
||||
}
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// FILE: A.java
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -23,7 +23,7 @@ class K2 : JavaClass() {
|
||||
}
|
||||
|
||||
class K3 : JavaClass() {
|
||||
override fun foo(x: Int, y: Continuation<String>): Any? = null
|
||||
override fun foo(x: Int, y: Continuation<String>): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Any?<!> = null
|
||||
}
|
||||
|
||||
fun builder(block: suspend () -> Unit) {}
|
||||
|
||||
+2
-2
@@ -7,7 +7,7 @@ interface Bar<T> {
|
||||
}
|
||||
|
||||
class Test1 : Foo {
|
||||
override val foo = {}
|
||||
override val foo = <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>{}<!>
|
||||
}
|
||||
|
||||
class Test2 : Foo {
|
||||
@@ -15,7 +15,7 @@ class Test2 : Foo {
|
||||
}
|
||||
|
||||
class Test3 : Bar<suspend () -> Unit> {
|
||||
override val bar = {}
|
||||
override val bar = <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>{}<!>
|
||||
}
|
||||
|
||||
class Test4 : Bar<suspend () -> Unit> {
|
||||
|
||||
+3
-3
@@ -11,12 +11,12 @@ abstract class Base {
|
||||
}
|
||||
|
||||
class Case1 : Base() {
|
||||
override fun foo(): Any
|
||||
override fun foo(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>Any<!>
|
||||
{
|
||||
return ""
|
||||
}
|
||||
|
||||
override val a: Any?
|
||||
override val a: <!PROPERTY_TYPE_MISMATCH_ON_OVERRIDE!>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(): <!RETURN_TYPE_MISMATCH_ON_OVERRIDE!>CharSequence?<!> {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user