[FIR] Fix ClassCastException when rendering ACTUAL_WITHOUT_EXPECT

#KT-57320
This commit is contained in:
Kirill Rakhman
2023-03-28 17:45:58 +02:00
committed by Space Team
parent 3b09313192
commit 3b0c36da3f
14 changed files with 131 additions and 25 deletions
@@ -361,6 +361,7 @@ internal object FirToKtConversionCreator {
FqName::class,
ClassId::class,
FirModuleData::class,
ExpectActualCompatibility::class,
ExpectActualCompatibility.Incompatible::class,
DeprecationInfo::class,
ApiVersion::class,
@@ -3215,8 +3215,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
NoActualForExpectImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic.b,
firDiagnostic.c.mapKeys { (incompatible, _) ->
incompatible
firDiagnostic.c.mapKeys { (expectActualCompatibility, _) ->
expectActualCompatibility
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol)
@@ -3229,8 +3229,8 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
add(FirErrors.ACTUAL_WITHOUT_EXPECT) { firDiagnostic ->
ActualWithoutExpectImpl(
firSymbolBuilder.buildSymbol(firDiagnostic.a),
firDiagnostic.b.mapKeys { (incompatible, _) ->
incompatible
firDiagnostic.b.mapKeys { (expectActualCompatibility, _) ->
expectActualCompatibility
}.mapValues { (_, collection) ->
collection.map { firBasedSymbol ->
firSymbolBuilder.buildSymbol(firBasedSymbol)
@@ -80,6 +80,7 @@ import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
@@ -2262,13 +2263,13 @@ sealed class KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = NoActualForExpect::class
abstract val declaration: KtSymbol
abstract val module: FirModuleData
abstract val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>
abstract val compatibility: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<KtSymbol>>
}
abstract class ActualWithoutExpect : KtFirDiagnostic<KtNamedDeclaration>() {
override val diagnosticClass get() = ActualWithoutExpect::class
abstract val declaration: KtSymbol
abstract val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>
abstract val compatibility: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<KtSymbol>>
}
abstract class AmbiguousActuals : KtFirDiagnostic<KtNamedDeclaration>() {
@@ -81,6 +81,7 @@ import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
@@ -2727,14 +2728,14 @@ internal class ExpectedFunctionSourceWithDefaultArgumentsNotFoundImpl(
internal class NoActualForExpectImpl(
override val declaration: KtSymbol,
override val module: FirModuleData,
override val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>,
override val compatibility: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<KtSymbol>>,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.NoActualForExpect(), KtAbstractFirDiagnostic<KtNamedDeclaration>
internal class ActualWithoutExpectImpl(
override val declaration: KtSymbol,
override val compatibility: Map<Incompatible<FirBasedSymbol<*>>, List<KtSymbol>>,
override val compatibility: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, List<KtSymbol>>,
override val firDiagnostic: KtPsiDiagnostic,
override val token: KtLifetimeToken,
) : KtFirDiagnostic.ActualWithoutExpect(), KtAbstractFirDiagnostic<KtNamedDeclaration>
@@ -556,6 +556,12 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/intermediateActualHasAdditionalSupertypes.kt");
}
@Test
@TestMetadata("kt57320.kt")
public void testKt57320() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.kt");
}
@Test
@TestMetadata("kt-55570.kt")
public void testKt_55570() throws Exception {
@@ -556,6 +556,12 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/intermediateActualHasAdditionalSupertypes.kt");
}
@Test
@TestMetadata("kt57320.kt")
public void testKt57320() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.kt");
}
@Test
@TestMetadata("kt-55570.kt")
public void testKt_55570() throws Exception {
@@ -33,6 +33,7 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
import kotlin.properties.PropertyDelegateProvider
@@ -1089,12 +1090,12 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val NO_ACTUAL_FOR_EXPECT by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) {
parameter<Symbol>("declaration")
parameter<FirModuleData>("module")
parameter<Map<Incompatible<Symbol>, Collection<Symbol>>>("compatibility")
parameter<Map<ExpectActualCompatibility<Symbol>, Collection<Symbol>>>("compatibility")
}
val ACTUAL_WITHOUT_EXPECT by error<KtNamedDeclaration> {
parameter<Symbol>("declaration")
parameter<Map<Incompatible<Symbol>, Collection<Symbol>>>("compatibility")
parameter<Map<ExpectActualCompatibility<Symbol>, Collection<Symbol>>>("compatibility")
}
val AMBIGUOUS_ACTUALS by error<KtNamedDeclaration>(PositioningStrategy.INCOMPATIBLE_DECLARATION) {
@@ -100,6 +100,7 @@ import org.jetbrains.kotlin.psi.KtWhenEntry
import org.jetbrains.kotlin.psi.KtWhenExpression
import org.jetbrains.kotlin.resolve.ForbiddenNamedArgumentsTarget
import org.jetbrains.kotlin.resolve.deprecation.DeprecationInfo
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
import org.jetbrains.kotlin.types.Variance
@@ -593,8 +594,8 @@ object FirErrors {
val ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS by error0<PsiElement>()
val ACTUAL_ANNOTATION_CONFLICTING_DEFAULT_ARGUMENT_VALUE by error1<PsiElement, FirVariableSymbol<*>>()
val EXPECTED_FUNCTION_SOURCE_WITH_DEFAULT_ARGUMENTS_NOT_FOUND by error0<PsiElement>()
val NO_ACTUAL_FOR_EXPECT by error3<KtNamedDeclaration, FirBasedSymbol<*>, FirModuleData, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val ACTUAL_WITHOUT_EXPECT by error2<KtNamedDeclaration, FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>()
val NO_ACTUAL_FOR_EXPECT by error3<KtNamedDeclaration, FirBasedSymbol<*>, FirModuleData, Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val ACTUAL_WITHOUT_EXPECT by error2<KtNamedDeclaration, FirBasedSymbol<*>, Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>()
val AMBIGUOUS_ACTUALS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirBasedSymbol<*>>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val AMBIGUOUS_EXPECTS by error2<KtNamedDeclaration, FirBasedSymbol<*>, Collection<FirModuleData>>(SourceElementPositioningStrategies.INCOMPATIBLE_DECLARATION)
val NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS by error2<KtNamedDeclaration, FirBasedSymbol<*>, List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>>(SourceElementPositioningStrategies.ACTUAL_DECLARATION_NAME)
@@ -58,14 +58,13 @@ object FirActualCallableDeclarationChecker : FirCallableDeclarationChecker() {
context.session
)
) {
@Suppress("UNCHECKED_CAST")
reporter.reportOn(
callableDeclaration.source,
FirErrors.ACTUAL_WITHOUT_EXPECT,
actualFunctionSymbol,
actualFunctionSymbol.expectForActual as Map<ExpectActualCompatibility.Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
actualFunctionSymbol.expectForActual as Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
context
)
}
}
}
}
@@ -93,12 +93,11 @@ object FirExpectActualDeclarationChecker : FirBasicDeclarationChecker() {
}
Compatible !in compatibilityToMembersMap -> {
@Suppress("UNCHECKED_CAST")
reporter.reportOn(
source,
FirErrors.ACTUAL_WITHOUT_EXPECT,
symbol,
compatibilityToMembersMap as Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
compatibilityToMembersMap,
context
)
}
@@ -18,13 +18,14 @@ package org.jetbrains.kotlin.fir.analysis.diagnostics
import org.jetbrains.kotlin.diagnostics.rendering.ContextIndependentParameterRenderer
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility
import org.jetbrains.kotlin.resolve.multiplatform.ExpectActualCompatibility.Incompatible
class FirPlatformIncompatibilityDiagnosticRenderer(
private val mode: MultiplatformDiagnosticRenderingMode
) : ContextIndependentParameterRenderer<Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> {
) : ContextIndependentParameterRenderer<Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>> {
override fun render(
obj: Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
obj: Map<ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
): String {
if (obj.isEmpty()) return ""
@@ -80,33 +81,33 @@ open class MultiplatformDiagnosticRenderingMode {
}
private fun StringBuilder.renderIncompatibilityInformation(
map: Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
map: Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>,
indent: String,
mode: MultiplatformDiagnosticRenderingMode
) {
for ((incompatibility, descriptors) in map) {
for ((compatibility, descriptors) in map) {
append(indent)
append("The following declaration")
if (descriptors.size == 1) append(" is") else append("s are")
append(" incompatible")
incompatibility.reason?.let { append(" because $it") }
(compatibility as? Incompatible)?.reason?.let { append(" because $it") }
append(":")
mode.renderList(this, descriptors.map { descriptor ->
{ mode.renderSymbol(this, descriptor, indent) }
})
if (incompatibility is Incompatible.ClassScopes) {
if (compatibility is Incompatible.ClassScopes) {
append(indent)
append("No actual members are found for expected members listed below:")
mode.newLine(this)
renderIncompatibleClassScopes(incompatibility.unfulfilled, indent, mode)
renderIncompatibleClassScopes(compatibility.unfulfilled, indent, mode)
}
}
}
private fun StringBuilder.renderIncompatibleClassScopes(
unfulfilled: List<Pair<FirBasedSymbol<*>, Map<Incompatible<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>,
unfulfilled: List<Pair<FirBasedSymbol<*>, Map<out ExpectActualCompatibility<FirBasedSymbol<*>>, Collection<FirBasedSymbol<*>>>>>,
indent: String,
mode: MultiplatformDiagnosticRenderingMode
) {
@@ -0,0 +1,42 @@
// !LANGUAGE: +MultiPlatformProjects
// MODULE: common
// TARGET_PLATFORM: Common
// FILE: StringValue.kt
<!NO_ACTUAL_FOR_EXPECT{JS}!>expect class StringValue<!>
<!NO_ACTUAL_FOR_EXPECT{JS}!>expect fun StringValue.plus(other: String): StringValue<!>
// MODULE: commonJS()()(common)
// TARGET_PLATFORM: JS
// FILE: StringValue.kt
actual class Strin<!NO_ACTUAL_FOR_EXPECT{JS}!>gValue(val value: String<!>)
<!NO_ACTUAL_FOR_EXPECT{JS}!>
actual fun StringValue.plus(other: String) = StringVal<!>ue(this.value + other)
// MODULE: intermediate()()(common)
// TARGET_PLATFORM: Common
// FILE: StringDemoInterface.kt
expect interface StringDemoInterface
interface KotlinXStringDemoInterface {
val value: String
}
expect fun StringDemoInterface.plusK(): String
// MODULE: js()()(common, intermediate)
// TARGET_PLATFORM: JS
// FILE: StringDemoInterface.kt
actual typealias StringDemoInterface = KotlinXStringDemoInterface
<!ACTUAL_WITHOUT_EXPECT("actual fun StringDemoInterface.plusK(): <ERROR TYPE REF: Unresolved name: value>; The following declaration is incompatible: expect fun StringDemoInterface.plusK(): String")!>actual fun StringDemoInterface.plusK() = <!RESOLUTION_TO_CLASSIFIER!>StringValue<!>(value).plus("K").<!UNRESOLVED_REFERENCE!>value<!><!>
// FILE: main.kt
class StringDemo(override val value: String) : StringDemoInterface
fun box() = StringDemo("O").plusK()
@@ -0,0 +1,42 @@
// !LANGUAGE: +MultiPlatformProjects
// MODULE: common
// TARGET_PLATFORM: Common
// FILE: StringValue.kt
expect class <!NO_ACTUAL_FOR_EXPECT{JS}!>StringValue<!>
expect fun StringValue.<!NO_ACTUAL_FOR_EXPECT{JS}!>plus<!>(other: String): StringValue
// MODULE: commonJS()()(common)
// TARGET_PLATFORM: JS
// FILE: StringValue.kt
actual class StringValue(val value: String)
actual fun StringValue.plus(other: String) = StringValue(this.value + other)
// MODULE: intermediate()()(common)
// TARGET_PLATFORM: Common
// FILE: StringDemoInterface.kt
expect interface StringDemoInterface
interface KotlinXStringDemoInterface {
val value: String
}
expect fun StringDemoInterface.plusK(): <!NO_ACTUAL_FOR_EXPECT{JS}!>String<!>
// MODULE: js()()(common, intermediate)
// TARGET_PLATFORM: JS
// FILE: StringDemoInterface.kt
actual typealias StringDemoInterface = KotlinXStringDemoInterface
actual fun StringDemoInterface.<!ACTUAL_WITHOUT_EXPECT("Actual function 'plusK'; The following declaration is incompatible because return type is different: public expect fun StringDemoInterface /* = KotlinXStringDemoInterface */.plusK(): String")!>plusK<!>() = <!RESOLUTION_TO_CLASSIFIER!>StringValue<!>(value).<!DEBUG_INFO_MISSING_UNRESOLVED!>plus<!>("K").<!DEBUG_INFO_MISSING_UNRESOLVED!>value<!>
// FILE: main.kt
class StringDemo(override val value: String) : StringDemoInterface
fun box() = StringDemo("O").plusK()
@@ -22257,6 +22257,12 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/intermediateActualHasAdditionalSupertypes.kt");
}
@Test
@TestMetadata("kt57320.kt")
public void testKt57320() throws Exception {
runTest("compiler/testData/diagnostics/tests/multiplatform/hmpp/kt57320.kt");
}
@Test
@TestMetadata("kt-55570.kt")
public void testKt_55570() throws Exception {