FIR resolve: support processing extensions before members (forEach case)

This commit is contained in:
Mikhail Glukhikh
2019-11-28 15:23:53 +03:00
parent 61c17d9110
commit 6401596d58
7 changed files with 70 additions and 36 deletions
@@ -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()
@@ -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|)
}
)