diff --git a/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java b/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java new file mode 100644 index 0000000000..c6d415606d --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java @@ -0,0 +1,78 @@ +/* + * Copyright 2024 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.staticanalysis; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Space; +import org.openrewrite.marker.Markers; + +import java.util.Collections; + +import static org.openrewrite.java.trait.Traits.annotated; + +public class NullableOnMethodReturnType extends Recipe { + + @Override + public String getDisplayName() { + return "Move `@Nullable` method annotations to the return type"; + } + + @Override + public String getDescription() { + return "This is the way the cool kids do it."; + } + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor() { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + return annotated(Nullable.class) + .lower(getCursor()) + .findFirst() + .map(nullable -> { + if (nullable.getCursor().getParentTreeCursor().getValue() != m) { + return m; + } + J.MethodDeclaration m2 = m; + m2 = m2.withLeadingAnnotations(ListUtils.map(m2.getLeadingAnnotations(), + a -> a == nullable.getTree() ? null : a)); + if (m2 != m) { + m2 = m2.withReturnTypeExpression(new J.AnnotatedType( + Tree.randomId(), + Space.SINGLE_SPACE, + Markers.EMPTY, + Collections.singletonList(nullable.getTree().withPrefix(Space.SINGLE_SPACE)), + m2.getReturnTypeExpression() + )); + m2 = autoFormat(m2, m2.getReturnTypeExpression(), ctx, getCursor().getParentOrThrow()); + m2 = m2.withPrefix(m2.getPrefix().withWhitespace(m2.getPrefix().getWhitespace().replace("\n\n\n", "\n\n"))); + } + return m2; + }) + .orElse(m); + } + }; + } +} diff --git a/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java new file mode 100644 index 0000000000..f8308f944c --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java @@ -0,0 +1,70 @@ +/* + * Copyright 2024 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.staticanalysis; + +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class NullableOnMethodReturnTypeTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new NullableOnMethodReturnType()); + } + + @Test + void nullableOnMethodReturnType() { + rewriteRun( + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + class Test { + @Nullable + public String test() { + } + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + class Test { + public @Nullable String test() { + } + } + """ + ) + ); + } + + @Test + void dontTouchArguments() { + rewriteRun( + java( + //language=java + """ + import org.openrewrite.internal.lang.Nullable; + class Test { + void test(@Nullable String s) { + } + } + """ + ) + ); + } +}