diff --git a/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java b/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java index 9200e77c2..e8221bb87 100755 --- a/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java +++ b/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java @@ -41,6 +41,7 @@ public class KotlinPrinter

extends KotlinVisitor> { private final KotlinJavaPrinter

delegate; + public KotlinPrinter() { delegate = delegate(); } @@ -153,7 +154,7 @@ public J visitDestructuringDeclaration(K.DestructuringDeclaration destructuringD } if (!destructuringDeclaration.getInitializer().getVariables().isEmpty() && - destructuringDeclaration.getInitializer().getVariables().get(0).getPadding().getInitializer() != null) { + destructuringDeclaration.getInitializer().getVariables().get(0).getPadding().getInitializer() != null) { visitSpace(Objects.requireNonNull(destructuringDeclaration.getInitializer().getVariables().get(0).getPadding() .getInitializer()).getBefore(), Space.Location.LANGUAGE_EXTENSION, p); p.append("="); @@ -272,14 +273,14 @@ public J visitProperty(K.Property property, PrintOutputCapture

p) { Extension extension = vd.getMarkers().findFirst(Extension.class).orElse(null); if (extension != null) { if (property.getSetter() != null && - !property.getSetter().getParameters().isEmpty() && - property.getSetter().getParameters().get(0) instanceof J.VariableDeclarations) { + !property.getSetter().getParameters().isEmpty() && + property.getSetter().getParameters().get(0) instanceof J.VariableDeclarations) { visit(((J.VariableDeclarations) property.getSetter().getParameters().get(0)).getTypeExpression(), p); delegate.visitSpace(property.getSetter().getPadding().getParameters().getPadding().getElements().get(0).getAfter(), Space.Location.LANGUAGE_EXTENSION, p); p.append("."); } else if (property.getGetter() != null && - !property.getGetter().getParameters().isEmpty() && - property.getGetter().getParameters().get(0) instanceof J.VariableDeclarations) { + !property.getGetter().getParameters().isEmpty() && + property.getGetter().getParameters().get(0) instanceof J.VariableDeclarations) { visit(((J.VariableDeclarations) property.getGetter().getParameters().get(0)).getTypeExpression(), p); delegate.visitSpace(property.getGetter().getPadding().getParameters().getPadding().getElements().get(0).getAfter(), Space.Location.LANGUAGE_EXTENSION, p); p.append("."); @@ -461,13 +462,13 @@ public J visitBlock(J.Block block, PrintOutputCapture

p) { p.append("="); } - boolean omitParens = block.getMarkers().findFirst(OmitBraces.class).isPresent(); - if (!omitParens) { + boolean omitBraces = block.getMarkers().findFirst(OmitBraces.class).isPresent(); + if (!omitBraces) { p.append("{"); } visitStatements(block.getPadding().getStatements(), JRightPadded.Location.BLOCK_STATEMENT, p); visitSpace(block.getEnd(), Space.Location.BLOCK_END, p); - if (!omitParens) { + if (!omitBraces) { p.append("}"); } afterSyntax(block, p); @@ -693,26 +694,28 @@ public J visitLabel(J.Label label, PrintOutputCapture

p) { @Override public J visitLambda(J.Lambda lambda, PrintOutputCapture

p) { beforeSyntax(lambda, Space.Location.LAMBDA_PREFIX, p); - boolean omitBraces = lambda.getMarkers().findFirst(OmitBraces.class).isPresent(); - if (!omitBraces) { - p.append('{'); - } - visitLambdaParameters(lambda.getParameters(), p); - if (!lambda.getParameters().getParameters().isEmpty()) { - visitSpace(lambda.getArrow(), Space.Location.LAMBDA_ARROW_PREFIX, p); - p.append("->"); - } - if (lambda.getBody() instanceof J.Block) { - J.Block block = (J.Block) lambda.getBody(); - visitStatements(block.getPadding().getStatements(), JRightPadded.Location.BLOCK_STATEMENT, p); - visitSpace(block.getEnd(), Space.Location.BLOCK_END, p); + if (lambda.getMarkers().findFirst(AnonymousFunction.class).isPresent()) { + p.append("fun"); + visitLambdaParameters(lambda.getParameters(), p); + visitBlock((J.Block) lambda.getBody(), p); } else { + boolean omitBraces = lambda.getMarkers().findFirst(OmitBraces.class).isPresent(); + if (!omitBraces) { + p.append('{'); + } + + visitLambdaParameters(lambda.getParameters(), p); + if (!lambda.getParameters().getParameters().isEmpty()) { + visitSpace(lambda.getArrow(), Space.Location.LAMBDA_ARROW_PREFIX, p); + p.append("->"); + } visit(lambda.getBody(), p); + if (!omitBraces) { + p.append('}'); + } } - if (!omitBraces) { - p.append('}'); - } + afterSyntax(lambda, p); return lambda; } @@ -874,7 +877,7 @@ private void visitArgumentsContainer(JContainer argContainer, Space. if (i > 0 && omitParensOnMethod && ( !args.get(0).getElement().getMarkers().findFirst(OmitParentheses.class).isPresent() && - !args.get(0).getElement().getMarkers().findFirst(OmitParentheses.class).isPresent())) { + !args.get(0).getElement().getMarkers().findFirst(OmitParentheses.class).isPresent())) { p.append(')'); } else if (i > 0) { p.append(','); diff --git a/src/main/java/org/openrewrite/kotlin/marker/AnonymousFunction.java b/src/main/java/org/openrewrite/kotlin/marker/AnonymousFunction.java new file mode 100644 index 000000000..ba20257aa --- /dev/null +++ b/src/main/java/org/openrewrite/kotlin/marker/AnonymousFunction.java @@ -0,0 +1,32 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.kotlin.marker; + +import lombok.Value; +import lombok.With; +import org.openrewrite.marker.Marker; + +import java.util.UUID; + +@Value +@With +public class AnonymousFunction implements Marker { + UUID id; + + public AnonymousFunction(UUID id) { + this.id = id; + } +} diff --git a/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt b/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt index f07dbe3ac..3f12ba700 100644 --- a/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt +++ b/src/main/kotlin/org/openrewrite/kotlin/internal/KotlinParserVisitor.kt @@ -452,9 +452,6 @@ class KotlinParserVisitor( if (body is J.Block && !omitBraces) { body = body.withEnd(sourceBefore("}")) } - if (body != null && anonymousFunction.valueParameters.isEmpty()) { - body = body.withMarkers(body.markers.removeByType(OmitBraces::class.java)) - } if (body == null) { body = J.Block( randomId(), @@ -481,10 +478,54 @@ class KotlinParserVisitor( anonymousFunctionExpression: FirAnonymousFunctionExpression, data: ExecutionContext ): J { - if (!anonymousFunctionExpression.anonymousFunction.isLambda) { - throw UnsupportedOperationException("Unsupported anonymous function expression.") + val anonymousFunction = anonymousFunctionExpression.anonymousFunction + if (anonymousFunction.isLambda) { + return visitAnonymousFunction(anonymousFunction, data) + } else { + val prefix = sourceBefore("fun") + val before = sourceBefore("(") + val params: List> = if (anonymousFunction.valueParameters.isNotEmpty()) + convertAll( + anonymousFunction.valueParameters, ",", ")", data + ) + else + listOf( + padRight( + J.Empty(randomId(), sourceBefore(")"), Markers.EMPTY), Space.EMPTY + ) + ) + + val body = mapFunctionBody(anonymousFunction, data)!! + return J.Lambda( + randomId(), + prefix, + Markers.EMPTY.addIfAbsent(AnonymousFunction(randomId())), + J.Lambda.Parameters(randomId(), before, Markers.EMPTY, true, params), + Space.EMPTY, + body, + null + ) + } + } + + private fun mapFunctionBody(function: FirFunction, data: ExecutionContext): J.Block? { + val saveCursor = cursor + val before = whitespace() + return if (function.body is FirSingleExpressionBlock) { + if (skip("=")) { + convertToBlock(function.body as FirSingleExpressionBlock, data).withPrefix(before) + } else { + throw IllegalStateException("Unexpected single block expression, cursor is likely at the wrong position.") + } + } else if (function.body is FirBlock) { + cursor(saveCursor) + visitElement(function.body!!, data) as J.Block? + } else if (function.body == null) { + cursor(saveCursor) + null + } else { + throw IllegalStateException("Unexpected function body.") } - return visitAnonymousFunction(anonymousFunctionExpression.anonymousFunction, data) } override fun visitAnonymousInitializer(anonymousInitializer: FirAnonymousInitializer, data: ExecutionContext): J { @@ -2291,17 +2332,7 @@ class KotlinParserVisitor( } else { cursor(saveCursor) } - var body: J.Block? = null - saveCursor = cursor - val blockPrefix = whitespace() - if (propertyAccessor.body is FirSingleExpressionBlock) { - if (skip("=")) { - body = convertToBlock(propertyAccessor.body as FirSingleExpressionBlock, data).withPrefix(blockPrefix) - } - } else { - cursor(saveCursor) - body = visitElement(propertyAccessor.body!!, data) as J.Block? - } + val body = mapFunctionBody(propertyAccessor, data) return J.MethodDeclaration( randomId(), prefix, @@ -2663,24 +2694,7 @@ class KotlinParserVisitor( } else { cursor(saveCursor) } - val body: J.Block? - saveCursor = cursor - before = whitespace() - if (simpleFunction.body is FirSingleExpressionBlock) { - if (skip("=")) { - body = convertToBlock(simpleFunction.body as FirSingleExpressionBlock, data).withPrefix(before) - } else { - throw IllegalStateException("Unexpected single block expression, cursor is likely at the wrong position.") - } - } else if (simpleFunction.body is FirBlock) { - cursor(saveCursor) - body = visitElement(simpleFunction.body!!, data) as J.Block? - } else if (simpleFunction.body == null) { - cursor(saveCursor) - body = null - } else { - throw IllegalStateException("Unexpected function body.") - } + val body = mapFunctionBody(simpleFunction, data) return J.MethodDeclaration( randomId(), diff --git a/src/test/java/org/openrewrite/kotlin/tree/AnonymousFunctionTest.java b/src/test/java/org/openrewrite/kotlin/tree/AnonymousFunctionTest.java new file mode 100644 index 000000000..badd2a40c --- /dev/null +++ b/src/test/java/org/openrewrite/kotlin/tree/AnonymousFunctionTest.java @@ -0,0 +1,37 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.kotlin.tree; + +import org.junit.jupiter.api.Test; +import org.openrewrite.Issue; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.kotlin.Assertions.kotlin; + +class AnonymousFunctionTest implements RewriteTest { + + @Test + @Issue("https://github.com/openrewrite/rewrite-kotlin/issues/287") + void notInitialized() { + rewriteRun( + kotlin( + """ + val positive = fun(i: Int) = i > 0 + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/kotlin/tree/ArrayTest.java b/src/test/java/org/openrewrite/kotlin/tree/ArrayTest.java index f0d812c56..64ee6b7d2 100644 --- a/src/test/java/org/openrewrite/kotlin/tree/ArrayTest.java +++ b/src/test/java/org/openrewrite/kotlin/tree/ArrayTest.java @@ -74,7 +74,7 @@ void conditionalArraySize() { rewriteRun( kotlin( """ - val arr = IntArray ( if (true) else 1 ) + val arr = IntArray ( if (true) 0 else 1 ) """ ) );