Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.

Commit 3804358

Browse files
New LST models to support use-site annotation and deprecate AnnotationUseSite marker (#549)
* New LST models to support use-site annotation and deprecate AnnotationUseSite marker * Remove legacy `typealias` handling from `KotlinPrinter` --------- Co-authored-by: Knut Wannheden <knut@moderne.io>
1 parent d38c9b8 commit 3804358

File tree

7 files changed

+277
-45
lines changed

7 files changed

+277
-45
lines changed

src/main/java/org/openrewrite/kotlin/Assertions.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import org.openrewrite.internal.lang.Nullable;
2424
import org.openrewrite.java.search.FindMissingTypes;
2525
import org.openrewrite.java.tree.*;
26-
import org.openrewrite.kotlin.marker.AnnotationUseSite;
2726
import org.openrewrite.kotlin.marker.Extension;
2827
import org.openrewrite.kotlin.marker.IndexedAccess;
2928
import org.openrewrite.kotlin.tree.K;
@@ -470,7 +469,7 @@ private boolean isAllowedToHaveNullType(J.Identifier ident) {
470469
return inPackageDeclaration() || inImport() || isClassName()
471470
|| isMethodName() || isMethodInvocationName() || isFieldAccess(ident) || isBeingDeclared(ident) || isParameterizedType(ident)
472471
|| isNewClass(ident) || isTypeParameter() || isMemberReference(ident) || isCaseLabel() || isLabel() || isAnnotationField(ident)
473-
|| isInJavaDoc(ident) || isWhenLabel();
472+
|| isInJavaDoc(ident) || isWhenLabel() || isUseSite();
474473
}
475474

476475
private boolean inPackageDeclaration() {
@@ -544,6 +543,11 @@ private boolean isWhenLabel() {
544543
return getCursor().getParentTreeCursor().getValue() instanceof K.WhenBranch;
545544
}
546545

546+
private boolean isUseSite() {
547+
Tree value = getCursor().getParentTreeCursor().getValue();
548+
return value instanceof K.AnnotationType || value instanceof K.MultiAnnotationType;
549+
}
550+
547551
private boolean isLabel() {
548552
return getCursor().firstEnclosing(J.Label.class) != null;
549553
}
@@ -557,7 +561,7 @@ private boolean isAnnotationField(J.Identifier ident) {
557561
private boolean isValidated(J.Identifier i) {
558562
J j = getCursor().dropParentUntil(it -> it instanceof J).getValue();
559563
// TODO: replace with AnnotationUseSite tree.
560-
return !j.getMarkers().findFirst(AnnotationUseSite.class).isPresent() && !(j instanceof K.KReturn);
564+
return !(j instanceof K.KReturn);
561565
}
562566

563567
private boolean isValidated(J.MethodInvocation mi) {

src/main/java/org/openrewrite/kotlin/KotlinVisitor.java

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,24 @@ public J visitUnary(K.Unary unary, P p) {
323323
return u;
324324
}
325325

326+
public J visitAnnotationType(K.AnnotationType annotationType, P p) {
327+
K.AnnotationType at = annotationType;
328+
at = at.withPrefix(visitSpace(at.getPrefix(), Space.Location.ANNOTATION_PREFIX, p));
329+
at = at.withMarkers(visitMarkers(at.getMarkers(), p));
330+
at = at.getPadding().withUseSite(visitRightPadded(at.getPadding().getUseSite(), JRightPadded.Location.ANNOTATION_ARGUMENT, p));
331+
at = at.withCallee(visitAndCast(at.getCallee(), p));
332+
return at;
333+
}
334+
335+
public J visitMultiAnnotationType(K.MultiAnnotationType multiAnnotationType, P p) {
336+
K.MultiAnnotationType mat = multiAnnotationType;
337+
mat = mat.withPrefix(visitSpace(mat.getPrefix(), Space.Location.ANNOTATION_PREFIX, p));
338+
mat = mat.withMarkers(visitMarkers(mat.getMarkers(), p));
339+
mat = mat.getPadding().withUseSite(visitRightPadded(mat.getPadding().getUseSite(), JRightPadded.Location.ANNOTATION_ARGUMENT, p));
340+
mat = mat.withAnnotations(visitContainer(mat.getAnnotations(), p));
341+
return mat;
342+
}
343+
326344
public J visitWhen(K.When when, P p) {
327345
K.When w = when;
328346
w = w.withPrefix(visitSpace(w.getPrefix(), KSpace.Location.WHEN_PREFIX, p));
@@ -417,10 +435,7 @@ public <T> JRightPadded<T> visitRightPadded(@Nullable JRightPadded<T> right, KRi
417435
@Override
418436
public <M extends Marker> M visitMarker(Marker marker, P p) {
419437
Marker m = super.visitMarker(marker, p);
420-
if (m instanceof AnnotationUseSite) {
421-
AnnotationUseSite acs = (AnnotationUseSite) marker;
422-
m = acs.withPrefix(visitSpace(acs.getPrefix(), KSpace.Location.ANNOTATION_CALL_SITE_PREFIX, p));
423-
} else if (marker instanceof TypeReferencePrefix) {
438+
if (marker instanceof TypeReferencePrefix) {
424439
TypeReferencePrefix tr = (TypeReferencePrefix) marker;
425440
m = tr.withPrefix(visitSpace(tr.getPrefix(), KSpace.Location.TYPE_REFERENCE_PREFIX, p));
426441
}

src/main/java/org/openrewrite/kotlin/internal/KotlinPrinter.java

Lines changed: 22 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,28 @@ public J visitUnary(K.Unary unary, PrintOutputCapture<P> p) {
417417
return unary;
418418
}
419419

420+
@Override
421+
public J visitAnnotationType(K.AnnotationType annotationType, PrintOutputCapture<P> p) {
422+
beforeSyntax(annotationType, Space.Location.ANNOTATION_PREFIX, p);
423+
visitRightPadded(annotationType.getPadding().getUseSite(), p);
424+
p.append(":");
425+
visit(annotationType.getCallee(), p);
426+
return annotationType;
427+
}
428+
429+
@Override
430+
public J visitMultiAnnotationType(K.MultiAnnotationType multiAnnotationType, PrintOutputCapture<P> p) {
431+
beforeSyntax(multiAnnotationType, Space.Location.ANNOTATION_PREFIX, p);
432+
visitRightPadded(multiAnnotationType.getPadding().getUseSite(), p);
433+
434+
if (!(multiAnnotationType.getUseSite() instanceof J.Empty)) {
435+
p.append(":");
436+
}
437+
438+
delegate.visitContainer("[", multiAnnotationType.getAnnotations(), JContainer.Location.TYPE_PARAMETERS, "", "]", p);
439+
return multiAnnotationType;
440+
}
441+
420442
@Override
421443
public J visitWhen(K.When when, PrintOutputCapture<P> p) {
422444
beforeSyntax(when, KSpace.Location.WHEN_PREFIX, p);
@@ -469,22 +491,6 @@ public J visitAnnotation(J.Annotation annotation, PrintOutputCapture<P> p) {
469491
String afterArgs = ")";
470492
String delimiter = ",";
471493

472-
AnnotationUseSite useSite = annotation.getMarkers().findFirst(AnnotationUseSite.class).orElse(null);
473-
if (useSite != null) {
474-
kotlinPrinter.visitSpace(useSite.getPrefix(), KSpace.Location.ANNOTATION_CALL_SITE_PREFIX, p);
475-
p.append(":");
476-
477-
if (!useSite.isImplicitBracket()) {
478-
beforeArgs = "[";
479-
afterArgs = "]";
480-
} else {
481-
beforeArgs = "";
482-
afterArgs = "";
483-
}
484-
485-
delimiter = "";
486-
}
487-
488494
visitContainer(beforeArgs, annotation.getPadding().getArguments(), JContainer.Location.ANNOTATION_ARGUMENTS, delimiter, afterArgs, p);
489495
afterSyntax(annotation, p);
490496
return annotation;
@@ -1129,14 +1135,6 @@ public J visitWildcard(J.Wildcard wildcard, PrintOutputCapture<P> p) {
11291135

11301136
@Override
11311137
public J visitVariableDeclarations(J.VariableDeclarations multiVariable, PrintOutputCapture<P> p) {
1132-
// TypeAliases are converted into a J.VariableDeclaration to re-use complex recipes like RenameVariable and ChangeType.
1133-
// However, a type alias has different syntax and is printed separately to reduce code complexity in visitVariableDeclarations.
1134-
// This is a temporary solution until K.TypeAlias is added to the model, and RenameVariable is revised to operator from a J.Identifier.
1135-
if (multiVariable.getLeadingAnnotations().stream().anyMatch(it -> "typealias".equals(it.getSimpleName()))) {
1136-
visitTypeAlias(multiVariable, p);
1137-
return multiVariable;
1138-
}
1139-
11401138
beforeSyntax(multiVariable, Space.Location.VARIABLE_DECLARATIONS_PREFIX, p);
11411139

11421140
visit(multiVariable.getLeadingAnnotations(), p);

src/main/java/org/openrewrite/kotlin/internal/KotlinTreeParserVisitor.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,32 +1732,44 @@ public J visitKtFile(KtFile file, ExecutionContext data) {
17321732

17331733
@Override
17341734
public J visitAnnotation(KtAnnotation annotation, ExecutionContext data) {
1735-
if (annotation.getUseSiteTarget() == null) {
1736-
throw new UnsupportedOperationException("TODO, Some cases we don't know");
1735+
Expression target;
1736+
1737+
if (annotation.getUseSiteTarget() != null) {
1738+
target = (J.Identifier) annotation.getUseSiteTarget().accept(this, data);
1739+
} else {
1740+
target = new J.Empty(randomId(), Space.EMPTY, Markers.EMPTY);
17371741
}
1738-
List<KtAnnotationEntry> annotationEntries = annotation.getEntries();
1739-
List<JRightPadded<Expression>> rpAnnotations = new ArrayList<>(annotationEntries.size());
17401742

1743+
List<KtAnnotationEntry> annotationEntries = annotation.getEntries();
1744+
List<JRightPadded<J.Annotation>> rpAnnotations = new ArrayList<>(annotationEntries.size());
1745+
J.Annotation anno = null;
17411746
for (KtAnnotationEntry ktAnnotationEntry : annotationEntries) {
1742-
J.Annotation anno = (J.Annotation) ktAnnotationEntry.accept(this, data);
1747+
anno = (J.Annotation) ktAnnotationEntry.accept(this, data);
17431748
anno = anno.withMarkers(anno.getMarkers().addIfAbsent(new AnnotationConstructor(randomId())));
17441749
rpAnnotations.add(padRight(anno, Space.EMPTY));
17451750
}
17461751

1747-
PsiElement maybeLBracket = findFirstChild(annotation, anno -> anno.getNode().getElementType() == KtTokens.LBRACKET);
1752+
PsiElement maybeLBracket = findFirstChild(annotation, an -> an.getNode().getElementType() == KtTokens.LBRACKET);
17481753
boolean isImplicitBracket = maybeLBracket == null;
17491754
Space beforeLBracket = isImplicitBracket ? Space.EMPTY : prefix(maybeLBracket);
17501755

17511756
if (!isImplicitBracket) {
17521757
rpAnnotations = ListUtils.mapLast(rpAnnotations,
1753-
rp -> rp.withAfter(prefix(findFirstChild(annotation, anno -> anno.getNode().getElementType() == KtTokens.RBRACKET))));
1758+
rp -> rp.withAfter(prefix(findFirstChild(annotation, an -> an.getNode().getElementType() == KtTokens.RBRACKET))));
1759+
}
1760+
1761+
NameTree annotationType;
1762+
if (isImplicitBracket) {
1763+
annotationType = new K.AnnotationType(randomId(), Space.EMPTY, Markers.EMPTY, padRight(target, suffix(annotation.getUseSiteTarget())), anno);
1764+
} else {
1765+
annotationType = new K.MultiAnnotationType(randomId(), Space.EMPTY, Markers.EMPTY, padRight(target, suffix(annotation.getUseSiteTarget())), JContainer.build(beforeLBracket, rpAnnotations, Markers.EMPTY));
17541766
}
17551767

17561768
return mapType(new J.Annotation(randomId(),
17571769
Space.EMPTY,
1758-
Markers.EMPTY.addIfAbsent(new AnnotationUseSite(randomId(), suffix(annotation.getUseSiteTarget()), isImplicitBracket)),
1759-
(NameTree) annotation.getUseSiteTarget().accept(this, data),
1760-
JContainer.build(beforeLBracket, rpAnnotations, Markers.EMPTY)
1770+
Markers.EMPTY,
1771+
annotationType,
1772+
null
17611773
));
17621774
}
17631775

@@ -1773,16 +1785,20 @@ public J visitAnnotationEntry(@NotNull KtAnnotationEntry annotationEntry, Execut
17731785
}
17741786

17751787
if (isUseSite) {
1776-
nameTree = (NameTree) annotationEntry.getUseSiteTarget().accept(this, data);
1777-
markers = markers.addIfAbsent(new AnnotationUseSite(randomId(), prefix(findFirstChild(annotationEntry, p -> p.getNode().getElementType() == KtTokens.COLON)), true));
1778-
J.Annotation argAnno = new J.Annotation(
1788+
J.Annotation callee = new J.Annotation(
17791789
randomId(),
17801790
Space.EMPTY,
17811791
Markers.EMPTY.addIfAbsent(new AnnotationConstructor(randomId())),
17821792
(NameTree) requireNonNull(annotationEntry.getCalleeExpression()).accept(this, data),
17831793
annotationEntry.getValueArgumentList() != null ? mapValueArguments(annotationEntry.getValueArgumentList(), data) : null
17841794
);
1785-
args = JContainer.build(Space.EMPTY, singletonList(padRight(argAnno, Space.EMPTY)), Markers.EMPTY);
1795+
1796+
nameTree = new K.AnnotationType(randomId(),
1797+
Space.EMPTY,
1798+
Markers.EMPTY,
1799+
padRight(convertToExpression(annotationEntry.getUseSiteTarget().accept(this, data)),
1800+
prefix(findFirstChild(annotationEntry, p -> p.getNode().getElementType() == KtTokens.COLON))),
1801+
callee);
17861802
} else {
17871803
nameTree = (NameTree) requireNonNull(annotationEntry.getCalleeExpression()).accept(this, data);
17881804
if (annotationEntry.getValueArgumentList() != null) {

src/main/java/org/openrewrite/kotlin/marker/AnnotationUseSite.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
import java.util.UUID;
2424

25+
@Deprecated
2526
@Value
2627
@With
2728
public class AnnotationUseSite implements Marker {

src/main/java/org/openrewrite/kotlin/tree/K.java

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2149,4 +2149,168 @@ public K.Unary withOperator(JLeftPadded<K.Unary.Type> operator) {
21492149
}
21502150
}
21512151

2152+
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
2153+
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
2154+
@RequiredArgsConstructor
2155+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
2156+
final class AnnotationType implements K, NameTree {
2157+
@Nullable
2158+
@NonFinal
2159+
transient WeakReference<K.AnnotationType.Padding> padding;
2160+
2161+
@Getter
2162+
@With
2163+
@EqualsAndHashCode.Include
2164+
UUID id;
2165+
2166+
@Getter
2167+
@With
2168+
Space prefix;
2169+
2170+
@Getter
2171+
@With
2172+
Markers markers;
2173+
2174+
JRightPadded<Expression> useSite;
2175+
2176+
@Getter
2177+
@With
2178+
J.Annotation callee;
2179+
2180+
public Expression getUseSite() {
2181+
return useSite.getElement();
2182+
}
2183+
2184+
@Override
2185+
public @Nullable JavaType getType() {
2186+
// use site has no type
2187+
return null;
2188+
}
2189+
2190+
@Override
2191+
public <T extends J> T withType(@Nullable JavaType type) {
2192+
return (T) this;
2193+
}
2194+
2195+
@Override
2196+
public <P> J acceptKotlin(KotlinVisitor<P> v, P p) {
2197+
return v.visitAnnotationType(this, p);
2198+
}
2199+
2200+
public K.AnnotationType.Padding getPadding() {
2201+
K.AnnotationType.Padding p;
2202+
if (this.padding == null) {
2203+
p = new K.AnnotationType.Padding(this);
2204+
this.padding = new WeakReference<>(p);
2205+
} else {
2206+
p = this.padding.get();
2207+
if (p == null || p.t != this) {
2208+
p = new K.AnnotationType.Padding(this);
2209+
this.padding = new WeakReference<>(p);
2210+
}
2211+
}
2212+
return p;
2213+
}
2214+
2215+
@RequiredArgsConstructor
2216+
public static class Padding {
2217+
private final K.AnnotationType t;
2218+
2219+
public JRightPadded<Expression> getUseSite() {
2220+
return t.useSite;
2221+
}
2222+
2223+
public K.AnnotationType withUseSite(JRightPadded<Expression> useSite) {
2224+
return t.useSite == useSite ? t : new K.AnnotationType(t.id,
2225+
t.prefix,
2226+
t.markers,
2227+
useSite,
2228+
t.callee
2229+
);
2230+
}
2231+
}
2232+
}
2233+
2234+
@FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
2235+
@EqualsAndHashCode(callSuper = false, onlyExplicitlyIncluded = true)
2236+
@RequiredArgsConstructor
2237+
@AllArgsConstructor(access = AccessLevel.PRIVATE)
2238+
final class MultiAnnotationType implements K, NameTree {
2239+
@Nullable
2240+
@NonFinal
2241+
transient WeakReference<K.MultiAnnotationType.Padding> padding;
2242+
2243+
@Getter
2244+
@With
2245+
@EqualsAndHashCode.Include
2246+
UUID id;
2247+
2248+
@Getter
2249+
@With
2250+
Space prefix;
2251+
2252+
@Getter
2253+
@With
2254+
Markers markers;
2255+
2256+
JRightPadded<Expression> useSite;
2257+
2258+
@Getter
2259+
@With
2260+
JContainer<J.Annotation> annotations;
2261+
2262+
public Expression getUseSite() {
2263+
return useSite.getElement();
2264+
}
2265+
2266+
@Override
2267+
public @Nullable JavaType getType() {
2268+
// use site has no type
2269+
return null;
2270+
}
2271+
2272+
@Override
2273+
public <T extends J> T withType(@Nullable JavaType type) {
2274+
return (T) this;
2275+
}
2276+
2277+
@Override
2278+
public <P> J acceptKotlin(KotlinVisitor<P> v, P p) {
2279+
return v.visitMultiAnnotationType(this, p);
2280+
}
2281+
2282+
public K.MultiAnnotationType.Padding getPadding() {
2283+
K.MultiAnnotationType.Padding p;
2284+
if (this.padding == null) {
2285+
p = new K.MultiAnnotationType.Padding(this);
2286+
this.padding = new WeakReference<>(p);
2287+
} else {
2288+
p = this.padding.get();
2289+
if (p == null || p.t != this) {
2290+
p = new K.MultiAnnotationType.Padding(this);
2291+
this.padding = new WeakReference<>(p);
2292+
}
2293+
}
2294+
return p;
2295+
}
2296+
2297+
@RequiredArgsConstructor
2298+
public static class Padding {
2299+
private final K.MultiAnnotationType t;
2300+
2301+
public JRightPadded<Expression> getUseSite() {
2302+
return t.useSite;
2303+
}
2304+
2305+
public K.MultiAnnotationType withUseSite(JRightPadded<Expression> useSite) {
2306+
return t.useSite == useSite ? t : new K.MultiAnnotationType(t.id,
2307+
t.prefix,
2308+
t.markers,
2309+
useSite,
2310+
t.annotations
2311+
);
2312+
}
2313+
}
2314+
}
2315+
21522316
}

0 commit comments

Comments
 (0)