diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt index af68853a416..086e465a324 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/ConversionsRunner.kt @@ -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) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt index c213f22cbc5..8da3571c7bd 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/JavaToJKTreeBuilder.kt @@ -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() + ?.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()?.classReference is JKMultiverseClassSymbol + }?.asTypeElement() ?: JKTypeElementImpl(JKNoTypeImpl) + + fun PsiMethodReferenceExpression.toJK(): JKMethodReferenceExpression { + val symbol = symbolProvider.provideSymbol(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 diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt index 90c58221b22..a9d8b433d06 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/NewCodeBuilder.kt @@ -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) { diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt new file mode 100644 index 00000000000..8370839f9fa --- /dev/null +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/conversions/MethodReferenceToLambdaConversion.kt @@ -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() + ?.takeIf { symbol.safeAs()?.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()?.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 + } +} \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/expressions.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/expressions.kt index eff0438e3be..98287c154b2 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/expressions.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/expressions.kt @@ -597,3 +597,11 @@ fun JKAnnotation.isVarargsArgument(index: Int): Boolean { fun JKExpression.asStatement(): JKExpressionStatement = JKExpressionStatementImpl(this) +inline fun 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 \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt index 27558ad1fcb..45ac3b57390 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/jk.kt @@ -391,11 +391,14 @@ class JKClassAccessExpressionImpl(override var identifier: JKClassSymbol) : JKCl class JKLambdaExpressionImpl( statement: JKStatement, parameters: List, + 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 accept(visitor: JKVisitor, 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 accept(visitor: JKVisitor, 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 accept(visitor: JKVisitor, data: D): R = visitor.visitMethodReferenceExpression(this, data) } \ No newline at end of file diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/symbols.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/symbols.kt index 3d8611aa029..655a262107e 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/symbols.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/impl/symbols.kt @@ -76,6 +76,28 @@ interface JKMethodSymbol : JKNamedSymbol { val returnType: JKType? } +val JKMethodSymbol.parameterNames: List? + 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()?.classKind == JKClass.ClassKind.COMPANION + is JKUnresolvedSymbol -> false + else -> false + } + + fun JKMethodSymbol.parameterTypesWithUnfoldedVarargs(): Sequence? { val realParameterTypes = parameterTypes ?: return null if (realParameterTypes.isEmpty()) return emptySequence() diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/jk.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/jk.kt index 58e1b888661..3de9d7805fb 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/jk.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/jk.kt @@ -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 val returnType: JKTypeElement var statement: JKStatement + val functionalType: JKTypeElement } interface JKDelegationConstructorCall : JKMethodCallExpression { diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt index ce9d5a0b218..ff385c2cdcd 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/types.kt @@ -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()?.expression?.type(symbolProvider) + is JKMethodReferenceExpression -> JKNoTypeImpl //TODO else -> TODO(this::class.java.toString()) } 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 0cd854570e1..58b706622b5 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitor.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitor.kt @@ -37,6 +37,7 @@ interface JKVisitor { 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) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorVoid.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorVoid.kt index 5791bc0fa3b..547e9bd6011 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorVoid.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorVoid.kt @@ -71,6 +71,8 @@ interface JKVisitorVoid : JKVisitor { 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) diff --git a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorWithCommentsPrinting.kt b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorWithCommentsPrinting.kt index ef4842b8105..a6698ee3d43 100644 --- a/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorWithCommentsPrinting.kt +++ b/nj2k/src/org/jetbrains/kotlin/nj2k/tree/visitors/JKVisitorWithCommentsPrinting.kt @@ -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)