[FIR] Prohibit calling functions with dynamic receiver over non-dynamics
^KT-63071 Fixed
This commit is contained in:
committed by
Space Team
parent
c69c34ff8b
commit
875e2b0cd6
+7
@@ -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),
|
||||
|
||||
+5
@@ -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
|
||||
|
||||
+6
@@ -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,
|
||||
|
||||
+4
@@ -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)
|
||||
|
||||
+1
@@ -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",
|
||||
|
||||
+7
@@ -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,
|
||||
|
||||
+5
-1
@@ -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) {
|
||||
|
||||
+4
@@ -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)
|
||||
|
||||
+6
@@ -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<!>()
|
||||
}
|
||||
|
||||
+2
-2
@@ -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) {}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
+2
-2
@@ -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() {}
|
||||
|
||||
Reference in New Issue
Block a user