FIR resolve: support processing extensions before members (forEach case)
This commit is contained in:
@@ -39,6 +39,7 @@ import org.jetbrains.kotlin.fir.types.impl.FirResolvedTypeRefImpl
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.ConstraintSystemBuilder
|
||||
import org.jetbrains.kotlin.resolve.calls.results.TypeSpecificityComparator
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.HIDES_MEMBERS_NAME_LIST
|
||||
|
||||
class FirCallResolver(
|
||||
components: BodyResolveComponents,
|
||||
@@ -92,7 +93,11 @@ class FirCallResolver(
|
||||
towerResolver.reset()
|
||||
|
||||
val consumer = createFunctionConsumer(session, name, info, this, towerResolver.collector, towerResolver)
|
||||
val result = towerResolver.runResolver(consumer, implicitReceiverStack.receiversAsReversed())
|
||||
val result = towerResolver.runResolver(
|
||||
consumer,
|
||||
implicitReceiverStack.receiversAsReversed(),
|
||||
shouldProcessExtensionsBeforeMembers = name in HIDES_MEMBERS_NAME_LIST
|
||||
)
|
||||
val bestCandidates = result.bestCandidates()
|
||||
val reducedCandidates = if (result.currentApplicability < CandidateApplicability.SYNTHETIC_RESOLVED) {
|
||||
bestCandidates.toSet()
|
||||
|
||||
+48
-21
@@ -140,6 +140,39 @@ class FirTowerResolver(
|
||||
return group
|
||||
}
|
||||
|
||||
private fun processTopLevelScope(
|
||||
towerDataConsumer: TowerDataConsumer,
|
||||
topLevelScope: FirScope,
|
||||
oldGroup: Int,
|
||||
extensionsOnly: Boolean = false
|
||||
): Int {
|
||||
var group = oldGroup
|
||||
// Top-level extensions via implicit receiver
|
||||
// fun Foo.bar() {}
|
||||
// class Foo {
|
||||
// fun test() { bar() }
|
||||
// }
|
||||
for (implicitReceiverValue in implicitReceiverValues) {
|
||||
if (towerDataConsumer.consume(
|
||||
TOWER_LEVEL,
|
||||
ScopeTowerLevel(
|
||||
session, components, topLevelScope,
|
||||
implicitExtensionReceiver = implicitReceiverValue,
|
||||
extensionsOnly = extensionsOnly
|
||||
),
|
||||
group++
|
||||
) == NONE
|
||||
) {
|
||||
return group
|
||||
}
|
||||
}
|
||||
// Member of top-level scope & importing scope
|
||||
// val x = 0
|
||||
// fun test() { x }
|
||||
towerDataConsumer.consume(TOWER_LEVEL, ScopeTowerLevel(session, components, topLevelScope), group++)
|
||||
return group
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
collector.newDataSet()
|
||||
}
|
||||
@@ -148,11 +181,23 @@ class FirTowerResolver(
|
||||
private lateinit var towerDataConsumer: TowerDataConsumer
|
||||
private lateinit var implicitReceiverValues: List<ImplicitReceiverValue<*>>
|
||||
|
||||
fun runResolver(consumer: TowerDataConsumer, implicitReceiverValues: List<ImplicitReceiverValue<*>>): CandidateCollector {
|
||||
fun runResolver(
|
||||
consumer: TowerDataConsumer,
|
||||
implicitReceiverValues: List<ImplicitReceiverValue<*>>,
|
||||
shouldProcessExtensionsBeforeMembers: Boolean = false
|
||||
): CandidateCollector {
|
||||
this.implicitReceiverValues = implicitReceiverValues
|
||||
towerDataConsumer = consumer
|
||||
|
||||
var group = 0
|
||||
|
||||
// Specific case when extension should be processed before members (Kotlin forEach vs Java forEach)
|
||||
if (shouldProcessExtensionsBeforeMembers) {
|
||||
for (topLevelScope in topLevelScopes) {
|
||||
group = processTopLevelScope(towerDataConsumer, topLevelScope, group, extensionsOnly = true)
|
||||
}
|
||||
}
|
||||
|
||||
// Member of explicit receiver' type (this stage does nothing without explicit receiver)
|
||||
// class Foo(val x: Int)
|
||||
// fun test(f: Foo) { f.x }
|
||||
@@ -200,26 +245,8 @@ class FirTowerResolver(
|
||||
}
|
||||
}
|
||||
|
||||
topLevelScopeLoop@ for (scope in topLevelScopes) {
|
||||
// Top-level extensions via implicit receiver
|
||||
// fun Foo.bar() {}
|
||||
// class Foo {
|
||||
// fun test() { bar() }
|
||||
// }
|
||||
for (implicitReceiverValue in implicitReceiverValues) {
|
||||
if (towerDataConsumer.consume(
|
||||
TOWER_LEVEL,
|
||||
ScopeTowerLevel(session, components, scope, implicitExtensionReceiver = implicitReceiverValue),
|
||||
group++
|
||||
) == NONE
|
||||
) {
|
||||
continue@topLevelScopeLoop
|
||||
}
|
||||
}
|
||||
// Member of top-level scope & importing scope
|
||||
// val x = 0
|
||||
// fun test() { x }
|
||||
towerDataConsumer.consume(TOWER_LEVEL, ScopeTowerLevel(session, components, scope), group++)
|
||||
for (topLevelScope in topLevelScopes) {
|
||||
group = processTopLevelScope(towerDataConsumer, topLevelScope, group)
|
||||
}
|
||||
|
||||
return collector
|
||||
|
||||
@@ -150,12 +150,14 @@ class MemberScopeTowerLevel(
|
||||
// (if explicit receiver exists, it always *should* be an extension receiver)
|
||||
class ScopeTowerLevel(
|
||||
session: FirSession,
|
||||
val bodyResolveComponents: BodyResolveComponents,
|
||||
private val bodyResolveComponents: BodyResolveComponents,
|
||||
val scope: FirScope,
|
||||
val implicitExtensionReceiver: ImplicitReceiverValue<*>? = null
|
||||
val implicitExtensionReceiver: ImplicitReceiverValue<*>? = null,
|
||||
private val extensionsOnly: Boolean = false
|
||||
) : SessionBasedTowerLevel(session) {
|
||||
private fun FirCallableSymbol<*>.hasConsistentReceivers(extensionReceiver: ReceiverValue?): Boolean =
|
||||
when {
|
||||
extensionsOnly && !hasExtensionReceiver() -> false
|
||||
!hasConsistentExtensionReceiver(extensionReceiver) -> false
|
||||
scope is FirAbstractImportingScope -> true
|
||||
else -> dispatchReceiverValue().let { it == null || it.klassSymbol.fir.classKind == ClassKind.OBJECT }
|
||||
|
||||
@@ -17,7 +17,7 @@ fun <D : Any> Call<D>.testForEach() {
|
||||
value.length
|
||||
}
|
||||
arguments.forEach {
|
||||
<!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>key<!>.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
<!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>value<!>.<!UNRESOLVED_REFERENCE!>length<!>
|
||||
it.key.length
|
||||
it.value.length
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ FILE: test.kt
|
||||
R|<local>/value|.R|kotlin/String.length|
|
||||
}
|
||||
)
|
||||
R|/Call.arguments|.R|FakeOverride<java/util/Map.forEach: R|kotlin/Unit|>|(<L> = forEach@fun <anonymous>(): R|kotlin/Unit| {
|
||||
<Unresolved name: it>#.<Unresolved name: key>#.<Unresolved name: length>#
|
||||
<Unresolved name: it>#.<Unresolved name: value>#.<Unresolved name: length>#
|
||||
R|/Call.arguments|.R|kotlin/collections/forEach|<R|ft<kotlin/String, kotlin/String?>!|, R|ft<kotlin/String, kotlin/String?>!|>(<L> = forEach@fun <anonymous>(it: R|kotlin/collections/Map.Entry<ft<kotlin/String, kotlin/String?>!, ft<kotlin/String, kotlin/String?>!>|): R|kotlin/Unit| <kind=UNKNOWN> {
|
||||
R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.key: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length|
|
||||
R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.value: R|ft<kotlin/String, kotlin/String?>!|>|.R|kotlin/String.length|
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ fun foo(conflicting: List<Diagnostic>) {
|
||||
conflicting.groupBy {
|
||||
it.name
|
||||
}.forEach {
|
||||
val diagnostics = <!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>value<!>
|
||||
val diagnostics = it.value
|
||||
filtered.addAll(
|
||||
diagnostics.<!AMBIGUITY!>filter<!> { me ->
|
||||
diagnostics.<!AMBIGUITY!>none<!> { other ->
|
||||
diagnostics.filter { me ->
|
||||
diagnostics.none { other ->
|
||||
me != other
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ FILE: noneWithForEach.kt
|
||||
R|<local>/conflicting|.R|kotlin/collections/groupBy|<R|Diagnostic|, R|kotlin/String|>(<L> = groupBy@fun <anonymous>(it: R|Diagnostic|): R|kotlin/String| <kind=UNKNOWN> {
|
||||
R|<local>/it|.R|/Diagnostic.name|
|
||||
}
|
||||
).R|FakeOverride<java/util/Map.forEach: R|kotlin/Unit|>|(<L> = forEach@fun <anonymous>(): R|kotlin/Unit| {
|
||||
lval diagnostics: <ERROR TYPE REF: Unresolved name: value> = <Unresolved name: it>#.<Unresolved name: value>#
|
||||
R|<local>/filtered|.R|FakeOverride<java/util/ArrayList.addAll: R|kotlin/Boolean|>|(R|<local>/diagnostics|.<Ambiguity: filter, [kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/collections/filter, kotlin/sequences/filter, kotlin/text/filter, kotlin/text/filter]>#(<L> = filter@fun <anonymous>(me: R|class error: No type for parameter|): <ERROR TYPE REF: Ambiguity: none, [kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/sequences/none, kotlin/text/none]> {
|
||||
R|<local>/diagnostics|.<Ambiguity: none, [kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/collections/none, kotlin/sequences/none, kotlin/text/none]>#(<L> = none@fun <anonymous>(other: R|class error: No type for parameter|): R|kotlin/Boolean| {
|
||||
).R|kotlin/collections/forEach|<R|kotlin/String|, R|kotlin/collections/List<Diagnostic>|>(<L> = forEach@fun <anonymous>(it: R|kotlin/collections/Map.Entry<kotlin/String, kotlin/collections/List<Diagnostic>>|): R|kotlin/Unit| <kind=UNKNOWN> {
|
||||
lval diagnostics: R|kotlin/collections/List<Diagnostic>| = R|<local>/it|.R|FakeOverride<kotlin/collections/Map.Entry.value: R|kotlin/collections/List<Diagnostic>|>|
|
||||
R|<local>/filtered|.R|FakeOverride<java/util/ArrayList.addAll: R|kotlin/Boolean|>|(R|<local>/diagnostics|.R|kotlin/collections/filter|<R|Diagnostic|>(<L> = filter@fun <anonymous>(me: R|Diagnostic|): R|kotlin/Boolean| <kind=UNKNOWN> {
|
||||
R|<local>/diagnostics|.R|kotlin/collections/none|<R|Diagnostic|>(<L> = none@fun <anonymous>(other: R|Diagnostic|): R|kotlin/Boolean| <kind=UNKNOWN> {
|
||||
!=(R|<local>/me|, R|<local>/other|)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user