New J2K: add support of method reference conversion

This commit is contained in:
Ilya Kirillov
2019-04-22 14:24:34 +03:00
parent 8f4f80256a
commit f11311a20b
12 changed files with 240 additions and 28 deletions
@@ -68,10 +68,8 @@ object ConversionsRunner {
+LabeledStatementConversion()
+TypeParametersNullabilityConversion()
+ArrayOperationsConversion(context)
+BuiltinMembersConversion(context)
+EqualsOperatorConversion(context)
+TypeMappingConversion(context)
+ImplicitCastsConversion(context)
+InternalDeclarationConversion(context)
//Kotlin --> Kotlin conversions
@@ -80,6 +78,10 @@ object ConversionsRunner {
+StaticsToCompanionExtractConversion()
+InterfaceWithFieldConversion()
+ClassToObjectPromotionConversion(context)
+MethodReferenceToLambdaConversion(context)
+BuiltinMembersConversion(context)
+ImplicitCastsConversion(context)
+CollectImportsConversion(context)
+SortClassMembersConversion()
+AddElementsInfoConversion(context)
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.nj2k
import com.intellij.codeInsight.daemon.impl.quickfix.AddTypeArgumentsFix
import com.intellij.lang.jvm.JvmAnnotatedElement
import com.intellij.lang.jvm.JvmModifier
import com.intellij.psi.*
@@ -27,12 +28,11 @@ import com.intellij.psi.impl.source.tree.java.PsiClassObjectAccessExpressionImpl
import com.intellij.psi.impl.source.tree.java.PsiLabeledStatementImpl
import com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl
import com.intellij.psi.impl.source.tree.java.PsiNewExpressionImpl
import com.intellij.psi.infos.MethodCandidateInfo
import com.intellij.psi.javadoc.PsiDocComment
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.InheritanceUtil
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.psi.util.PsiUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.asJava.elements.KtLightField
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
@@ -49,7 +49,6 @@ import org.jetbrains.kotlin.nj2k.tree.impl.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isExtensionDeclaration
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
import org.jetbrains.kotlin.utils.addToStdlib.cast
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
@@ -265,21 +264,32 @@ class JavaToJKTreeBuilder constructor(
else -> JKBlockStatementImpl(JKBodyStubImpl)
}
},
with(declarationMapper) { parameterList.parameters.map { it.toJK() } }
with(declarationMapper) { parameterList.parameters.map { it.toJK() } },
functionalType()
).also {
it.assignNonCodeElements(this)
}
}
private fun JKExpression.qualified(qualifier: JKExpression?) =
if (qualifier != null && qualifier !is JKStubExpression) {
JKQualifiedExpressionImpl(qualifier, JKJavaQualifierImpl.DOT, this)
} else this
private fun PsiMethodCallExpression.getExplicitTypeArguments(): PsiReferenceParameterList {
if (typeArguments.isNotEmpty()) return typeArgumentList
val resolveResult = resolveMethodGenerics()
if (resolveResult is MethodCandidateInfo && resolveResult.isApplicable) {
val method = resolveResult.element
if (method.isConstructor || !method.hasTypeParameters()) return typeArgumentList
}
return AddTypeArgumentsFix.addTypeArguments(this, null)
?.safeAs<PsiMethodCallExpression>()
?.typeArgumentList
?: typeArgumentList
}
//TODO mostly copied from old j2k, refactor
fun PsiMethodCallExpression.toJK(): JKExpression {
val arguments = argumentList
val typeArguments = typeArgumentList.toJK()
val typeArguments = getExplicitTypeArguments().toJK()
val qualifier = methodExpression.qualifierExpression?.toJK()
val target = methodExpression.resolve()
val symbol = target?.let {
@@ -367,7 +377,30 @@ class JavaToJKTreeBuilder constructor(
}
}
fun PsiFunctionalExpression.functionalType(): JKTypeElement =
functionalInterfaceType?.toJK(symbolProvider)?.takeIf { type ->
type.safeAs<JKClassType>()?.classReference is JKMultiverseClassSymbol
}?.asTypeElement() ?: JKTypeElementImpl(JKNoTypeImpl)
fun PsiMethodReferenceExpression.toJK(): JKMethodReferenceExpression {
val symbol = symbolProvider.provideSymbol<JKNamedSymbol>(this).let { symbol ->
when {
symbol.isUnresolved() && isConstructor -> JKUnresolvedClassSymbol(qualifier?.text ?: text)
symbol.isUnresolved() && !isConstructor -> JKUnresolvedMethod(referenceName ?: text)
else -> symbol
}
}
return JKMethodReferenceExpressionImpl(
qualifierExpression?.toJK() ?: JKStubExpressionImpl(),
symbol,
functionalType(),
isConstructor
)
}
fun PsiReferenceExpression.toJK(): JKExpression {
if (this is PsiMethodReferenceExpression) return toJK()
val target = resolve()
if (target is KtLightClassForFacade
|| target is KtLightClassForDecompiledDeclaration
@@ -702,7 +702,7 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
}
renderClassSymbol(javaNewExpression.classSymbol, javaNewExpression)
javaNewExpression.typeArgumentList.accept(this)
if (!javaNewExpression.classSymbol.isInterface()) {
if (!javaNewExpression.classSymbol.isInterface() || javaNewExpression.arguments.arguments.isNotEmpty()) {
printer.par(ROUND) {
javaNewExpression.arguments.accept(this)
}
@@ -775,6 +775,18 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
printer.printWithNoIndent(packageAccessExpression.identifier.name.escaped())
}
override fun visitMethodReferenceExpressionRaw(methodReferenceExpression: JKMethodReferenceExpression) {
methodReferenceExpression.qualifier.accept(this)
printer.printWithNoIndent("::")
val needFqName = methodReferenceExpression.qualifier is JKStubExpression
val displayName =
if (needFqName) methodReferenceExpression.identifier.getDisplayName()
else methodReferenceExpression.identifier.name
printer.printWithNoIndent(displayName.escapedAsQualifiedName())
}
override fun visitDelegationConstructorCallRaw(delegationConstructorCall: JKDelegationConstructorCall) {
delegationConstructorCall.expression.accept(this)
printer.par {
@@ -839,24 +851,35 @@ class NewCodeBuilder(context: NewJ2kConverterContext) {
}
override fun visitLambdaExpressionRaw(lambdaExpression: JKLambdaExpression) {
printer.par(CURVED) {
if (lambdaExpression.statement.statements.size > 1)
printer.println()
lambdaExpression.parameters.firstOrNull()?.accept(this)
lambdaExpression.parameters.asSequence().drop(1).forEach { printer.printWithNoIndent(", "); it.accept(this) }
if (lambdaExpression.parameters.isNotEmpty()) {
printer.printWithNoIndent(" -> ")
}
val printLambda = {
printer.par(CURVED) {
if (lambdaExpression.statement.statements.size > 1)
printer.println()
lambdaExpression.parameters.firstOrNull()?.accept(this)
lambdaExpression.parameters.asSequence().drop(1).forEach { printer.printWithNoIndent(", "); it.accept(this) }
if (lambdaExpression.parameters.isNotEmpty()) {
printer.printWithNoIndent(" -> ")
}
val statement = lambdaExpression.statement
if (statement is JKBlockStatement) {
renderList(statement.block.statements, { printer.println() }) { it.accept(this) }
} else {
statement.accept(this)
val statement = lambdaExpression.statement
if (statement is JKBlockStatement) {
renderList(statement.block.statements, { printer.println() }) { it.accept(this) }
} else {
statement.accept(this)
}
if (lambdaExpression.statement.statements.size > 1)
printer.println()
}
if (lambdaExpression.statement.statements.size > 1)
printer.println()
}
if (lambdaExpression.functionalType.present()) {
renderType(lambdaExpression.functionalType.type, lambdaExpression)
printer.printWithNoIndent(" ")
printer.par(ROUND, printLambda)
} else {
printLambda()
}
}
override fun visitBlockStatementRaw(blockStatement: JKBlockStatement) {
@@ -0,0 +1,88 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.nj2k.conversions
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.nullIfStubExpression
import org.jetbrains.kotlin.nj2k.qualified
import org.jetbrains.kotlin.nj2k.tree.*
import org.jetbrains.kotlin.nj2k.tree.impl.*
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class MethodReferenceToLambdaConversion(private val context: NewJ2kConverterContext) : RecursiveApplicableConversionBase() {
override fun applyToElement(element: JKTreeElement): JKTreeElement {
if (element !is JKMethodReferenceExpression) return recurse(element)
val symbol = element.identifier
val parameters =
if (symbol is JKMethodSymbol) {
(symbol.parameterNames ?: return recurse(element)).zip(
symbol.parameterTypes ?: return recurse(element)
) { name, type ->
JKParameterImpl(
JKTypeElementImpl(type),
JKNameIdentifierImpl(name),
isVarArgs = false
)
}
} else emptyList()
val receiverParameter = element.qualifier
.nullIfStubExpression()
?.safeAs<JKClassAccessExpression>()
?.takeIf { symbol.safeAs<JKMethodSymbol>()?.isStatic == false && !element.isConstructorCall }
?.let { classAccessExpression ->
JKParameterImpl(
JKTypeElementImpl(JKClassTypeImpl(classAccessExpression.identifier)),
JKNameIdentifierImpl(RECEIVER_NAME),
isVarArgs = false
)
}
val arguments = parameters.map { parameter ->
val parameterSymbol = context.symbolProvider.provideUniverseSymbol(parameter)
JKArgumentImpl(JKFieldAccessExpressionImpl(parameterSymbol))
}
val callExpression =
when (symbol) {
is JKMethodSymbol ->
JKKtCallExpressionImpl(
symbol,
JKArgumentListImpl(arguments)
)
is JKClassSymbol -> JKJavaNewExpressionImpl(symbol, JKArgumentListImpl(), JKTypeArgumentListImpl())
is JKUnresolvedSymbol -> return recurse(element)
else -> error("Symbol should be either method symbol or class symbol, but it is ${symbol::class}")
}
val qualifier = when {
receiverParameter != null ->
JKFieldAccessExpressionImpl(context.symbolProvider.provideUniverseSymbol(receiverParameter))
element.isConstructorCall -> element.qualifier.safeAs<JKQualifiedExpression>()?.let { it::receiver.detached() }
else -> element::qualifier.detached().nullIfStubExpression()
}
val lambda = JKLambdaExpressionImpl(
JKExpressionStatementImpl(callExpression.qualified(qualifier)),
listOfNotNull(receiverParameter) + parameters,
element::functionalType.detached(),
JKTypeElementImpl(
when (symbol) {
is JKMethodSymbol -> symbol.returnType ?: JKNoTypeImpl
is JKClassSymbol -> JKClassTypeImpl(symbol)
is JKUnresolvedSymbol -> return recurse(element)
else -> error("Symbol should be either method symbol or class symbol, but it is ${symbol::class}")
}
)
)
return recurse(lambda)
}
companion object {
private const val RECEIVER_NAME = "obj" //name taken from old j2k
}
}
@@ -597,3 +597,11 @@ fun JKAnnotation.isVarargsArgument(index: Int): Boolean {
fun JKExpression.asStatement(): JKExpressionStatement =
JKExpressionStatementImpl(this)
inline fun <T : JKExpression> T.nullIfStubExpression(): T? =
if (this is JKStubExpression) null
else this
inline fun JKExpression.qualified(qualifier: JKExpression?) =
if (qualifier != null && qualifier !is JKStubExpression) {
JKQualifiedExpressionImpl(qualifier, JKJavaQualifierImpl.DOT, this)
} else this
@@ -391,11 +391,14 @@ class JKClassAccessExpressionImpl(override var identifier: JKClassSymbol) : JKCl
class JKLambdaExpressionImpl(
statement: JKStatement,
parameters: List<JKParameter>,
functionalType: JKTypeElement = JKTypeElementImpl(JKNoTypeImpl),
returnType: JKTypeElement = JKTypeElementImpl(JKContextType)//TODO use function type
) : JKLambdaExpression, JKBranchElementBase(), PsiOwner by PsiOwnerImpl() {
override var statement by child(statement)
override val returnType by child(returnType)
override var parameters by children(parameters)
override var functionalType by child(functionalType)
override fun <R, D> accept(visitor: JKVisitor<R, D>, data: D): R = visitor.visitLambdaExpression(this, data)
}
@@ -634,4 +637,17 @@ class JKVisibilityModifierElementImpl(override var visibility: Visibility) : JKV
class JKExtraModifierElementImpl(override var extraModifier: ExtraModifier) : JKExtraModifierElement() {
override fun <R, D> accept(visitor: JKVisitor<R, D>, data: D): R = visitor.visitExtraModifierElement(this, data)
}
class JKMethodReferenceExpressionImpl(
qualifier: JKExpression,
override val identifier: JKNamedSymbol,
functionalType: JKTypeElement,
override val isConstructorCall: Boolean
) : JKMethodReferenceExpression, JKBranchElementBase(), PsiOwner by PsiOwnerImpl() {
override val qualifier by child(qualifier)
override val functionalType by child(functionalType)
override fun <R, D> accept(visitor: JKVisitor<R, D>, data: D): R = visitor.visitMethodReferenceExpression(this, data)
}
@@ -76,6 +76,28 @@ interface JKMethodSymbol : JKNamedSymbol {
val returnType: JKType?
}
val JKMethodSymbol.parameterNames: List<String>?
get() {
return when (this) {
is JKMultiverseFunctionSymbol -> target.valueParameters.map { it.name ?: return null }
is JKMultiverseMethodSymbol -> target.parameters.map { it.name ?: return null }
is JKUniverseMethodSymbol -> target.parameters.map { it.name.value }
is JKUnresolvedSymbol -> null
else -> null
}
}
val JKMethodSymbol.isStatic: Boolean
get() = when (this) {
is JKMultiverseFunctionSymbol -> target.parent is KtObjectDeclaration
is JKMultiverseMethodSymbol -> target.hasModifierProperty(PsiModifier.STATIC)
is JKUniverseMethodSymbol -> target.parent?.parent?.safeAs<JKClass>()?.classKind == JKClass.ClassKind.COMPANION
is JKUnresolvedSymbol -> false
else -> false
}
fun JKMethodSymbol.parameterTypesWithUnfoldedVarargs(): Sequence<JKType>? {
val realParameterTypes = parameterTypes ?: return null
if (realParameterTypes.isEmpty()) return emptySequence()
@@ -291,6 +291,13 @@ interface JKNameIdentifier : JKIdentifier {
interface JKExpression : JKTreeElement, JKAnnotationMemberValue
interface JKMethodReferenceExpression : JKExpression, PsiOwner {
val qualifier: JKExpression
val identifier: JKNamedSymbol
val functionalType: JKTypeElement
val isConstructorCall: Boolean
}
abstract class JKExpressionStatement : JKStatement() {
abstract val expression: JKExpression
}
@@ -455,6 +462,7 @@ interface JKLambdaExpression : JKExpression {
var parameters: List<JKParameter>
val returnType: JKTypeElement
var statement: JKStatement
val functionalType: JKTypeElement
}
interface JKDelegationConstructorCall : JKMethodCallExpression {
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.util.getJavaClassDescriptor
import org.jetbrains.kotlin.j2k.ast.Nullability
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.JKSymbolProvider
import org.jetbrains.kotlin.nj2k.NewJ2kConverterContext
import org.jetbrains.kotlin.nj2k.kotlinTypeByName
import org.jetbrains.kotlin.nj2k.tree.impl.*
import org.jetbrains.kotlin.psi.KtClass
@@ -68,6 +68,7 @@ fun JKExpression.type(symbolProvider: JKSymbolProvider): JKType? =
is JKLambdaExpression -> returnType.type
is JKLabeledStatement ->
statement.safeAs<JKExpressionStatement>()?.expression?.type(symbolProvider)
is JKMethodReferenceExpression -> JKNoTypeImpl //TODO
else -> TODO(this::class.java.toString())
}
@@ -37,6 +37,7 @@ interface JKVisitor<out R, in D> {
fun visitIdentifier(identifier: JKIdentifier, data: D): R = visitTreeElement(identifier, data)
fun visitNameIdentifier(nameIdentifier: JKNameIdentifier, data: D): R = visitIdentifier(nameIdentifier, data)
fun visitExpression(expression: JKExpression, data: D): R = visitTreeElement(expression, data)
fun visitMethodReferenceExpression(methodReferenceExpression: JKMethodReferenceExpression, data: D): R = visitExpression(methodReferenceExpression, data)
fun visitExpressionStatement(expressionStatement: JKExpressionStatement, data: D): R = visitStatement(expressionStatement, data)
fun visitDeclarationStatement(declarationStatement: JKDeclarationStatement, data: D): R = visitStatement(declarationStatement, data)
fun visitOperatorExpression(operatorExpression: JKOperatorExpression, data: D): R = visitExpression(operatorExpression, data)
@@ -71,6 +71,8 @@ interface JKVisitorVoid : JKVisitor<Unit, Nothing?> {
override fun visitNameIdentifier(nameIdentifier: JKNameIdentifier, data: Nothing?) = visitNameIdentifier(nameIdentifier)
fun visitExpression(expression: JKExpression) = visitTreeElement(expression, null)
override fun visitExpression(expression: JKExpression, data: Nothing?) = visitExpression(expression)
fun visitMethodReferenceExpression(methodReferenceExpression: JKMethodReferenceExpression) = visitExpression(methodReferenceExpression, null)
override fun visitMethodReferenceExpression(methodReferenceExpression: JKMethodReferenceExpression, data: Nothing?) = visitMethodReferenceExpression(methodReferenceExpression)
fun visitExpressionStatement(expressionStatement: JKExpressionStatement) = visitStatement(expressionStatement, null)
override fun visitExpressionStatement(expressionStatement: JKExpressionStatement, data: Nothing?) = visitExpressionStatement(expressionStatement)
fun visitDeclarationStatement(declarationStatement: JKDeclarationStatement) = visitStatement(declarationStatement, null)
@@ -278,6 +278,14 @@ interface JKVisitorWithCommentsPrinting : JKVisitorVoid {
fun visitExpressionRaw(expression: JKExpression) = visitTreeElementRaw(expression)
override fun visitMethodReferenceExpression(methodReferenceExpression: JKMethodReferenceExpression) {
printLeftNonCodeElements(methodReferenceExpression)
visitMethodReferenceExpressionRaw(methodReferenceExpression)
printRightNonCodeElements(methodReferenceExpression)
}
fun visitMethodReferenceExpressionRaw(methodReferenceExpression: JKMethodReferenceExpression) = visitExpressionRaw(methodReferenceExpression)
override fun visitExpressionStatement(expressionStatement: JKExpressionStatement) {
printLeftNonCodeElements(expressionStatement)
visitExpressionStatementRaw(expressionStatement)