FIR JvmMappedScope: substitute type parameters in Java scope with Kotlin ones

Without it, override mapping does not work properly.
NB: at this moment Java forEach often supersedes Kotlin forEach,
because call isn't reported as erroneous properly.
This commit is contained in:
Mikhail Glukhikh
2019-11-28 12:36:56 +03:00
parent 2c611d75bc
commit e43a1ee1c5
8 changed files with 107 additions and 7 deletions
@@ -100,7 +100,7 @@ class JavaSymbolProvider(
useLazyNestedClassifierScope = regularClass is FirJavaClass,
existingNames = (regularClass as? FirJavaClass)?.existingNestedClassifierNames
)
val wrappedDeclaredScope = wrapScopeWithJvmMapped(regularClass.classId, declaredScope, useSiteSession, scopeSession)
val wrappedDeclaredScope = wrapScopeWithJvmMapped(regularClass, declaredScope, useSiteSession, scopeSession)
val superTypeEnhancementScopes =
lookupSuperTypes(regularClass, lookupInterfaces = true, deep = false, useSiteSession = useSiteSession)
.mapNotNull { useSiteSuperType ->
@@ -21,6 +21,8 @@ import org.jetbrains.kotlin.fir.scopes.jvm.JvmMappedScope
import org.jetbrains.kotlin.fir.symbols.*
import org.jetbrains.kotlin.fir.symbols.impl.*
import org.jetbrains.kotlin.fir.types.ConeClassErrorType
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
import org.jetbrains.kotlin.fir.types.impl.ConeTypeParameterTypeImpl
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -57,11 +59,12 @@ abstract class FirSymbolProvider : FirSessionComponent {
): FirScope?
protected fun wrapScopeWithJvmMapped(
classId: ClassId,
klass: FirClass<*>,
declaredMemberScope: FirScope,
useSiteSession: FirSession,
scopeSession: ScopeSession
): FirScope {
val classId = klass.classId
val javaClassId = JavaToKotlinClassMap.mapKotlinToJava(classId.asSingleFqName().toUnsafe())
?: return declaredMemberScope
val symbolProvider = useSiteSession.firSymbolProvider
@@ -69,8 +72,17 @@ abstract class FirSymbolProvider : FirSessionComponent {
?: return declaredMemberScope
val preparedSignatures = JvmMappedScope.prepareSignatures(javaClass)
return if (preparedSignatures.isNotEmpty()) {
symbolProvider.getClassUseSiteMemberScope(javaClassId, useSiteSession, scopeSession)?.let {
JvmMappedScope(declaredMemberScope, it, preparedSignatures)
symbolProvider.getClassUseSiteMemberScope(javaClassId, useSiteSession, scopeSession)?.let { javaClassUseSiteScope ->
val jvmMappedScope = JvmMappedScope(declaredMemberScope, javaClassUseSiteScope, preparedSignatures)
if (klass !is FirRegularClass) {
jvmMappedScope
} else {
// We should substitute Java type parameters with base Kotlin type parameters to match overrides perfectly
(klass.symbol.constructType(
klass.typeParameters.map { ConeTypeParameterTypeImpl(it.symbol.toLookupTag(), false) }.toTypedArray(),
false
) as ConeClassLikeType).wrapSubstitutionScopeIfNeed(useSiteSession, jvmMappedScope, klass, scopeSession)
}
} ?: declaredMemberScope
} else {
declaredMemberScope
@@ -82,7 +94,7 @@ abstract class FirSymbolProvider : FirSessionComponent {
val declaredScope = declaredMemberScope(klass)
val wrappedDeclaredScope = wrapScopeWithJvmMapped(
klass.classId, declaredScope, useSiteSession, scopeSession
klass, declaredScope, useSiteSession, scopeSession
)
val scopes = lookupSuperTypes(klass, lookupInterfaces = true, deep = false, useSiteSession = useSiteSession)
.mapNotNull { useSiteSuperType ->
@@ -15,14 +15,14 @@ import org.jetbrains.kotlin.name.Name
class JvmMappedScope(
private val declaredMemberScope: FirScope,
private val javaMappedScope: FirScope,
private val javaMappedClassUseSiteScope: FirScope,
private val whiteListSignaturesByName: Map<Name, List<String>>
) : FirScope() {
override fun processFunctionsByName(name: Name, processor: (FirFunctionSymbol<*>) -> ProcessorAction): ProcessorAction {
val whiteListSignatures = whiteListSignaturesByName[name]
?: return declaredMemberScope.processFunctionsByName(name, processor)
if (!javaMappedScope.processFunctionsByName(name) { symbol ->
if (!javaMappedClassUseSiteScope.processFunctionsByName(name) { symbol ->
val jvmSignature = symbol.fir.computeJvmDescriptor()
if (jvmSignature !in whiteListSignatures) {
ProcessorAction.NEXT
@@ -0,0 +1,23 @@
// FULL_JDK
// FILE: Call.java
import org.jetbrains.annotations.NotNull;
import java.util.*;
public interface Call<D> {
@NotNull
Map<String, String> getArguments();
}
// FILE: test.kt
fun <D : Any> Call<D>.testForEach() {
arguments.forEach { key, value ->
key.length
value.length
}
arguments.forEach {
<!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>key<!>.<!UNRESOLVED_REFERENCE!>length<!>
<!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>value<!>.<!UNRESOLVED_REFERENCE!>length<!>
}
}
@@ -0,0 +1,13 @@
FILE: test.kt
public final fun <D : R|kotlin/Any|> R|Call<D>|.testForEach(): R|kotlin/Unit| {
R|/Call.arguments|.R|FakeOverride<java/util/Map.forEach: R|kotlin/Unit|>|(<L> = forEach@fun <anonymous>(key: R|kotlin/String!|, value: R|kotlin/String!|): R|kotlin/Unit| {
R|<local>/key|.R|kotlin/String.length|
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>#
}
)
}
@@ -0,0 +1,19 @@
interface Diagnostic {
val name: String
}
fun foo(conflicting: List<Diagnostic>) {
val filtered = arrayListOf<Diagnostic>()
conflicting.groupBy {
it.name
}.forEach {
val diagnostics = <!UNRESOLVED_REFERENCE!>it<!>.<!UNRESOLVED_REFERENCE!>value<!>
filtered.addAll(
diagnostics.<!AMBIGUITY!>filter<!> { me ->
diagnostics.<!AMBIGUITY!>none<!> { other ->
me != other
}
}
)
}
}
@@ -0,0 +1,23 @@
FILE: noneWithForEach.kt
public abstract interface Diagnostic : R|kotlin/Any| {
public abstract val name: R|kotlin/String|
public get(): R|kotlin/String|
}
public final fun foo(conflicting: R|kotlin/collections/List<Diagnostic>|): R|kotlin/Unit| {
lval filtered: R|kotlin/collections/ArrayList<Diagnostic>| = R|kotlin/collections/arrayListOf|<R|Diagnostic|>()
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|<local>/me|, R|<local>/other|)
}
)
}
))
}
)
}
@@ -133,6 +133,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
runTest("compiler/fir/resolve/testData/resolve/stdlib/multipleImplicitReceivers.kt");
}
@TestMetadata("noneWithForEach.kt")
public void testNoneWithForEach() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/stdlib/noneWithForEach.kt");
}
@TestMetadata("nullableTypeParameter.kt")
public void testNullableTypeParameter() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/stdlib/nullableTypeParameter.kt");
@@ -524,6 +529,11 @@ public class FirDiagnosticsWithStdlibTestGenerated extends AbstractFirDiagnostic
runTest("compiler/fir/resolve/testData/resolve/stdlib/j+k/StaticGenericMethod.kt");
}
@TestMetadata("SyntheticWithForEach.kt")
public void testSyntheticWithForEach() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/stdlib/j+k/SyntheticWithForEach.kt");
}
@TestMetadata("typeParameterUse.kt")
public void testTypeParameterUse() throws Exception {
runTest("compiler/fir/resolve/testData/resolve/stdlib/j+k/typeParameterUse.kt");