[FIR] Perform more accurate pre-check of candidate receiver type
This commit is contained in:
-34
@@ -5,21 +5,12 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.calls.tower
|
||||
|
||||
import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeStarProjection
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.constructClassType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
|
||||
internal class CandidateFactoriesAndCollectors(
|
||||
// Common calls
|
||||
@@ -88,31 +79,6 @@ private class TowerScopeLevelProcessor(
|
||||
scope: FirScope,
|
||||
builtInExtensionFunctionReceiverValue: ReceiverValue?
|
||||
) {
|
||||
// Check explicit extension receiver for default package members
|
||||
if (symbol is FirNamedFunctionSymbol && dispatchReceiverValue == null &&
|
||||
extensionReceiverValue != null &&
|
||||
callInfo.explicitReceiver !is FirResolvedQualifier &&
|
||||
symbol.callableId.packageName.startsWith(defaultPackage)
|
||||
) {
|
||||
val extensionReceiverType = extensionReceiverValue.type as? ConeClassLikeType
|
||||
if (extensionReceiverType != null) {
|
||||
val declarationReceiverType = (symbol as? FirCallableSymbol<*>)?.fir?.receiverTypeRef?.coneType
|
||||
if (declarationReceiverType is ConeClassLikeType) {
|
||||
if (!AbstractTypeChecker.isSubtypeOf(
|
||||
candidateFactory.context.session.typeContext,
|
||||
extensionReceiverType,
|
||||
declarationReceiverType.lookupTag.constructClassType(
|
||||
declarationReceiverType.typeArguments.map { ConeStarProjection }.toTypedArray(),
|
||||
isNullable = true
|
||||
)
|
||||
)
|
||||
) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ---
|
||||
resultCollector.consumeCandidate(
|
||||
group, candidateFactory.createCandidate(
|
||||
callInfo,
|
||||
|
||||
+43
-15
@@ -15,12 +15,18 @@ import org.jetbrains.kotlin.fir.resolve.calls.*
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.FirDefaultStarImportingScope
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.importedFromObjectData
|
||||
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.typeContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.ConeStarProjection
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.constructClassType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
|
||||
abstract class TowerScopeLevel {
|
||||
@@ -178,12 +184,6 @@ class ScopeTowerLevel(
|
||||
private val extensionsOnly: Boolean,
|
||||
private val includeInnerConstructors: Boolean
|
||||
) : SessionBasedTowerLevel(session) {
|
||||
private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: Receiver?): Boolean =
|
||||
when {
|
||||
extensionsOnly && !hasExtensionReceiver() -> false
|
||||
!hasConsistentExtensionReceiver(extensionReceiver) -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? {
|
||||
candidate.fir.importedFromObjectData?.let { data ->
|
||||
@@ -215,20 +215,48 @@ class ScopeTowerLevel(
|
||||
}
|
||||
}
|
||||
|
||||
private fun shouldSkipCandidateWithInconsistentExtensionReceiver(candidate: FirCallableSymbol<*>): Boolean {
|
||||
// Pre-check explicit extension receiver for default package top-level members
|
||||
if (scope is FirDefaultStarImportingScope && extensionReceiver != null) {
|
||||
val extensionReceiverType = extensionReceiver.type
|
||||
if (extensionReceiverType is ConeClassLikeType) {
|
||||
val declarationReceiverType = candidate.fir.receiverTypeRef?.coneType
|
||||
if (declarationReceiverType is ConeClassLikeType) {
|
||||
if (!AbstractTypeChecker.isSubtypeOf(
|
||||
session.typeContext,
|
||||
extensionReceiverType,
|
||||
declarationReceiverType.lookupTag.constructClassType(
|
||||
declarationReceiverType.typeArguments.map { ConeStarProjection }.toTypedArray(),
|
||||
isNullable = true
|
||||
)
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun <T : AbstractFirBasedSymbol<*>> consumeCallableCandidate(
|
||||
candidate: FirCallableSymbol<*>,
|
||||
processor: TowerScopeLevelProcessor<T>
|
||||
) {
|
||||
if (candidate.hasConsistentReceivers(extensionReceiver)) {
|
||||
val dispatchReceiverValue = dispatchReceiverValue(candidate)
|
||||
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
processor.consumeCandidate(
|
||||
unwrappedCandidate as T, dispatchReceiverValue,
|
||||
extensionReceiverValue = extensionReceiver,
|
||||
scope
|
||||
)
|
||||
val candidateReceiverTypeRef = candidate.fir.receiverTypeRef
|
||||
val receiverExpected = extensionsOnly || extensionReceiver != null
|
||||
if (candidateReceiverTypeRef == null == receiverExpected) return
|
||||
val dispatchReceiverValue = dispatchReceiverValue(candidate)
|
||||
if (dispatchReceiverValue == null && shouldSkipCandidateWithInconsistentExtensionReceiver(candidate)) {
|
||||
return
|
||||
}
|
||||
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
processor.consumeCandidate(
|
||||
unwrappedCandidate as T, dispatchReceiverValue,
|
||||
extensionReceiverValue = extensionReceiver,
|
||||
scope
|
||||
)
|
||||
}
|
||||
|
||||
override fun processFunctionsByName(
|
||||
|
||||
Vendored
+1
-1
@@ -1 +1 @@
|
||||
val foo = <!INAPPLICABLE_CANDIDATE!>UInt<!>()
|
||||
val foo = <!HIDDEN!>UInt<!>()
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ inline fun test(crossinline c: () -> Unit) {
|
||||
}
|
||||
}
|
||||
val l = { c() }
|
||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
suspend fun calculate() = "OK"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ inline fun test(noinline c: () -> Unit) {
|
||||
}
|
||||
}
|
||||
val l = { c() }
|
||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
suspend fun calculate() = "OK"
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ inline fun test(c: () -> Unit) {
|
||||
}
|
||||
}
|
||||
val l = { c() }
|
||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ suspend inline fun test(crossinline c: () -> Unit) {
|
||||
}
|
||||
}
|
||||
val l = { c() }
|
||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ suspend inline fun test(noinline c: () -> Unit) {
|
||||
}
|
||||
}
|
||||
val l = { c() }
|
||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ suspend inline fun test(c: () -> Unit) {
|
||||
}
|
||||
}
|
||||
val l = { c() }
|
||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
suspend fun calculate() = "OK"
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
class Case1(val nothing: Nothing)
|
||||
|
||||
fun case1() {
|
||||
val res = <!INAPPLICABLE_CANDIDATE!>Case1<!>(<!INAPPLICABLE_CANDIDATE!>Nothing<!>())
|
||||
val res = Case1(<!HIDDEN!>Nothing<!>())
|
||||
}
|
||||
|
||||
|
||||
|
||||
Vendored
+2
-2
@@ -30,14 +30,14 @@ import contracts.*
|
||||
// TESTCASE NUMBER: 1
|
||||
fun case_1(value: Any) {
|
||||
if (contracts.case_1_2(contracts.case_1_1(value is Char))) {
|
||||
println(value.<!INAPPLICABLE_CANDIDATE!>category<!>)
|
||||
<!AMBIGUITY!>println<!>(value.<!UNRESOLVED_REFERENCE!>category<!>)
|
||||
}
|
||||
}
|
||||
|
||||
// TESTCASE NUMBER: 2
|
||||
fun case_2(value: Any) {
|
||||
if (contracts.case_2(value is Char) is Boolean) {
|
||||
println(value.<!INAPPLICABLE_CANDIDATE!>category<!>)
|
||||
<!AMBIGUITY!>println<!>(value.<!UNRESOLVED_REFERENCE!>category<!>)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ fun case_16() {
|
||||
// TESTCASE NUMBER: 17
|
||||
val case_17 = if (nullableIntProperty == null == true == false) 0 else {
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>.java
|
||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>.<!UNRESOLVED_REFERENCE!>java<!>
|
||||
}
|
||||
|
||||
//TESTCASE NUMBER: 18
|
||||
|
||||
Reference in New Issue
Block a user