diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java index c91e6bf1a19..92b8ac89496 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsTestGenerated.java @@ -21658,12 +21658,30 @@ public class FirOldFrontendDiagnosticsTestGenerated extends AbstractFirDiagnosti KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("deepTypeHierarchy.kt") + public void testDeepTypeHierarchy() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt"); + } + @Test @TestMetadata("inferenceFrom.kt") public void testInferenceFrom() throws Exception { runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); } + @Test + @TestMetadata("javaOutProjection.kt") + public void testJavaOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt"); + } + + @Test + @TestMetadata("kotlinOutProjection.kt") + public void testKotlinOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { diff --git a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java index 1038d9f9612..2fda0495226 100644 --- a/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java +++ b/compiler/fir/analysis-tests/tests-gen/org/jetbrains/kotlin/test/runners/FirOldFrontendDiagnosticsWithLightTreeTestGenerated.java @@ -21658,12 +21658,30 @@ public class FirOldFrontendDiagnosticsWithLightTreeTestGenerated extends Abstrac KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("deepTypeHierarchy.kt") + public void testDeepTypeHierarchy() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt"); + } + @Test @TestMetadata("inferenceFrom.kt") public void testInferenceFrom() throws Exception { runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); } + @Test + @TestMetadata("javaOutProjection.kt") + public void testJavaOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt"); + } + + @Test + @TestMetadata("kotlinOutProjection.kt") + public void testKotlinOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { 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 d0f37f3ddd7..e7927014ec0 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 @@ -1231,6 +1231,13 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) val RETURN_FOR_BUILT_IN_SUSPEND by error() } + + val JVM by object : DiagnosticGroup("jvm") { + val JAVA_TYPE_MISMATCH by error { + parameter("expectedType") + parameter("actualType") + } + } } private val exposedVisibilityDiagnosticInit: DiagnosticBuilder.() -> Unit = { diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt new file mode 100644 index 00000000000..f26428df98f --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/JvmExpressionCheckers.kt @@ -0,0 +1,17 @@ +/* + * Copyright 2010-2021 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.jvm.checkers + +import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.expression.FirJavaGenericVarianceViolationTypeChecker + +object JvmExpressionCheckers : ExpressionCheckers() { + override val functionCallCheckers: Set + get() = setOf( + FirJavaGenericVarianceViolationTypeChecker, + ) +} diff --git a/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt new file mode 100644 index 00000000000..e634f4f720c --- /dev/null +++ b/compiler/fir/checkers/checkers.jvm/src/org/jetbrains/kotlin/fir/analysis/jvm/checkers/expression/FirJavaGenericVarianceViolationTypeChecker.kt @@ -0,0 +1,187 @@ +/* + * Copyright 2010-2021 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.jvm.checkers.expression + +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirFunctionCallChecker +import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn +import org.jetbrains.kotlin.fir.declarations.FirDeclarationOrigin +import org.jetbrains.kotlin.fir.expressions.FirFunctionCall +import org.jetbrains.kotlin.fir.expressions.argumentMapping +import org.jetbrains.kotlin.fir.expressions.toResolvedCallableSymbol +import org.jetbrains.kotlin.fir.originalOrSelf +import org.jetbrains.kotlin.fir.resolve.inference.inferenceComponents +import org.jetbrains.kotlin.fir.resolve.substitution.substitutorByMap +import org.jetbrains.kotlin.fir.symbols.impl.FirFunctionSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirTypeParameterSymbol +import org.jetbrains.kotlin.fir.types.* +import org.jetbrains.kotlin.fir.types.impl.ConeClassLikeTypeImpl +import org.jetbrains.kotlin.types.AbstractTypeChecker +import org.jetbrains.kotlin.types.TypeApproximatorConfiguration +import org.jetbrains.kotlin.types.model.TypeConstructorMarker +import org.jetbrains.kotlin.types.model.typeConstructor +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +/** + * Checks compatibility of variance of type argument for Java collections. + * + * Java collection interfaces all include methods mutating the collection. Hence, they naturally map to mutable versions of Kotlin + * collections. But Kotlin provides immutable collections and it's good practice to enforce immutability when possible. Hence, to make it + * easier to use immutable collections in Kotlin, Java collection types are instead mapped to flexible type with mutable collection as the + * lower type and immutable collection as upper type. However, flexible types make type checking unsound. Hence we have this check that + * catches a common mistake allowed by flexible type. + * + * Consider a Java method accepting type `List`, which is mapped to `MutableList..List<(out) Any?>`. If one passes a mutable + * list of some more concrete type than `Any` (ArrayList, LinkedList, etc.) to this Java method, then any writes to this + * mutable collection could cause `ClassCastException`s if the same mutable list is read elsewhere. It's the purpose of this checker to + * reject such code. + * + * On the other hand, if one passes an immutable list of some more concrete type, then any writes to it on the Java side would cause + * `UnsupportedOperationException`, which is expected and the price we pay in order to make immutable collection easier to use. This checker + * doesn't do anything to prevent this from happening. + */ +object FirJavaGenericVarianceViolationTypeChecker : FirFunctionCallChecker() { + private val javaOrigin = setOf(FirDeclarationOrigin.Java, FirDeclarationOrigin.Enhancement) + + override fun check(expression: FirFunctionCall, context: CheckerContext, reporter: DiagnosticReporter) { + val calleeFunction = expression.calleeReference.toResolvedCallableSymbol() as? FirFunctionSymbol<*> ?: return + if (calleeFunction.originalOrSelf().origin !in javaOrigin) { + return + } + val argumentMapping = expression.argumentMapping ?: return + val typeArgumentMap = mutableMapOf() + for (i in 0 until expression.typeArguments.size) { + val type = expression.typeArguments[i].safeAs()?.typeRef?.coneTypeSafe() + if (type != null) { + typeArgumentMap[calleeFunction.typeParameterSymbols[i]] = type + } + } + val typeParameterSubstitutor = substitutorByMap(typeArgumentMap, context.session) + for ((arg, param) in argumentMapping) { + val expectedType = typeParameterSubstitutor.substituteOrSelf(param.returnTypeRef.coneType) + + // optimization: if no arguments or flexibility, everything is OK + if (expectedType !is ConeFlexibleType || expectedType.typeArguments.isEmpty()) continue + + // Anything is acceptable for raw types + if (expectedType is ConeRawType) continue + + val argType = arg.typeRef.coneType + + val lowerBound = expectedType.lowerBound + val upperBound = expectedType.upperBound + val typeCtx = context.session.inferenceComponents.ctx + val lowerConstructor = lowerBound.typeConstructor(typeCtx) + val upperConstructor = upperBound.typeConstructor(typeCtx) + + // Use site variance projection is always the same for flexible types. So there is no need to check if declaration site is the + // same. + if (lowerConstructor == upperConstructor) continue + + // If the base class of the argument type is not equal or a sub class of the lower bound, then we simply allow it so that + // Kotlin immutable collections can be used in place of Java collection types. + if (!typeCtx.isTypeConstructorEqualOrSubClassOf(argType, lowerBound)) continue + + // In general, out type projection makes a mutable collection "readonly". A priori such a projected type is not a subtype of a + // non-projected type because projection has "removed" the ability to write to this collection. But for the purpose of this + // checker, we don't care about the readability/writability of collections. + // + // More importantly, removing such out projection is a + // shortcut to make this type comparable with the expected type by the function (after removing type capturing below). Consider + // the code below (here we use kotlin code in the example, but the same idea applies to calls to Java methods as well) + // + // ``` + // fun get(): MutableList = ... + // fun take(l: MutableList) = ... + // fun test() { + // take(get()) + // } + // ``` + // + // The type of `get()` is `MutableList`. After type parameter instantiation, `take` takes a + // `MutableList>`. Obviously, a `MutableList` is not a subtype of `MutableList>` because we have lost the identity of the captured type at this point: we no longer know that the captured type is + // actually created because of type projection from `get`. Hence, to workaround this problem, we simply remove all the out + // projection and type capturing and compare the types after such erasure. This way, we won't incorrectly reject any valid code + // though we may accept some invalid code. But in presence of the unsound flexible types, we are allowing invalid code already. + val argTypeWithoutOutProjection = argType.removeOutProjection(true) + val lowerBoundWithoutCapturing = context.session.inferenceComponents.approximator.approximateToSuperType( + lowerBound, + TypeApproximatorConfiguration.FinalApproximationAfterResolutionAndInference + ) ?: lowerBound + + if (!AbstractTypeChecker.isSubtypeOf( + typeCtx, + argTypeWithoutOutProjection, + lowerBoundWithoutCapturing.withNullability(ConeNullability.NULLABLE, typeCtx) + ) + ) { + reporter.reportOn(arg.source, FirErrors.JAVA_TYPE_MISMATCH, argType, expectedType, context) + } + } + } + + private fun ConeKotlinType.removeOutProjection(positive: Boolean): ConeKotlinType { + return when (this) { + is ConeFlexibleType -> ConeFlexibleType(lowerBound.removeOutProjection(positive), upperBound.removeOutProjection(positive)) + is ConeCapturedType -> ConeCapturedType( + captureStatus, + lowerType?.removeOutProjection(positive), + nullability, + constructor.apply { + ConeCapturedTypeConstructor( + projection.removeOutProjection(positive), + supertypes?.map { it.removeOutProjection(positive) }, + typeParameterMarker + ) + }, + attributes, + isProjectionNotNull + ) + is ConeDefinitelyNotNullType -> ConeDefinitelyNotNullType(original.removeOutProjection(positive)) + is ConeIntersectionType -> ConeIntersectionType( + intersectedTypes.map { it.removeOutProjection(positive) }, + alternativeType?.removeOutProjection(positive) + ) + is ConeClassLikeTypeImpl -> ConeClassLikeTypeImpl( + lookupTag, + typeArguments.map { it.removeOutProjection(positive) }.toTypedArray(), + isNullable, + attributes + ) + else -> this + } + } + + private fun ConeTypeProjection.removeOutProjection(positive: Boolean): ConeTypeProjection { + return when (this) { + is ConeKotlinTypeProjectionOut -> if (positive) type else this + is ConeKotlinType -> this.removeOutProjection(true) + is ConeKotlinTypeConflictingProjection -> ConeKotlinTypeConflictingProjection(type.removeOutProjection(true)) + is ConeKotlinTypeProjectionIn -> ConeKotlinTypeProjectionIn(type.removeOutProjection(!positive)) + is ConeStarProjection -> this + } + } + + private fun ConeInferenceContext.isTypeConstructorEqualOrSubClassOf(subType: ConeKotlinType, superType: ConeKotlinType): Boolean { + return isTypeConstructorEqualOrSubClassOf(subType.typeConstructor(), superType.typeConstructor()) + } + + private fun ConeInferenceContext.isTypeConstructorEqualOrSubClassOf( + subTypeConstructor: TypeConstructorMarker, + superTypeConstructor: TypeConstructorMarker + ): Boolean { + if (subTypeConstructor == superTypeConstructor) return true + for (immediateSuperType in subTypeConstructor.supertypes()) { + val immediateSuperTypeConstructor = immediateSuperType.typeConstructor() + if (superTypeConstructor == immediateSuperTypeConstructor) return true + if (this@isTypeConstructorEqualOrSubClassOf.isTypeConstructorEqualOrSubClassOf(immediateSuperTypeConstructor, superTypeConstructor)) return true + } + return false + } +} \ No newline at end of file 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 379112c7055..85567bd68a8 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 @@ -636,4 +636,7 @@ object FirErrors { val MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val RETURN_FOR_BUILT_IN_SUSPEND by error0() + // jvm + val JAVA_TYPE_MISMATCH by error2() + } 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 1bea1a19341..9ded99d3f0d 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 @@ -239,6 +239,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVALID_TYPE_OF_A import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INVISIBLE_REFERENCE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IS_ENUM_ENTRY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ITERATOR_AMBIGUITY +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.JAVA_TYPE_MISMATCH import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.KCLASS_WITH_NULLABLE_TYPE_PARAMETER_IN_SIGNATURE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LEAKED_IN_PLACE_LAMBDA import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.LOCAL_ANNOTATION_CLASS_ERROR @@ -1579,6 +1580,9 @@ class FirDefaultErrorMessages { ) map.put(RETURN_FOR_BUILT_IN_SUSPEND, "Using implicit label for this lambda is prohibited") + // JVM + map.put(JAVA_TYPE_MISMATCH, "Java type mismatch expected {0} but found {1}. Use explicit cast", RENDER_TYPE, RENDER_TYPE) + // Extended checkers group map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier") map.put(REDUNDANT_MODALITY_MODIFIER, "Redundant modality modifier") diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CheckersContainers.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CheckersContainers.kt index 246c0be6a89..34416431ab7 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CheckersContainers.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/checkers/CheckersContainers.kt @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.checkers import org.jetbrains.kotlin.fir.analysis.checkers.* import org.jetbrains.kotlin.fir.analysis.jvm.checkers.JvmDeclarationCheckers +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.JvmExpressionCheckers import org.jetbrains.kotlin.fir.session.FirSessionFactory fun FirSessionFactory.FirSessionConfigurator.registerCommonCheckers() { @@ -22,4 +23,5 @@ fun FirSessionFactory.FirSessionConfigurator.registerExtendedCommonCheckers() { fun FirSessionFactory.FirSessionConfigurator.registerJvmCheckers() { useCheckers(JvmDeclarationCheckers) + useCheckers(JvmExpressionCheckers) } diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt new file mode 100644 index 00000000000..de343ec982c --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt @@ -0,0 +1,25 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_VARIABLE +// FILE: A.java + +import java.util.*; + + +public class A { + void foo(List x) {} + void foo2(List x) {} +} +// FILE: main.kt + +fun main(a: A) { + a.foo(bar()) + a.foo2(bar()) +} + +interface MyList1: MutableList +interface MyList2: MyList1 +interface MyList3 : MyList2 + +fun bar(): MyList3 { + TODO() +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.txt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.txt new file mode 100644 index 00000000000..6d206a8c2b5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.txt @@ -0,0 +1,94 @@ +package + +public fun bar(): MyList3 +public fun main(/*0*/ a: A): kotlin.Unit + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public/*package*/ open fun foo(/*0*/ x: kotlin.collections.(Mutable)List!): kotlin.Unit + public/*package*/ open fun foo2(/*0*/ x: kotlin.collections.(Mutable)List!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface MyList1 : kotlin.collections.MutableList { + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ element: T): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ index: kotlin.Int, /*1*/ element: T): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ index: kotlin.Int, /*1*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun contains(/*0*/ element: T): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun containsAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ index: kotlin.Int): T + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun indexOf(/*0*/ element: T): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun iterator(): kotlin.collections.MutableIterator + public abstract override /*1*/ /*fake_override*/ fun lastIndexOf(/*0*/ element: T): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun listIterator(): kotlin.collections.MutableListIterator + public abstract override /*1*/ /*fake_override*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.collections.MutableListIterator + public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ element: T): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun removeAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun removeAt(/*0*/ index: kotlin.Int): T + public abstract override /*1*/ /*fake_override*/ fun retainAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun set(/*0*/ index: kotlin.Int, /*1*/ element: T): T + public abstract override /*1*/ /*fake_override*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.collections.MutableList + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface MyList2 : MyList1 { + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ element: B): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ index: kotlin.Int, /*1*/ element: B): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ index: kotlin.Int, /*1*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun contains(/*0*/ element: B): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun containsAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ index: kotlin.Int): B + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun indexOf(/*0*/ element: B): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun iterator(): kotlin.collections.MutableIterator + public abstract override /*1*/ /*fake_override*/ fun lastIndexOf(/*0*/ element: B): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun listIterator(): kotlin.collections.MutableListIterator + public abstract override /*1*/ /*fake_override*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.collections.MutableListIterator + public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ element: B): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun removeAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun removeAt(/*0*/ index: kotlin.Int): B + public abstract override /*1*/ /*fake_override*/ fun retainAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun set(/*0*/ index: kotlin.Int, /*1*/ element: B): B + public abstract override /*1*/ /*fake_override*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.collections.MutableList + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public interface MyList3 : MyList2 { + public abstract override /*1*/ /*fake_override*/ val size: kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ element: B): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun add(/*0*/ index: kotlin.Int, /*1*/ element: B): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ index: kotlin.Int, /*1*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun addAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun clear(): kotlin.Unit + public abstract override /*1*/ /*fake_override*/ fun contains(/*0*/ element: B): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun containsAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun get(/*0*/ index: kotlin.Int): B + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun indexOf(/*0*/ element: B): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun isEmpty(): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun iterator(): kotlin.collections.MutableIterator + public abstract override /*1*/ /*fake_override*/ fun lastIndexOf(/*0*/ element: B): kotlin.Int + public abstract override /*1*/ /*fake_override*/ fun listIterator(): kotlin.collections.MutableListIterator + public abstract override /*1*/ /*fake_override*/ fun listIterator(/*0*/ index: kotlin.Int): kotlin.collections.MutableListIterator + public abstract override /*1*/ /*fake_override*/ fun remove(/*0*/ element: B): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun removeAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun removeAt(/*0*/ index: kotlin.Int): B + public abstract override /*1*/ /*fake_override*/ fun retainAll(/*0*/ elements: kotlin.collections.Collection): kotlin.Boolean + public abstract override /*1*/ /*fake_override*/ fun set(/*0*/ index: kotlin.Int, /*1*/ element: B): B + public abstract override /*1*/ /*fake_override*/ fun subList(/*0*/ fromIndex: kotlin.Int, /*1*/ toIndex: kotlin.Int): kotlin.collections.MutableList + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt similarity index 51% rename from compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.fir.kt rename to compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt index a83e2c15f3c..6ec3dfb6a6a 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.fir.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt @@ -1,14 +1,18 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE // FILE: A.java import java.util.*; + public class A { void foo(List x) {} - List bar() {} + void foo2(List x) {} + List bar() {} } // FILE: main.kt fun main(a: A) { - a.foo(a.bar()) + a.foo(a.bar()) + a.foo2(a.bar()) } diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.txt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.txt new file mode 100644 index 00000000000..84b399a84d0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.txt @@ -0,0 +1,13 @@ +package + +public fun main(/*0*/ a: A): kotlin.Unit + +public open class A { + public constructor A() + public/*package*/ open fun bar(): (kotlin.collections.MutableList..kotlin.collections.List?) + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public/*package*/ open fun foo(/*0*/ x: kotlin.collections.(Mutable)List!): kotlin.Unit + public/*package*/ open fun foo2(/*0*/ x: kotlin.collections.(Mutable)List!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt new file mode 100644 index 00000000000..5e752bd438c --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt @@ -0,0 +1,21 @@ +// FIR_IDENTICAL +// !DIAGNOSTICS: -UNUSED_VARIABLE +// FILE: A.java + +import java.util.*; + + +public class A { + void foo(List x) {} + void foo2(List x) {} +} +// FILE: main.kt + +fun main(a: A) { + a.foo(bar()) + a.foo2(bar()) +} + +fun bar() : MutableList { + TODO() +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.txt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.txt new file mode 100644 index 00000000000..9c9c7b034b7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.txt @@ -0,0 +1,13 @@ +package + +public fun bar(): kotlin.collections.MutableList +public fun main(/*0*/ a: A): kotlin.Unit + +public open class A { + public constructor A() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public/*package*/ open fun foo(/*0*/ x: kotlin.collections.(Mutable)List!): kotlin.Unit + public/*package*/ open fun foo2(/*0*/ x: kotlin.collections.(Mutable)List!): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.fir.kt deleted file mode 100644 index d14ca7e5698..00000000000 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.fir.kt +++ /dev/null @@ -1,16 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE -// FILE: A.java - -import java.util.*; - -public class A { - void foo(List x) {} -} -// FILE: main.kt - -abstract class B : MutableList - -fun main(a: A, b: B) { - a.foo(b) - a.foo(b as List) -} diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.kt index bf945327fce..c5c2b68e97e 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/listSuperType.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE // FILE: A.java diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/simple.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/simple.fir.kt index 1e1af3fc634..f8f49d2993f 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/simple.fir.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/simple.fir.kt @@ -25,7 +25,7 @@ fun main( mll: MutableList>, ll: List> ) { // Lists - a.foo(ml) + a.foo(ml) a.foo(l) a.foo(ml as MutableList) a.foo(l as List) @@ -41,25 +41,25 @@ fun main( a.foo(l.iterator()) // Sets - a.foo(ms) + a.foo(ms) a.foo(s) a.foo(ms as MutableSet) a.foo(s as Set) // Maps - a.foo(mm) + a.foo(mm) a.foo(m) a.foo(mm as MutableMap) a.foo(m as Map) // Map entries - a.foo(mme) + a.foo(mme) a.foo(me) a.foo(mme as MutableMap.MutableEntry) a.foo(me as Map.Entry) // Lists of lists - a.foo1(mll) + a.foo1(mll) a.foo1(ll) a.foo1(mll as MutableList>) a.foo1(ll as List>) diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt index e5b210a4c71..cfedd5af10a 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/smartCast.fir.kt @@ -10,7 +10,7 @@ public class A { fun main(a: A, ml: Any) { if (ml is MutableList) { - a.foo(ml) + a.foo(ml) a.foo(ml as List) } } diff --git a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.kt b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.kt index 09777f731c6..e3cfa96b959 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/valueFromJava.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE // FILE: A.java diff --git a/compiler/testData/diagnostics/tests/platformTypes/methodCall/list.kt b/compiler/testData/diagnostics/tests/platformTypes/methodCall/list.kt index 3391c9d196a..97bf383f416 100644 --- a/compiler/testData/diagnostics/tests/platformTypes/methodCall/list.kt +++ b/compiler/testData/diagnostics/tests/platformTypes/methodCall/list.kt @@ -16,4 +16,4 @@ fun test(ls: List, mls: MutableList, lsn: List, mlsn: M J().list(mls) J().list(lsn) J().list(mlsn) -} \ No newline at end of file +} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java index 856b56f2964..aa64ae61bdc 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/DiagnosticTestGenerated.java @@ -21664,12 +21664,30 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.*)\\.kts?$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("deepTypeHierarchy.kt") + public void testDeepTypeHierarchy() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt"); + } + @Test @TestMetadata("inferenceFrom.kt") public void testInferenceFrom() throws Exception { runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); } + @Test + @TestMetadata("javaOutProjection.kt") + public void testJavaOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt"); + } + + @Test + @TestMetadata("kotlinOutProjection.kt") + public void testKotlinOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostics/AbstractFirIdeDiagnosticsCollector.kt b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostics/AbstractFirIdeDiagnosticsCollector.kt index 70287b3ab5d..6aad9fd3614 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostics/AbstractFirIdeDiagnosticsCollector.kt +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/src/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostics/AbstractFirIdeDiagnosticsCollector.kt @@ -10,13 +10,14 @@ import org.jetbrains.kotlin.fir.analysis.CheckersComponentInternal import org.jetbrains.kotlin.fir.analysis.checkers.* import org.jetbrains.kotlin.fir.analysis.checkers.declaration.ComposedDeclarationCheckers import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers +import org.jetbrains.kotlin.fir.analysis.checkers.expression.ComposedExpressionCheckers import org.jetbrains.kotlin.fir.analysis.checkers.expression.ExpressionCheckers import org.jetbrains.kotlin.fir.analysis.checkers.type.TypeCheckers import org.jetbrains.kotlin.fir.analysis.collectors.AbstractDiagnosticCollector import org.jetbrains.kotlin.fir.analysis.collectors.components.* import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.jvm.checkers.JvmDeclarationCheckers -import org.jetbrains.kotlin.fir.checkers.* +import org.jetbrains.kotlin.fir.analysis.jvm.checkers.JvmExpressionCheckers import org.jetbrains.kotlin.fir.moduleData import org.jetbrains.kotlin.idea.fir.low.level.api.sessions.moduleSourceInfo import org.jetbrains.kotlin.platform.SimplePlatform @@ -42,7 +43,7 @@ private object CheckersFactory { val moduleInfo = session.moduleData.moduleSourceInfo val platform = moduleInfo.platform.componentPlatforms.first() val declarationCheckers = createDeclarationCheckers(useExtendedCheckers, platform) - val expressionCheckers = createExpressionCheckers(useExtendedCheckers) + val expressionCheckers = createExpressionCheckers(useExtendedCheckers, platform) val typeCheckers = createTypeCheckers(useExtendedCheckers) @OptIn(ExperimentalStdlibApi::class) @@ -71,8 +72,18 @@ private object CheckersFactory { } } - private fun createExpressionCheckers(useExtendedCheckers: Boolean): ExpressionCheckers = - if (useExtendedCheckers) ExtendedExpressionCheckers else CommonExpressionCheckers + private fun createExpressionCheckers(useExtendedCheckers: Boolean, platform: SimplePlatform): ExpressionCheckers { + return if (useExtendedCheckers) { + ExtendedExpressionCheckers + } else { + createExpressionCheckers { + add(CommonExpressionCheckers) + when (platform) { + is JvmPlatform -> add(JvmExpressionCheckers) + } + } + } + } private fun createTypeCheckers(useExtendedCheckers: Boolean): TypeCheckers? = if (useExtendedCheckers) null else CommonTypeCheckers @@ -94,4 +105,19 @@ private object CheckersFactory { } } } + + @OptIn(ExperimentalStdlibApi::class) + private inline fun createExpressionCheckers( + createExpressionCheckers: MutableList.() -> Unit + ): ExpressionCheckers = createExpressionCheckers(buildList(createExpressionCheckers)) + + @OptIn(CheckersComponentInternal::class) + private fun createExpressionCheckers(expressionCheckers: List): ExpressionCheckers { + return when (expressionCheckers.size) { + 1 -> expressionCheckers.single() + else -> ComposedExpressionCheckers().apply { + expressionCheckers.forEach(::register) + } + } + } } diff --git a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java index 3676b5cb724..b6a24e7db7e 100644 --- a/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java +++ b/idea/idea-frontend-fir/idea-fir-low-level-api/tests/org/jetbrains/kotlin/idea/fir/low/level/api/diagnostic/compiler/based/DiagnosisCompilerTestFE10TestdataTestGenerated.java @@ -21658,12 +21658,30 @@ public class DiagnosisCompilerTestFE10TestdataTestGenerated extends AbstractDiag KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation"), Pattern.compile("^(.+)\\.kt$"), Pattern.compile("^(.+)\\.fir\\.kts?$"), true); } + @Test + @TestMetadata("deepTypeHierarchy.kt") + public void testDeepTypeHierarchy() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/deepTypeHierarchy.kt"); + } + @Test @TestMetadata("inferenceFrom.kt") public void testInferenceFrom() throws Exception { runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/inferenceFrom.kt"); } + @Test + @TestMetadata("javaOutProjection.kt") + public void testJavaOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/javaOutProjection.kt"); + } + + @Test + @TestMetadata("kotlinOutProjection.kt") + public void testKotlinOutProjection() throws Exception { + runTest("compiler/testData/diagnostics/tests/platformTypes/genericVarianceViolation/kotlinOutProjection.kt"); + } + @Test @TestMetadata("listSuperType.kt") public void testListSuperType() throws Exception { 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 cb7161415a4..0da0a23f592 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 @@ -3314,6 +3314,14 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.JAVA_TYPE_MISMATCH) { firDiagnostic -> + JavaTypeMismatchImpl( + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a), + firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.b), + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic -> ConflictingJvmDeclarationsImpl( firDiagnostic as FirPsiDiagnostic, 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 f6ae724659b..0b3a09e0239 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 @@ -2311,6 +2311,12 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = ReturnForBuiltInSuspend::class } + abstract class JavaTypeMismatch : KtFirDiagnostic() { + override val diagnosticClass get() = JavaTypeMismatch::class + abstract val expectedType: KtType + abstract val actualType: KtType + } + abstract class ConflictingJvmDeclarations : KtFirDiagnostic() { override val diagnosticClass get() = ConflictingJvmDeclarations::class } 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 06f45adab48..5d81feae209 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 @@ -3737,6 +3737,15 @@ internal class ReturnForBuiltInSuspendImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class JavaTypeMismatchImpl( + override val expectedType: KtType, + override val actualType: KtType, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.JavaTypeMismatch(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class ConflictingJvmDeclarationsImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,