diff --git a/src/main/java/org/openrewrite/java/migrate/ChangeMethodInvocationReturnType.java b/src/main/java/org/openrewrite/java/migrate/ChangeMethodInvocationReturnType.java new file mode 100644 index 0000000000..1c7e6bb555 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/ChangeMethodInvocationReturnType.java @@ -0,0 +1,115 @@ +/* + * 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.java.migrate; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Option; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.marker.Markers; + +import static java.util.Collections.emptyList; + +@Value +@EqualsAndHashCode(callSuper = true) +public class ChangeMethodInvocationReturnType extends Recipe { + + @Option(displayName = "Method pattern", + description = "A method pattern that is used to find matching method declarations/invocations.", + example = "org.mockito.Matchers anyVararg()") + String methodPattern; + + @Option(displayName = "New method invocation return type", + description = "The return return type of method invocation.", + example = "long") + String newReturnType; + + @Override + public String getDisplayName() { + return "Change method invocation return type"; + } + + @Override + public String getDescription() { + return "Changes the return type of a method invocation."; + } + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor() { + private final MethodMatcher methodMatcher = new MethodMatcher(methodPattern, false); + + private boolean methodUpdated; + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(method, ctx); + JavaType.Method type = m.getMethodType(); + if (methodMatcher.matches(method) && type != null && !newReturnType.equals(type.getReturnType().toString())) { + type = type.withReturnType(JavaType.buildType(newReturnType)); + m = m.withMethodType(type); + methodUpdated = true; + } + return m; + } + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + methodUpdated = false; + JavaType.FullyQualified originalType = multiVariable.getTypeAsFullyQualified(); + J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); + + if (methodUpdated) { + JavaType newType = JavaType.buildType(newReturnType); + JavaType.FullyQualified newFieldType = TypeUtils.asFullyQualified(newType); + + maybeAddImport(newFieldType); + maybeRemoveImport(originalType); + + mv = mv.withTypeExpression(mv.getTypeExpression() == null ? + null : + new J.Identifier(mv.getTypeExpression().getId(), + mv.getTypeExpression().getPrefix(), + Markers.EMPTY, + emptyList(), + newReturnType, + newType, + null + ) + ); + + mv = mv.withVariables(ListUtils.map(mv.getVariables(), var -> { + JavaType.FullyQualified varType = TypeUtils.asFullyQualified(var.getType()); + if (varType != null && !varType.equals(newType)) { + return var.withType(newType).withName(var.getName().withType(newType)); + } + return var; + })); + } + + return mv; + } + }; + } +} diff --git a/src/main/resources/META-INF/rewrite/java-version-17.yml b/src/main/resources/META-INF/rewrite/java-version-17.yml index 7e131cdfef..c10e4cb6e7 100644 --- a/src/main/resources/META-INF/rewrite/java-version-17.yml +++ b/src/main/resources/META-INF/rewrite/java-version-17.yml @@ -80,10 +80,12 @@ description: Avoid using the deprecated methods in `java.util.logging.LogRecord` tags: - java17 recipeList: - # Disabled for now, as the new methods returns a long instead of an int, which causes compilation errors - #- org.openrewrite.java.ChangeMethodName: - # methodPattern: java.util.logging.LogRecord getThreadID() - # newMethodName: getLongThreadID + - org.openrewrite.java.ChangeMethodName: + methodPattern: java.util.logging.LogRecord getThreadID() + newMethodName: getLongThreadID + - org.openrewrite.java.migrate.ChangeMethodInvocationReturnType: + methodPattern: java.util.logging.LogRecord getLongThreadID() + newReturnType: long - org.openrewrite.java.ChangeMethodName: methodPattern: java.util.logging.LogRecord setThreadID(int) newMethodName: setLongThreadID diff --git a/src/test/java/org/openrewrite/java/migrate/ChangeMethodInvocationReturnTypeTest.java b/src/test/java/org/openrewrite/java/migrate/ChangeMethodInvocationReturnTypeTest.java new file mode 100644 index 0000000000..ba64623779 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/ChangeMethodInvocationReturnTypeTest.java @@ -0,0 +1,82 @@ +/* + * 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.java.migrate; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class ChangeMethodInvocationReturnTypeTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new ChangeMethodInvocationReturnType("java.lang.Integer parseInt(String)", "long")); + } + + @Test + @DocumentExample + void replaceVariableAssignment() { + rewriteRun( + //language=java + java( + """ + class Foo { + void bar() { + int one = Integer.parseInt("1"); + } + } + """, + """ + class Foo { + void bar() { + long one = Integer.parseInt("1"); + } + } + """ + ) + ); + } + + @Test + void shouldOnlyChangeTargetMethodAssignments() { + rewriteRun( + //language=java + java( + """ + class Foo { + void bar() { + int zero = Integer.valueOf("0"); + int one = Integer.parseInt("1"); + int two = Integer.valueOf("2"); + } + } + """, + """ + class Foo { + void bar() { + int zero = Integer.valueOf("0"); + long one = Integer.parseInt("1"); + int two = Integer.valueOf("2"); + } + } + """ + ) + ); + } +} \ No newline at end of file diff --git a/src/test/java/org/openrewrite/java/migrate/UpgradeToJava17Test.java b/src/test/java/org/openrewrite/java/migrate/UpgradeToJava17Test.java index fb8d80aa04..85f7b19ec5 100644 --- a/src/test/java/org/openrewrite/java/migrate/UpgradeToJava17Test.java +++ b/src/test/java/org/openrewrite/java/migrate/UpgradeToJava17Test.java @@ -271,6 +271,7 @@ void replaceLogRecordSetThreadID() { class Foo { void bar(LogRecord record) { + int threadID = record.getThreadID(); record.setThreadID(1); } } @@ -280,6 +281,7 @@ void bar(LogRecord record) { class Foo { void bar(LogRecord record) { + long threadID = record.getLongThreadID(); record.setLongThreadID(1); } }