diff --git a/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java new file mode 100644 index 0000000000..8854e07dbb --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocations.java @@ -0,0 +1,116 @@ +/* + * Copyright 2021 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.lang.var; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import org.openrewrite.*; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.search.UsesJavaVersion; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; + +public class UseVarForGenericMethodInvocations extends Recipe { + @Override + public String getDisplayName() { + //language=markdown + return "Apply `var` to Generic Method Invocations"; + } + + @Override + public String getDescription() { + //language=markdown + return "Apply `var` to variables initialized by invocations of Generic Methods. " + + "This recipe ignores generic factory methods without parameters, because open rewrite cannot handle them correctly ATM."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesJavaVersion<>(10), + new UseVarForGenericMethodInvocations.UseVarForGenericsVisitor()); + } + + static final class UseVarForGenericsVisitor extends JavaIsoVisitor { + private final JavaTemplate template = JavaTemplate.builder("var #{} = #{any()}") + .javaParser(JavaParser.fromJavaVersion()).build(); + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations vd, ExecutionContext ctx) { + vd = super.visitVariableDeclarations(vd, ctx); + + boolean isGeneralApplicable = DeclarationCheck.isVarApplicable(this.getCursor(), vd); + if (!isGeneralApplicable) return vd; + + // recipe specific + boolean isPrimitive = DeclarationCheck.isPrimitive(vd); + boolean usesNoGenerics = !DeclarationCheck.useGenerics(vd); + boolean usesTernary = DeclarationCheck.initializedByTernary(vd); + if (isPrimitive || usesTernary || usesNoGenerics) return vd; + + //now we deal with generics, check for method invocations + Expression initializer = vd.getVariables().get(0).getInitializer(); + boolean isMethodInvocation = initializer != null && initializer.unwrap() instanceof J.MethodInvocation; + if (!isMethodInvocation) return vd; + + //if no type paramters are present and no arguments we assume the type is hard to determine a needs manual action + boolean hasNoTypeParams = ((J.MethodInvocation) initializer).getTypeParameters() == null; + boolean argumentsEmpty = ((J.MethodInvocation) initializer).getArguments().stream().allMatch(p -> p instanceof J.Empty); + if (hasNoTypeParams && argumentsEmpty) return vd; + + return transformToVar(vd, new ArrayList<>(), new ArrayList<>()); + } + + private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List leftTypes, List rightTypes) { + Expression initializer = vd.getVariables().get(0).getInitializer(); + String simpleName = vd.getVariables().get(0).getSimpleName(); + + // if left is defined but not right, copy types to initializer + if(rightTypes.isEmpty() && !leftTypes.isEmpty()) { + // we need to switch type infos from left to right here + List typeArgument = leftTypes.stream() + .map(t -> + new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, ((JavaType.Class)t).getClassName(), t, null)) + .collect(Collectors.toList()); + J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) ((J.NewClass) initializer).getClazz()).withTypeParameters(typeArgument); + initializer = ((J.NewClass) initializer).withClazz(typedInitializerClazz); + } + + J.VariableDeclarations result = template.apply(getCursor(), vd.getCoordinates().replace(), simpleName, initializer) + .withPrefix(vd.getPrefix()); + + // apply modifiers like final + List modifiers = vd.getModifiers(); + boolean hasModifiers = !modifiers.isEmpty(); + if (hasModifiers) { + result = result.withModifiers(modifiers); + } + + // apply prefix to type expression + TypeTree resultingTypeExpression = result.getTypeExpression(); + boolean resultHasTypeExpression = resultingTypeExpression != null; + if (resultHasTypeExpression) { + result = result.withTypeExpression(resultingTypeExpression.withPrefix(vd.getTypeExpression().getPrefix())); + } + + return result; + } + } +} diff --git a/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructors.java b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructors.java new file mode 100644 index 0000000000..384867ef87 --- /dev/null +++ b/src/main/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructors.java @@ -0,0 +1,147 @@ +/* + * Copyright 2021 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.lang.var; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.openrewrite.*; +import org.openrewrite.internal.lang.Nullable; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.search.UsesJavaVersion; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; + +public class UseVarForGenericsConstructors extends Recipe { + @Override + public String getDisplayName() { + //language=markdown + return "Apply `var` to Generic Constructors"; + } + + @Override + public String getDescription() { + //language=markdown + return "Apply `var` to generics variables initialized by constructor calls."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesJavaVersion<>(10), + new UseVarForGenericsConstructors.UseVarForGenericsVisitor()); + } + + static final class UseVarForGenericsVisitor extends JavaIsoVisitor { + private final JavaTemplate template = JavaTemplate.builder("var #{} = #{any()}") + .javaParser(JavaParser.fromJavaVersion()).build(); + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations vd, ExecutionContext ctx) { + vd = super.visitVariableDeclarations(vd, ctx); + + boolean isGeneralApplicable = DeclarationCheck.isVarApplicable(this.getCursor(), vd); + if (!isGeneralApplicable) return vd; + + // recipe specific + boolean isPrimitive = DeclarationCheck.isPrimitive(vd); + boolean usesNoGenerics = !DeclarationCheck.useGenerics(vd); + boolean usesTernary = DeclarationCheck.initializedByTernary(vd); + if (isPrimitive || usesTernary || usesNoGenerics) return vd; + + //now we deal with generics + J.VariableDeclarations.NamedVariable variable = vd.getVariables().get(0); + List leftTypes = extractParameters(variable.getVariableType()); + List rightTypes = extractParameters(variable.getInitializer()); + if (rightTypes == null || (leftTypes.isEmpty() && rightTypes.isEmpty())) return vd; + + return transformToVar(vd, leftTypes, rightTypes); + } + + /** + * Tries to extract the genric parameters from the expression, + * if the Initializer is no new class or not of a parameterized type, returns null to signale "no info". + * if the initializer uses empty diamonds use an empty list to signale no type information + * @param initializer to extract parameters from + * @return null or list of type parameters in diamond + */ + private @Nullable List extractParameters(@Nullable Expression initializer) { + if (initializer instanceof J.NewClass) { + TypeTree clazz = ((J.NewClass) initializer).getClazz(); + if (clazz instanceof J.ParameterizedType) { + List typeParameters = ((J.ParameterizedType) clazz).getTypeParameters(); + if (typeParameters != null) { + return typeParameters + .stream() + .map(Expression::getType) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } else { + return new ArrayList<>(); + } + } + } + return null; + } + + private List extractParameters(@Nullable JavaType.Variable variableType) { + if (variableType != null && variableType.getType() instanceof JavaType.Parameterized) { + return ((JavaType.Parameterized) variableType.getType()).getTypeParameters(); + } else { + return new ArrayList<>(); + } + } + + private J.VariableDeclarations transformToVar(J.VariableDeclarations vd, List leftTypes, List rightTypes) { + Expression initializer = vd.getVariables().get(0).getInitializer(); + String simpleName = vd.getVariables().get(0).getSimpleName(); + + // if left is defined but not right, copy types to initializer + if(rightTypes.isEmpty() && !leftTypes.isEmpty()) { + // we need to switch type infos from left to right here + List typeArgument = leftTypes.stream() + .map(t -> + new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, ((JavaType.Class)t).getClassName(), t, null)) + .collect(Collectors.toList()); + J.ParameterizedType typedInitializerClazz = ((J.ParameterizedType) ((J.NewClass) initializer).getClazz()).withTypeParameters(typeArgument); + initializer = ((J.NewClass) initializer).withClazz(typedInitializerClazz); + } + + J.VariableDeclarations result = template.apply(getCursor(), vd.getCoordinates().replace(), simpleName, initializer) + .withPrefix(vd.getPrefix()); + + // apply modifiers like final + List modifiers = vd.getModifiers(); + boolean hasModifiers = !modifiers.isEmpty(); + if (hasModifiers) { + result = result.withModifiers(modifiers); + } + + // apply prefix to type expression + TypeTree resultingTypeExpression = result.getTypeExpression(); + boolean resultHasTypeExpression = resultingTypeExpression != null; + if (resultHasTypeExpression) { + result = result.withTypeExpression(resultingTypeExpression.withPrefix(vd.getTypeExpression().getPrefix())); + } + + return result; + } + } +} diff --git a/src/main/resources/META-INF/rewrite/java-lang-var.yml b/src/main/resources/META-INF/rewrite/java-lang-var.yml index d88ae95e61..b63cc81402 100644 --- a/src/main/resources/META-INF/rewrite/java-lang-var.yml +++ b/src/main/resources/META-INF/rewrite/java-lang-var.yml @@ -17,7 +17,8 @@ type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.migrate.lang.UseVar displayName: Use local variable type inference -description: Apply local variable type inference (`var`) for primitives and objects. +description: Apply local variable type inference (`var`) for primitives and objects. These recipes can cause unused + imports, be advised to run `org.openrewrite.java.RemoveUnusedImports afterwards. tags: - refactoring - java10 @@ -25,3 +26,5 @@ tags: recipeList: - org.openrewrite.java.migrate.lang.var.UseVarForObject - org.openrewrite.java.migrate.lang.var.UseVarForPrimitive + - org.openrewrite.java.migrate.lang.var.UseVarForGenericsConstructors + - org.openrewrite.java.migrate.lang.var.UseVarForGenericMethodInvocations diff --git a/src/test/java/org/openrewrite/java/migrate/lang/UseVarKeywordTest.java b/src/test/java/org/openrewrite/java/migrate/lang/UseVarKeywordTest.java index 0dc9c63ae6..510cc3ce3e 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/UseVarKeywordTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/UseVarKeywordTest.java @@ -15,6 +15,9 @@ */ package org.openrewrite.java.migrate.lang; +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.version; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -22,9 +25,6 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.java.Assertions.version; - class UseVarKeywordTest implements RewriteTest { @Override @@ -155,173 +155,6 @@ void m() { ) ); } - - @Nested - class Generics { - @Test - void ifWelldefined() { - //language=java - rewriteRun( - version( - java(""" - package com.example.app; - - import java.util.ArrayList; - - class A { - void m() { - List strs = new ArrayList(); - } - } - """), - 10 - ) - ); - } - - @Test - void forNoDiamondOperators() { - //language=java - rewriteRun( - version( - java( - """ - package com.example.app; - - import java.util.ArrayList; - import java.util.List; - - class A { - void m() { - List strs = new ArrayList(); - } - } - """,""" - package com.example.app; - - import java.util.ArrayList; - import java.util.List; - - class A { - void m() { - var strs = new ArrayList(); - } - } - """), - 10 - ) - ); - } - - @Test - void withFactoryMethods() { - //language=java - rewriteRun( - version( - java( - """ - package com.example.app; - - import java.util.List; - - class A { - void m() { - List strs = List.of("one", "two"); - } - } - """), - 10 - ) - ); - } - - @Test - void forEmptyFactoryMethod() { - //language=java - rewriteRun( - version( - java( - """ - package com.example.app; - - import java.util.List; - - class A { - void m() { - List strs = List.of(); - } - } - """), - 10 - ) - ); - } - - @Test - void withDiamondOperatorOnRaw() { - //language=java - rewriteRun( - version( - java(""" - package com.example.app; - - import java.util.ArrayList; - - class A { - void m() { - List strs = new ArrayList(); - } - } - """), - 10 - ) - ); - } - - @Test - void withDiamondOperator() { - //language=java - rewriteRun( - version( - java(""" - package com.example.app; - - import java.util.ArrayList; - - class A { - void m() { - List strs = new ArrayList<>(); - } - } - """), - 10 - ) - ); - } - - @Test - void forEmptyDiamondOperators() { - //language=java - rewriteRun( - version( - java( - """ - package com.example.app; - - import java.util.ArrayList; - import java.util.List; - - class A { - void m() { - List strs = new ArrayList<>(); - } - } - """), - 10 - ) - ); - } - } } @Nested @@ -800,4 +633,205 @@ void m() { } } } + + @Nested + class Generics { + @Nested + class NotApplicable { + @Test + void forEmptyFactoryMethod() { + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of(); + } + } + """), + 10 + ) + ); + } + @Test + void forEmptyDiamondOperators() { + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.ArrayList; + import java.util.List; + + class A { + void m() { + List strs = new ArrayList<>(); + } + } + """), + 10 + ) + ); + } + @Test + void withDiamondOperatorOnRaw() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + } + + @Nested + class Applicable { + @Test + void ifWelldefined() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList(); + } + } + """,""" + package com.example.app; + + import java.util.ArrayList; + + class A { + void m() { + var strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + @Test + void forNoDiamondOperators() { + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.ArrayList; + import java.util.List; + + class A { + void m() { + List strs = new ArrayList(); + } + } + """,""" + package com.example.app; + + import java.util.ArrayList; + import java.util.List; + + class A { + void m() { + var strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + @Test + void withDiamondOperator() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList<>(); + } + } + """,""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + var strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + + @Test + @Disabled("not yet implemented by UseVarForMethodInvocations") // todo mboegers in PR #249 + void withFactoryMethods() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of("one", "two"); + } + } + """,""" + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of("one", "two"); + } + } + """), + 10 + ) + ); + } + } + } } diff --git a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java new file mode 100644 index 0000000000..5e930f074e --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericMethodInvocationsTest.java @@ -0,0 +1,232 @@ +/* + * Copyright 2021 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.lang.var; + +import static org.openrewrite.java.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class UseVarForGenericMethodInvocationsTest implements RewriteTest { + public void defaults(RecipeSpec spec) { + spec.recipe(new UseVarForGenericMethodInvocations()) + .allSources(s -> s.markers(javaVersion(10))); + } + + @Nested + class NotApplicable { + @Test + void forNonGenericMethod() { + // this one is handled/covered by UseVarForObjects/Test#staticMethods + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.Arrays; + import java.util.List; + import java.util.stream.Collectors; + + class A { + static String myString(String ... values) { + return String.join("",values); + } + void m() { + String strs = myString(); + } + } + """), + 10 + ) + ); + } + + @Nested + class NotSupportedByOpenRewrite { + // this is possible because `myList()`s type is fixed to `List` but it is not distinguishable from + // a generic method with generic var args like `List.of()` + @Test + void withStaticMethods() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + static List myList() { + return List.of("one", "two"); + } + void m() { + List strs = myList(); + } + } + """), + 10 + ) + ); + } + @Test + void withEmptyOnwNonStaticFactoryMethods() { + //if detectable this could be `var strs = this.myList();` + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + List myList(T ... values) { + return List.of(values); + } + void m() { + List strs = myList(); + } + } + """), + 10 + ) + ); + } + @Test + void withEmptyOnwFactoryMethods() { + // if detectable this could be `var strs = A.myList();` + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + static List myList(T ... values) { + return List.of(values); + } + void m() { + List strs = myList(); + } + } + """), + 10 + ) + ); + } + @Test + void forEmptyJDKFactoryMethod() { + // if detectable this could be `var strs = List.of();` + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of(); + } + } + """), + 10 + ) + ); + } + } + } + + @Nested + class Applicable { + @Test + void withJDKFactoryMethods() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of("one", "two"); + } + } + """,""" + package com.example.app; + + import java.util.List; + + class A { + void m() { + var strs = List.of("one", "two"); + } + } + """), + 10 + ) + ); + } + + + + @Test + void withOwnFactoryMethods() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + static List myList(T ... values) { + return List.of(values); + } + void m() { + List strs = myList("one", "two"); + } + } + """,""" + package com.example.app; + + import java.util.List; + + class A { + static List myList(T ... values) { + return List.of(values); + } + void m() { + var strs = myList("one", "two"); + } + } + """), + 10 + ) + ); + } + } +} diff --git a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructorsTest.java b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructorsTest.java new file mode 100644 index 0000000000..269f9e3227 --- /dev/null +++ b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForGenericsConstructorsTest.java @@ -0,0 +1,212 @@ +/* + * Copyright 2021 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.lang.var; + +import static org.openrewrite.java.Assertions.*; + +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +public class UseVarForGenericsConstructorsTest implements RewriteTest { + public void defaults(RecipeSpec spec) { + spec.recipe(new UseVarForGenericsConstructors()) + .allSources(s -> s.markers(javaVersion(10))); + } + + @Nested + class NotApplicable { + @Test + void forEmptyFactoryMethod() { + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of(); + } + } + """), + 10 + ) + ); + } + @Test + void withFactoryMethods() { + // this one is handled by UseVarForMethodInvocations + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + + class A { + void m() { + List strs = List.of("one", "two"); + } + } + """), + 10 + ) + ); + } + @Test + void forEmptyDiamondOperators() { + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList<>(); + } + } + """), + 10 + ) + ); + } + @Test + void withDiamondOperatorOnRaw() { + //todo check if this may be possible!, We could transform ArrayList into ArrayList + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + @Test + void forNoDiamondOperators() { + // this one fails for generics because it's covered by UseVarForObjects + //language=java + rewriteRun( + version( + java( + """ + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + } + + @Nested + class Applicable { + @Test + void ifWelldefined() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList(); + } + } + """,""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + var strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + + @Test + void withTypeParameterInDefinitionOnly() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + List strs = new ArrayList<>(); + } + } + """,""" + package com.example.app; + + import java.util.List; + import java.util.ArrayList; + + class A { + void m() { + var strs = new ArrayList(); + } + } + """), + 10 + ) + ); + } + } +} diff --git a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForObjectsTest.java b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForObjectsTest.java index a3e48f764a..861d4a2192 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForObjectsTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/var/UseVarForObjectsTest.java @@ -15,15 +15,14 @@ */ package org.openrewrite.java.migrate.lang.var; +import static org.openrewrite.java.Assertions.*; + import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.java.Assertions.javaVersion; - class UseVarForObjectsTest extends VarBaseTest { public void defaults(RecipeSpec spec) { @@ -181,6 +180,124 @@ class A { ) ); } + + @Nested + class InitilizedByMethod { + @Test + void sameType() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + class A { + String getHello() { + return "Hello"; + } + void m() { + String phrase = getHello(); + } + } + """, """ + package com.example.app; + + class A { + String getHello() { + return "Hello"; + } + void m() { + var phrase = getHello(); + } + } + """), + 10 + ) + ); + } + + @Test + void subType() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + class A { + class CustomTokenizer extends java.util.StringTokenizer { + CustomTokenizer() { + super(""); + } + } + CustomTokenizer getHello() { + return new CustomTokenizer(); + } + void m() { + CustomTokenizer phrase = getHello(); + } + } + """, """ + package com.example.app; + + class A { + class CustomTokenizer extends java.util.StringTokenizer { + CustomTokenizer() { + super(""); + } + } + CustomTokenizer getHello() { + return new CustomTokenizer(); + } + void m() { + var phrase = getHello(); + } + } + """), + 10 + ) + ); + } + + @Test + void staticMethods() { + //language=java + rewriteRun( + version( + java(""" + package com.example.app; + + class A { + static class B { + private B() {} + static B newInstance() { + return new B(); + } + } + void m() { + B b = B.newInstance(); + } + } + """, """ + package com.example.app; + + class A { + static class B { + private B() {} + static B newInstance() { + return new B(); + } + } + void m() { + var b = B.newInstance(); + } + } + """), + 10 + ) + ); + } + } } @Nested diff --git a/src/test/java/org/openrewrite/java/migrate/lang/var/VarBaseTest.java b/src/test/java/org/openrewrite/java/migrate/lang/var/VarBaseTest.java index 4c5eb38a74..81c9772d44 100644 --- a/src/test/java/org/openrewrite/java/migrate/lang/var/VarBaseTest.java +++ b/src/test/java/org/openrewrite/java/migrate/lang/var/VarBaseTest.java @@ -15,13 +15,12 @@ */ package org.openrewrite.java.migrate.lang.var; +import static org.openrewrite.java.Assertions.*; + import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.openrewrite.test.RewriteTest; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.java.Assertions.version; - abstract class VarBaseTest implements RewriteTest { @Nested class GeneralNotApplicable { @@ -181,9 +180,12 @@ void inDefinition() { java(""" package com.example.app; + import java.util.List; + import java.util.ArrayList; + class A { void m() { - List os = new List(); + List os = new ArrayList<>(); } } """ @@ -197,10 +199,13 @@ void inInitializer() { rewriteRun( java(""" package com.example.app; + + import java.util.ArrayList; + import java.util.List; class A { void m() { - List os = new List(); + List os = new ArrayList(); } } """