diff --git a/src/main/java/org/openrewrite/kotlin/KotlinVisitor.java b/src/main/java/org/openrewrite/kotlin/KotlinVisitor.java index 6468de0ac..6b581f810 100644 --- a/src/main/java/org/openrewrite/kotlin/KotlinVisitor.java +++ b/src/main/java/org/openrewrite/kotlin/KotlinVisitor.java @@ -276,8 +276,7 @@ public J visitProperty(K.Property property, P p) { pr = pr.withVariableDeclarations(visitAndCast(pr.getVariableDeclarations(), p)); pr = pr.getPadding().withReceiver(visitRightPadded(pr.getPadding().getReceiver(), p)); - pr = pr.withGetter(visitAndCast(pr.getGetter(), p)); - pr = pr.withSetter(visitAndCast(pr.getSetter(), p)); + pr = pr.withAccessors(visitContainer(pr.getAccessors(), p)); return pr; } diff --git a/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java b/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java index 59c8fa4f9..8d89ea6e7 100755 --- a/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java +++ b/src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java @@ -395,14 +395,7 @@ public J visitProperty(K.Property property, PrintOutputCapture

p) { delegate.visitContainer("where", property.getTypeConstraints().getPadding().getConstraints(), JContainer.Location.TYPE_PARAMETERS, ",", "", p); } - if (property.isSetterFirst()) { - visit(property.getSetter(), p); - visit(property.getGetter(), p); - } else { - visit(property.getGetter(), p); - visit(property.getSetter(), p); - } - + visitContainer(property.getAccessors(), p); afterSyntax(property, p); return property; } diff --git a/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java b/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java index a535a1478..2cf3fef07 100644 --- a/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java +++ b/src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java @@ -2811,13 +2811,10 @@ public J visitProperty(KtProperty property, ExecutionContext data) { List modifiers = mapModifiers(property.getModifierList(), leadingAnnotations, lastAnnotations, data); TypeTree typeExpression = null; List> variables = new ArrayList<>(); - J.MethodDeclaration getter = null; - J.MethodDeclaration setter = null; JRightPadded receiver = null; JContainer typeParameters = property.getTypeParameterList() != null ? JContainer.build(prefix(property.getTypeParameterList()), mapTypeParameters(property.getTypeParameterList(), data), Markers.EMPTY) : null; K.TypeConstraints typeConstraints = null; - boolean isSetterFirst = false; Set prefixConsumedSet = preConsumedInfix(property); modifiers.add(new J.Modifier( @@ -2867,18 +2864,6 @@ public J visitProperty(KtProperty property, ExecutionContext data) { typeExpression = (TypeTree) requireNonNull(property.getTypeReference()).accept(this, data); } - if (property.getGetter() != null) { - getter = (J.MethodDeclaration) property.getGetter().accept(this, data); - } - - if (property.getSetter() != null) { - setter = (J.MethodDeclaration) property.getSetter().accept(this, data); - } - - if (getter != null && setter != null) { - isSetterFirst = property.getSetter().getTextRange().getStartOffset() < property.getGetter().getTextRange().getStartOffset(); - } - J.VariableDeclarations variableDeclarations = new J.VariableDeclarations( Tree.randomId(), endFixPrefixAndInfix(property), // overlaps with right-padding of previous statement @@ -2898,7 +2883,16 @@ public J visitProperty(KtProperty property, ExecutionContext data) { .withPrefix(prefix(whereKeyword)); } - if (getter != null || setter != null || receiver != null || typeConstraints != null) { + List ktPropertyAccessors = property.getAccessors(); + if (!ktPropertyAccessors.isEmpty() || receiver != null || typeConstraints != null) { + List> accessors = new ArrayList<>(ktPropertyAccessors.size()); + PsiElement lastChild = findLastChild(property, c -> !isSpace(c.getNode()) && !isSemicolon(c)); + + for (KtPropertyAccessor ktPropertyAccessor : ktPropertyAccessors) { + J.MethodDeclaration accessor = (J.MethodDeclaration) ktPropertyAccessor.accept(this, data); + accessors.add((lastChild == ktPropertyAccessor) ? padRight(accessor, Space.EMPTY) : maybeTrailingSemicolon(accessor, ktPropertyAccessor)); + } + return new K.Property( randomId(), deepPrefix(property), @@ -2906,9 +2900,7 @@ public J visitProperty(KtProperty property, ExecutionContext data) { typeParameters, variableDeclarations.withPrefix(Space.EMPTY), typeConstraints, - getter, - setter, - isSetterFirst, + JContainer.build(accessors), receiver ); } else { @@ -3671,14 +3663,12 @@ private J.Block convertToBlock(KtExpression ktExpression, ExecutionContext data) private JRightPadded maybeTrailingSemicolon(J2 j, KtElement element) { PsiElement maybeSemicolon = findLastNotSpaceChild(element); - boolean hasSemicolon = maybeSemicolon instanceof LeafPsiElement && ((LeafPsiElement) maybeSemicolon).getElementType() == KtTokens.SEMICOLON; - - if (hasSemicolon) { + if (isSemicolon(maybeSemicolon)) { return new JRightPadded<>(j, prefix(maybeSemicolon), Markers.EMPTY.add(new Semicolon(randomId()))); } maybeSemicolon = PsiTreeUtil.skipWhitespacesAndCommentsForward(element); - if (maybeSemicolon instanceof LeafPsiElement && ((LeafPsiElement) maybeSemicolon).getElementType() == KtTokens.SEMICOLON) { + if (isSemicolon(maybeSemicolon)) { return new JRightPadded<>(j, deepPrefix(maybeSemicolon), Markers.EMPTY.add(new Semicolon(randomId()))); } diff --git a/src/main/java/org/openrewrite/kotlin/tree/K.java b/src/main/java/org/openrewrite/kotlin/tree/K.java index bc461145c..a1279543f 100644 --- a/src/main/java/org/openrewrite/kotlin/tree/K.java +++ b/src/main/java/org/openrewrite/kotlin/tree/K.java @@ -41,6 +41,7 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Path; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.UUID; @@ -1503,16 +1504,57 @@ public List getTypeParameters() { @Nullable TypeConstraints typeConstraints; - @Nullable - J.MethodDeclaration getter; + // A replacement of `getter`,`setter` and `isSetterFirst` + JContainer accessors; @Nullable - J.MethodDeclaration setter; + JRightPadded receiver; - boolean isSetterFirst; + // For backward compatibility, handle removed fields `getter`, 'setter' and `isSetterFirst` which has been relocated to `accessors` + // Todo, Remove when all kotlin LSTs have been rebuilt. + @Deprecated + @JsonCreator + public Property(UUID id, + Space prefix, + Markers markers, + JContainer typeParameters, + VariableDeclarations variableDeclarations, + @Nullable K.TypeConstraints typeConstraints, + @Nullable @JsonProperty("getter") J.MethodDeclaration getter, + @Nullable @JsonProperty("setter") J.MethodDeclaration setter, + @Nullable @JsonProperty("isSetterFirst") Boolean isSetterFirst, + JContainer accessors, + @Nullable JRightPadded receiver + ) { + this.id = id; + this.prefix = prefix; + this.markers = markers; + this.typeParameters = typeParameters; + this.variableDeclarations = variableDeclarations; + this.typeConstraints = typeConstraints; - @Nullable - JRightPadded receiver; + if (getter != null || setter != null || isSetterFirst != null) { + List> rps = new ArrayList<>(2); + + // handle old LST removed fields `getter`, 'setter' and `isSetterFirst` which has been relocated to `accessors` + if (setter != null) { + rps.add(new JRightPadded<>(setter, Space.EMPTY, Markers.EMPTY)); + } + + if (getter != null) { + rps.add(new JRightPadded<>(getter, Space.EMPTY, Markers.EMPTY)); + } + + if (Boolean.FALSE.equals(isSetterFirst)) { + Collections.reverse(rps); + } + this.accessors = JContainer.build(rps); + } else { + this.accessors = accessors; + } + + this.receiver = receiver; + } @Nullable public Expression getReceiver() { @@ -1562,7 +1604,7 @@ public JContainer getTypeParameters() { public Property withTypeParameters(@Nullable JContainer typeParameters) { return t.typeParameters == typeParameters ? t : new Property(t.id, t.prefix, t.markers, typeParameters, - t.variableDeclarations, t.typeConstraints, t.getter, t.setter, t.isSetterFirst, t.receiver); + t.variableDeclarations, t.typeConstraints, t.accessors, t.receiver); } @Nullable @@ -1573,7 +1615,7 @@ public JRightPadded getReceiver() { @Nullable public Property withReceiver(@Nullable JRightPadded receiver) { return t.receiver == receiver ? t : new Property(t.id, t.prefix, t.markers, t.typeParameters, - t.variableDeclarations, t.typeConstraints, t.getter, t.setter, t.isSetterFirst, receiver); + t.variableDeclarations, t.typeConstraints, t.accessors, receiver); } } } diff --git a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java index 409a08a9e..3642a22a6 100644 --- a/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java +++ b/src/test/java/org/openrewrite/kotlin/KotlinTypeMappingTest.java @@ -110,18 +110,20 @@ void fieldType() { assertThat(id.getType()).isInstanceOf(JavaType.Class.class); assertThat(id.getType().toString()).isEqualTo("kotlin.Int"); - JavaType.FullyQualified declaringType = property.getGetter().getMethodType().getDeclaringType(); + J.MethodDeclaration getter = property.getAccessors().getElements().stream().filter(x -> x.getName().getSimpleName().equals("get")).findFirst().orElse(null); + JavaType.FullyQualified declaringType = getter.getMethodType().getDeclaringType(); assertThat(declaringType.getFullyQualifiedName()).isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat"); - assertThat(property.getGetter().getMethodType().getName()).isEqualTo("get"); - assertThat(property.getGetter().getMethodType().getReturnType()).isEqualTo(id.getType()); - assertThat(property.getGetter().getName().getType()).isEqualTo(property.getGetter().getMethodType()); - assertThat(property.getGetter().getMethodType().toString().substring(declaringType.toString().length())).isEqualTo("{name=get,return=kotlin.Int,parameters=[]}"); + assertThat(getter.getMethodType().getName()).isEqualTo("get"); + assertThat(getter.getMethodType().getReturnType()).isEqualTo(id.getType()); + assertThat(getter.getName().getType()).isEqualTo(getter.getMethodType()); + assertThat(getter.getMethodType().toString().substring(declaringType.toString().length())).isEqualTo("{name=get,return=kotlin.Int,parameters=[]}"); - declaringType = property.getSetter().getMethodType().getDeclaringType(); + J.MethodDeclaration setter = property.getAccessors().getElements().stream().filter(x -> x.getName().getSimpleName().equals("set")).findFirst().orElse(null); + declaringType = setter.getMethodType().getDeclaringType(); assertThat(declaringType.getFullyQualifiedName()).isEqualTo("org.openrewrite.kotlin.KotlinTypeGoat"); - assertThat(property.getSetter().getMethodType().getName()).isEqualTo("set"); - assertThat(property.getSetter().getMethodType()).isEqualTo(property.getSetter().getName().getType()); - assertThat(property.getSetter().getMethodType().toString().substring(declaringType.toString().length())).isEqualTo("{name=set,return=kotlin.Unit,parameters=[kotlin.Int]}"); + assertThat(setter.getMethodType().getName()).isEqualTo("set"); + assertThat(setter.getMethodType()).isEqualTo(setter.getName().getType()); + assertThat(setter.getMethodType().toString().substring(declaringType.toString().length())).isEqualTo("{name=set,return=kotlin.Unit,parameters=[kotlin.Int]}"); } @Test diff --git a/src/test/java/org/openrewrite/kotlin/tree/VariableDeclarationTest.java b/src/test/java/org/openrewrite/kotlin/tree/VariableDeclarationTest.java index 2fd1a642d..499e0e354 100644 --- a/src/test/java/org/openrewrite/kotlin/tree/VariableDeclarationTest.java +++ b/src/test/java/org/openrewrite/kotlin/tree/VariableDeclarationTest.java @@ -485,6 +485,23 @@ class Test { ); } + @Test + void getterSetterWithTrailingSemiColon() { + rewriteRun( + kotlin( + """ + class Test { + var stringRepresentation : String = "" + get ( ) = field ; + set ( value ) { + field = value + } ; + } + """ + ) + ); + } + @Issue("https://github.com/openrewrite/rewrite-kotlin/issues/93") @Test void setterBeforeGetter() {