[FIR] Prohibit calling functions with dynamic receiver over non-dynamics

^KT-63071 Fixed
This commit is contained in:
Nikolay Lunyak
2023-11-28 12:08:05 +02:00
committed by Space Team
parent c69c34ff8b
commit 875e2b0cd6
16 changed files with 61 additions and 11 deletions
@@ -2467,6 +2467,13 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert
token,
)
}
add(FirErrors.DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC) { firDiagnostic ->
DynamicReceiverExpectedButWasNonDynamicImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
firDiagnostic as KtPsiDiagnostic,
token,
)
}
add(FirErrors.INCOMPATIBLE_TYPES) { firDiagnostic ->
IncompatibleTypesImpl(
firSymbolBuilder.typeBuilder.buildKtType(firDiagnostic.a),
@@ -1748,6 +1748,11 @@ sealed interface KtFirDiagnostic<PSI : PsiElement> : KtDiagnosticWithPsi<PSI> {
override val diagnosticClass get() = DynamicReceiverNotAllowed::class
}
interface DynamicReceiverExpectedButWasNonDynamic : KtFirDiagnostic<PsiElement> {
override val diagnosticClass get() = DynamicReceiverExpectedButWasNonDynamic::class
val actualType: KtType
}
interface IncompatibleTypes : KtFirDiagnostic<KtElement> {
override val diagnosticClass get() = IncompatibleTypes::class
val typeA: KtType
@@ -2103,6 +2103,12 @@ internal class DynamicReceiverNotAllowedImpl(
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<KtElement>(firDiagnostic, token), KtFirDiagnostic.DynamicReceiverNotAllowed
internal class DynamicReceiverExpectedButWasNonDynamicImpl(
override val actualType: KtType,
firDiagnostic: KtPsiDiagnostic,
token: KtLifetimeToken,
) : KtAbstractFirDiagnostic<PsiElement>(firDiagnostic, token), KtFirDiagnostic.DynamicReceiverExpectedButWasNonDynamic
internal class IncompatibleTypesImpl(
override val typeA: KtType,
override val typeB: KtType,
@@ -810,6 +810,10 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") {
val DYNAMIC_RECEIVER_NOT_ALLOWED by error<KtElement>()
val DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC by error<PsiElement> {
parameter<ConeKotlinType>("actualType")
}
val INCOMPATIBLE_TYPES by error<KtElement> {
parameter<ConeKotlinType>("typeA")
parameter<ConeKotlinType>("typeB")
@@ -471,6 +471,7 @@ object FirErrors {
val DYNAMIC_SUPERTYPE by error0<KtTypeReference>()
val DYNAMIC_UPPER_BOUND by error0<KtTypeReference>()
val DYNAMIC_RECEIVER_NOT_ALLOWED by error0<KtElement>()
val DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC by error1<PsiElement, ConeKotlinType>()
val INCOMPATIBLE_TYPES by error2<KtElement, ConeKotlinType, ConeKotlinType>()
val INCOMPATIBLE_TYPES_WARNING by warning2<KtElement, ConeKotlinType, ConeKotlinType>()
val TYPE_VARIANCE_CONFLICT_ERROR by error4<PsiElement, FirTypeParameterSymbol, Variance, Variance, ConeKotlinType>(SourceElementPositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT)
@@ -309,6 +309,7 @@ val FIR_NON_SUPPRESSIBLE_ERROR_NAMES: Set<String> = setOf(
"DYNAMIC_SUPERTYPE",
"DYNAMIC_UPPER_BOUND",
"DYNAMIC_RECEIVER_NOT_ALLOWED",
"DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC",
"INCOMPATIBLE_TYPES",
"TYPE_VARIANCE_CONFLICT_ERROR",
"TYPE_VARIANCE_CONFLICT_IN_EXPANDED_TYPE",
@@ -205,6 +205,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DSL_SCOPE_VIOLATI
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DUPLICATE_LABEL_IN_WHEN
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DUPLICATE_PARAMETER_NAME_IN_FUNCTION_TYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_RECEIVER_NOT_ALLOWED
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_SUPERTYPE
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.DYNAMIC_UPPER_BOUND
@@ -1467,6 +1468,12 @@ object FirErrorsDefaultMessages : BaseDiagnosticRendererFactory() {
map.put(DYNAMIC_RECEIVER_NOT_ALLOWED, "Dynamic receivers are prohibited.")
map.put(
DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC,
"Calling functions expecting the ''dynamic'' receiver over a non-''dynamic'' one such as ''{0}'' is prohibited.",
RENDER_TYPE,
)
// Reflection
map.put(
EXTENSION_IN_CLASS_REFERENCE_NOT_ALLOWED,
@@ -93,7 +93,11 @@ private fun ConeDiagnostic.toKtDiagnostic(
is ConeTypeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(smallestUnresolvablePrefix.last().source)
is ConeVisibilityError -> symbol.toInvisibleReferenceDiagnostic(source)
is ConeInapplicableWrongReceiver -> FirErrors.UNRESOLVED_REFERENCE_WRONG_RECEIVER.createOn(source, this.candidateSymbols)
is ConeInapplicableWrongReceiver -> when (val diagnostic = primaryDiagnostic) {
is DynamicReceiverExpectedButWasNonDynamic ->
FirErrors.DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC.createOn(source, diagnostic.actualType)
else -> FirErrors.UNRESOLVED_REFERENCE_WRONG_RECEIVER.createOn(source, this.candidateSymbols)
}
is ConeNoCompanionObject -> FirErrors.NO_COMPANION_OBJECT.createOn(source, this.candidateSymbol as FirClassLikeSymbol<*>)
is ConeAmbiguityError -> when {
applicability.isSuccess -> FirErrors.OVERLOAD_RESOLUTION_AMBIGUITY.createOn(source, this.candidates.map { it.symbol })
@@ -392,6 +392,11 @@ private fun checkApplicabilityForArgumentType(
return
}
if (isReceiver && expectedType is ConeDynamicType && argumentType !is ConeDynamicType) {
sink.reportDiagnostic(DynamicReceiverExpectedButWasNonDynamic(argumentType))
return
}
if (!csBuilder.addSubtypeConstraintIfCompatible(argumentType, expectedType, position)) {
val smartcastExpression = argument as? FirSmartCastExpression
if (smartcastExpression != null && !smartcastExpression.isStable) {
@@ -93,6 +93,10 @@ class InapplicableWrongReceiver(
val actualType: ConeKotlinType? = null,
) : ResolutionDiagnostic(INAPPLICABLE_WRONG_RECEIVER)
class DynamicReceiverExpectedButWasNonDynamic(
val actualType: ConeKotlinType,
) : ResolutionDiagnostic(INAPPLICABLE_WRONG_RECEIVER)
object NoCompanionObject : ResolutionDiagnostic(K2_NO_COMPANION_OBJECT)
class UnsafeCall(val actualType: ConeKotlinType) : ResolutionDiagnostic(UNSAFE_CALL)
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.fir.expressions.FirOperation
import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression
import org.jetbrains.kotlin.fir.render
import org.jetbrains.kotlin.fir.resolve.calls.AbstractCandidate
import org.jetbrains.kotlin.fir.resolve.calls.ResolutionDiagnostic
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeKotlinType
@@ -120,6 +121,11 @@ class ConeInapplicableWrongReceiver(override val candidates: Collection<Abstract
get() = "None of the following candidates is applicable because of receiver type mismatch: ${
candidateSymbols.map { describeSymbol(it) }
}"
val primaryDiagnostic: ResolutionDiagnostic?
get() = candidates.singleOrNull()
?.diagnostics
?.singleOrNull { it.applicability == CandidateApplicability.INAPPLICABLE_WRONG_RECEIVER }
}
class ConeInapplicableCandidateError(
@@ -10,5 +10,5 @@ fun test() {
fun <T> dynamic(body: dynamic.() -> T): T {
val topLevel = null
return topLevel.body()
return topLevel.<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>body<!>()
}
@@ -21,8 +21,8 @@ fun test(d: dynamic, a: Any?) {
println(item)
}
a.<!UNRESOLVED_REFERENCE!>onDynamicFun<!>()
a.<!UNRESOLVED_REFERENCE!>onDynamicProperty<!>
a.<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamicFun<!>()
a.<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamicProperty<!>
}
fun <T> eatT(t: T) {}
@@ -24,7 +24,7 @@ fun test(d: dynamic) {
(d as String).onStringVal
(d as Any).onAnyVal
(d as Any?).onNullableAnyVal
(d as Any).<!UNRESOLVED_REFERENCE!>onDynamicVal<!>
(d as Any).<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamicVal<!>
}
fun testReassignmentWithSafeCall(d: dynamic) {
@@ -35,7 +35,7 @@ fun testReassignmentWithStaticCalls(d: dynamic) {
(d as String).<!VAL_REASSIGNMENT!>onStringVal<!> = 1
(d as Any).<!VAL_REASSIGNMENT!>onAnyVal<!> = 1
(d as Any?).<!VAL_REASSIGNMENT!>onNullableAnyVal<!> = 1
(d as Any).<!UNRESOLVED_REFERENCE!>onDynamicVal<!> = 1
(d as Any).<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC, VAL_REASSIGNMENT!>onDynamicVal<!> = 1
}
val Any.onAnyVal: Int get() = 1
@@ -22,17 +22,17 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onDynamicVar<!> = 1
d?.<!UNRESOLVED_REFERENCE!>onDynamicVar<!> = 1
d?.<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamicVar<!> = 1
(d as String).onStringVar
(d as Any).onAnyVar
(d as Any?).onNullableAnyVar
(d as Any).<!UNRESOLVED_REFERENCE!>onDynamicVar<!>
(d as Any).<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamicVar<!>
(d as String).onStringVar = 1
(d as Any).onAnyVar = 1
(d as Any?).onNullableAnyVar = 1
(d as Any).<!UNRESOLVED_REFERENCE!>onDynamicVar<!> = 1
(d as Any).<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamicVar<!> = 1
}
var Any.onAnyVar: Int
@@ -14,12 +14,12 @@ fun test(d: dynamic) {
d.<!DEBUG_INFO_DYNAMIC!>onString<!>()
d.<!DEBUG_INFO_DYNAMIC!>onDynamic<!>()
d?.<!UNRESOLVED_REFERENCE!>onDynamic<!>()
d?.<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamic<!>()
(d as String).onString()
(d as Any).onAny()
(d as Any?).onNullableAny()
(d as Any).<!UNRESOLVED_REFERENCE!>onDynamic<!>()
(d as Any).<!DYNAMIC_RECEIVER_EXPECTED_BUT_WAS_NON_DYNAMIC!>onDynamic<!>()
}
fun Any.onAny() {}