From 468af0bb85e53fe381361a1853baa745227bd129 Mon Sep 17 00:00:00 2001 From: Simon Ogorodnik Date: Fri, 17 Jul 2020 17:22:39 +0300 Subject: [PATCH] J2K: Fix type parameters in LHS of callable reference --- .../kotlin/nj2k/JavaToJKTreeBuilder.kt | 10 ++++++++- .../MethodReferenceToLambdaConversion.kt | 18 ++++++++++------ .../kotlin/nj2k/printing/JKCodeBuilder.kt | 4 ++++ .../jetbrains/kotlin/nj2k/tree/expressions.kt | 5 +++++ .../kotlin/nj2k/tree/visitors/JKVisitor.kt | 1 + .../function/genericMemberFunctionRef.java | 21 +++++++++++++++++++ .../function/genericMemberFunctionRef.kt | 12 +++++++++++ ...otlinConverterSingleFileTestGenerated.java | 5 +++++ 8 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 nj2k/testData/newJ2k/function/genericMemberFunctionRef.java create mode 100644 nj2k/testData/newJ2k/function/genericMemberFunctionRef.kt diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt index 1c7e20f87c8..ed99e0815ce 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt @@ -450,13 +450,20 @@ class JavaToJKTreeBuilder constructor( } return JKMethodReferenceExpression( - qualifierExpression?.toJK() ?: JKStubExpression(), + methodReferenceQualifier(), symbol, functionalType(), isConstructor ) } + fun PsiMethodReferenceExpression.methodReferenceQualifier(): JKExpression { + val qualifierType = qualifierType + if (qualifierType != null) return JKTypeQualifierExpression(typeFactory.fromPsiType(qualifierType.type)) + + return qualifierExpression?.toJK() ?: JKStubExpression() + } + fun PsiReferenceExpression.toJK(): JKExpression { if (this is PsiMethodReferenceExpression) return toJK() val target = resolve() @@ -476,6 +483,7 @@ class JavaToJKTreeBuilder constructor( is JKFieldSymbol -> JKFieldAccessExpression(symbol) is JKPackageSymbol -> JKPackageAccessExpression(symbol) is JKMethodSymbol -> JKMethodAccessExpression(symbol) + is JKTypeParameterSymbol -> JKTypeQualifierExpression(JKTypeParameterType(symbol)) else -> throwCanNotConvertError("unexpected symbol ${symbol::class}") }.qualified(qualifierExpression?.toJK()).also { it.withFormattingFrom(this) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt index d3366e12ec4..45895d533e0 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt @@ -32,19 +32,25 @@ class MethodReferenceToLambdaConversion(context: NewJ2kConverterContext) : Recur .safeAs() ?.singleFunctionParameterTypes() - val receiverParameter = element.qualifier + val qualifierExpression = element.qualifier .nullIfStubExpression() - ?.safeAs() - ?.takeIf { symbol.safeAs()?.isStatic == false && !element.isConstructorCall } - ?.let { classAccessExpression -> + + val receiverParameter = if (symbol.safeAs()?.isStatic == false && !element.isConstructorCall) { + val type = when (qualifierExpression) { + is JKTypeQualifierExpression -> qualifierExpression.type + is JKClassAccessExpression -> JKClassType(qualifierExpression.identifier) + else -> null + } + if (type != null) { JKParameter( JKTypeElement( - parametersTypesByFunctionalInterface?.firstOrNull() ?: JKClassType(classAccessExpression.identifier) + parametersTypesByFunctionalInterface?.firstOrNull() ?: type ), JKNameIdentifier(RECEIVER_NAME), isVarArgs = false ) - } + } else null + } else null val explicitParameterTypesByFunctionalInterface = if (receiverParameter != null) parametersTypesByFunctionalInterface?.drop(1) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt index 86d090dceba..33b672d3fd2 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/printing/JKCodeBuilder.kt @@ -146,6 +146,10 @@ internal class JKCodeBuilder(context: NewJ2kConverterContext) { printer.renderSymbol(methodAccessExpression.identifier, methodAccessExpression) } + override fun visitTypeQualifierExpression(typeQualifierExpression: JKTypeQualifierExpression) { + printer.renderType(typeQualifierExpression.type, typeQualifierExpression) + } + override fun visitFileRaw(file: JKFile) { if (file.packageDeclaration.name.value.isNotEmpty()) { file.packageDeclaration.accept(this) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/expressions.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/expressions.kt index eaf256fc53c..99923883450 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/expressions.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/expressions.kt @@ -201,6 +201,11 @@ class JKMethodAccessExpression(val identifier: JKMethodSymbol) : JKExpression() override fun accept(visitor: JKVisitor) = visitor.visitMethodAccessExpression(this) } +class JKTypeQualifierExpression(val type: JKType) : JKExpression() { + override fun calculateType(typeFactory: JKTypeFactory): JKType? = null + override fun accept(visitor: JKVisitor) = visitor.visitTypeQualifierExpression(this) +} + class JKMethodReferenceExpression( qualifier: JKExpression, val identifier: JKSymbol, diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitor.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitor.kt index e0572b84b41..84bb3a6796b 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitor.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitor.kt @@ -84,6 +84,7 @@ abstract class JKVisitor { open fun visitPackageAccessExpression(packageAccessExpression: JKPackageAccessExpression) = visitExpression(packageAccessExpression) open fun visitClassAccessExpression(classAccessExpression: JKClassAccessExpression) = visitExpression(classAccessExpression) open fun visitMethodAccessExpression(methodAccessExpression: JKMethodAccessExpression) = visitExpression(methodAccessExpression) + open fun visitTypeQualifierExpression(typeQualifierExpression: JKTypeQualifierExpression) = visitExpression(typeQualifierExpression) open fun visitMethodReferenceExpression(methodReferenceExpression: JKMethodReferenceExpression) = visitExpression(methodReferenceExpression) diff --git a/nj2k/testData/newJ2k/function/genericMemberFunctionRef.java b/nj2k/testData/newJ2k/function/genericMemberFunctionRef.java new file mode 100644 index 00000000000..eac9437bc52 --- /dev/null +++ b/nj2k/testData/newJ2k/function/genericMemberFunctionRef.java @@ -0,0 +1,21 @@ +// RUNTIME_WITH_FULL_JDK + +import java.util.List; + +public class TestLambda { + public static class Box { + + private Q value; + public Q unbox() { + return value; + } + + public Box(Q q) { + value = q; + } + } + + public String toStringAllBox(List> list) { + return list.stream().map(Box::unbox).map(T::toString).reduce((s1,s2) -> s1 + ", " + s2 ).orElse(""); + } +} \ No newline at end of file diff --git a/nj2k/testData/newJ2k/function/genericMemberFunctionRef.kt b/nj2k/testData/newJ2k/function/genericMemberFunctionRef.kt new file mode 100644 index 00000000000..b4805175998 --- /dev/null +++ b/nj2k/testData/newJ2k/function/genericMemberFunctionRef.kt @@ -0,0 +1,12 @@ +class TestLambda { + class Box(private val value: Q) { + fun unbox(): Q { + return value + } + } + + fun toStringAllBox(list: List>): String { + return list.stream().map { obj: Box -> obj.unbox() }.map { obj: T -> obj.toString() } + .reduce { s1: String, s2: String -> "$s1, $s2" }.orElse("") + } +} \ No newline at end of file diff --git a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java index 755e97a6cbc..e6f0a87e36e 100644 --- a/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java +++ b/nj2k/tests/org/jetbrains/kotlin/nj2k/NewJavaToKotlinConverterSingleFileTestGenerated.java @@ -2422,6 +2422,11 @@ public class NewJavaToKotlinConverterSingleFileTestGenerated extends AbstractNew runTest("nj2k/testData/newJ2k/function/functionInFinalClass.java"); } + @TestMetadata("genericMemberFunctionRef.java") + public void testGenericMemberFunctionRef() throws Exception { + runTest("nj2k/testData/newJ2k/function/genericMemberFunctionRef.java"); + } + @TestMetadata("internal.java") public void testInternal() throws Exception { runTest("nj2k/testData/newJ2k/function/internal.java");