[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
|
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.resolve.calls.*
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
|
||||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
|
||||||
|
|
||||||
internal class CandidateFactoriesAndCollectors(
|
internal class CandidateFactoriesAndCollectors(
|
||||||
// Common calls
|
// Common calls
|
||||||
@@ -88,31 +79,6 @@ private class TowerScopeLevelProcessor(
|
|||||||
scope: FirScope,
|
scope: FirScope,
|
||||||
builtInExtensionFunctionReceiverValue: ReceiverValue?
|
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(
|
resultCollector.consumeCandidate(
|
||||||
group, candidateFactory.createCandidate(
|
group, candidateFactory.createCandidate(
|
||||||
callInfo,
|
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.resolve.transformers.body.resolve.resultType
|
||||||
import org.jetbrains.kotlin.fir.scopes.FirScope
|
import org.jetbrains.kotlin.fir.scopes.FirScope
|
||||||
import org.jetbrains.kotlin.fir.scopes.ProcessorAction
|
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.impl.importedFromObjectData
|
||||||
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
|
import org.jetbrains.kotlin.fir.scopes.processClassifiersByName
|
||||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
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.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.name.Name
|
||||||
|
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
abstract class TowerScopeLevel {
|
abstract class TowerScopeLevel {
|
||||||
@@ -178,12 +184,6 @@ class ScopeTowerLevel(
|
|||||||
private val extensionsOnly: Boolean,
|
private val extensionsOnly: Boolean,
|
||||||
private val includeInnerConstructors: Boolean
|
private val includeInnerConstructors: Boolean
|
||||||
) : SessionBasedTowerLevel(session) {
|
) : SessionBasedTowerLevel(session) {
|
||||||
private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: Receiver?): Boolean =
|
|
||||||
when {
|
|
||||||
extensionsOnly && !hasExtensionReceiver() -> false
|
|
||||||
!hasConsistentExtensionReceiver(extensionReceiver) -> false
|
|
||||||
else -> true
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? {
|
private fun dispatchReceiverValue(candidate: FirCallableSymbol<*>): ReceiverValue? {
|
||||||
candidate.fir.importedFromObjectData?.let { data ->
|
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(
|
private fun <T : AbstractFirBasedSymbol<*>> consumeCallableCandidate(
|
||||||
candidate: FirCallableSymbol<*>,
|
candidate: FirCallableSymbol<*>,
|
||||||
processor: TowerScopeLevelProcessor<T>
|
processor: TowerScopeLevelProcessor<T>
|
||||||
) {
|
) {
|
||||||
if (candidate.hasConsistentReceivers(extensionReceiver)) {
|
val candidateReceiverTypeRef = candidate.fir.receiverTypeRef
|
||||||
val dispatchReceiverValue = dispatchReceiverValue(candidate)
|
val receiverExpected = extensionsOnly || extensionReceiver != null
|
||||||
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
|
if (candidateReceiverTypeRef == null == receiverExpected) return
|
||||||
@Suppress("UNCHECKED_CAST")
|
val dispatchReceiverValue = dispatchReceiverValue(candidate)
|
||||||
processor.consumeCandidate(
|
if (dispatchReceiverValue == null && shouldSkipCandidateWithInconsistentExtensionReceiver(candidate)) {
|
||||||
unwrappedCandidate as T, dispatchReceiverValue,
|
return
|
||||||
extensionReceiverValue = extensionReceiver,
|
|
||||||
scope
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
val unwrappedCandidate = candidate.fir.importedFromObjectData?.original?.symbol ?: candidate
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
processor.consumeCandidate(
|
||||||
|
unwrappedCandidate as T, dispatchReceiverValue,
|
||||||
|
extensionReceiverValue = extensionReceiver,
|
||||||
|
scope
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun processFunctionsByName(
|
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() }
|
val l = { c() }
|
||||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun calculate() = "OK"
|
suspend fun calculate() = "OK"
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ inline fun test(noinline c: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val l = { c() }
|
val l = { c() }
|
||||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun calculate() = "OK"
|
suspend fun calculate() = "OK"
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ inline fun test(c: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val l = { c() }
|
val l = { c() }
|
||||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(c: suspend () -> Unit) {
|
fun builder(c: suspend () -> Unit) {
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ suspend inline fun test(crossinline c: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val l = { c() }
|
val l = { c() }
|
||||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(c: suspend () -> Unit) {
|
fun builder(c: suspend () -> Unit) {
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ suspend inline fun test(noinline c: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val l = { c() }
|
val l = { c() }
|
||||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun builder(c: suspend () -> Unit) {
|
fun builder(c: suspend () -> Unit) {
|
||||||
|
|||||||
+1
-1
@@ -20,7 +20,7 @@ suspend inline fun test(c: () -> Unit) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
val l = { c() }
|
val l = { c() }
|
||||||
c.<!UNRESOLVED_REFERENCE!>startCoroutine<!>(EmptyContinuation)
|
c.startCoroutine(EmptyContinuation)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun calculate() = "OK"
|
suspend fun calculate() = "OK"
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
class Case1(val nothing: Nothing)
|
class Case1(val nothing: Nothing)
|
||||||
|
|
||||||
fun case1() {
|
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
|
// TESTCASE NUMBER: 1
|
||||||
fun case_1(value: Any) {
|
fun case_1(value: Any) {
|
||||||
if (contracts.case_1_2(contracts.case_1_1(value is Char))) {
|
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
|
// TESTCASE NUMBER: 2
|
||||||
fun case_2(value: Any) {
|
fun case_2(value: Any) {
|
||||||
if (contracts.case_2(value is Char) is Boolean) {
|
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
|
// TESTCASE NUMBER: 17
|
||||||
val case_17 = if (nullableIntProperty == null == true == false) 0 else {
|
val case_17 = if (nullableIntProperty == null == true == false) 0 else {
|
||||||
<!DEBUG_INFO_EXPRESSION_TYPE("kotlin.Int?")!>nullableIntProperty<!>
|
<!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
|
//TESTCASE NUMBER: 18
|
||||||
|
|||||||
Reference in New Issue
Block a user