Skip to content

Commit

Permalink
Named function type parameters support (#282)
Browse files Browse the repository at this point in the history
* Function type parameters support

Up until now the function type were in the LST represented using lambdas. This approach falls down, because with function type parameters the type is mandatory and the name is optional, whereas for lambdas it is the other way around. Also, a lambda as a body, whereas a function type has a return type.

The `K.FunctionType` now declares an explicit `K.FunctionType.Parameters parameters` object, a `Space arrow` for the arrow prefix, and a `TypedTree returnType` for the return type.

* Use correct location in visitor

* Continued work

* Polish

* Add `@JsonAlias` annotation for backwards compatibility

* Use `TypedTree` for `K.FunctionType#returnType`

* Remove some legacy uses of `K.FunctionType`

* Polish

* New `K.FunctionType.Parameter`

* More progress

* Remove `KCoordinateBuilder`

* Complete `KotlinVisitor`

* Support nullable function types

Fixes: #275

* Add `colon` space

* Add one more test

* `K.FunctionType#parameters` is now declared non-null

* Implement basic formatting for function type parameters

* Fix printing of opening nullable parenthesis
  • Loading branch information
knutwannheden authored Sep 8, 2023
1 parent cf9c2dc commit 456e275
Show file tree
Hide file tree
Showing 12 changed files with 349 additions and 109 deletions.
5 changes: 5 additions & 0 deletions src/main/java/org/openrewrite/kotlin/KotlinIsoVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ public K.FunctionType visitFunctionType(K.FunctionType functionType, P p) {
return (K.FunctionType) super.visitFunctionType(functionType, p);
}

@Override
public K.FunctionType.Parameter visitFunctionTypeParameter(K.FunctionType.Parameter parameter, P p) {
return (K.FunctionType.Parameter) super.visitFunctionTypeParameter(parameter, p);
}

@Override
public K.KReturn visitKReturn(K.KReturn kReturn, P p) {
return (K.KReturn) super.visitKReturn(kReturn, p);
Expand Down
70 changes: 63 additions & 7 deletions src/main/java/org/openrewrite/kotlin/KotlinVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,13 @@
import org.openrewrite.kotlin.marker.*;
import org.openrewrite.kotlin.service.KotlinAutoFormatService;
import org.openrewrite.kotlin.tree.K;
import org.openrewrite.kotlin.tree.KContainer;
import org.openrewrite.kotlin.tree.KRightPadded;
import org.openrewrite.kotlin.tree.KSpace;
import org.openrewrite.marker.Marker;
import org.openrewrite.marker.Markers;

import java.util.List;

/**
* Visit K types.
Expand Down Expand Up @@ -145,19 +150,26 @@ public J visitFunctionType(K.FunctionType functionType, P p) {
K.FunctionType f = functionType;
f = f.withPrefix(visitSpace(f.getPrefix(), KSpace.Location.FUNCTION_TYPE_PREFIX, p));
f = f.withMarkers(visitMarkers(f.getMarkers(), p));
Expression temp = (Expression) visitExpression(f, p);
if (!(temp instanceof K.FunctionType)) {
return temp;
} else {
f = (K.FunctionType) temp;
}
f = f.withLeadingAnnotations(ListUtils.map(f.getLeadingAnnotations(), a -> visitAndCast(a, p)));
f = f.withModifiers(ListUtils.map(f.getModifiers(), e -> visitAndCast(e, p)));
f = f.withReceiver(visitRightPadded(f.getReceiver(), p));
f = f.withTypedTree(visitAndCast(f.getTypedTree(), p));
if (f.getPadding().getParameters() != null) {
f = f.getPadding().withParameters(this.visitContainer(f.getPadding().getParameters(), KContainer.Location.FUNCTION_TYPE_PARAMETERS, p));
}
f = f.withReturnType(visitAndCast(f.getReturnType(), p));
return f;
}

public J visitFunctionTypeParameter(K.FunctionType.Parameter parameter, P p) {
K.FunctionType.Parameter pa = parameter;
pa = pa.withMarkers(visitMarkers(pa.getMarkers(), p));
if (pa.getName() != null) {
pa = pa.withName(visitAndCast(pa.getName(), p));
}
pa = pa.withParameterType(visitAndCast(pa.getParameterType(), p));
return pa;
}

public J visitKReturn(K.KReturn kReturn, P p) {
K.KReturn r = kReturn;
r = r.withPrefix(visitSpace(r.getPrefix(), KSpace.Location.KRETURN_PREFIX, p));
Expand Down Expand Up @@ -303,6 +315,50 @@ public <J2 extends J> JContainer<J2> visitContainer(JContainer<J2> container, P
return super.visitContainer(container, JContainer.Location.LANGUAGE_EXTENSION, p);
}

public <J2 extends J> JContainer<J2> visitContainer(@Nullable JContainer<J2> container,
KContainer.Location loc, P p) {
if (container == null) {
//noinspection ConstantConditions
return null;
}
setCursor(new Cursor(getCursor(), container));

Space before = visitSpace(container.getBefore(), loc.getBeforeLocation(), p);
List<JRightPadded<J2>> js = ListUtils.map(container.getPadding().getElements(), t -> visitRightPadded(t, loc.getElementLocation(), p));

setCursor(getCursor().getParent());

return js == container.getPadding().getElements() && before == container.getBefore() ?
container :
JContainer.build(before, js, container.getMarkers());
}

public <T> JRightPadded<T> visitRightPadded(@Nullable JRightPadded<T> right, KRightPadded.Location loc, P p) {
if (right == null) {
//noinspection ConstantConditions
return null;
}

setCursor(new Cursor(getCursor(), right));

T t = right.getElement();
if (t instanceof J) {
//noinspection unchecked
t = visitAndCast((J) right.getElement(), p);
}

setCursor(getCursor().getParent());
if (t == null) {
//noinspection ConstantConditions
return null;
}

Space after = visitSpace(right.getAfter(), loc.getAfterLocation(), p);
Markers markers = visitMarkers(right.getMarkers(), p);
return (after == right.getAfter() && t == right.getElement() && markers == right.getMarkers()) ?
right : new JRightPadded<>(t, after, markers);
}

@Override
public <M extends Marker> M visitMarker(Marker marker, P p) {
Marker m = super.visitMarker(marker, p);
Expand Down
19 changes: 12 additions & 7 deletions src/main/java/org/openrewrite/kotlin/format/SpacesVisitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -922,16 +922,21 @@ public K.FunctionType visitFunctionType(K.FunctionType functionType, P p) {
K.FunctionType kf = super.visitFunctionType(functionType, p);

// handle space around arrow in function type
TypedTree tree = kf.getTypedTree();
if (tree instanceof J.Lambda) {
J.Lambda l = (J.Lambda) tree;
l = l.withArrow(updateSpace(l.getArrow(), style.getOther().getAroundArrowInFunctionTypes()));
l = l.withBody(spaceBefore(l.getBody(), style.getOther().getAroundArrowInFunctionTypes()));
kf = kf.withTypedTree(l);
}
kf = kf.withArrow(updateSpace(kf.getArrow(), style.getOther().getAroundArrowInFunctionTypes()));
kf = kf.withReturnType(spaceBefore(kf.getReturnType(), style.getOther().getAroundArrowInFunctionTypes()));
return kf;
}

@Override
public K.FunctionType.Parameter visitFunctionTypeParameter(K.FunctionType.Parameter parameter, P p) {
K.FunctionType.Parameter pa = super.visitFunctionTypeParameter(parameter, p);
if (pa.getColon() != null) {
pa = pa.withColon(updateSpace(pa.getColon(), style.getOther().getBeforeColonAfterDeclarationName()));
pa = pa.withParameterType(spaceBefore(pa.getParameterType(), style.getOther().getAfterColonBeforeDeclarationType()));
}
return pa;
}

@Override
public J.Lambda visitLambda(J.Lambda lambda, P p) {
J.Lambda l = super.visitLambda(lambda, p);
Expand Down
25 changes: 24 additions & 1 deletion src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,21 +162,44 @@ public J visitDestructuringDeclaration(K.DestructuringDeclaration destructuringD

@Override
public J visitFunctionType(K.FunctionType functionType, PrintOutputCapture<P> p) {
boolean nullable = functionType.getMarkers().findFirst(IsNullable.class).isPresent();

beforeSyntax(functionType, KSpace.Location.FUNCTION_TYPE_PREFIX, p);
visit(functionType.getLeadingAnnotations(), p);
for (J.Modifier modifier : functionType.getModifiers()) {
delegate.visitModifier(modifier, p);
}

if (nullable) {
p.append("(");
}
if (functionType.getReceiver() != null) {
visitRightPadded(functionType.getReceiver(), p);
p.append(".");
}
visit(functionType.getTypedTree(), p);
delegate.visitContainer("(", functionType.getPadding().getParameters(), JContainer.Location.TYPE_PARAMETERS, ",", ")", p);
visitSpace(functionType.getArrow(), KSpace.Location.FUNCTION_TYPE_ARROW_PREFIX, p);
p.append("->");
visit(functionType.getReturnType(), p);
if (nullable) {
p.append(")");
}
afterSyntax(functionType, p);
return functionType;
}

@Override
public J visitFunctionTypeParameter(K.FunctionType.Parameter parameter, PrintOutputCapture<P> p) {
if (parameter.getName() != null) {
visit(parameter.getName(), p);
//noinspection DataFlowIssue
visitSpace(parameter.getColon(), KSpace.Location.FUNCTION_TYPE_PARAMETER_COLON, p);
p.append(":");
}
visit(parameter.getParameterType(), p);
return parameter;
}

@Override
public J visitKReturn(K.KReturn kReturn, PrintOutputCapture<P> p) {
// backwards compatibility: leave this in until `K.KReturn#annotations` has been deleted
Expand Down
Loading

0 comments on commit 456e275

Please sign in to comment.