From 093655f354304fd4ee3cab5c6fbb826d48d502b4 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 31 Jan 2024 21:12:14 +0100 Subject: [PATCH 001/183] Do not adopt Arrays.toString for varargs arguments (#246) * Do not adopt Arrays.toString for varargs arguments Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/230 * Validate an additional case --- ...RemoveToStringCallsFromArrayInstances.java | 17 ++++- ...veToStringCallsFromArrayInstancesTest.java | 69 +++++++++++-------- 2 files changed, 56 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java b/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java index 30537759b..792b8a840 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java @@ -19,7 +19,10 @@ import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypedTree; import java.util.Arrays; import java.util.Collections; @@ -75,9 +78,17 @@ public J visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) { return buildReplacement(select, mi); } else if (METHOD_MATCHERS.stream().anyMatch(matcher -> matcher.matches(mi))) { // deals with edge cases where .toString() is called implicitly + JavaType.Method methodType = mi.getMethodType(); + if (methodType == null) { + return mi; + } + List parameterTypes = methodType.getParameterTypes(); List arguments = mi.getArguments(); - for (Expression arg : arguments) { - if (arg.getType() instanceof JavaType.Array) { + for (int i = 0; i < arguments.size(); i++) { + Expression arg = arguments.get(i); + if (arg.getType() instanceof JavaType.Array && + (i > parameterTypes.size() - 1 || + !(parameterTypes.get(i) instanceof JavaType.Array))) { getCursor().putMessage("METHOD_KEY", mi); break; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java index 9eeb732f5..08846e618 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java @@ -27,8 +27,7 @@ public class RemoveToStringCallsFromArrayInstancesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec - .recipe(new RemoveToStringCallsFromArrayInstances()); + spec.recipe(new RemoveToStringCallsFromArrayInstances()); } @Test @@ -227,16 +226,6 @@ public static void main(String[] args) { System.out.println(String.format("s=%s", s)); } } - """, - """ - import java.util.Arrays; - - class SomeClass { - public static void main(String[] args) { - int[] s = new int[]{1, 2, 3}; - System.out.println(String.format("s=%s", Arrays.toString(s))); - } - } """ ) ); @@ -429,7 +418,7 @@ public static void main(String[] args) { } @Test - void worksWithPrintStreamFormat() { + void doesNotRunOnPrintStreamFormat() { //language=java rewriteRun( java( @@ -445,20 +434,6 @@ public static void main(String[] args) { ps.flush(); } } - """, - """ - import java.io.PrintStream; - import java.util.Arrays; - - class SomeClass { - public static void main(String[] args) { - PrintStream ps = new PrintStream(System.out); - String[] arr = new String[]{"test", "array"}; - - ps.format("formatting array: %s", Arrays.toString(arr)); - ps.flush(); - } - } """ ) ); @@ -500,4 +475,44 @@ public static void main(String[] args) { ); } + @Test + void varargs() { + //language=java + rewriteRun( + java( + """ + class SomeClass { + String foo(Object[] strings) { + return String.format("%s %s", strings); + } + } + """ + ) + ); + } + + @Test + void varargsButTwoArrays() { + //language=java + rewriteRun( + java( + """ + class SomeClass { + String foo(Object[] array1, Object[] array2) { + return String.format("%s %s", array1, array2); + } + } + """, + """ + import java.util.Arrays; + + class SomeClass { + String foo(Object[] array1, Object[] array2) { + return String.format("%s %s", Arrays.toString(array1), Arrays.toString(array2)); + } + } + """ + ) + ); + } } From 106421ad4d1db3fe062c8743d042627e07598a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Fri, 2 Feb 2024 19:02:01 +0000 Subject: [PATCH 002/183] refactor: Use of `@EqualsAndHashCode` on `Recipe` Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.recipes.RecipeEqualsAndHashCodeCallSuper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/DeclarationSiteTypeVariance.java | 2 +- .../openrewrite/staticanalysis/DefaultComesLastVisitor.java | 2 +- .../org/openrewrite/staticanalysis/EmptyBlockVisitor.java | 2 +- .../openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java | 2 +- .../staticanalysis/ExplicitInitializationVisitor.java | 2 +- .../org/openrewrite/staticanalysis/FallThroughVisitor.java | 2 +- .../openrewrite/staticanalysis/FinalizeLocalVariables.java | 2 +- .../openrewrite/staticanalysis/FinalizeMethodArguments.java | 2 +- .../org/openrewrite/staticanalysis/HiddenFieldVisitor.java | 2 +- .../openrewrite/staticanalysis/InstanceOfPatternMatch.java | 2 +- .../org/openrewrite/staticanalysis/MethodNameCasing.java | 2 +- .../staticanalysis/RemoveUnusedLocalVariables.java | 6 +++--- .../staticanalysis/RemoveUnusedPrivateFields.java | 2 +- .../staticanalysis/ReplaceDuplicateStringLiterals.java | 2 +- .../ReplaceOptionalIsPresentWithIfPresent.java | 2 +- .../org/openrewrite/staticanalysis/UnnecessaryCatch.java | 2 +- 16 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java b/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java index 6520a4f9f..9d51a63d4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java +++ b/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java @@ -33,7 +33,7 @@ import static org.openrewrite.java.tree.J.Wildcard.Bound.Super; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class DeclarationSiteTypeVariance extends Recipe { @Option(displayName = "Variant types", diff --git a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java index aa80eb9df..65d300b8a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java @@ -33,7 +33,7 @@ import java.util.stream.Collectors; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class DefaultComesLastVisitor

extends JavaIsoVisitor

{ DefaultComesLastStyle style; diff --git a/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java index 27bfe66d4..cdd8764f9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java @@ -39,7 +39,7 @@ import static org.openrewrite.Tree.randomId; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class EmptyBlockVisitor

extends JavaIsoVisitor

{ EmptyBlockStyle emptyBlockStyle; JavaTemplate continueStatement = JavaTemplate.builder("continue;").build(); diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index 104c96555..30d28769a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -31,7 +31,7 @@ import static java.util.Collections.singletonList; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class EqualsAvoidsNullVisitor

extends JavaIsoVisitor

{ private static final MethodMatcher STRING_EQUALS = new MethodMatcher("String equals(java.lang.Object)"); private static final MethodMatcher STRING_EQUALS_IGNORE_CASE = new MethodMatcher("String equalsIgnoreCase(java.lang.String)"); diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java index ca0552403..854a859aa 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java @@ -31,7 +31,7 @@ import java.util.Iterator; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class ExplicitInitializationVisitor

extends JavaIsoVisitor

{ private static final AnnotationMatcher LOMBOK_VALUE = new AnnotationMatcher("@lombok.Value"); private static final AnnotationMatcher LOMBOK_BUILDER_DEFAULT = new AnnotationMatcher("@lombok.Builder.Default"); diff --git a/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java index 3635a9d3a..b799089dd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java @@ -29,7 +29,7 @@ import java.util.regex.Pattern; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class FallThroughVisitor

extends JavaIsoVisitor

{ /** * Ignores any fall-through commented with a text matching the regex pattern. diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizeLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/FinalizeLocalVariables.java index 5ff11339d..c6ff0d9f7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizeLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizeLocalVariables.java @@ -98,7 +98,7 @@ private boolean isDeclaredInForLoopControl(Cursor cursor) { } @Value - @EqualsAndHashCode(callSuper = true) + @EqualsAndHashCode(callSuper = false) private static class FindAssignmentReferencesToVariable extends JavaIsoVisitor { J.VariableDeclarations.NamedVariable variable; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java index af6e78374..9be22e761 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java @@ -111,7 +111,7 @@ private static boolean isAbstractMethod(MethodDeclaration method) { } @Value - @EqualsAndHashCode(callSuper = true) + @EqualsAndHashCode(callSuper = false) private static class FindAssignmentReferencesToVariable extends JavaIsoVisitor { VariableDeclarations.NamedVariable variable; diff --git a/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java b/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java index 7e0b6c1c2..ca260c8d6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java @@ -38,7 +38,7 @@ import java.util.stream.Collectors; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) @Incubating(since = "7.6.0") public class HiddenFieldVisitor

extends JavaIsoVisitor

{ private static final Pattern NEXT_NAME_PATTERN = Pattern.compile("(.+)(\\d+)"); diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 94d2c33c8..0364fba44 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -41,7 +41,7 @@ @Incubating(since = "7.36.0") @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class InstanceOfPatternMatch extends Recipe { @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index 0d14afe86..d98deee56 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -38,7 +38,7 @@ import static java.util.Objects.requireNonNull; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class MethodNameCasing extends ScanningRecipe> { private static final Pattern STANDARD_METHOD_NAME = Pattern.compile("^[a-z][a-zA-Z0-9]*$"); diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index 1ec55ba59..89a784982 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -33,7 +33,7 @@ import java.util.function.Predicate; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) @SuppressWarnings("ConstantConditions") public class RemoveUnusedLocalVariables extends Recipe { @Incubating(since = "7.17.2") @@ -204,7 +204,7 @@ public J.Assignment visitAssignment(J.Assignment assignment, AtomicBoolean resul * and remove the assignment, leaving behind the value being assigned. */ @Value - @EqualsAndHashCode(callSuper = true) + @EqualsAndHashCode(callSuper = false) private static class PruneAssignmentExpression extends JavaIsoVisitor { J.Assignment assignment; @@ -225,7 +225,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation m, ExecutionC } @Value - @EqualsAndHashCode(callSuper = true) + @EqualsAndHashCode(callSuper = false) private static class AssignmentToLiteral extends JavaVisitor { J.Assignment assignment; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java index fca969d28..0bf95213f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java @@ -37,7 +37,7 @@ import java.util.stream.Collectors; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class RemoveUnusedPrivateFields extends Recipe { private static final AnnotationMatcher LOMBOK_ANNOTATION = new AnnotationMatcher("@lombok.*"); diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 2a371aaf0..43771be35 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -35,7 +35,7 @@ import static org.openrewrite.Tree.randomId; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class ReplaceDuplicateStringLiterals extends Recipe { @Option(displayName = "Apply recipe to test source set", diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java index 116129410..4497bb1f2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java @@ -223,7 +223,7 @@ private static List collectFields(J.ClassDeclaration classDecl) { } @Value - @EqualsAndHashCode(callSuper = true) + @EqualsAndHashCode(callSuper = false) public static class ReplaceMethodCallWithVariableVisitor extends JavaVisitor { J.Identifier lambdaParameterIdentifier; J.Identifier methodSelector; diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCatch.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCatch.java index 85351ae61..925a2d4a6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCatch.java @@ -32,7 +32,7 @@ import java.util.concurrent.atomic.AtomicBoolean; @Value -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode(callSuper = false) public class UnnecessaryCatch extends Recipe { @Option(displayName = "Include `java.lang.Exception`", From 0727defddf094ec2225dc7fec2cb4deba797e6e6 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Fri, 2 Feb 2024 14:27:46 -0500 Subject: [PATCH 003/183] Add BigDecimalDoubleConstructor Recipe (#252) Simple recipe for fixing the double constructor. Signed-off-by: Jonathan Leitschuh Co-authored-by: Tim te Beek --- .../BigDecimalDoubleConstructor.java | 43 ++++++++++++++ .../rewrite/common-static-analysis.yml | 1 + .../BigDecimalDoubleConstructorTest.java | 57 +++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java b/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java new file mode 100644 index 000000000..775c038e1 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java @@ -0,0 +1,43 @@ +/* + * 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 com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.BeforeTemplate; +import org.openrewrite.java.template.RecipeDescriptor; + +import java.math.BigDecimal; + +@RecipeDescriptor( + name = "`new BigDecimal(double)` should not be used", + description = "Use of `new BigDecimal(double)` constructor can lead to loss of precision. Use `BigDecimal.valueOf(double)` instead.\n" + + "For example writing `new BigDecimal(0.1)` does not create a `BigDecimal` which is exactly equal to `0.1`, " + + "but it is equal to `0.1000000000000000055511151231257827021181583404541015625`. " + + "This is because `0.1` cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length).", + tags = {"RSPEC-2111"} +) +public class BigDecimalDoubleConstructor { + + @BeforeTemplate + BigDecimal bigDecimalDoubleConstructor(double d) { + return new BigDecimal(d); + } + + @AfterTemplate + BigDecimal bigDecimalValueOf(double d) { + return BigDecimal.valueOf(d); + } +} diff --git a/src/main/resources/META-INF/rewrite/common-static-analysis.yml b/src/main/resources/META-INF/rewrite/common-static-analysis.yml index ab70b7570..d18a10ba5 100644 --- a/src/main/resources/META-INF/rewrite/common-static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/common-static-analysis.yml @@ -21,6 +21,7 @@ description: Resolve common static analysis issues discovered through 3rd party recipeList: # - org.openrewrite.staticanalysis.AddSerialVersionUidToSerializable - org.openrewrite.staticanalysis.AtomicPrimitiveEqualsUsesGet + - org.openrewrite.staticanalysis.BigDecimalDoubleConstructorRecipe - org.openrewrite.staticanalysis.BigDecimalRoundingConstantsToEnums - org.openrewrite.staticanalysis.BooleanChecksNotInverted - org.openrewrite.staticanalysis.CaseInsensitiveComparisonsDoNotChangeCase diff --git a/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java b/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java new file mode 100644 index 000000000..a64cc258a --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java @@ -0,0 +1,57 @@ +/* + * 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; + +@SuppressWarnings("UnpredictableBigDecimalConstructorCall") +public class BigDecimalDoubleConstructorTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new BigDecimalDoubleConstructorRecipe()); + } + + @Test + void bigDecimalDoubleConstructor() { + rewriteRun( + java( + """ + import java.math.BigDecimal; + class Test { + void test(double d) { + BigDecimal bd = new BigDecimal(1.0); + BigDecimal bd2 = new BigDecimal(d); + } + } + """, + """ + import java.math.BigDecimal; + class Test { + void test(double d) { + BigDecimal bd = BigDecimal.valueOf(1.0); + BigDecimal bd2 = BigDecimal.valueOf(d); + } + } + """ + ) + ); + } +} From fe755883ac156d5ec93269cc224418a01ebd76eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Sat, 3 Feb 2024 02:42:26 +0000 Subject: [PATCH 004/183] refactor: Update Gradle wrapper Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew.bat | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 86339a8f5..e4a6f4de6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip -distributionSha256Sum=9d926787066a081739e8200858338b4a69e837c3a821a33aca9db09dd4a41026 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew.bat b/gradlew.bat index 6689b85be..7101f8e46 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From b38c77ca22649441430b3cf35dacf009f6a034c6 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 4 Feb 2024 17:20:25 +0100 Subject: [PATCH 005/183] Add missing example values for UseAsBuilder --- .../org/openrewrite/staticanalysis/UseAsBuilder.java | 11 +++++------ .../HideUtilityClassConstructorTest.java | 4 ++-- .../staticanalysis/RemoveUnusedPrivateFieldsTest.java | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java b/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java index f9563c6b2..2ca74676e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java @@ -35,24 +35,23 @@ public class UseAsBuilder extends Recipe { @Option( displayName = "Builder Type", - description = "Fully qualified name of the Builder" - ) + description = "Fully qualified name of the Builder", + example = "org.example.Buildable.Builder") String builderType; @Option( displayName = "Immutable state", description = "The builder is immutable if you must assign the result of calls to intermediate variables " + "or use directly. Defaults to true as many purpose-built builders will be immutable.", - required = false - ) + required = false) @Nullable Boolean immutable; @Option( displayName = "Builder creator method", description = "The method that creates the builder instance, which may not be a method of the builder itself.", - required = false - ) + required = false, + example = "org.example.Buildable builder()") @Nullable String builderCreator; diff --git a/src/test/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorTest.java b/src/test/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorTest.java index e7dacc1b6..2c0e808c2 100644 --- a/src/test/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorTest.java @@ -157,7 +157,7 @@ void changeApplicableNestedClass() { public class A { public A() {} public static String foo() { return "foo"; } - private static class Builder() { + private static class Builder { public Builder() {} public static String foo() { return "foo"; } } @@ -167,7 +167,7 @@ public Builder() {} public class A { private A() {} public static String foo() { return "foo"; } - private static class Builder() { + private static class Builder { private Builder() {} public static String foo() { return "foo"; } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java index e7c08110a..fb8346b7f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java @@ -237,7 +237,7 @@ public class VehicleUsage { private final String vehicleId; } - public doSomethingWithAVehicle() { + public void doSomethingWithAVehicle() { vehicleUsage = new VehicleUsage(); vehicleUsage.vehicleId = "vu50"; } From c7459b9e9f01d9a9ae0c4c67e7188fb1d50c949b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 4 Feb 2024 18:20:32 +0100 Subject: [PATCH 006/183] refactor: Automatically select recipe examples from the unit test cases of a recipe (#253) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.recipes.SelectRecipeExamples?organizationId=ZDI4ZjE4NTAtNTg0Yi00Zjk1LWFkZDgtOGQzY2ZiMzgwNmM4 Co-authored-by: Moderne --- .../staticanalysis/BigDecimalDoubleConstructorTest.java | 2 ++ .../ReplaceOptionalIsPresentWithIfPresentTest.java | 2 ++ .../org/openrewrite/staticanalysis/SimplifyTernaryTest.java | 2 ++ .../staticanalysis/SortedSetStreamToLinkedHashSetTest.java | 2 ++ .../staticanalysis/groovy/MinimumSwitchCasesTest.java | 2 ++ .../kotlin/RenameLocalVariablesToCamelCaseTest.java | 2 ++ .../staticanalysis/kotlin/SimplifyBooleanExpressionTest.java | 2 ++ 7 files changed, 14 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java b/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java index a64cc258a..5ab866cde 100644 --- a/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -29,6 +30,7 @@ public void defaults(RecipeSpec spec) { spec.recipe(new BigDecimalDoubleConstructorRecipe()); } + @DocumentExample @Test void bigDecimalDoubleConstructor() { rewriteRun( diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresentTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresentTest.java index f7dbd50f1..42c47652e 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresentTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresentTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -120,6 +121,7 @@ Integer method(Optional o) { ); } + @DocumentExample @Test void ignoreReturnInsideLambda() { rewriteRun( diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java index 30dbe58dc..c11cd2ffd 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java @@ -16,11 +16,13 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; class SimplifyTernaryTest implements RewriteTest { + @DocumentExample @Test void simplified() { rewriteRun( diff --git a/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java b/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java index 538ca44b2..5b003f7ed 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -29,6 +30,7 @@ public void defaults(RecipeSpec spec) { spec.recipe(new SortedSetStreamToLinkedHashSet()); } + @DocumentExample @Test void changeSortedSetStreamToLinkedHashSet() { rewriteRun( diff --git a/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java b/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java index 7546d36a6..c10fa95a5 100644 --- a/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.ExpectedToFail; +import org.openrewrite.DocumentExample; import org.openrewrite.Issue; import org.openrewrite.staticanalysis.MinimumSwitchCases; import org.openrewrite.test.RecipeSpec; @@ -63,6 +64,7 @@ void test(OpenOption o) { ); } + @DocumentExample @ExpectedToFail("Temporarily until we have investigated why the behavior has changed here") @Test void twoCases() { diff --git a/src/test/java/org/openrewrite/staticanalysis/kotlin/RenameLocalVariablesToCamelCaseTest.java b/src/test/java/org/openrewrite/staticanalysis/kotlin/RenameLocalVariablesToCamelCaseTest.java index a4e90b4b2..9847e060a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/kotlin/RenameLocalVariablesToCamelCaseTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/kotlin/RenameLocalVariablesToCamelCaseTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.staticanalysis.RenameLocalVariablesToCamelCase; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -30,6 +31,7 @@ public void defaults(RecipeSpec spec) { spec.recipe(new RenameLocalVariablesToCamelCase()); } + @DocumentExample @Test void regular() { rewriteRun( diff --git a/src/test/java/org/openrewrite/staticanalysis/kotlin/SimplifyBooleanExpressionTest.java b/src/test/java/org/openrewrite/staticanalysis/kotlin/SimplifyBooleanExpressionTest.java index 6d18706eb..239dd0d65 100644 --- a/src/test/java/org/openrewrite/staticanalysis/kotlin/SimplifyBooleanExpressionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/kotlin/SimplifyBooleanExpressionTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis.kotlin; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.staticanalysis.SimplifyBooleanExpression; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -29,6 +30,7 @@ public void defaults(RecipeSpec spec) { spec.recipe(new SimplifyBooleanExpression()); } + @DocumentExample @Test void regular() { rewriteRun( From c7bc491dce07b3806aa1834ed65aea0e92f5ee79 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 4 Feb 2024 21:04:13 +0000 Subject: [PATCH 007/183] refactor: Use `Tree.randomId()` in LST constructors Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.recipes.UseTreeRandomId?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../NoDoubleBraceInitialization.java | 2 +- .../NoPrimitiveWrappersForToStringOrCompareTo.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java index fa6c1c301..2b90b5a08 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java @@ -124,7 +124,7 @@ public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { JavaTemplate template = JavaTemplate.builder(newInitializer).imports(fq.getFullyQualifiedName()).build(); nc = template.apply(getCursor(), nc.getCoordinates().replace()); initStatements = addSelectToInitStatements(initStatements, var.getName(), ctx); - initStatements.add(0, new J.Assignment(UUID.randomUUID(), Space.EMPTY, Markers.EMPTY, var.getName().withId(UUID.randomUUID()), JLeftPadded.build(nc), fq)); + initStatements.add(0, new J.Assignment(Tree.randomId(), Space.EMPTY, Markers.EMPTY, var.getName().withId(UUID.randomUUID()), JLeftPadded.build(nc), fq)); parentBlockCursor.computeMessageIfAbsent("INIT_STATEMENTS", v -> new HashMap>()).put(varDeclsCursor.getValue(), initStatements); } } else if (parentBlockCursor.getParent().getValue() instanceof J.MethodDeclaration) { diff --git a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java index 8ace2f205..fa22b683a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java @@ -15,10 +15,7 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; +import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; @@ -31,7 +28,10 @@ import org.openrewrite.marker.Markers; import java.time.Duration; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; import static java.util.Collections.emptyList; @@ -97,7 +97,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu } if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) { JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType(); - mi = mi.withSelect(new J.Identifier(UUID.randomUUID(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null)); + mi = mi.withSelect(new J.Identifier(Tree.randomId(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null)); //noinspection ArraysAsListWithZeroOrOneArgument mi = mi.withArguments(Arrays.asList(arg)); } @@ -114,7 +114,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) { JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType(); - mi = mi.withSelect(new J.Identifier(UUID.randomUUID(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null)); + mi = mi.withSelect(new J.Identifier(Tree.randomId(), mi.getSelect().getPrefix(), Markers.EMPTY, emptyList(), fq.getClassName(), fq, null)); mi = mi.withArguments(ListUtils.concat(arg, mi.getArguments())); mi = maybeAutoFormat(mi, mi.withName(mi.getName().withSimpleName("compare")), ctx); } From 1ff3eeee3ce5f639dffcd2a7485595e57e6655fa Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 5 Feb 2024 09:08:42 +0000 Subject: [PATCH 008/183] refactor: Remove `test` prefix from JUnit 5 tests Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.testing.cleanup.RemoveTestPrefix?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/ReplaceDuplicateStringLiteralsTest.java | 2 +- .../openrewrite/staticanalysis/UseCollectionInterfacesTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index 7fbf531ad..2a3f5876b 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -69,7 +69,7 @@ class A { @Issue("https://github.com/openrewrite/rewrite/issues/1740") @Test - void testSourcesEnabled() { + void sourcesEnabled() { rewriteRun( srcTestJava( //language=java diff --git a/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java b/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java index 9d091fff2..ed52a9331 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java @@ -938,7 +938,7 @@ Set method() { @Issue("https://github.com/openrewrite/rewrite/issues/2973") @Test @ExpectedToFail - void testExplicitImplementationClassInApi() { + void explicitImplementationClassInApi() { rewriteRun( //language=java java( From eb59a56f904fae2c52ed840db0d0e716e81f3a2f Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 5 Feb 2024 09:10:12 +0000 Subject: [PATCH 009/183] refactor: Remove unused imports Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.RemoveUnusedImports?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../org/openrewrite/staticanalysis/FallThroughVisitor.java | 4 +++- .../org/openrewrite/staticanalysis/FinalClassVisitor.java | 5 ++++- .../openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java | 5 ++++- .../staticanalysis/RemoveUnreachableCodeVisitor.java | 2 -- .../openrewrite/staticanalysis/ReplaceStackWithDeque.java | 5 ++++- .../openrewrite/staticanalysis/ReplaceWeekYearWithYear.java | 3 ++- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java index b799089dd..00b699646 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java @@ -24,7 +24,9 @@ import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; -import java.util.*; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import java.util.function.Predicate; import java.util.regex.Pattern; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java index 72a275f82..0e7355701 100755 --- a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java @@ -22,7 +22,10 @@ import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; import static java.util.Collections.emptyList; import static org.openrewrite.Tree.randomId; diff --git a/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java b/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java index 9e922c97f..488fbbeb9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java +++ b/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java @@ -15,7 +15,10 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnreachableCodeVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnreachableCodeVisitor.java index ba01cb821..4818ac532 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnreachableCodeVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnreachableCodeVisitor.java @@ -16,12 +16,10 @@ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; -import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.Statement; -import java.util.Collections; import java.util.List; import java.util.Optional; diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java index 7ce1cf6e0..aada7c5fc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java @@ -15,7 +15,10 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; import org.openrewrite.analysis.dataflow.DataFlowNode; import org.openrewrite.analysis.dataflow.FindLocalFlowPaths; import org.openrewrite.analysis.dataflow.DataFlowSpec; diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java index 83300347e..a09caa222 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java @@ -21,7 +21,8 @@ import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.*; -import java.util.*; +import java.util.Collections; +import java.util.Set; public class ReplaceWeekYearWithYear extends Recipe { private static final MethodMatcher SIMPLE_DATE_FORMAT_CONSTRUCTOR_MATCHER = new MethodMatcher("java.text.SimpleDateFormat (..)"); From 450d8690db46ae71f04ed7611041e0d6ee525bac Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 5 Feb 2024 11:45:50 +0000 Subject: [PATCH 010/183] refactor: Automatically review PRs Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/J6z7fPbqf?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .github/workflows/comment-pr.yml | 16 ++++++++++++++++ .github/workflows/receive-pr.yml | 12 ++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 .github/workflows/comment-pr.yml create mode 100644 .github/workflows/receive-pr.yml diff --git a/.github/workflows/comment-pr.yml b/.github/workflows/comment-pr.yml new file mode 100644 index 000000000..d9b2c499c --- /dev/null +++ b/.github/workflows/comment-pr.yml @@ -0,0 +1,16 @@ +name: comment-pr + +# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow +on: + workflow_run: + workflows: ["receive-pr"] + types: + - completed + +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ +# Since this pull request has write permissions on the target repo, we should **NOT** execute any untrusted code. +jobs: + post-suggestions: + uses: openrewrite/gh-automation/.github/workflows/comment-pr.yml@main + secrets: + GH_PAT_ACTIONS_READ: ${{ secrets.GH_PAT_ACTIONS_READ }} diff --git a/.github/workflows/receive-pr.yml b/.github/workflows/receive-pr.yml new file mode 100644 index 000000000..f336716fa --- /dev/null +++ b/.github/workflows/receive-pr.yml @@ -0,0 +1,12 @@ +name: receive-pr +on: + pull_request: + types: [opened, synchronize] + branches: + - main + +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ +# Since this pull request receives untrusted code, we should **NOT** have any secrets in the environment. +jobs: + upload-patch: + uses: openrewrite/gh-automation/.github/workflows/receive-pr.yml@main From 0941830e4dc2c9921fe86136345680dbc23bf99d Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 5 Feb 2024 11:52:43 +0000 Subject: [PATCH 011/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../CatchClauseOnlyRethrows.java | 6 +--- .../FinalizeMethodArguments.java | 13 +-------- .../staticanalysis/ReplaceStackWithDeque.java | 2 +- .../TernaryOperatorsShouldNotBeNested.java | 29 +++++-------------- ...TernaryOperatorsShouldNotBeNestedTest.java | 6 ++-- .../ReplaceLambdaWithMethodReferenceTest.java | 1 - 6 files changed, 14 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index a12c29cc1..5f1215838 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -20,11 +20,7 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.J.Try.Catch; -import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.java.tree.*; import java.time.Duration; import java.util.Set; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java index 9be22e761..ba44949c3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java @@ -15,18 +15,7 @@ import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.J.Empty; -import org.openrewrite.java.tree.J.MethodDeclaration; -import org.openrewrite.java.tree.J.Modifier; -import org.openrewrite.java.tree.J.Modifier.Type; -import org.openrewrite.java.tree.J.VariableDeclarations; -import org.openrewrite.java.tree.JavaSourceFile; -import org.openrewrite.java.tree.JavaType.FullyQualified; -import org.openrewrite.java.tree.JavaType.FullyQualified.Kind; -import org.openrewrite.java.tree.JavaType.Method; -import org.openrewrite.java.tree.Space; -import org.openrewrite.java.tree.Statement; +import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import java.util.List; diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java index aada7c5fc..138b65bd6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceStackWithDeque.java @@ -20,8 +20,8 @@ import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.analysis.dataflow.DataFlowNode; -import org.openrewrite.analysis.dataflow.FindLocalFlowPaths; import org.openrewrite.analysis.dataflow.DataFlowSpec; +import org.openrewrite.analysis.dataflow.FindLocalFlowPaths; import org.openrewrite.java.ChangeType; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.UsesType; diff --git a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java index 6ad941013..d2b21d35e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java +++ b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java @@ -15,19 +15,6 @@ */ package org.openrewrite.staticanalysis; -import static org.openrewrite.Tree.randomId; -import static org.openrewrite.java.tree.J.Binary.Type.Equal; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.Tree; @@ -36,16 +23,16 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.marker.JavaVersion; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.Flag; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JContainer; -import org.openrewrite.java.tree.JRightPadded; -import org.openrewrite.java.tree.Space; -import org.openrewrite.java.tree.Statement; -import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.openrewrite.Tree.randomId; +import static org.openrewrite.java.tree.J.Binary.Type.Equal; + public class TernaryOperatorsShouldNotBeNested extends Recipe { diff --git a/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java b/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java index f7bbfa4f3..8fde85ee5 100644 --- a/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java @@ -15,9 +15,6 @@ */ package org.openrewrite.staticanalysis; -import static org.openrewrite.java.Assertions.java; -import static org.openrewrite.java.Assertions.javaVersion; - import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junitpioneer.jupiter.ExpectedToFail; @@ -26,6 +23,9 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; + class TernaryOperatorsShouldNotBeNestedTest { @Nested diff --git a/src/test/java/org/openrewrite/staticanalysis/kotlin/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/kotlin/ReplaceLambdaWithMethodReferenceTest.java index 05ed6eac2..6a4312545 100644 --- a/src/test/java/org/openrewrite/staticanalysis/kotlin/ReplaceLambdaWithMethodReferenceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/kotlin/ReplaceLambdaWithMethodReferenceTest.java @@ -21,7 +21,6 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; - import static org.openrewrite.kotlin.Assertions.kotlin; class ReplaceLambdaWithMethodReferenceTest implements RewriteTest { From cdcd0424a036344d7ae88973a3ebc2bdcaf38da5 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 5 Feb 2024 13:20:31 +0100 Subject: [PATCH 012/183] Fix compilation after nested type imports were removed --- .../CatchClauseOnlyRethrows.java | 4 +- .../FinalizeMethodArguments.java | 55 +++++++++---------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index 5f1215838..3e3e0e9d3 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -76,7 +76,7 @@ public J.Try visitTry(J.Try tryable, ExecutionContext ctx) { // if a subsequent catch is a wider exception type and doesn't rethrow, we should // keep this one for (int j = i + 1; j < tryable.getCatches().size(); j++) { - Catch next = tryable.getCatches().get(j); + J.Try.Catch next = tryable.getCatches().get(j); if (!onlyRethrows(next) && TypeUtils.isAssignableTo(next.getParameter().getType(), aCatch.getParameter().getType())) { return aCatch; @@ -88,7 +88,7 @@ public J.Try visitTry(J.Try tryable, ExecutionContext ctx) { })); } - private boolean onlyRethrows(Catch aCatch) { + private boolean onlyRethrows(J.Try.Catch aCatch) { if (aCatch.getBody().getStatements().size() != 1 || !(aCatch.getBody().getStatements().get(0) instanceof J.Throw)) { return false; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java index ba44949c3..d1f388f55 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java @@ -43,8 +43,8 @@ public String getDescription() { public TreeVisitor getVisitor() { return new JavaIsoVisitor() { @Override - public MethodDeclaration visitMethodDeclaration(MethodDeclaration methodDeclaration, ExecutionContext ctx) { - MethodDeclaration declarations = super.visitMethodDeclaration(methodDeclaration, ctx); + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) { + J.MethodDeclaration declarations = super.visitMethodDeclaration(methodDeclaration, ctx); if (isWrongKind(methodDeclaration) || isEmpty(declarations.getParameters()) || @@ -66,8 +66,8 @@ public MethodDeclaration visitMethodDeclaration(MethodDeclaration methodDeclarat } private void checkIfAssigned(final AtomicBoolean assigned, final Statement p) { - if (p instanceof VariableDeclarations) { - VariableDeclarations variableDeclarations = (VariableDeclarations) p; + if (p instanceof J.VariableDeclarations) { + J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) p; if (variableDeclarations.getVariables().stream() .anyMatch(namedVariable -> FindAssignmentReferencesToVariable.find(getCursor() @@ -79,38 +79,33 @@ private void checkIfAssigned(final AtomicBoolean assigned, final Statement p) { } } } - - @Override - public boolean isAcceptable(final SourceFile sourceFile, final ExecutionContext ctx) { - return sourceFile instanceof JavaSourceFile; - } }; } - private static boolean isWrongKind(final MethodDeclaration methodDeclaration) { + private static boolean isWrongKind(final J.MethodDeclaration methodDeclaration) { return Optional.ofNullable(methodDeclaration.getMethodType()) - .map(Method::getDeclaringType) - .map(FullyQualified::getKind) - .filter(Kind.Interface::equals) + .map(JavaType.Method::getDeclaringType) + .map(JavaType.FullyQualified::getKind) + .filter(JavaType.FullyQualified.Kind.Interface::equals) .isPresent(); } - private static boolean isAbstractMethod(MethodDeclaration method) { - return method.getModifiers().stream().anyMatch(modifier -> modifier.getType() == Type.Abstract); + private static boolean isAbstractMethod(J.MethodDeclaration method) { + return method.getModifiers().stream().anyMatch(modifier -> modifier.getType() == J.Modifier.Type.Abstract); } @Value @EqualsAndHashCode(callSuper = false) private static class FindAssignmentReferencesToVariable extends JavaIsoVisitor { - VariableDeclarations.NamedVariable variable; + J.VariableDeclarations.NamedVariable variable; /** * @param subtree The subtree to search. - * @param variable A {@link VariableDeclarations.NamedVariable} to check for any reassignment calls. + * @param variable A {@link J.VariableDeclarations.NamedVariable} to check for any reassignment calls. * @return An {@link AtomicBoolean} that is true if the variable has been reassigned and false otherwise. */ - static AtomicBoolean find(J subtree, VariableDeclarations.NamedVariable variable) { + static AtomicBoolean find(J subtree, J.VariableDeclarations.NamedVariable variable) { return new FindAssignmentReferencesToVariable(variable) .reduce(subtree, new AtomicBoolean()); } @@ -169,10 +164,10 @@ public J.AssignmentOperation visitAssignmentOperation(final J.AssignmentOperatio } private static Statement updateParam(final Statement p) { - if (p instanceof VariableDeclarations) { - VariableDeclarations variableDeclarations = (VariableDeclarations) p; + if (p instanceof J.VariableDeclarations) { + J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) p; if (variableDeclarations.getModifiers().isEmpty()) { - variableDeclarations = updateModifiers(variableDeclarations, !((VariableDeclarations) p).getLeadingAnnotations().isEmpty()); + variableDeclarations = updateModifiers(variableDeclarations, !((J.VariableDeclarations) p).getLeadingAnnotations().isEmpty()); variableDeclarations = updateDeclarations(variableDeclarations); return variableDeclarations; } @@ -180,18 +175,18 @@ private static Statement updateParam(final Statement p) { return p; } - private static VariableDeclarations updateDeclarations(final VariableDeclarations variableDeclarations) { + private static J.VariableDeclarations updateDeclarations(final J.VariableDeclarations variableDeclarations) { return variableDeclarations.withTypeExpression(variableDeclarations.getTypeExpression() != null ? variableDeclarations.getTypeExpression().withPrefix(Space.SINGLE_SPACE) : null); } - private static VariableDeclarations updateModifiers(final VariableDeclarations variableDeclarations, final boolean leadingAnnotations) { - List modifiers = variableDeclarations.getModifiers(); - Modifier finalModifier = new Modifier(Tree.randomId(), + private static J.VariableDeclarations updateModifiers(final J.VariableDeclarations variableDeclarations, final boolean leadingAnnotations) { + List modifiers = variableDeclarations.getModifiers(); + J.Modifier finalModifier = new J.Modifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, - Type.Final, + J.Modifier.Type.Final, emptyList()); if (leadingAnnotations) { finalModifier = finalModifier.withPrefix(Space.SINGLE_SPACE); @@ -201,17 +196,17 @@ private static VariableDeclarations updateModifiers(final VariableDeclarations v private boolean hasFinalModifiers(final List parameters) { return parameters.stream().allMatch(p -> { - if (p instanceof VariableDeclarations) { - final List modifiers = ((VariableDeclarations) p).getModifiers(); + if (p instanceof J.VariableDeclarations) { + final List modifiers = ((J.VariableDeclarations) p).getModifiers(); return !modifiers.isEmpty() && modifiers.stream() - .allMatch(m -> m.getType().equals(Type.Final)); + .allMatch(m -> m.getType().equals(J.Modifier.Type.Final)); } return false; }); } private boolean isEmpty(final List parameters) { - return parameters.size() == 1 && (parameters.get(0) instanceof Empty); + return parameters.size() == 1 && (parameters.get(0) instanceof J.Empty); } } From f7e6121d3506244599e098f60a336b87c4966641 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 5 Feb 2024 13:50:07 +0100 Subject: [PATCH 013/183] ReplaceLambdaWithMethodReference skip parameterized method return types (#240) * ReplaceLambdaWithMethodReference skip parameterized method return types Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/237 * Apply suggestions from code review --- .../ReplaceLambdaWithMethodReference.java | 5 ++++ .../ReplaceLambdaWithMethodReferenceTest.java | 27 +++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index d66c7e029..ae56e951f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -170,6 +170,11 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { if (methodType != null && !isMethodReferenceAmbiguous(methodType)) { if (methodType.hasFlags(Flag.Static) || methodSelectMatchesFirstLambdaParameter(method, lambda)) { + if (method.getType() instanceof JavaType.Parameterized && + ((JavaType.Parameterized) method.getType()).getTypeParameters().stream() + .anyMatch(JavaType.GenericTypeVariable.class::isInstance)) { + return l; + } J.MemberReference updated = newStaticMethodReference(methodType, true, lambda.getType()).withPrefix(lambda.getPrefix()); doAfterVisit(service(ImportService.class).shortenFullyQualifiedTypeReferencesIn(updated)); return updated; diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java index 1da23d4d3..a16f2585f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java @@ -1318,4 +1318,31 @@ void bar(Stream stream) { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/237") + void groupingByGetClass() { + rewriteRun( + //language=java + java( + """ + import java.util.*; + import java.util.stream.*; + + class Animal {} + class Cat extends Animal {} + class Dog extends Animal {} + + class Test { + public void groupOnGetClass() { + List animals = List.of(new Cat(), new Dog()); + Map, List> collect; + collect = animals.stream().collect(Collectors.groupingBy(a -> a.getClass())); + } + } + """ + ) + ); + } + } From 8760cf03d4b8af9385ecd1a8eb8f9debd2a6d7e7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 7 Feb 2024 11:26:16 +0000 Subject: [PATCH 014/183] refactor: Automatically review pull requests Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/OUSuHnCME?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .github/workflows/comment-pr.yml | 3 +-- .github/workflows/receive-pr.yml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/comment-pr.yml b/.github/workflows/comment-pr.yml index d9b2c499c..f30bbd9fe 100644 --- a/.github/workflows/comment-pr.yml +++ b/.github/workflows/comment-pr.yml @@ -1,16 +1,15 @@ name: comment-pr - # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow on: workflow_run: workflows: ["receive-pr"] types: - completed - # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ # Since this pull request has write permissions on the target repo, we should **NOT** execute any untrusted code. jobs: post-suggestions: + if: ${{ github.event.workflow_run.conclusion == 'success' }} uses: openrewrite/gh-automation/.github/workflows/comment-pr.yml@main secrets: GH_PAT_ACTIONS_READ: ${{ secrets.GH_PAT_ACTIONS_READ }} diff --git a/.github/workflows/receive-pr.yml b/.github/workflows/receive-pr.yml index f336716fa..a93c527d2 100644 --- a/.github/workflows/receive-pr.yml +++ b/.github/workflows/receive-pr.yml @@ -4,7 +4,6 @@ on: types: [opened, synchronize] branches: - main - # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ # Since this pull request receives untrusted code, we should **NOT** have any secrets in the environment. jobs: From 59bf4803e744f45741f72896a65b280ff04e9815 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 9 Feb 2024 20:09:30 +0100 Subject: [PATCH 015/183] Do not ReplaceLambdaWithMethodReference for class fields (#256) * Do not ReplaceLambdaWithMethodReference for class fields As references might change. * Also check if fields are final --- .../ReplaceLambdaWithMethodReference.java | 9 +++++ .../ReplaceLambdaWithMethodReferenceTest.java | 39 ++++++++++++++++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index ae56e951f..467bc140e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -159,6 +159,7 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { } if (hasSelectWithPotentialSideEffects(method) || + hasSelectWhoseReferenceMightChange(method) || !methodArgumentsMatchLambdaParameters(method, lambda) || method instanceof J.MemberReference) { return l; @@ -212,6 +213,14 @@ private boolean hasSelectWithPotentialSideEffects(MethodCall method) { ((J.MethodInvocation) method).getSelect() instanceof MethodCall; } + private boolean hasSelectWhoseReferenceMightChange(MethodCall method) { + if (method instanceof J.MethodInvocation && ((J.MethodInvocation) method).getSelect() instanceof J.Identifier) { + JavaType.Variable fieldType = ((J.Identifier) ((J.MethodInvocation) method).getSelect()).getFieldType(); + return fieldType != null && fieldType.getOwner() instanceof JavaType.Class && !fieldType.hasFlags(Flag.Final); + } + return false; + } + private boolean methodArgumentsMatchLambdaParameters(MethodCall method, J.Lambda lambda) { JavaType.Method methodType = method.getMethodType(); if (methodType == null) { diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java index a16f2585f..9b764f038 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java @@ -63,7 +63,8 @@ void multipleMethodInvocations() { """ import java.nio.file.Path; import java.nio.file.Paths; - import java.util.List;import java.util.stream.Collectors; + import java.util.List; + import java.util.stream.Collectors; class Test { Path path = Paths.get(""); @@ -190,6 +191,7 @@ List method(List input) { } } """, + //language=java """ import java.util.List; import java.util.stream.Collectors; @@ -237,6 +239,7 @@ List method(List input) { } } """, + //language=java """ import org.test.CheckType; @@ -288,6 +291,7 @@ Stream method(List input) { } } """, + //language=java """ import java.util.List; import java.util.stream.Stream; @@ -409,8 +413,10 @@ public void run() { Collections.singletonList(1).forEach(n -> run()); } } - Test t = new Test(); - Runnable r = () -> t.run(); + void foo() { + Test t = new Test(); + Runnable r = () -> t.run(); + } } """, """ @@ -423,8 +429,10 @@ public void run() { Collections.singletonList(1).forEach(n -> run()); } } - Test t = new Test(); - Runnable r = t::run; + void foo() { + Test t = new Test(); + Runnable r = t::run; + } } """ ) @@ -595,6 +603,7 @@ List filter(List l) { } } """, + //language=java """ import org.test.CheckType; @@ -1293,8 +1302,8 @@ private Integer foo(String bar) { @Test void newClassSelector() { + //language=java rewriteRun( - //language=java java( """ class A { @@ -1345,4 +1354,22 @@ public void groupOnGetClass() { ); } + @Test + void dontReplaceNullableFieldReferences() { + //language=java + rewriteRun( + java( + """ + import java.util.function.Supplier; + class A { + Object field; + void foo() { + // Runtime exception when replaced with field::toString + Supplier supplier = () -> field.toString(); + } + } + """ + ) + ); + } } From 683933a0b098da77467ce5b770778a4400ccb078 Mon Sep 17 00:00:00 2001 From: Michel Gonzalez <67339220+Mgonzalez-droid@users.noreply.github.com> Date: Sun, 11 Feb 2024 07:18:18 -0500 Subject: [PATCH 016/183] Fully remove part provider and last remaining references (#248) * Removed deprecated class references, replaced with new template. Need to test implementation. * Update ReplaceStringBuilderWithString.java * Update ReplaceStringBuilderWithString.java * Remove unused field --------- Co-authored-by: Tim te Beek --- .../ReplaceStringBuilderWithString.java | 220 ++++++++---------- 1 file changed, 94 insertions(+), 126 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java index 964beea3e..2879b22f8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java @@ -16,9 +16,10 @@ package org.openrewrite.staticanalysis; import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.PartProvider; import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; @@ -33,8 +34,6 @@ public class ReplaceStringBuilderWithString extends Recipe { private static final MethodMatcher STRING_BUILDER_APPEND = new MethodMatcher("java.lang.StringBuilder append(..)"); private static final MethodMatcher STRING_BUILDER_TO_STRING = new MethodMatcher("java.lang.StringBuilder toString()"); - private static J.Parentheses parenthesesTemplate; - private static J.MethodInvocation stringValueOfTemplate; @Override public String getDisplayName() { @@ -55,149 +54,118 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(Preconditions.and(new UsesMethod<>(STRING_BUILDER_APPEND), new UsesMethod<>(STRING_BUILDER_TO_STRING)), new JavaVisitor() { - @Override - public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - - if (STRING_BUILDER_TO_STRING.matches(method)) { - List methodCallsChain = new ArrayList<>(); - List arguments = new ArrayList<>(); - boolean isFlattenable = flatMethodInvocationChain(method, methodCallsChain, arguments); - if (!isFlattenable) { - return m; - } - - Collections.reverse(arguments); - adjustExpressions(arguments); - if (arguments.isEmpty()) { - return m; - } + return Preconditions.check( + Preconditions.and( + new UsesMethod<>(STRING_BUILDER_APPEND), + new UsesMethod<>(STRING_BUILDER_TO_STRING)), + new StringBuilderToAppendVisitor() + ); + } - Expression additive = ChainStringBuilderAppendCalls.additiveExpression(arguments) - .withPrefix(method.getPrefix()); + private static class StringBuilderToAppendVisitor extends JavaVisitor { + @Override + public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); + + if (STRING_BUILDER_TO_STRING.matches(method)) { + List methodCallsChain = new ArrayList<>(); + List arguments = new ArrayList<>(); + boolean isFlattenable = flatMethodInvocationChain(method, methodCallsChain, arguments); + if (!isFlattenable || arguments.isEmpty()) { + return m; + } - if (isAMethodSelect(method)) { - additive = wrapExpression(additive); - } + Collections.reverse(arguments); + arguments = adjustExpressions(method, arguments); - return additive; + Expression additive = ChainStringBuilderAppendCalls.additiveExpression(arguments).withPrefix(method.getPrefix()); + if (isAMethodSelect(method)) { + additive = new J.Parentheses<>(randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(additive)); } - return m; - } - // Check if a method call is a select of another method call - private boolean isAMethodSelect(J.MethodInvocation method) { - Cursor parent = getCursor().getParent(2); // 2 means skip right padded cursor - if (parent == null || !(parent.getValue() instanceof J.MethodInvocation)) { - return false; - } - return ((J.MethodInvocation) parent.getValue()).getSelect() == method; + return additive; } - }); - } + return m; + } - private J.Literal toStringLiteral(J.Literal input) { - if (input.getType() == JavaType.Primitive.String) { - return input; + // Check if a method call is a select of another method call + private boolean isAMethodSelect(J.MethodInvocation method) { + Cursor parent = getCursor().getParent(2); // 2 means skip right padded cursor + if (parent == null || !(parent.getValue() instanceof J.MethodInvocation)) { + return false; + } + return ((J.MethodInvocation) parent.getValue()).getSelect() == method; } - String value = input.getValueSource(); - return new J.Literal(randomId(), Space.EMPTY, Markers.EMPTY, value, - "\"" + value + "\"", null, JavaType.Primitive.String); - } + private J.Literal toStringLiteral(J.Literal input) { + if (input.getType() == JavaType.Primitive.String) { + return input; + } - private void adjustExpressions(List arguments) { - for (int i = 0; i < arguments.size(); i++) { - if (i == 0) { - // the first expression must be a String type to support case like `new StringBuilder().append(1)` - if (!TypeUtils.isString(arguments.get(0).getType())) { - if (arguments.get(0) instanceof J.Literal) { - // wrap by "" - arguments.set(0, toStringLiteral((J.Literal) arguments.get(0))); - } else { - J.MethodInvocation stringValueOf = getStringValueOfMethodInvocationTemplate() - .withArguments(Collections.singletonList(arguments.get(0))) - .withPrefix(arguments.get(0).getPrefix()); - arguments.set(0, stringValueOf); + String value = input.getValueSource(); + return new J.Literal(randomId(), Space.EMPTY, Markers.EMPTY, value, + "\"" + value + "\"", null, JavaType.Primitive.String); + } + + private List adjustExpressions(J.MethodInvocation method, List arguments) { + return ListUtils.map(arguments, (i, arg) -> { + if (i == 0) { + if (!TypeUtils.isString(arg.getType())) { + if (arg instanceof J.Literal) { + return toStringLiteral((J.Literal) arg); + } else { + return JavaTemplate.builder("String.valueOf(#{any()})").build() + .apply(getCursor(), method.getCoordinates().replace(), arg) + .withPrefix(arg.getPrefix()); + } } + } else if (!(arg instanceof J.Identifier || arg instanceof J.Literal || arg instanceof J.MethodInvocation)) { + return new J.Parentheses<>(randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(arg)); } - } else { - // wrap by parentheses to support case like `.append(1+2)` - Expression arg = arguments.get(i); - if (!(arg instanceof J.Identifier || arg instanceof J.Literal || arg instanceof J.MethodInvocation)) { - arguments.set(i, wrapExpression(arg)); - } - } + return arg; + }); } - } - /** - * Return true if the method calls chain is like "new StringBuilder().append("A")....append("B");" - * - * @param method a StringBuilder.toString() method call - * @param methodChain output methods chain - * @param arguments output expression list to be chained by '+'. - */ - private static boolean flatMethodInvocationChain(J.MethodInvocation method, - List methodChain, - List arguments - ) { - Expression select = method.getSelect(); - while (select != null) { - methodChain.add(select); - if (!(select instanceof J.MethodInvocation)) { - break; - } + /** + * Return true if the method calls chain is like "new StringBuilder().append("A")....append("B");" + * + * @param method a StringBuilder.toString() method call + * @param methodChain output methods chain + * @param arguments output expression list to be chained by '+'. + */ + private boolean flatMethodInvocationChain(J.MethodInvocation method, List methodChain, List arguments) { + Expression select = method.getSelect(); + while (select != null) { + methodChain.add(select); + if (!(select instanceof J.MethodInvocation)) { + break; + } - J.MethodInvocation selectMethod = (J.MethodInvocation) select; - select = selectMethod.getSelect(); + J.MethodInvocation selectMethod = (J.MethodInvocation) select; + select = selectMethod.getSelect(); - if (!STRING_BUILDER_APPEND.matches(selectMethod)) { - return false; - } + if (!STRING_BUILDER_APPEND.matches(selectMethod)) { + return false; + } - List args = selectMethod.getArguments(); - if (args.size() != 1) { - return false; - } else { - arguments.add(args.get(0)); + List args = selectMethod.getArguments(); + if (args.size() != 1) { + return false; + } else { + arguments.add(args.get(0)); + } } - } - if (select instanceof J.NewClass && - ((J.NewClass) select).getClazz() != null && - TypeUtils.isOfClassType(((J.NewClass) select).getClazz().getType(), "java.lang.StringBuilder")) { - J.NewClass nc = (J.NewClass) select; - if (nc.getArguments().size() == 1 && TypeUtils.isString(nc.getArguments().get(0).getType())) { - arguments.add(nc.getArguments().get(0)); + if (select instanceof J.NewClass && + ((J.NewClass) select).getClazz() != null && + TypeUtils.isOfClassType(((J.NewClass) select).getClazz().getType(), "java.lang.StringBuilder")) { + J.NewClass nc = (J.NewClass) select; + if (nc.getArguments().size() == 1 && TypeUtils.isString(nc.getArguments().get(0).getType())) { + arguments.add(nc.getArguments().get(0)); + } + return true; } - return true; + return false; } - return false; - } - - public static J.Parentheses getParenthesesTemplate() { - if (parenthesesTemplate == null) { - parenthesesTemplate = PartProvider.buildPart("class B { void foo() { (\"A\" + \"B\").length(); } } ", J.Parentheses.class); - } - return parenthesesTemplate; - } - - public static J.MethodInvocation getStringValueOfMethodInvocationTemplate() { - if (stringValueOfTemplate == null) { - stringValueOfTemplate = PartProvider.buildPart("class C {\n" + - " void foo() {\n" + - " Object obj = 1 + 2;\n" + - " String.valueOf(obj);\n" + - " }\n" + - "}", - J.MethodInvocation.class); - } - return stringValueOfTemplate; - } - - public static J.Parentheses wrapExpression(Expression exp) { - return getParenthesesTemplate().withTree(exp).withPrefix(exp.getPrefix()); } } From 3014a608e55281f4ff7591feb48e30f1b2fe2eac Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 11 Feb 2024 13:13:08 +0000 Subject: [PATCH 017/183] refactor: Remove public modifiers from RewriteTest classes Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/AvoidBoxedBooleanExpressionsTest.java | 2 +- .../staticanalysis/BigDecimalDoubleConstructorTest.java | 2 +- .../CommonStaticAnalysisIssuesPerformanceTest.java | 2 +- .../staticanalysis/CompareEnumsWithEqualityOperatorTest.java | 2 +- .../staticanalysis/FixStringFormatExpressionsTest.java | 2 +- .../openrewrite/staticanalysis/RemoveSystemOutPrintlnTest.java | 2 +- .../RemoveToStringCallsFromArrayInstancesTest.java | 2 +- .../staticanalysis/ReplaceRedundantFormatWithPrintfTest.java | 2 +- .../openrewrite/staticanalysis/ReplaceStackWithDequeTest.java | 2 +- .../staticanalysis/SimplifyDurationCreationUnitsTest.java | 2 +- .../staticanalysis/UpperCaseLiteralSuffixesTest.java | 2 +- .../UseForEachRemoveInsteadOfSetRemoveAllTest.java | 2 +- .../java/org/openrewrite/staticanalysis/UseListSortTest.java | 2 +- .../staticanalysis/groovy/MinimumSwitchCasesTest.java | 2 +- .../groovy/ReplaceLambdaWithMethodReferenceTest.java | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressionsTest.java b/src/test/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressionsTest.java index 1645e09c6..8952cfe4d 100644 --- a/src/test/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressionsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressionsTest.java @@ -22,7 +22,7 @@ import static org.openrewrite.java.Assertions.java; -public class AvoidBoxedBooleanExpressionsTest implements RewriteTest { +class AvoidBoxedBooleanExpressionsTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java b/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java index 5ab866cde..508b68fb0 100644 --- a/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructorTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings("UnpredictableBigDecimalConstructorCall") -public class BigDecimalDoubleConstructorTest implements RewriteTest { +class BigDecimalDoubleConstructorTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/CommonStaticAnalysisIssuesPerformanceTest.java b/src/test/java/org/openrewrite/staticanalysis/CommonStaticAnalysisIssuesPerformanceTest.java index f67ca3260..f4bffd037 100644 --- a/src/test/java/org/openrewrite/staticanalysis/CommonStaticAnalysisIssuesPerformanceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CommonStaticAnalysisIssuesPerformanceTest.java @@ -22,7 +22,7 @@ import static org.openrewrite.java.Assertions.java; -public class CommonStaticAnalysisIssuesPerformanceTest implements RewriteTest { +class CommonStaticAnalysisIssuesPerformanceTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperatorTest.java b/src/test/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperatorTest.java index a9d58c481..ab31eb487 100644 --- a/src/test/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperatorTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperatorTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; -public class CompareEnumsWithEqualityOperatorTest implements RewriteTest { +class CompareEnumsWithEqualityOperatorTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipe(new CompareEnumsWithEqualityOperator()); diff --git a/src/test/java/org/openrewrite/staticanalysis/FixStringFormatExpressionsTest.java b/src/test/java/org/openrewrite/staticanalysis/FixStringFormatExpressionsTest.java index 5a51c2bad..e2a37c2ed 100644 --- a/src/test/java/org/openrewrite/staticanalysis/FixStringFormatExpressionsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/FixStringFormatExpressionsTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings("ALL") -public class FixStringFormatExpressionsTest implements RewriteTest { +class FixStringFormatExpressionsTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipe(new FixStringFormatExpressions()); diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintlnTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintlnTest.java index 9ca93f3df..5de77314c 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintlnTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintlnTest.java @@ -22,7 +22,7 @@ import static org.openrewrite.java.Assertions.java; -public class RemoveSystemOutPrintlnTest implements RewriteTest { +class RemoveSystemOutPrintlnTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java index 08846e618..8053a052c 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstancesTest.java @@ -24,7 +24,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings({"ImplicitArrayToString", "UnnecessaryLocalVariable", "RedundantStringFormatCall", "MalformedFormatString", "PrimitiveArrayArgumentToVarargsMethod"}) -public class RemoveToStringCallsFromArrayInstancesTest implements RewriteTest { +class RemoveToStringCallsFromArrayInstancesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipe(new RemoveToStringCallsFromArrayInstances()); diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintfTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintfTest.java index a1cbbb96c..c0b0c3af1 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintfTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintfTest.java @@ -22,7 +22,7 @@ import static org.openrewrite.java.Assertions.java; -public class ReplaceRedundantFormatWithPrintfTest implements RewriteTest { +class ReplaceRedundantFormatWithPrintfTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceStackWithDequeTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceStackWithDequeTest.java index 33ef91c5b..5455113d0 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceStackWithDequeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceStackWithDequeTest.java @@ -24,7 +24,7 @@ import static org.openrewrite.java.Assertions.java; -public class ReplaceStackWithDequeTest implements RewriteTest { +class ReplaceStackWithDequeTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipe(new ReplaceStackWithDeque()); diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnitsTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnitsTest.java index 366a19004..c2f210337 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnitsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnitsTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; -public class SimplifyDurationCreationUnitsTest implements RewriteTest { +class SimplifyDurationCreationUnitsTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixesTest.java b/src/test/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixesTest.java index cb6c0b6c0..7d54c7354 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixesTest.java @@ -24,7 +24,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings("ALL") -public class UpperCaseLiteralSuffixesTest implements RewriteTest { +class UpperCaseLiteralSuffixesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAllTest.java b/src/test/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAllTest.java index 1d2c085f4..1e34bf201 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAllTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAllTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings("ALL") -public class UseForEachRemoveInsteadOfSetRemoveAllTest implements RewriteTest { +class UseForEachRemoveInsteadOfSetRemoveAllTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/UseListSortTest.java b/src/test/java/org/openrewrite/staticanalysis/UseListSortTest.java index bd9c79d9f..378003039 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseListSortTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseListSortTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings("ALL") -public class UseListSortTest implements RewriteTest { +class UseListSortTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec.recipe(new UseListSort()); diff --git a/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java b/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java index c10fa95a5..0256565a0 100644 --- a/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/groovy/MinimumSwitchCasesTest.java @@ -25,7 +25,7 @@ import static org.openrewrite.groovy.Assertions.groovy; -public class MinimumSwitchCasesTest implements RewriteTest { +class MinimumSwitchCasesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { diff --git a/src/test/java/org/openrewrite/staticanalysis/groovy/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/groovy/ReplaceLambdaWithMethodReferenceTest.java index 7ab9f7b64..af0a36d6c 100644 --- a/src/test/java/org/openrewrite/staticanalysis/groovy/ReplaceLambdaWithMethodReferenceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/groovy/ReplaceLambdaWithMethodReferenceTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.groovy.Assertions.groovy; -public class ReplaceLambdaWithMethodReferenceTest implements RewriteTest { +class ReplaceLambdaWithMethodReferenceTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { From a049e1c75c5b3e976612a0448d0088039bfaf246 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 12 Feb 2024 10:22:20 +0100 Subject: [PATCH 018/183] Do not remove type casts from lambdas or method references (#257) In response to a reported issue. --- .../RemoveRedundantTypeCast.java | 18 +++++--- .../RemoveRedundantTypeCastTest.java | 46 +++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index 510e74b39..f39ba0a8f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -101,14 +101,20 @@ public J visitTypeCast(J.TypeCast typeCast, ExecutionContext ctx) { JavaType expressionType = visitedTypeCast.getExpression().getType(); JavaType castType = visitedTypeCast.getType(); - if (targetType == null || - (targetType instanceof JavaType.Primitive || castType instanceof JavaType.Primitive) && castType != expressionType || - (typeCast.getExpression() instanceof J.Lambda || typeCast.getExpression() instanceof J.MemberReference) && castType instanceof JavaType.Parameterized) { + if (targetType == null) { + return visitedTypeCast; + } + if ((targetType instanceof JavaType.Primitive || castType instanceof JavaType.Primitive) && castType != expressionType) { + return visitedTypeCast; + } + if (typeCast.getExpression() instanceof J.Lambda || typeCast.getExpression() instanceof J.MemberReference) { // Not currently supported, this will be more accurate with dataflow analysis. return visitedTypeCast; - } else if (!(targetType instanceof JavaType.Array) && TypeUtils.isOfClassType(targetType, "java.lang.Object") || - TypeUtils.isOfType(targetType, expressionType) || - TypeUtils.isAssignableTo(targetType, expressionType)) { + } + + if (!(targetType instanceof JavaType.Array) && TypeUtils.isOfClassType(targetType, "java.lang.Object") || + TypeUtils.isOfType(targetType, expressionType) || + TypeUtils.isAssignableTo(targetType, expressionType)) { JavaType.FullyQualified fullyQualified = TypeUtils.asFullyQualified(castType); if (fullyQualified != null) { maybeRemoveImport(fullyQualified.getFullyQualifiedName()); diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java index 652079347..3a518a6fc 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCastTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.Issue; +import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -394,6 +395,7 @@ class ExtendTest extends Test { @Test void lambdaWithComplexTypeInference() { rewriteRun( + //language=java java( """ import java.util.LinkedHashMap; @@ -425,6 +427,7 @@ public MapDropdownChoice(Supplier> choiceMap) { @Test void returnPrimitiveIntToWrapperLong() { rewriteRun( + //language=java java( """ class Test { @@ -440,6 +443,7 @@ Long method() { @Test void castWildcard() { rewriteRun( + //language=java java( """ import java.util.ArrayList; @@ -459,6 +463,7 @@ void method() { @Test void removeImport() { rewriteRun( + //language=java java( """ import java.util.ArrayList; @@ -482,4 +487,45 @@ List method(List list) { ) ); } + + @Test + void retainCastInMarshaller() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion() + //language=java + .dependsOn( + """ + package org.glassfish.jaxb.core.marshaller; + import java.io.IOException; + import java.io.Writer; + + public interface CharacterEscapeHandler { + void escape( char[] ch, int start, int length, boolean isAttVal, Writer out ) throws IOException;\s + } + """, + """ + package javax.xml.bind; + + public interface Marshaller { + void setProperty(String var1, Object var2); + } + """ + ) + ), + //language=java + java( + """ + import javax.xml.bind.Marshaller; + import org.glassfish.jaxb.core.marshaller.CharacterEscapeHandler; + + class Foo { + void bar(Marshaller marshaller) { + marshaller.setProperty("org.glassfish.jaxb.characterEscapeHandler", (CharacterEscapeHandler) (ch, start, length, isAttVal, out) -> { + }); + } + } + """ + ) + ); + } } From b43b53dee08c895b7d3c7161a1db6e8836355bbb Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 20 Feb 2024 16:59:43 +0100 Subject: [PATCH 019/183] Do not escape newline in FixStringFormatExpressions (#261) * Do not escape newline in FixStringFormatExpressions * Show progression of number of slashes --- .../FixStringFormatExpressions.java | 6 +-- .../FixStringFormatExpressionsTest.java | 38 +++++++++++++++++-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java index bafdf1ba6..b25ab0ce4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java +++ b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java @@ -124,12 +124,12 @@ private static Expression replaceNewLineChars(Expression arg0) { if (arg0 instanceof J.Literal) { J.Literal fmt = (J.Literal) arg0; if (fmt.getValue() != null) { - fmt = fmt.withValue(fmt.getValue().toString().replace("\n", "%n")); + fmt = fmt.withValue(fmt.getValue().toString().replaceAll("(? Date: Wed, 21 Feb 2024 00:16:20 +0000 Subject: [PATCH 020/183] refactor: Refaster rules related to expressions dealing with time Use this link to re-run the recipe: https://app.moderne.io/recipes/tech.picnic.errorprone.refasterrules.TimeRulesRecipes?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/AvoidBoxedBooleanExpressions.java | 3 +-- .../openrewrite/staticanalysis/FixStringFormatExpressions.java | 3 +-- .../java/org/openrewrite/staticanalysis/InlineVariable.java | 3 +-- .../openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java | 3 +-- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java b/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java index 56cbc64ac..830081557 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java +++ b/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java @@ -24,7 +24,6 @@ import org.openrewrite.java.tree.TypeUtils; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.Set; import static java.util.Collections.singleton; @@ -48,7 +47,7 @@ public Set getTags() { @Override public Duration getEstimatedEffortPerOccurrence() { - return Duration.of(5, ChronoUnit.MINUTES); + return Duration.ofMinutes(5); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java index b25ab0ce4..063474a74 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java +++ b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java @@ -27,7 +27,6 @@ import org.openrewrite.java.tree.J; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Set; import java.util.regex.Matcher; @@ -51,7 +50,7 @@ public Set getTags() { @Override public Duration getEstimatedEffortPerOccurrence() { - return Duration.of(5, ChronoUnit.MINUTES); + return Duration.ofMinutes(5); } diff --git a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java index 475e6bf32..da63cfaf1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java +++ b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java @@ -27,7 +27,6 @@ import org.openrewrite.java.tree.Statement; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.List; import java.util.Set; @@ -53,7 +52,7 @@ public Set getTags() { @Override public Duration getEstimatedEffortPerOccurrence() { - return Duration.of(2, ChronoUnit.MINUTES); + return Duration.ofMinutes(2); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java index edad53f25..d0e419ae3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java +++ b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java @@ -26,7 +26,6 @@ import org.openrewrite.java.tree.JavaType; import java.time.Duration; -import java.time.temporal.ChronoUnit; import java.util.Collections; import java.util.Set; @@ -48,7 +47,7 @@ public Set getTags() { @Override public @Nullable Duration getEstimatedEffortPerOccurrence() { - return Duration.of(2, ChronoUnit.MINUTES); + return Duration.ofMinutes(2); } @Override From 6fc920533401e0f9a098fde655bc648d4dfb4087 Mon Sep 17 00:00:00 2001 From: Rick Ossendrijver Date: Wed, 21 Feb 2024 12:54:15 +0100 Subject: [PATCH 021/183] refactor: Refaster rules related to expressions dealing with `String`s (#262) Use this link to re-run the recipe: https://app.moderne.io/recipes/tech.picnic.errorprone.refasterrules.StringRulesRecipes?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/ReplaceLambdaWithMethodReference.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index 467bc140e..8f1f3a6da 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -25,7 +25,10 @@ import org.openrewrite.kotlin.tree.K; import java.time.Duration; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import static org.openrewrite.staticanalysis.JavaElementFactory.*; @@ -205,7 +208,7 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { private String className(J.NewClass method) { TypeTree clazz = method.getClazz(); return clazz instanceof J.ParameterizedType ? ((J.ParameterizedType) clazz).getClazz().toString() : - Objects.toString(clazz); + String.valueOf(clazz); } private boolean hasSelectWithPotentialSideEffects(MethodCall method) { From 1d098efb6f5d7393ba1a59a70604263c373bce64 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 25 Feb 2024 13:13:34 +0100 Subject: [PATCH 022/183] Don't replace lambda for field access either Fixes https://github.com/moderneinc/support-app/issues/33 --- .../ReplaceLambdaWithMethodReference.java | 12 +++++++++--- .../ReplaceLambdaWithMethodReferenceTest.java | 1 + 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index 8f1f3a6da..3cefcf87e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -217,9 +217,15 @@ private boolean hasSelectWithPotentialSideEffects(MethodCall method) { } private boolean hasSelectWhoseReferenceMightChange(MethodCall method) { - if (method instanceof J.MethodInvocation && ((J.MethodInvocation) method).getSelect() instanceof J.Identifier) { - JavaType.Variable fieldType = ((J.Identifier) ((J.MethodInvocation) method).getSelect()).getFieldType(); - return fieldType != null && fieldType.getOwner() instanceof JavaType.Class && !fieldType.hasFlags(Flag.Final); + if (method instanceof J.MethodInvocation) { + Expression select = ((J.MethodInvocation) method).getSelect(); + if (select instanceof J.Identifier) { + JavaType.Variable fieldType = ((J.Identifier) select).getFieldType(); + return fieldType != null && fieldType.getOwner() instanceof JavaType.Class && !fieldType.hasFlags(Flag.Final); + } else if (select instanceof J.FieldAccess) { + JavaType.Variable fieldType = ((J.FieldAccess) select).getName().getFieldType(); + return fieldType != null && fieldType.getOwner() instanceof JavaType.Class && !fieldType.hasFlags(Flag.Final); + } } return false; } diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java index 9b764f038..0403d02d6 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java @@ -1366,6 +1366,7 @@ class A { void foo() { // Runtime exception when replaced with field::toString Supplier supplier = () -> field.toString(); + Supplier supplier = () -> this.field.toString(); } } """ From 85a29d362d394422c483b1943b2454467f3665a0 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 28 Feb 2024 08:33:37 +0100 Subject: [PATCH 023/183] Improve performance of `MethodNameCasing` This is the slowest of the CSA recipes. --- .../staticanalysis/MethodNameCasing.java | 39 +++++++++---------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index d98deee56..02b96efa8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -35,8 +35,6 @@ import java.util.*; import java.util.regex.Pattern; -import static java.util.Objects.requireNonNull; - @Value @EqualsAndHashCode(callSuper = false) public class MethodNameCasing extends ScanningRecipe> { @@ -87,7 +85,7 @@ public TreeVisitor getScanner(List change @Override public J preVisit(J tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { - JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); + JavaSourceFile cu = (JavaSourceFile) tree; Optional sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class); if (!sourceSet.isPresent()) { stopAfterPreVisit(); @@ -104,22 +102,22 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex if (enclosingClass == null || enclosingClass.getKind() != J.ClassDeclaration.Kind.Type.Class) { return method; } + String simpleName = method.getSimpleName(); if (containsValidModifiers(method) && method.getMethodType() != null && enclosingClass.getType() != null && !method.isConstructor() && - !TypeUtils.isOverride(method.getMethodType()) && - !STANDARD_METHOD_NAME.matcher(method.getSimpleName()).matches() && - !method.getSimpleName().startsWith("_")) { + !simpleName.startsWith("_") && + !STANDARD_METHOD_NAME.matcher(simpleName).matches()) { StringBuilder standardized = new StringBuilder(); - String normalized = VariableNameUtils.normalizeName(method.getSimpleName()); - char[] name = normalized.toCharArray(); + String normalized = VariableNameUtils.normalizeName(simpleName); if (SNAKE_CASE.matcher(normalized).matches()) { standardized.append(NameCaseConvention.format(NameCaseConvention.LOWER_CAMEL, normalized)); } else { - for (int i = 0; i < name.length; i++) { - char c = name[i]; + int nameLength = normalized.length(); + for (int i = 0; i < nameLength; i++) { + char c = normalized.charAt(i); if (i == 0) { // the java specification requires identifiers to start with [a-zA-Z$_] @@ -128,11 +126,11 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } } else { if (!Character.isLetterOrDigit(c)) { - while (i < name.length && (!Character.isLetterOrDigit(name[i]) || name[i] > 'z')) { - i++; + while (i < nameLength && (!Character.isLetterOrDigit(c) || c > 'z')) { + c = normalized.charAt(i++); } - if (i < name.length) { - standardized.append(Character.toUpperCase(name[i])); + if (i < nameLength) { + standardized.append(Character.toUpperCase(c)); } } else { standardized.append(c); @@ -140,12 +138,11 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } } } - if (!StringUtils.isBlank(standardized.toString()) - && !methodExists(method.getMethodType(), standardized.toString())) { - String toName = standardized.toString(); - if (!StringUtils.isNumeric(toName)) { - changes.add(new ChangeMethodName(MethodMatcher.methodPattern(method), standardized.toString(), true, false)); - } + + String toName = standardized.toString(); + if (!StringUtils.isBlank(toName) && !StringUtils.isNumeric(toName) && + !methodExists(method.getMethodType(), toName)) { + changes.add(new ChangeMethodName(MethodMatcher.methodPattern(method), toName, false, false)); } } @@ -157,7 +154,7 @@ private boolean containsValidModifiers(J.MethodDeclaration method) { } private boolean methodExists(JavaType.Method method, String newName) { - return TypeUtils.findDeclaredMethod(method.getDeclaringType(), newName, method.getParameterTypes()).orElse(null) != null; + return TypeUtils.findDeclaredMethod(method.getDeclaringType(), newName, method.getParameterTypes()).isPresent(); } }; } From dec9ebcf4f3fed387a8ddab68836fa43de217145 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 28 Feb 2024 09:10:12 +0100 Subject: [PATCH 024/183] Improve performance of `MethodNameCasing` Private methods don't need to be considered for other CUs. --- .../staticanalysis/MethodNameCasing.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index 02b96efa8..abcbcf899 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -37,7 +37,7 @@ @Value @EqualsAndHashCode(callSuper = false) -public class MethodNameCasing extends ScanningRecipe> { +public class MethodNameCasing extends ScanningRecipe> { private static final Pattern STANDARD_METHOD_NAME = Pattern.compile("^[a-z][a-zA-Z0-9]*$"); private static final Pattern SNAKE_CASE = Pattern.compile("^[a-zA-Z0-9]+_\\w+$"); @@ -75,16 +75,18 @@ public Duration getEstimatedEffortPerOccurrence() { } @Override - public List getInitialValue(ExecutionContext ctx) { + public List getInitialValue(ExecutionContext ctx) { return new ArrayList<>(); } @Override - public TreeVisitor getScanner(List changes) { + public TreeVisitor getScanner(List changes) { return new JavaIsoVisitor() { + UUID scope; @Override public J preVisit(J tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { + scope = tree.getId(); JavaSourceFile cu = (JavaSourceFile) tree; Optional sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class); if (!sourceSet.isPresent()) { @@ -142,7 +144,11 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex String toName = standardized.toString(); if (!StringUtils.isBlank(toName) && !StringUtils.isNumeric(toName) && !methodExists(method.getMethodType(), toName)) { - changes.add(new ChangeMethodName(MethodMatcher.methodPattern(method), toName, false, false)); + changes.add(new MethodNameChange( + scope, + method.hasModifier(J.Modifier.Type.Private), + new ChangeMethodName(MethodMatcher.methodPattern(method), toName, false, false)) + ); } } @@ -160,14 +166,16 @@ private boolean methodExists(JavaType.Method method, String newName) { } @Override - public TreeVisitor getVisitor(List changes) { + public TreeVisitor getVisitor(List changes) { return new JavaIsoVisitor() { @Override public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) tree; - for (ChangeMethodName changeMethodName : changes) { - cu = (JavaSourceFile) changeMethodName.getVisitor().visitNonNull(cu, ctx); + for (MethodNameChange nameChange : changes) { + if (!nameChange.isPrivateMethod() || tree.getId().equals(nameChange.getScope())) { + cu = (JavaSourceFile) nameChange.getRecipe().getVisitor().visitNonNull(cu, ctx); + } } return cu; } @@ -175,4 +183,11 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { } }; } + + @Value + static class MethodNameChange { + UUID scope; + boolean privateMethod; + ChangeMethodName recipe; + } } From e7bcae496a3c07b9fb85770176b5e7c9ad97141c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 28 Feb 2024 12:40:35 +0000 Subject: [PATCH 025/183] refactor: Add concurrency group to automated code reviews Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/uQki1Qy59?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .github/workflows/receive-pr.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/receive-pr.yml b/.github/workflows/receive-pr.yml index a93c527d2..f2751ff1a 100644 --- a/.github/workflows/receive-pr.yml +++ b/.github/workflows/receive-pr.yml @@ -1,11 +1,17 @@ name: receive-pr + on: pull_request: types: [opened, synchronize] branches: - main + +concurrency: + group: '${{ github.workflow }} @ ${{ github.ref }}' + cancel-in-progress: true + # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ # Since this pull request receives untrusted code, we should **NOT** have any secrets in the environment. jobs: upload-patch: - uses: openrewrite/gh-automation/.github/workflows/receive-pr.yml@main + uses: openrewrite/gh-automation/.github/workflows/receive-pr.yml@main \ No newline at end of file From 3c01b181bec411bea25ea42e108b0bd002da95a7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 28 Feb 2024 13:52:14 +0000 Subject: [PATCH 026/183] refactor: Remove token from automated code reviews Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/GoAESmXCQ?organizationId=NTdlNDAzNjYtNmJkOC00MzNjLWJmMDQtMGI2MWRkZTEyYzg5 Co-authored-by: Moderne --- .github/workflows/comment-pr.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/comment-pr.yml b/.github/workflows/comment-pr.yml index f30bbd9fe..e55961025 100644 --- a/.github/workflows/comment-pr.yml +++ b/.github/workflows/comment-pr.yml @@ -1,15 +1,15 @@ name: comment-pr + # https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow on: workflow_run: workflows: ["receive-pr"] types: - completed + # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ # Since this pull request has write permissions on the target repo, we should **NOT** execute any untrusted code. jobs: post-suggestions: if: ${{ github.event.workflow_run.conclusion == 'success' }} uses: openrewrite/gh-automation/.github/workflows/comment-pr.yml@main - secrets: - GH_PAT_ACTIONS_READ: ${{ secrets.GH_PAT_ACTIONS_READ }} From aaf0da8aca137d6fee220732447dffbbabae0929 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 29 Feb 2024 11:40:03 +0100 Subject: [PATCH 027/183] refactor: Upgrade Develocity (#264) Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/1iLtcZtyo?organizationId=MzllMWZlODYtNDdhMS00ZDMzLTlhOWYtYTkwN2M4ZmJlYzBm Co-authored-by: Moderne --- .github/workflows/ci.yml | 2 -- settings.gradle.kts | 15 ++++----------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 011eec1de..379e49ff3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,8 +23,6 @@ jobs: uses: openrewrite/gh-automation/.github/workflows/ci-gradle.yml@main secrets: gradle_enterprise_access_key: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }} - gradle_enterprise_cache_username: ${{ secrets.GRADLE_ENTERPRISE_CACHE_USERNAME }} - gradle_enterprise_cache_password: ${{ secrets.GRADLE_ENTERPRISE_CACHE_PASSWORD }} ossrh_username: ${{ secrets.OSSRH_USERNAME }} ossrh_token: ${{ secrets.OSSRH_TOKEN }} ossrh_signing_key: ${{ secrets.OSSRH_SIGNING_KEY }} diff --git a/settings.gradle.kts b/settings.gradle.kts index 0f2341dab..bc0227384 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,19 +14,12 @@ plugins { gradleEnterprise { val isCiServer = System.getenv("CI")?.equals("true") ?: false server = "https://ge.openrewrite.org/" - val gradleCacheRemoteUsername: String? = System.getenv("GRADLE_ENTERPRISE_CACHE_USERNAME") - val gradleCacheRemotePassword: String? = System.getenv("GRADLE_ENTERPRISE_CACHE_PASSWORD") buildCache { - remote(HttpBuildCache::class) { - url = uri("https://ge.openrewrite.org/cache/") - isPush = isCiServer - if (!gradleCacheRemoteUsername.isNullOrBlank() && !gradleCacheRemotePassword.isNullOrBlank()) { - credentials { - username = gradleCacheRemoteUsername - password = gradleCacheRemotePassword - } - } + remote(gradleEnterprise.buildCache) { + isEnabled = true + val accessKey = System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY") + isPush = isCiServer && !accessKey.isNullOrBlank() } } From f6fa6f9e796284d7d98cffa905ea30b6e587fec7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 4 Mar 2024 21:16:12 +0100 Subject: [PATCH 028/183] Adopt new expectation for SimplifyBooleanExpression --- .../staticanalysis/SimplifyBooleanExpressionTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java index b45bceab3..6804963fe 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java @@ -315,6 +315,14 @@ public class A { boolean i=a!=true; } } + """, + """ + public class A { + { + boolean a=true; + boolean i=!a; + } + } """ ) ); From 760383f27927c50e9d64cb99e36eb3c34b07cb20 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Tue, 5 Mar 2024 09:26:14 +0100 Subject: [PATCH 029/183] Improve performance of `ReplaceLambdaWithMethodReference` --- .../ReplaceLambdaWithMethodReference.java | 126 +++++++++++------- 1 file changed, 79 insertions(+), 47 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index 3cefcf87e..c69a5c4f3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -25,11 +25,7 @@ import org.openrewrite.kotlin.tree.K; import java.time.Duration; -import java.util.Collections; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; +import java.util.*; import static org.openrewrite.staticanalysis.JavaElementFactory.*; @@ -80,17 +76,17 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { J.Lambda l = (J.Lambda) super.visitLambda(lambda, ctx); updateCursor(l); - String code = ""; J body = l.getBody(); if (body instanceof J.Block && ((J.Block) body).getStatements().size() == 1) { Statement statement = ((J.Block) body).getStatements().get(0); - if (statement instanceof J.MethodInvocation) { - body = statement; - } else if (statement instanceof J.Return && - (((J.Return) statement).getExpression()) instanceof MethodCall) { + if (statement instanceof J.Return) { body = ((J.Return) statement).getExpression(); + } else { + body = statement; } - } else if (body instanceof J.InstanceOf) { + } + + if (body instanceof J.InstanceOf) { J.InstanceOf instanceOf = (J.InstanceOf) body; J j = instanceOf.getClazz(); if ((j instanceof J.Identifier || j instanceof J.FieldAccess) && @@ -101,17 +97,18 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { JavaType.FullyQualified rawClassType = ((JavaType.Parameterized) classLiteral.getType()).getType(); Optional isInstanceMethod = rawClassType.getMethods().stream().filter(m -> m.getName().equals("isInstance")).findFirst(); if (isInstanceMethod.isPresent()) { - J.MemberReference updated = newInstanceMethodReference(isInstanceMethod.get(), classLiteral, lambda.getType()).withPrefix(lambda.getPrefix()); + J.MemberReference updated = newInstanceMethodReference(classLiteral, isInstanceMethod.get(), lambda.getType()).withPrefix(lambda.getPrefix()); doAfterVisit(service(ImportService.class).shortenFullyQualifiedTypeReferencesIn(updated)); return updated; } } } + return l; } else if (body instanceof J.TypeCast && l.getParameters().getParameters().size() == 1) { J.TypeCast cast = (J.TypeCast) body; J param = l.getParameters().getParameters().get(0); if (cast.getExpression() instanceof J.Identifier && param instanceof J.VariableDeclarations && - ((J.Identifier) cast.getExpression()).getSimpleName().equals(((J.VariableDeclarations) param).getVariables().get(0).getSimpleName())) { + ((J.Identifier) cast.getExpression()).getSimpleName().equals(((J.VariableDeclarations) param).getVariables().get(0).getSimpleName())) { J.ControlParentheses j = cast.getClazz(); J tree = j.getTree(); if ((tree instanceof J.Identifier || tree instanceof J.FieldAccess) && @@ -122,20 +119,23 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { JavaType.FullyQualified classType = ((JavaType.Parameterized) classLiteral.getType()).getType(); Optional castMethod = classType.getMethods().stream().filter(m -> m.getName().equals("cast")).findFirst(); if (castMethod.isPresent()) { - J.MemberReference updated = newInstanceMethodReference(castMethod.get(), classLiteral, lambda.getType()).withPrefix(lambda.getPrefix()); + J.MemberReference updated = newInstanceMethodReference(classLiteral, castMethod.get(), lambda.getType()).withPrefix(lambda.getPrefix()); doAfterVisit(service(ImportService.class).shortenFullyQualifiedTypeReferencesIn(updated)); return updated; } } } } + return l; } + String code = ""; if (body instanceof J.Binary) { J.Binary binary = (J.Binary) body; - if (isNullCheck(binary.getLeft(), binary.getRight()) || + if ((binary.getOperator() == J.Binary.Type.Equal || binary.getOperator() == J.Binary.Type.NotEqual) && + isNullCheck(binary.getLeft(), binary.getRight()) || isNullCheck(binary.getRight(), binary.getLeft())) { - code = J.Binary.Type.Equal.equals(binary.getOperator()) ? "java.util.Objects::isNull" : + code = J.Binary.Type.Equal == binary.getOperator() ? "java.util.Objects::isNull" : "java.util.Objects::nonNull"; J updated = JavaTemplate.builder(code) .contextSensitive() @@ -159,19 +159,21 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { } } } + } else if (method instanceof J.MemberReference) { + return l; } - if (hasSelectWithPotentialSideEffects(method) || + if (method.getMethodType() == null || + hasSelectWithPotentialSideEffects(method) || hasSelectWhoseReferenceMightChange(method) || - !methodArgumentsMatchLambdaParameters(method, lambda) || - method instanceof J.MemberReference) { + !methodArgumentsMatchLambdaParameters(method, lambda)) { return l; } - Expression select = - method instanceof J.MethodInvocation ? ((J.MethodInvocation) method).getSelect() : null; JavaType.Method methodType = method.getMethodType(); if (methodType != null && !isMethodReferenceAmbiguous(methodType)) { + Expression select = + method instanceof J.MethodInvocation ? ((J.MethodInvocation) method).getSelect() : null; if (methodType.hasFlags(Flag.Static) || methodSelectMatchesFirstLambdaParameter(method, lambda)) { if (method.getType() instanceof JavaType.Parameterized && @@ -183,19 +185,19 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { doAfterVisit(service(ImportService.class).shortenFullyQualifiedTypeReferencesIn(updated)); return updated; } else if (method instanceof J.NewClass) { - return JavaTemplate.builder("#{}::new") - .contextSensitive() - .build() - .apply(getCursor(), l.getCoordinates().replace(), className((J.NewClass) method)); + NameTree clazz = ((J.NewClass) method).getClazz(); + clazz = clazz instanceof J.ParameterizedType ? ((J.ParameterizedType) clazz).getClazz() : clazz; + return newInstanceMethodReference(clazz.withPrefix(Space.EMPTY), "new", methodType, lambda.getType()).withPrefix(lambda.getPrefix()); } else if (select != null) { - return newInstanceMethodReference(methodType, select, lambda.getType()).withPrefix(lambda.getPrefix()); + return newInstanceMethodReference(select, methodType, lambda.getType()).withPrefix(lambda.getPrefix()); } else { Cursor owner = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || (is instanceof J.NewClass && ((J.NewClass) is).getBody() != null) || is instanceof J.Lambda); return JavaElementFactory.newInstanceMethodReference( - method.getMethodType(), - JavaElementFactory.newThis(owner.getValue().getType()), lambda.getType() + JavaElementFactory.newThis(owner.getValue().getType()), + methodType, + lambda.getType() ).withPrefix(lambda.getPrefix()); } } @@ -204,13 +206,6 @@ public J visitLambda(J.Lambda lambda, ExecutionContext ctx) { return l; } - // returns the class name as given in the source code (qualified or unqualified) - private String className(J.NewClass method) { - TypeTree clazz = method.getClazz(); - return clazz instanceof J.ParameterizedType ? ((J.ParameterizedType) clazz).getClazz().toString() : - String.valueOf(clazz); - } - private boolean hasSelectWithPotentialSideEffects(MethodCall method) { return method instanceof J.MethodInvocation && ((J.MethodInvocation) method).getSelect() instanceof MethodCall; @@ -236,12 +231,8 @@ private boolean methodArgumentsMatchLambdaParameters(MethodCall method, J.Lambda return false; } boolean static_ = methodType.hasFlags(Flag.Static); - List methodArgs = method.getArguments().stream().filter(a -> !(a instanceof J.Empty)) - .collect(Collectors.toList()); - List lambdaParameters = lambda.getParameters().getParameters() - .stream().filter(J.VariableDeclarations.class::isInstance) - .map(J.VariableDeclarations.class::cast).map(v -> v.getVariables().get(0)) - .collect(Collectors.toList()); + List methodArgs = getMethodArguments(method); + List lambdaParameters = getLambdaParameters(lambda); if (methodArgs.isEmpty() && lambdaParameters.isEmpty()) { return true; } @@ -264,6 +255,43 @@ private boolean methodArgumentsMatchLambdaParameters(MethodCall method, J.Lambda return true; } + private static List getMethodArguments(MethodCall method) { + List list = new ArrayList<>(); + if (method instanceof J.MethodInvocation) { + // avoid additional `ArrayList` allocation by using `JContainer#getElements()` + for (Expression a : ((J.MethodInvocation) method).getPadding().getArguments().getElements()) { + if (!(a instanceof J.Empty)) { + list.add(a); + } + } + } else if (method instanceof J.NewClass) { + // avoid additional `ArrayList` allocation by using `JContainer#getElements()` + for (Expression a : ((J.NewClass) method).getPadding().getArguments().getElements()) { + if (!(a instanceof J.Empty)) { + list.add(a); + } + } + } else { + for (Expression a : method.getArguments()) { + if (!(a instanceof J.Empty)) { + list.add(a); + } + } + } + return list; + } + + private static List getLambdaParameters(J.Lambda lambda) { + List list = new ArrayList<>(); + for (J j : lambda.getParameters().getParameters()) { + if (j instanceof J.VariableDeclarations) { + J.VariableDeclarations.NamedVariable namedVariable = ((J.VariableDeclarations) j).getVariables().get(0); + list.add(namedVariable); + } + } + return list; + } + private boolean methodSelectMatchesFirstLambdaParameter(MethodCall method, J.Lambda lambda) { if (!(method instanceof J.MethodInvocation) || !(((J.MethodInvocation) method).getSelect() instanceof J.Identifier) || @@ -282,12 +310,16 @@ private boolean isNullCheck(J j1, J j2) { "null".equals(((J.Literal) j2).getValueSource()); } - private boolean isMethodReferenceAmbiguous(JavaType.Method _method) { - return _method.getDeclaringType().getMethods().stream() - .filter(meth -> meth.getName().equals(_method.getName())) - .filter(meth -> !meth.getName().equals("println")) - .filter(meth -> !meth.isConstructor()) - .count() > 1; + private boolean isMethodReferenceAmbiguous(JavaType.Method method) { + int count = 0; + for (JavaType.Method meth : method.getDeclaringType().getMethods()) { + if (meth.getName().equals(method.getName()) && !meth.getName().equals("println") && !meth.isConstructor()) { + if (++count > 1) { + return true; + } + } + } + return false; } } From 88f3531b9a5fad4ebb97d0222b38c969012969b2 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Tue, 5 Mar 2024 09:27:16 +0100 Subject: [PATCH 030/183] Add missing changes to `JavaElementFactory` --- .../staticanalysis/JavaElementFactory.java | 12 ++++++++---- .../staticanalysis/JavaElementFactoryTest.java | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java b/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java index ff9665b81..b0e3f1412 100644 --- a/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java +++ b/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java @@ -42,7 +42,7 @@ static J.Binary newLogicalExpression(J.Binary.Type operator, Expression left, Ex static J.MemberReference newStaticMethodReference(JavaType.Method method, boolean qualified, @Nullable JavaType type) { JavaType.FullyQualified declaringType = method.getDeclaringType(); Expression containing = className(declaringType, qualified); - return newInstanceMethodReference(method, containing, type); + return newInstanceMethodReference(containing, method, type); } static Expression className(JavaType type, boolean qualified) { @@ -102,16 +102,20 @@ static Expression className(JavaType type, boolean qualified) { return name; } - static J.MemberReference newInstanceMethodReference(JavaType.Method method, Expression containing, @Nullable JavaType type) { + static J.MemberReference newInstanceMethodReference(Expression containing, JavaType.Method method, @Nullable JavaType type) { + return newInstanceMethodReference(containing, method.getName(), method, type); + } + + static J.MemberReference newInstanceMethodReference(Expression containing, String methodName, JavaType.Method methodType, @Nullable JavaType type) { return new J.MemberReference( randomId(), Space.EMPTY, Markers.EMPTY, new JRightPadded<>(containing, Space.EMPTY, Markers.EMPTY), null, - new JLeftPadded<>(Space.EMPTY, new J.Identifier(randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), method.getName(), null, null), Markers.EMPTY), + new JLeftPadded<>(Space.EMPTY, new J.Identifier(randomId(), Space.EMPTY, Markers.EMPTY, emptyList(), methodName, null, null), Markers.EMPTY), type, - method, + methodType, null ); } diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java b/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java index cd7ae3756..8c844a913 100644 --- a/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java @@ -76,7 +76,7 @@ public J.MemberReference visitMemberReference(J.MemberReference memberRef, Atomi }.reduce(sourceFile, new AtomicReference<>(null)).get(); assertThat(reference).isNotNull(); - J.MemberReference methodReference = newInstanceMethodReference(fooMethod, reference.getContaining(), reference.getType()); + J.MemberReference methodReference = newInstanceMethodReference(reference.getContaining(), fooMethod, reference.getType()); assertThat(SemanticallyEqual.areEqual(reference, methodReference)).isTrue(); } From ad1cf8e0b71e23b0e323e47548f3f5250f829b04 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 6 Mar 2024 15:43:34 +0100 Subject: [PATCH 031/183] `NoEmptyCollectionWithRawType`: Minor tweaks --- .../staticanalysis/NoEmptyCollectionWithRawType.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index 0f5d4252c..c183ee85f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -58,7 +58,6 @@ public Duration getEstimatedEffortPerOccurrence() { public TreeVisitor getVisitor() { return Preconditions.check(new UsesType<>("java.util.Collections", false), new JavaVisitor() { final Map updateFields = new HashMap<>(); - { updateFields.put("EMPTY_LIST", "emptyList"); updateFields.put("EMPTY_MAP", "emptyMap"); @@ -79,13 +78,13 @@ public J visitImport(J.Import anImport, ExecutionContext ctx) { public J visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { J.Identifier name = fieldAccess.getName(); JavaType.Variable varType = name.getFieldType(); - if (varType != null && TypeUtils.isOfClassType(varType.getOwner(), "java.util.Collections") && - varType.getName().startsWith("EMPTY_")) { - return JavaTemplate.builder("#{any(java.util.Collections)}." + updateFields.get(varType.getName()) + "()") + if (varType != null && varType.getName().startsWith("EMPTY_") && + TypeUtils.isOfClassType(varType.getOwner(), "java.util.Collections")) { + return JavaTemplate.builder("java.util.Collections." + updateFields.get(varType.getName()) + "()") .contextSensitive() // context sensitive due to generics - .imports("java.util.Collections") .build() - .apply(getCursor(), fieldAccess.getCoordinates().replace(), fieldAccess.getTarget()); + .apply(getCursor(), fieldAccess.getCoordinates().replace()) + .withSelect(fieldAccess.getTarget()); } return super.visitFieldAccess(fieldAccess, ctx); } From bcb3dcda1daa9bd1ff7aaa2c094643e327c26501 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 6 Mar 2024 17:03:42 +0100 Subject: [PATCH 032/183] `NoEmptyCollectionWithRawType`: Minor tweaks --- .../NoEmptyCollectionWithRawType.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index c183ee85f..aad0f8db3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -21,7 +21,7 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.search.UsesField; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; @@ -56,8 +56,14 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesType<>("java.util.Collections", false), new JavaVisitor() { + TreeVisitor precondition = Preconditions.or( + new UsesField<>("java.util.Collections", "EMPTY_LIST"), + new UsesField<>("java.util.Collections", "EMPTY_MAP"), + new UsesField<>("java.util.Collections", "EMPTY_SET") + ); + return Preconditions.check(precondition, new JavaVisitor() { final Map updateFields = new HashMap<>(); + { updateFields.put("EMPTY_LIST", "emptyList"); updateFields.put("EMPTY_MAP", "emptyMap"); @@ -71,7 +77,7 @@ public J visitImport(J.Import anImport, ExecutionContext ctx) { TypeUtils.isOfClassType(anImport.getQualid().getTarget().getType(), "java.util.Collections")) { return anImport.withQualid(anImport.getQualid().withName(name.withSimpleName(updateFields.get(name.getSimpleName())))); } - return super.visitImport(anImport, ctx); + return anImport; } @Override @@ -91,19 +97,17 @@ public J visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { @Override public J visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { - J.Identifier id = (J.Identifier) super.visitIdentifier(identifier, ctx); - JavaType.Variable varType = id.getFieldType(); - if (varType != null && TypeUtils.isOfClassType(varType.getOwner(), "java.util.Collections") && - varType.getName().startsWith("EMPTY_")) { + JavaType.Variable varType = identifier.getFieldType(); + if (varType != null && varType.getName().startsWith("EMPTY_") && + TypeUtils.isOfClassType(varType.getOwner(), "java.util.Collections")) { return JavaTemplate.builder(updateFields.get(varType.getName()) + "()") .contextSensitive() // context sensitive due to generics .staticImports("java.util.Collections." + updateFields.get(varType.getName())) .build() .apply(getCursor(), identifier.getCoordinates().replace()); - } - return id; + return identifier; } }); } From 8420c57b166a0f493f2721b6355ccd8960bc6810 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 6 Mar 2024 17:17:22 +0100 Subject: [PATCH 033/183] `NoEmptyCollectionWithRawType`: More minor tweaks --- .../SimplifyBooleanExpressionTest.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java index 6804963fe..8bdbbbb98 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java @@ -353,4 +353,30 @@ public class A { ) ); } + + @Test + void ternary() { + rewriteRun( + java( + """ + public class A { + { + if (!true || !true) { + System.out.println(""); + } + } + } + """, + """ + public class A { + { + if (false) { + System.out.println(""); + } + } + } + """ + ) + ); + } } From f1672bbd8e3a4be3c2e81dc84f76afe66fd6be4a Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 6 Mar 2024 17:19:05 +0100 Subject: [PATCH 034/183] `NoEmptyCollectionWithRawType`: Use pattern in `UsesField` --- .../staticanalysis/NoEmptyCollectionWithRawType.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index aad0f8db3..fc6f90195 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -56,12 +56,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - TreeVisitor precondition = Preconditions.or( - new UsesField<>("java.util.Collections", "EMPTY_LIST"), - new UsesField<>("java.util.Collections", "EMPTY_MAP"), - new UsesField<>("java.util.Collections", "EMPTY_SET") - ); - return Preconditions.check(precondition, new JavaVisitor() { + return Preconditions.check(new UsesField<>("java.util.Collections", "EMPTY_*"), new JavaVisitor() { final Map updateFields = new HashMap<>(); { From c6e7a0f8cf9d424331845b688e51c71e1a46f363 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Wed, 6 Mar 2024 17:46:58 +0100 Subject: [PATCH 035/183] Better combine `UsesType` with `UsesField` pattern? --- .../staticanalysis/NoEmptyCollectionWithRawType.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index fc6f90195..2cb8f3727 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -22,6 +22,7 @@ import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.search.UsesField; +import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; @@ -56,7 +57,10 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesField<>("java.util.Collections", "EMPTY_*"), new JavaVisitor() { + return Preconditions.check(Preconditions.and( + new UsesType<>("java.util.Collections", false), + new UsesField<>("java.util.Collections", "EMPTY_*") + ), new JavaVisitor() { final Map updateFields = new HashMap<>(); { From 5319d0deaf9ec3a1af2d2a8de05372afddfadb6d Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 7 Mar 2024 08:10:15 +0100 Subject: [PATCH 036/183] `IsEmptyCallOnCollections`: Remove context-sensitive template --- .../IsEmptyCallOnCollections.java | 50 ++++++++++--------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java b/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java index 8270ce05f..7f1e8d12c 100755 --- a/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java +++ b/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java @@ -25,10 +25,12 @@ import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaCoordinates; import java.time.Duration; import java.util.Arrays; import java.util.LinkedHashSet; +import java.util.Objects; import java.util.Set; public class IsEmptyCallOnCollections extends Recipe { @@ -57,11 +59,6 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesMethod<>(COLLECTION_SIZE), new JavaVisitor() { - final JavaTemplate isEmpty = JavaTemplate.builder("#{}#{any(java.util.Collection)}.isEmpty()") - .build(); - final JavaTemplate isEmptyNoReceiver = JavaTemplate.builder("#{}isEmpty()") - .contextSensitive() - .build(); @Override public J visitBinary(J.Binary binary, ExecutionContext ctx) { @@ -73,17 +70,13 @@ public J visitBinary(J.Binary binary, ExecutionContext ctx) { J maybeSizeCall = zeroRight ? binary.getLeft() : binary.getRight(); if (maybeSizeCall instanceof J.MethodInvocation) { J.MethodInvocation maybeSizeCallMethod = (J.MethodInvocation) maybeSizeCall; - if (COLLECTION_SIZE.matches(maybeSizeCallMethod)) { - String op = binary.getOperator() == J.Binary.Type.Equal ? "" : "!"; - return (maybeSizeCallMethod.getSelect() == null ? - isEmptyNoReceiver.apply(getCursor(), binary.getCoordinates().replace(), op) : - isEmpty.apply(getCursor(), binary.getCoordinates().replace(), op, maybeSizeCallMethod.getSelect()) - ).withPrefix(binary.getPrefix()); + if (COLLECTION_SIZE.matches(maybeSizeCallMethod) && maybeSizeCallMethod.getMethodType() != null) { + return newIsEmptyCall(maybeSizeCallMethod, binary.getCoordinates().replace(), binary.getOperator() != J.Binary.Type.Equal) + .withPrefix(binary.getPrefix()); } } } - } - if (isOne(binary.getLeft()) || isOne(binary.getRight())) { + } else if (isOne(binary.getLeft()) || isOne(binary.getRight())) { boolean oneRight = isOne(binary.getRight()); if ((oneRight && binary.getOperator() == J.Binary.Type.LessThan) || (!oneRight && binary.getOperator() == J.Binary.Type.GreaterThan) @@ -92,30 +85,39 @@ public J visitBinary(J.Binary binary, ExecutionContext ctx) { J maybeSizeCall = oneRight ? binary.getLeft() : binary.getRight(); if (maybeSizeCall instanceof J.MethodInvocation) { J.MethodInvocation maybeSizeCallMethod = (J.MethodInvocation) maybeSizeCall; - if (COLLECTION_SIZE.matches(maybeSizeCallMethod)) { - String op = ""; - if (binary.getOperator() == J.Binary.Type.GreaterThanOrEqual || binary.getOperator() == J.Binary.Type.LessThanOrEqual) { - op = "!"; - } - return (maybeSizeCallMethod.getSelect() == null ? - isEmptyNoReceiver.apply(getCursor(), binary.getCoordinates().replace(), op) : - isEmpty.apply(getCursor(), binary.getCoordinates().replace(), op, maybeSizeCallMethod.getSelect()) - ).withPrefix(binary.getPrefix()); + if (COLLECTION_SIZE.matches(maybeSizeCallMethod) && maybeSizeCallMethod.getMethodType() != null) { + return newIsEmptyCall(maybeSizeCallMethod, binary.getCoordinates().replace(), + binary.getOperator() == J.Binary.Type.GreaterThanOrEqual || binary.getOperator() == J.Binary.Type.LessThanOrEqual) + .withPrefix(binary.getPrefix()); } } } } return super.visitBinary(binary, ctx); } + + private J newIsEmptyCall(J.MethodInvocation method, JavaCoordinates coordinates, boolean negate) { + JavaTemplate isEmpty = JavaTemplate.builder("#{}#{any(java.util.Collection)}.isEmpty()").build(); + if (method.getSelect() == null) { + assert method.getMethodType() != null; + J.Identifier this_ = JavaElementFactory.newThis(method.getMethodType().getDeclaringType()); + J isEmptyCall = isEmpty.apply(getCursor(), coordinates, negate ? "!" : "", this_); + if (negate) { + return ((J.Unary) isEmptyCall).withExpression(((J.MethodInvocation) ((J.Unary) isEmptyCall).getExpression()).withSelect(null)); + } + return ((J.MethodInvocation) isEmptyCall).withSelect(null); + } + return isEmpty.apply(getCursor(), coordinates, negate ? "!" : "", method.getSelect()); + } }); } private static boolean isZero(Expression expression) { - return expression instanceof J.Literal && Integer.valueOf(0).equals(((J.Literal) expression).getValue()); + return expression instanceof J.Literal && Objects.equals(0, ((J.Literal) expression).getValue()); } private static boolean isOne(Expression expression) { - return expression instanceof J.Literal && Integer.valueOf(1).equals(((J.Literal) expression).getValue()); + return expression instanceof J.Literal && Objects.equals(1, ((J.Literal) expression).getValue()); } } From c67103873da5ddd395eef138767a4ba66504e8bb Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 12 Mar 2024 09:12:47 +0000 Subject: [PATCH 037/183] refactor: Add missing `@Override` to overriding and implementing methods Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.staticanalysis.MissingOverrideAnnotation?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../org/openrewrite/staticanalysis/EqualsToContentEquals.java | 1 + .../staticanalysis/RemoveToStringCallsFromArrayInstances.java | 1 + .../org/openrewrite/staticanalysis/UseSystemLineSeparator.java | 1 + 3 files changed, 3 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java b/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java index a261c13f1..e4fa2e74c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java @@ -45,6 +45,7 @@ public String getDescription() { return "Use `String.contentEquals(CharSequence)` instead of `String.equals(CharSequence.toString())`."; } + @Override public TreeVisitor getVisitor() { return Preconditions.check(PRECONDITION, new EqualsToContentEqualsVisitor()); } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java b/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java index 792b8a840..18d18db48 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java @@ -62,6 +62,7 @@ public String getDescription() { " the contents of the array. `Arrays.toString(array)` should be used instead as it gives the contents of the array."; } + @Override public TreeVisitor getVisitor() { return new RemoveToStringFromArraysVisitor(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseSystemLineSeparator.java b/src/main/java/org/openrewrite/staticanalysis/UseSystemLineSeparator.java index 7d478a306..7e1e2e8ce 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseSystemLineSeparator.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseSystemLineSeparator.java @@ -49,6 +49,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesMethod<>(GET_PROPERTY), new JavaVisitor() { + @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation invocation = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); From 225de9510ff31f6da33479991d8756f9d8582c73 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 12 Mar 2024 09:52:46 +0000 Subject: [PATCH 038/183] refactor: Fix missing braces Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.staticanalysis.NeedBraces?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/AddSerialVersionUidToSerializable.java | 4 +++- .../ReplaceOptionalIsPresentWithIfPresent.java | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java index 8eb9e9171..56e29ac08 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java @@ -133,7 +133,9 @@ private boolean requiresSerialVersionField(@Nullable JavaType type) { //All other parameterized types fall through } else if (type instanceof JavaType.FullyQualified) { JavaType.FullyQualified fq = (JavaType.FullyQualified) type; - if (fq.getKind() == JavaType.Class.Kind.Enum) return false; + if (fq.getKind() == JavaType.Class.Kind.Enum) { + return false; + } if (fq.getKind() != JavaType.Class.Kind.Interface && !fq.isAssignableTo("java.lang.Throwable")) { diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java index 4497bb1f2..d7206b163 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java @@ -239,9 +239,10 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) if (OPTIONAL_GET.matches(mi) && mi.getSelect() instanceof J.Identifier) { J.Identifier selectToBeReplaced = (J.Identifier) mi.getSelect(); if (methodSelector.getSimpleName().equals(selectToBeReplaced.getSimpleName()) && - methodSelector.getFieldType() != null && - methodSelector.getFieldType().equals(selectToBeReplaced.getFieldType())) + methodSelector.getFieldType() != null && + methodSelector.getFieldType().equals(selectToBeReplaced.getFieldType())) { return lambdaParameterIdentifier.withPrefix(method.getPrefix()); + } } return mi; } From 3dac84a258ca5f73d5356964eb2638bb947e9862 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 12 Mar 2024 11:31:02 +0000 Subject: [PATCH 039/183] refactor: Common static analysis issues Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/ggZMncs1O?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../openrewrite/staticanalysis/JavaElementFactoryTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java b/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java index 8c844a913..765efe3d8 100644 --- a/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java @@ -64,7 +64,7 @@ Supplier bar() { JavaType.Class fooType = typeCache.get("foo.Foo"); assertThat(fooType).isNotNull(); - JavaType.Method fooMethod = fooType.getMethods().stream().filter(m -> m.getName().equals("foo")).findFirst().get(); + JavaType.Method fooMethod = fooType.getMethods().stream().filter(m -> "foo".equals(m.getName())).findFirst().get(); assertThat(fooMethod).isNotNull(); J.MemberReference reference = new JavaIsoVisitor>() { @@ -103,7 +103,7 @@ long bar() { JavaType.Class fooType = typeCache.get("foo.Foo"); assertThat(fooType).isNotNull(); - JavaType.Method fooMethod = fooType.getMethods().stream().filter(m -> m.getName().equals("foo")).findFirst().get(); + JavaType.Method fooMethod = fooType.getMethods().stream().filter(m -> "foo".equals(m.getName())).findFirst().get(); assertThat(fooMethod).isNotNull(); J.MemberReference reference = new JavaIsoVisitor>() { From 9bb285aad0a263ca6bda20e5b46049d8bb21ab8b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 12 Mar 2024 12:35:35 +0100 Subject: [PATCH 040/183] Skip RenamePrivateFieldsToCamelCase when using lombok (#268) Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/267 --- .../RenamePrivateFieldsToCamelCase.java | 16 +++++++++ .../RenamePrivateFieldsToCamelCaseTest.java | 35 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java index 4a37446cb..4adacef76 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java @@ -17,6 +17,8 @@ import org.openrewrite.*; import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.AnnotationMatcher; +import org.openrewrite.java.service.AnnotationService; import org.openrewrite.java.tree.Flag; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; @@ -40,6 +42,7 @@ * - The recipe will not rename fields if the result already exists in a class or the result will be a java reserved keyword. */ public class RenamePrivateFieldsToCamelCase extends Recipe { + private static final AnnotationMatcher LOMBOK_ANNOTATION = new AnnotationMatcher("@lombok.*"); @Override public String getDisplayName() { @@ -83,6 +86,15 @@ protected boolean shouldRename(Set hasNameKey, J.VariableDeclarations.Na ); } + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + // Skip classes annotated with Lombok annotations, as their fields might be set or exposed by Lombok. + if (service(AnnotationService.class).matches(getCursor(), LOMBOK_ANNOTATION)) { + return classDecl; + } + return super.visitClassDeclaration(classDecl, ctx); + } + @SuppressWarnings("all") @Override public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { @@ -123,6 +135,10 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations @Override public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + if (service(AnnotationService.class).matches(getCursor(), LOMBOK_ANNOTATION)) { + return multiVariable; + } + J.VariableDeclarations vds = super.visitVariableDeclarations(multiVariable, ctx); if (getCursor().getMessage("ADD_STATIC", false)) { return vds.withModifiers(ListUtils.insert(vds.getModifiers(), diff --git a/src/test/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCaseTest.java b/src/test/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCaseTest.java index 2bd8f7b25..5045df1cc 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCaseTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCaseTest.java @@ -19,6 +19,7 @@ import org.openrewrite.DocumentExample; import org.openrewrite.Issue; import org.openrewrite.Recipe; +import org.openrewrite.java.JavaParser; import org.openrewrite.java.marker.JavaVersion; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -585,4 +586,38 @@ public record MyRecord( ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/267") + void doNotChangeLombokAnnotatedClasses() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().classpath("lombok")), + //language=java + java( + """ + @lombok.RequiredArgsConstructor + class Test { + private String D_TYPE_CONNECT = ""; + } + """ + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/267") + void doNotChangeLombokAnnotatedFields() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().classpath("lombok")), + //language=java + java( + """ + class Test { + @lombok.Setter + private String D_TYPE_CONNECT = ""; + } + """ + ) + ); + } } From 8007d4f611116e924a15cc4b30e21c8f39f4c40f Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 14 Mar 2024 19:53:40 +0100 Subject: [PATCH 041/183] Speed up `RenameLocalVariablesToCamelCase` a bit --- .../RenameLocalVariablesToCamelCase.java | 23 +++++++++++-------- .../RenamePrivateFieldsToCamelCase.java | 4 ++-- .../staticanalysis/RenameToCamelCase.java | 18 +++++++++++---- 3 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java index 8fa99363c..baa08a507 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java @@ -25,7 +25,6 @@ import java.time.Duration; import java.util.Collections; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -75,12 +74,11 @@ public Duration getEstimatedEffortPerOccurrence() { public TreeVisitor getVisitor() { return new RenameToCamelCase() { @Override - protected boolean shouldRename(Set hasNameKey, J.VariableDeclarations.NamedVariable variable, String toName) { + protected boolean shouldRename(Set hasNameSet, J.VariableDeclarations.NamedVariable variable, String toName) { if (toName.isEmpty() || !Character.isAlphabetic(toName.charAt(0))) { return false; } - Set keys = computeAllKeys(toName, variable); - return keys.stream().noneMatch(hasNameKey::contains); + return isAvailableIdentifier(toName, variable, hasNameSet); } @Override @@ -171,22 +169,27 @@ private Cursor getCursorToParentScope(Cursor cursor) { ); } - private Set computeAllKeys(String identifier, J context) { - Set keys = new HashSet<>(); - keys.add(identifier); + private boolean isAvailableIdentifier(String identifier, J context, Set hasNameSet) { + if (hasNameSet.contains(identifier)) { + return false; + } JavaType.Variable fieldType = getFieldType(context); if (fieldType != null && fieldType.getOwner() != null) { - keys.add(fieldType.getOwner() + " " + identifier); + if (hasNameSet.contains(fieldType.getOwner() + " " + identifier)) { + return false; + } if (fieldType.getOwner() instanceof JavaType.Method) { // Add all enclosing classes JavaType.FullyQualified declaringType = ((JavaType.Method) fieldType.getOwner()).getDeclaringType(); while (declaringType != null) { - keys.add(declaringType + " " + identifier); + if (hasNameSet.contains(declaringType + " " + identifier)) { + return false; + } declaringType = declaringType.getOwningClass(); } } } - return keys; + return true; } }; } diff --git a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java index 4adacef76..1dd544b2a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java @@ -74,11 +74,11 @@ public Duration getEstimatedEffortPerOccurrence() { public TreeVisitor getVisitor() { return new RenameToCamelCase() { @Override - protected boolean shouldRename(Set hasNameKey, J.VariableDeclarations.NamedVariable variable, String toName) { + protected boolean shouldRename(Set hasNameSet, J.VariableDeclarations.NamedVariable variable, String toName) { if (toName.isEmpty() || !Character.isAlphabetic(toName.charAt(0))) { return false; } - return hasNameKey.stream().noneMatch(key -> + return hasNameSet.stream().noneMatch(key -> key.equals(toName) || key.equals(variable.getSimpleName()) || key.endsWith(" " + toName) || diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java index a877aa1d0..52ee5f7b1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java @@ -33,6 +33,16 @@ public abstract class RenameToCamelCase extends JavaIsoVisitor { + @Nullable + private Cursor sourceFileCursor; + + private Cursor getSourceFileCursor() { + if (sourceFileCursor == null) { + sourceFileCursor = getCursor().getPathAsCursors(c -> c.getValue() instanceof JavaSourceFile).next(); + } + return sourceFileCursor; + } + @Override public @Nullable J postVisit(J tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { @@ -52,17 +62,17 @@ public abstract class RenameToCamelCase extends JavaIsoVisitor return super.postVisit(tree, ctx); } - protected abstract boolean shouldRename(Set hasNameKey, J.VariableDeclarations.NamedVariable variable, + protected abstract boolean shouldRename(Set hasNameSet, J.VariableDeclarations.NamedVariable variable, String toName); protected void renameVariable(J.VariableDeclarations.NamedVariable variable, String toName) { - Cursor cu = getCursor().getPathAsCursors(c -> c.getValue() instanceof JavaSourceFile).next(); - cu.computeMessageIfAbsent("RENAME_VARIABLES_KEY", k -> new LinkedHashMap<>()) + getSourceFileCursor() + .computeMessageIfAbsent("RENAME_VARIABLES_KEY", k -> new LinkedHashMap<>()) .put(variable, toName); } protected void hasNameKey(String variableName) { - getCursor().getPathAsCursors(c -> c.getValue() instanceof JavaSourceFile).next() + getSourceFileCursor() .computeMessageIfAbsent("HAS_NAME_KEY", k -> new HashSet<>()) .add(variableName); } From 5dc62ae0e5889c62d86f6943cad34941af10e37d Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 14 Mar 2024 20:49:33 +0100 Subject: [PATCH 042/183] `IndexOfReplaceableByContainsTest`: Fix error in recipe --- .../IndexOfReplaceableByContains.java | 2 +- .../IndexOfReplaceableByContainsTest.java | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java b/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java index c8be5cde7..d57353119 100644 --- a/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java +++ b/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java @@ -72,7 +72,7 @@ public J visitBinary(J.Binary binary, ExecutionContext ctx) { J.Binary asBinary = (J.Binary) j; if (asBinary.getLeft() instanceof J.MethodInvocation) { J.MethodInvocation mi = (J.MethodInvocation) asBinary.getLeft(); - if (STRING_INDEX_MATCHER.matches(mi) || LIST_INDEX_MATCHER.matches(mi)) { + if (STRING_INDEX_MATCHER.matches(mi) || mi.getSelect() != null && LIST_INDEX_MATCHER.matches(mi)) { if (asBinary.getRight() instanceof J.Literal) { String valueSource = ((J.Literal) asBinary.getRight()).getValueSource(); boolean isGreaterThanNegativeOne = asBinary.getOperator() == J.Binary.Type.GreaterThan && "-1".equals(valueSource); diff --git a/src/test/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContainsTest.java b/src/test/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContainsTest.java index ecf4e7dec..90fbe2e59 100644 --- a/src/test/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContainsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContainsTest.java @@ -86,4 +86,22 @@ static boolean hasIndex(List strList, String str) { ) ); } + + @Test + void listIndexOfInImplementationClass() { + rewriteRun( + //language=java + java( + """ + import java.util.List; + + abstract class Test implements List { + boolean m(Object o) { + return indexOf(o) >= 0; + } + } + """ + ) + ); + } } From 408568f5a019cd4c4c3cb31227bf85e2ddbb98f6 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Fri, 22 Mar 2024 14:55:57 -0500 Subject: [PATCH 043/183] refactor: Update Gradle wrapper (#281) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch delta 34118 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 delta 34176 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 SGW6ptSs!X7{rG*o_g?|vpSEZK diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e4a6f4de6..4089a1daa 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip -distributionSha256Sum=9631d53cf3e74bfa726893aee1f8994fee4e060c401335946dba2156f440f24c +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 5fcd82f00f80272f4ef3ec0c86a4932b28353be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Fri, 12 Apr 2024 12:22:57 -0400 Subject: [PATCH 044/183] Update common-static-analysis.yml --- .../resources/META-INF/rewrite/common-static-analysis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/META-INF/rewrite/common-static-analysis.yml b/src/main/resources/META-INF/rewrite/common-static-analysis.yml index d18a10ba5..9aa660627 100644 --- a/src/main/resources/META-INF/rewrite/common-static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/common-static-analysis.yml @@ -1,4 +1,4 @@ -# +x# # Copyright 2021 the original author or authors. #

    # Licensed under the Apache License, Version 2.0 (the "License"); @@ -17,7 +17,7 @@ type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.staticanalysis.CommonStaticAnalysis displayName: Common static analysis issues -description: Resolve common static analysis issues discovered through 3rd party tools. +description: Resolve common static analysis issues (also known as SAST issues). recipeList: # - org.openrewrite.staticanalysis.AddSerialVersionUidToSerializable - org.openrewrite.staticanalysis.AtomicPrimitiveEqualsUsesGet From de87864224d13f2f2d5f4193f3101e5f348b3e67 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 15 Apr 2024 16:14:04 +0200 Subject: [PATCH 045/183] Remove excess x from yaml header --- src/main/resources/META-INF/rewrite/common-static-analysis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/META-INF/rewrite/common-static-analysis.yml b/src/main/resources/META-INF/rewrite/common-static-analysis.yml index 9aa660627..2c01c6bf8 100644 --- a/src/main/resources/META-INF/rewrite/common-static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/common-static-analysis.yml @@ -1,4 +1,4 @@ -x# +# # Copyright 2021 the original author or authors. #

    # Licensed under the Apache License, Version 2.0 (the "License"); From d8a0f023f9a97c7e3a34db706826e5820ef23515 Mon Sep 17 00:00:00 2001 From: jrivadeneira <36925807+jrivadeneira@users.noreply.github.com> Date: Mon, 15 Apr 2024 12:25:48 -0400 Subject: [PATCH 046/183] ReplaceCollectionToArrayArgWithEmptyArray Recipe (#259) * ReplaceCollectionToArrayArgWithEmptyArray Recipe Created recipe for ReplaceCollectionToArrayArgWithEmptyArray with test. * Update src/test/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArrayTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Minor polish --------- Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- ...aceCollectionToArrayArgWithEmptyArray.java | 88 +++++++++++++++++++ ...ollectionToArrayArgWithEmptyArrayTest.java | 86 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArray.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArrayTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArray.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArray.java new file mode 100644 index 000000000..56ab17863 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArray.java @@ -0,0 +1,88 @@ +/* + * 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.*; +import org.openrewrite.analysis.InvocationMatcher; +import org.openrewrite.analysis.search.UsesInvocation; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.Space; +import org.openrewrite.marker.Markers; + +import java.util.Collections; + +import static java.util.Objects.requireNonNull; + +public class ReplaceCollectionToArrayArgWithEmptyArray extends Recipe { + + @Override + public String getDisplayName() { + return "Use Empty Array for `Collection.toArray()`"; + } + + @Override + public String getDescription() { + return "Changes new array creation with `Collection#toArray(T[])` to use an empty array argument, which is better for performance.\n" + + "\n" + + "According to the `Collection#toArray(T[])` documentation:\n" + + "\n" + + "> If the collection fits in the specified array, it is returned therein.\n" + + "\n" + + "However, although it's not intuitive, " + + "allocating a right-sized array ahead of time to pass to the API appears to be [generally worse for performance](https://shipilev.net/blog/2016/arrays-wisdom-ancients/#_conclusion) " + + "according to benchmarking and JVM developers due to a number of implementation details in both Java and the virtual machine.\n" + + "\n" + + "H2 achieved significant performance gains by [switching to empty arrays instead pre-sized ones](https://github.com/h2database/h2database/issues/311)."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesInvocation<>(ReplaceCollectionToArrayArgWithEmptyArrayVisitor.COLLECTION_TO_ARRAY), + new ReplaceCollectionToArrayArgWithEmptyArrayVisitor<>() + ); + } + + private static class ReplaceCollectionToArrayArgWithEmptyArrayVisitor

    extends JavaIsoVisitor

    { + private static final InvocationMatcher COLLECTION_TO_ARRAY = + InvocationMatcher.fromMethodMatcher("java.util.Collection toArray(..)"); + + @Override + public J.NewArray visitNewArray(J.NewArray newArray, P p) { + if (COLLECTION_TO_ARRAY.advanced().isFirstArgument(getCursor()) && newArray.getDimensions().size() == 1) { + J.NewArray newArrayZero = newArray.withDimensions(ListUtils.mapFirst(newArray.getDimensions(), d -> { + if (d.getIndex() instanceof J.Literal && Integer.valueOf(0).equals(((J.Literal) d.getIndex()).getValue())) { + return d; + } + return d.withIndex(new J.Literal( + Tree.randomId(), + Space.EMPTY, + Markers.EMPTY, + 0, + "0", + Collections.emptyList(), + (JavaType.Primitive) requireNonNull(d.getIndex().getType()) + )); + })); + return maybeAutoFormat(newArray, newArrayZero, p); + } + return super.visitNewArray(newArray, p); + } + } +} diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArrayTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArrayTest.java new file mode 100644 index 000000000..cf031ffa4 --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceCollectionToArrayArgWithEmptyArrayTest.java @@ -0,0 +1,86 @@ +/* + * 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.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +@SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") +class ReplaceCollectionToArrayArgWithEmptyArrayTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new ReplaceCollectionToArrayArgWithEmptyArray()); + } + + @Test + @DocumentExample + void replaceSizeArgumentWithZero() { + rewriteRun( + //language=java + java( + """ + import java.util.Collection; + + class A { + void test(Collection args){ + Integer [] array = args.toArray(new Integer[args.size()]); + } + } + """, + """ + import java.util.Collection; + + class A { + void test(Collection args){ + Integer [] array = args.toArray(new Integer[0]); + } + } + """ + ) + ); + } + + @Test + void replaceConstantValueWithZero() { + rewriteRun( + //language=java + java( + """ + import java.util.Collection; + + class A { + void test(Collection args){ + Integer[] array = args.toArray(new Integer[4]); + } + } + """, + """ + import java.util.Collection; + + class A { + void test(Collection args){ + Integer[] array = args.toArray(new Integer[0]); + } + } + """ + ) + ); + } +} From 11a7706f60305cad3a7abfe5281826a23bf494a7 Mon Sep 17 00:00:00 2001 From: Marklinzi <163907924+Marklinzi@users.noreply.github.com> Date: Fri, 19 Apr 2024 12:55:26 +0200 Subject: [PATCH 047/183] Combine semantically equal catch blocks with same var declarations (#280) * Add test for CombineSemanticallyEqualCatchBlocks having equal catch blocks with variable declaration * Do not try to compare nullable varargs in catch blocks for CombineSemanticallyEqualCatchBlocks recipe. Fixes NPE. * Revert "Do not try to compare nullable varargs in catch blocks for" This reverts commit a248d1d8199620c7feb717f6dac55ff0b077d1de. * Fix NPE in CombineSemanticallyEqualCatchBlocks recipe when trying to determine if catch blocks do not contain same comments --------- Co-authored-by: Tim te Beek --- .../CombineSemanticallyEqualCatchBlocks.java | 6 +++- ...mbineSemanticallyEqualCatchBlocksTest.java | 36 +++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java index 5355d157f..2cbc4874b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java +++ b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java @@ -299,7 +299,11 @@ private boolean nullMissMatch(Object obj1, Object obj2) { } private boolean doesNotContainSameComments(Space space1, Space space2) { - if (space1.getComments().size() != space2.getComments().size()) { + if (space1 == null && space2 == null) { + return false; + } + + if (space1 == null || space2 == null || space1.getComments().size() != space2.getComments().size()) { return true; } diff --git a/src/test/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocksTest.java b/src/test/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocksTest.java index f0eb7c09f..93f3af54f 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocksTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocksTest.java @@ -448,4 +448,40 @@ void log(String msg) {} ) ); } + + + @Test + void combineSameCatchBlocksWithVariableDeclaration() { + rewriteRun( + //language=java + java("class A extends RuntimeException {}"), + //language=java + java("class B extends RuntimeException {}"), + //language=java + java( + """ + class Test { + void method() { + try { + } catch (A ex) { + String s = "foo"; + } catch (B ex) { + String s = "foo"; + } + } + } + """, + """ + class Test { + void method() { + try { + } catch (A | B ex) { + String s = "foo"; + } + } + } + """ + ) + ); + } } From 32afee0f2b4901229b8980c940396211d52be9ec Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 23 Apr 2024 09:12:53 +0000 Subject: [PATCH 048/183] refactor: Prefix RSPEC identifiers with capital S Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.text.FindAndReplace?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/AddSerialVersionUidToSerializable.java | 2 +- .../staticanalysis/AtomicPrimitiveEqualsUsesGet.java | 2 +- .../staticanalysis/AvoidBoxedBooleanExpressions.java | 2 +- .../staticanalysis/BigDecimalDoubleConstructor.java | 2 +- .../staticanalysis/BigDecimalRoundingConstantsToEnums.java | 2 +- .../staticanalysis/BooleanChecksNotInverted.java | 2 +- .../CaseInsensitiveComparisonsDoNotChangeCase.java | 2 +- .../openrewrite/staticanalysis/CatchClauseOnlyRethrows.java | 2 +- .../staticanalysis/CombineSemanticallyEqualCatchBlocks.java | 2 +- .../staticanalysis/CompareEnumsWithEqualityOperator.java | 2 +- .../openrewrite/staticanalysis/ControlFlowIndentation.java | 2 +- .../org/openrewrite/staticanalysis/CovariantEquals.java | 2 +- .../org/openrewrite/staticanalysis/DefaultComesLast.java | 2 +- .../java/org/openrewrite/staticanalysis/EmptyBlock.java | 2 +- .../org/openrewrite/staticanalysis/EqualsAvoidsNull.java | 2 +- .../openrewrite/staticanalysis/ExplicitInitialization.java | 2 +- .../staticanalysis/ExplicitLambdaArgumentTypes.java | 2 +- .../staticanalysis/ExternalizableHasNoArgsConstructor.java | 2 +- .../java/org/openrewrite/staticanalysis/FallThrough.java | 2 +- .../java/org/openrewrite/staticanalysis/FinalClass.java | 2 +- .../staticanalysis/FixStringFormatExpressions.java | 2 +- .../staticanalysis/ForLoopIncrementInUpdate.java | 2 +- .../java/org/openrewrite/staticanalysis/HiddenField.java | 2 +- .../staticanalysis/HideUtilityClassConstructor.java | 2 +- .../IndexOfChecksShouldUseAStartPosition.java | 2 +- .../staticanalysis/IndexOfReplaceableByContains.java | 2 +- .../IndexOfShouldNotCompareGreaterThanZero.java | 2 +- .../java/org/openrewrite/staticanalysis/InlineVariable.java | 2 +- .../staticanalysis/IsEmptyCallOnCollections.java | 2 +- .../org/openrewrite/staticanalysis/LowercasePackage.java | 2 +- .../org/openrewrite/staticanalysis/MethodNameCasing.java | 2 +- .../org/openrewrite/staticanalysis/MinimumSwitchCases.java | 2 +- .../staticanalysis/MissingOverrideAnnotation.java | 2 +- .../java/org/openrewrite/staticanalysis/ModifierOrder.java | 2 +- .../staticanalysis/MultipleVariableDeclarations.java | 2 +- .../java/org/openrewrite/staticanalysis/NeedBraces.java | 2 +- .../openrewrite/staticanalysis/NestedEnumsAreNotStatic.java | 2 +- .../NewStringBuilderBufferWithCharArgument.java | 2 +- .../staticanalysis/NoDoubleBraceInitialization.java | 2 +- .../staticanalysis/NoEmptyCollectionWithRawType.java | 2 +- .../staticanalysis/NoEqualityInForCondition.java | 2 +- .../java/org/openrewrite/staticanalysis/NoFinalizer.java | 2 +- .../NoPrimitiveWrappersForToStringOrCompareTo.java | 2 +- .../staticanalysis/NoRedundantJumpStatements.java | 2 +- .../openrewrite/staticanalysis/NoToStringOnStringType.java | 2 +- .../openrewrite/staticanalysis/NoValueOfOnStringType.java | 2 +- .../staticanalysis/ObjectFinalizeCallsSuper.java | 2 +- .../PrimitiveWrapperClassConstructorToValueOf.java | 2 +- .../staticanalysis/ReferentialEqualityToObjectEquals.java | 2 +- .../staticanalysis/RemoveCallsToObjectFinalize.java | 2 +- .../openrewrite/staticanalysis/RemoveCallsToSystemGc.java | 2 +- .../openrewrite/staticanalysis/RemoveExtraSemicolons.java | 2 +- .../RemoveHashCodeCallsFromArrayInstances.java | 2 +- .../openrewrite/staticanalysis/RemoveRedundantTypeCast.java | 2 +- .../RemoveToStringCallsFromArrayInstances.java | 2 +- .../staticanalysis/RemoveUnusedLocalVariables.java | 2 +- .../staticanalysis/RemoveUnusedPrivateFields.java | 6 +++--- .../staticanalysis/RemoveUnusedPrivateMethods.java | 2 +- .../staticanalysis/RenameLocalVariablesToCamelCase.java | 2 +- .../RenameMethodsNamedHashcodeEqualOrTostring.java | 2 +- .../staticanalysis/RenamePrivateFieldsToCamelCase.java | 2 +- .../staticanalysis/ReplaceDuplicateStringLiterals.java | 2 +- .../staticanalysis/ReplaceLambdaWithMethodReference.java | 2 +- .../openrewrite/staticanalysis/ReplaceWeekYearWithYear.java | 2 +- .../staticanalysis/SimplifyBooleanExpression.java | 2 +- .../openrewrite/staticanalysis/SimplifyBooleanReturn.java | 2 +- .../openrewrite/staticanalysis/StaticMethodNotFinal.java | 2 +- .../openrewrite/staticanalysis/StringLiteralEquality.java | 4 ++-- .../staticanalysis/TernaryOperatorsShouldNotBeNested.java | 2 +- .../staticanalysis/UnnecessaryCloseInTryWithResources.java | 2 +- .../openrewrite/staticanalysis/UnnecessaryParentheses.java | 2 +- .../staticanalysis/UnnecessaryPrimitiveAnnotations.java | 2 +- .../org/openrewrite/staticanalysis/UnnecessaryThrows.java | 2 +- .../staticanalysis/UpperCaseLiteralSuffixes.java | 2 +- .../openrewrite/staticanalysis/UseCollectionInterfaces.java | 2 +- .../org/openrewrite/staticanalysis/UseDiamondOperator.java | 2 +- .../staticanalysis/UseJavaStyleArrayDeclarations.java | 2 +- .../staticanalysis/UseLambdaForFunctionalInterface.java | 2 +- .../org/openrewrite/staticanalysis/UseObjectNotifyAll.java | 2 +- .../org/openrewrite/staticanalysis/UseStringReplace.java | 4 ++-- .../org/openrewrite/staticanalysis/WhileInsteadOfFor.java | 2 +- .../staticanalysis/WriteOctalValuesAsDecimal.java | 2 +- src/main/resources/META-INF/rewrite/static-analysis.yml | 4 ++-- 83 files changed, 88 insertions(+), 88 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java index 56e29ac08..6b75b8511 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2057"); + return Collections.singleton("RSPEC-S2057"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java index e1efc9a5a..99649ef95 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java +++ b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java @@ -53,7 +53,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2204"); + return Collections.singleton("RSPEC-S2204"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java b/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java index 830081557..257ea9015 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java +++ b/src/main/java/org/openrewrite/staticanalysis/AvoidBoxedBooleanExpressions.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return singleton("RSPEC-5411"); + return singleton("RSPEC-S5411"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java b/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java index 775c038e1..fc9288908 100644 --- a/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/BigDecimalDoubleConstructor.java @@ -27,7 +27,7 @@ "For example writing `new BigDecimal(0.1)` does not create a `BigDecimal` which is exactly equal to `0.1`, " + "but it is equal to `0.1000000000000000055511151231257827021181583404541015625`. " + "This is because `0.1` cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length).", - tags = {"RSPEC-2111"} + tags = {"RSPEC-S2111"} ) public class BigDecimalDoubleConstructor { diff --git a/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java b/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java index 7e9d8ceba..ce690768a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java +++ b/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java @@ -49,7 +49,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2111"); + return Collections.singleton("RSPEC-S2111"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/BooleanChecksNotInverted.java b/src/main/java/org/openrewrite/staticanalysis/BooleanChecksNotInverted.java index a88e6f562..66881993f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/BooleanChecksNotInverted.java +++ b/src/main/java/org/openrewrite/staticanalysis/BooleanChecksNotInverted.java @@ -39,7 +39,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1940"); + return Collections.singleton("RSPEC-S1940"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java b/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java index f32a61738..46d1967af 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1157"); + return Collections.singleton("RSPEC-S1157"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index 3e3e0e9d3..9b05e49e8 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return singleton("RSPEC-2737"); + return singleton("RSPEC-S2737"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java index 2cbc4874b..b68c76cab 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java +++ b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2147"); + return Collections.singleton("RSPEC-S2147"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperator.java b/src/main/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperator.java index 236ef90f4..bc95d597c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperator.java +++ b/src/main/java/org/openrewrite/staticanalysis/CompareEnumsWithEqualityOperator.java @@ -40,7 +40,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-4551"); + return Collections.singleton("RSPEC-S4551"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java index 0caa747c7..4d784dc9f 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java +++ b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java @@ -49,7 +49,7 @@ public String getDescription() { @Override public Set getTags() { - return singleton("RSPEC-2681"); + return singleton("RSPEC-S2681"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java b/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java index 873a238e8..9ed4e4f6a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java @@ -40,7 +40,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2162"); + return Collections.singleton("RSPEC-S2162"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java index 139c62da4..a8f85e1dc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java +++ b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-4524"); + return Collections.singleton("RSPEC-S4524"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java b/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java index 6cccf403f..0f636271a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java +++ b/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-108"); + return Collections.singleton("RSPEC-S108"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java index 469c53df1..a1d5554a2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1132"); + return Collections.singleton("RSPEC-S1132"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java index cc71fe94b..f988d8392 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-3052"); + return Collections.singleton("RSPEC-S3052"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java index 1a37cdf6f..9bf1ae0a3 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2211"); + return Collections.singleton("RSPEC-S2211"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java b/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java index 7d122a137..65a833099 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java @@ -51,7 +51,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2060"); + return Collections.singleton("RSPEC-S2060"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/FallThrough.java b/src/main/java/org/openrewrite/staticanalysis/FallThrough.java index e12a6e43a..ba23f852f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FallThrough.java +++ b/src/main/java/org/openrewrite/staticanalysis/FallThrough.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-128"); + return Collections.singleton("RSPEC-S128"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalClass.java b/src/main/java/org/openrewrite/staticanalysis/FinalClass.java index 6310044c5..a0d08973d 100755 --- a/src/main/java/org/openrewrite/staticanalysis/FinalClass.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalClass.java @@ -38,7 +38,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2974"); + return Collections.singleton("RSPEC-S2974"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java index 063474a74..db905298a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java +++ b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-3457"); + return Collections.singleton("RSPEC-S3457"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java b/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java index d17db337c..110fac692 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java +++ b/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java @@ -46,7 +46,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1994"); + return Collections.singleton("RSPEC-S1994"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/HiddenField.java b/src/main/java/org/openrewrite/staticanalysis/HiddenField.java index 77ed5bb37..0a2b13286 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HiddenField.java +++ b/src/main/java/org/openrewrite/staticanalysis/HiddenField.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1117"); + return Collections.singleton("RSPEC-S1117"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java index 5249a11c2..43026d399 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java @@ -44,7 +44,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1118"); + return Collections.singleton("RSPEC-S1118"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/IndexOfChecksShouldUseAStartPosition.java b/src/main/java/org/openrewrite/staticanalysis/IndexOfChecksShouldUseAStartPosition.java index 3d3a6d077..8912e88dd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/IndexOfChecksShouldUseAStartPosition.java +++ b/src/main/java/org/openrewrite/staticanalysis/IndexOfChecksShouldUseAStartPosition.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2912"); + return Collections.singleton("RSPEC-S2912"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java b/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java index d57353119..868df23ec 100644 --- a/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java +++ b/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2692"); + return Collections.singleton("RSPEC-S2692"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java b/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java index e0e701d25..bed28337b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java +++ b/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java @@ -46,7 +46,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2692"); + return Collections.singleton("RSPEC-S2692"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java index da63cfaf1..8e3714663 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java +++ b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java @@ -47,7 +47,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1488"); + return Collections.singleton("RSPEC-S1488"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java b/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java index 7f1e8d12c..9b2bb5eb9 100755 --- a/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java +++ b/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return new LinkedHashSet<>(Arrays.asList("RSPEC-1155", "RSPEC-3981")); + return new LinkedHashSet<>(Arrays.asList("RSPEC-S1155", "RSPEC-S3981")); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java b/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java index 326c41f4a..f465ccb92 100644 --- a/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java +++ b/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java @@ -44,7 +44,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-120"); + return Collections.singleton("RSPEC-S120"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index abcbcf899..ca0fef805 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -66,7 +66,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-100"); + return Collections.singleton("RSPEC-S100"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java b/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java index b4fbcb1d9..02a0d9ee9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java +++ b/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java @@ -53,7 +53,7 @@ public String getDescription() { @Override public Set getTags() { - return singleton("RSPEC-1301"); + return singleton("RSPEC-S1301"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java b/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java index 3d5150c2f..c9c157b0c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java +++ b/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java @@ -55,7 +55,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1161"); + return Collections.singleton("RSPEC-S1161"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java b/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java index 441bee9a0..a70a063ca 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java +++ b/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1124"); + return Collections.singleton("RSPEC-S1124"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/MultipleVariableDeclarations.java b/src/main/java/org/openrewrite/staticanalysis/MultipleVariableDeclarations.java index 5ee40c229..25fa71320 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MultipleVariableDeclarations.java +++ b/src/main/java/org/openrewrite/staticanalysis/MultipleVariableDeclarations.java @@ -39,7 +39,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1659"); + return Collections.singleton("RSPEC-S1659"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java b/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java index 0a714248e..9c9adf6ed 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java +++ b/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java @@ -46,7 +46,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-121"); + return Collections.singleton("RSPEC-S121"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java b/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java index 0a187a95c..180e9c84f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java +++ b/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java @@ -41,7 +41,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2786"); + return Collections.singleton("RSPEC-S2786"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java b/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java index 45ca09382..296a36766 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java +++ b/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java @@ -47,7 +47,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1317"); + return Collections.singleton("RSPEC-S1317"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java index 2b90b5a08..ecd07ebe6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java @@ -46,7 +46,7 @@ public String getDescription() { @Override public Set getTags() { - return new LinkedHashSet<>(Arrays.asList("RSPEC-1171", "RSPEC-3599")); + return new LinkedHashSet<>(Arrays.asList("RSPEC-S1171", "RSPEC-S3599")); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index 2cb8f3727..a3b22fcd8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -47,7 +47,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1596"); + return Collections.singleton("RSPEC-S1596"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java b/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java index 05b1095e5..edba8b70a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java @@ -39,7 +39,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-888"); + return Collections.singleton("RSPEC-S888"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java b/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java index 565bebbce..f50acdd49 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java @@ -40,7 +40,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1111"); + return Collections.singleton("RSPEC-S1111"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java index fa22b683a..4e32ff2ff 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java @@ -54,7 +54,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1158"); + return Collections.singleton("RSPEC-S1158"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoRedundantJumpStatements.java b/src/main/java/org/openrewrite/staticanalysis/NoRedundantJumpStatements.java index cd010ff7b..dfcf1b100 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoRedundantJumpStatements.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoRedundantJumpStatements.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-3626"); + return Collections.singleton("RSPEC-S3626"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java b/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java index 85b32a502..cd45f50a8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1858"); + return Collections.singleton("RSPEC-S1858"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java b/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java index 0e922a16a..123801262 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java @@ -49,7 +49,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1153"); + return Collections.singleton("RSPEC-S1153"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java b/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java index 488fbbeb9..e75a49225 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java +++ b/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1114"); + return Collections.singleton("RSPEC-S1114"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java b/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java index 3fc20580d..5b69c1ed5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java +++ b/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2129"); + return Collections.singleton("RSPEC-S2129"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java b/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java index 102e6145a..f1ce4e83c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1698"); + return Collections.singleton("RSPEC-S1698"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java index d8d893995..958faafa3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java @@ -44,7 +44,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1111"); + return Collections.singleton("RSPEC-S1111"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java index f8b1c10af..a53b105ed 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1215"); + return Collections.singleton("RSPEC-S1215"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveExtraSemicolons.java b/src/main/java/org/openrewrite/staticanalysis/RemoveExtraSemicolons.java index a17e42eb4..df4489bd0 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveExtraSemicolons.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveExtraSemicolons.java @@ -41,7 +41,7 @@ public String getDescription() { @Override public Set getTags() { - return new LinkedHashSet<>(Arrays.asList("RSPEC-1116", "RSPEC-2959")); + return new LinkedHashSet<>(Arrays.asList("RSPEC-S1116", "RSPEC-S2959")); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java b/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java index fa01557bd..09f9bfdce 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java @@ -46,7 +46,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2116"); + return Collections.singleton("RSPEC-S2116"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java index f39ba0a8f..1a53b8d60 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveRedundantTypeCast.java @@ -45,7 +45,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1905"); + return Collections.singleton("RSPEC-S1905"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java b/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java index 18d18db48..1b88c38cd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveToStringCallsFromArrayInstances.java @@ -48,7 +48,7 @@ public class RemoveToStringCallsFromArrayInstances extends Recipe { @Override public Set getTags() { - return Collections.singleton("RSPEC-2116"); + return Collections.singleton("RSPEC-S2116"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index 89a784982..5f297ed16 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -56,7 +56,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1481"); + return Collections.singleton("RSPEC-S1481"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java index 0bf95213f..137342669 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java @@ -53,7 +53,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1068"); + return Collections.singleton("RSPEC-S1068"); } @Override @@ -90,7 +90,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex Statement statement = statements.get(i); if (statement instanceof J.VariableDeclarations) { J.VariableDeclarations vd = (J.VariableDeclarations) statement; - // RSPEC-1068 does not apply serialVersionUID of Serializable classes, or fields with annotations. + // RSPEC-S1068 does not apply serialVersionUID of Serializable classes, or fields with annotations. if (!(skipSerialVersionUID && isSerialVersionUid(vd)) && vd.getLeadingAnnotations().isEmpty() && vd.hasModifier(J.Modifier.Type.Private)) { @@ -98,7 +98,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex checkFields.add(new CheckField(vd, nextStatement)); } } else if (statement instanceof J.MethodDeclaration) { - // RSPEC-1068 does not apply fields from classes with native methods. + // RSPEC-S1068 does not apply fields from classes with native methods. J.MethodDeclaration md = (J.MethodDeclaration) statement; if (md.hasModifier(J.Modifier.Type.Native)) { return cd; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java index b3ccb3eba..de4cf3090 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1144"); + return Collections.singleton("RSPEC-S1144"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java index baa08a507..0247ca1d2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java @@ -62,7 +62,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-117"); + return Collections.singleton("RSPEC-S117"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java b/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java index 8de57a6fb..9ad7044f4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1221"); + return Collections.singleton("RSPEC-S1221"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java index 1dd544b2a..3b421a594 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java @@ -62,7 +62,7 @@ public String getDescription() { @Override public Set getTags() { - return new LinkedHashSet<>(Arrays.asList("RSPEC-116", "RSPEC-3008")); + return new LinkedHashSet<>(Arrays.asList("RSPEC-S116", "RSPEC-S3008")); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 43771be35..aafa8adce 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -56,7 +56,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1192"); + return Collections.singleton("RSPEC-S1192"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index c69a5c4f3..873117f44 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1612"); + return Collections.singleton("RSPEC-S1612"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java index a09caa222..ede5bed36 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java @@ -41,7 +41,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-3986"); + return Collections.singleton("RSPEC-S3986"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java index 0390c1bd6..5216ad9bc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1125"); + return Collections.singleton("RSPEC-S1125"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java index dbb414d34..eefb68bd6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java @@ -49,7 +49,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1126"); + return Collections.singleton("RSPEC-S1126"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java b/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java index cf1f223ab..8d9e613a6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java +++ b/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java @@ -39,7 +39,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2333"); + return Collections.singleton("RSPEC-S2333"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java b/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java index d3e8131ed..87540c7f1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java +++ b/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java @@ -46,7 +46,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-4973"); + return Collections.singleton("RSPEC-S4973"); } @Override @@ -57,7 +57,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { // Don't change for Kotlin because In Kotlin, `==` means structural equality, so it's redundant to call equals(). - // see https://rules.sonarsource.com/kotlin/RSPEC-6519/ + // see https://rules.sonarsource.com/kotlin/RSPEC-S6519/ TreeVisitor preconditions = Preconditions.and( Preconditions.and( Preconditions.not(new KotlinFileChecker<>()), diff --git a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java index d2b21d35e..f81a59ecc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java +++ b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java @@ -48,7 +48,7 @@ public String getDescription() { } @Override public Set getTags() { - return Collections.singleton("RSPEC-3358"); + return Collections.singleton("RSPEC-S3358"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java index 23fb22d7a..2f35fdb0f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java @@ -45,7 +45,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public Set getTags() { - return Collections.singleton("RSPEC-4087"); + return Collections.singleton("RSPEC-S4087"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryParentheses.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryParentheses.java index fdf1bce99..a2f50d1e1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryParentheses.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryParentheses.java @@ -39,7 +39,7 @@ public String getDescription() { @Override public Set getTags() { - return new LinkedHashSet<>(Arrays.asList("RSPEC-1110", "RSPEC-1611")); + return new LinkedHashSet<>(Arrays.asList("RSPEC-S1110", "RSPEC-S1611")); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java index 9a0a66f02..83cd834f9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java @@ -47,7 +47,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-4682"); + return Collections.singleton("RSPEC-S4682"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java index 8795536c8..1d63357ce 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1130"); + return Collections.singleton("RSPEC-S1130"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java index d0e419ae3..23006a5bb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java +++ b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-818"); + return Collections.singleton("RSPEC-S818"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java index faa813322..a087b51b0 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java @@ -48,7 +48,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1319"); + return Collections.singleton("RSPEC-S1319"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java index 9cec97d87..22f339b73 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java @@ -45,7 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2293"); + return Collections.singleton("RSPEC-S2293"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java b/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java index e34ed4e03..2ee425af3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java @@ -44,7 +44,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1197"); + return Collections.singleton("RSPEC-S1197"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java index 6c06a2dcc..bd6367896 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java @@ -50,7 +50,7 @@ public String getDescription() { @Override public Set getTags() { - return singleton("RSPEC-1604"); + return singleton("RSPEC-S1604"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UseObjectNotifyAll.java b/src/main/java/org/openrewrite/staticanalysis/UseObjectNotifyAll.java index 92c6f3fd3..c1b1df39a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseObjectNotifyAll.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseObjectNotifyAll.java @@ -43,7 +43,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-2446"); + return Collections.singleton("RSPEC-S2446"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java index 35b3d6919..6caad84dc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java @@ -39,7 +39,7 @@ * method each time it is called even if the first argument is not a regular expression. This has a significant * performance cost and therefore should be used with care. * - * @see + * @see * @see */ public class UseStringReplace extends Recipe { @@ -57,7 +57,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-5361"); + return Collections.singleton("RSPEC-S5361"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/WhileInsteadOfFor.java b/src/main/java/org/openrewrite/staticanalysis/WhileInsteadOfFor.java index 12077e0ed..b305e5f3c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/WhileInsteadOfFor.java +++ b/src/main/java/org/openrewrite/staticanalysis/WhileInsteadOfFor.java @@ -40,7 +40,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1264"); + return Collections.singleton("RSPEC-S1264"); } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java b/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java index d071ab7bd..b9a633fb6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java +++ b/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java @@ -38,7 +38,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-1314"); + return Collections.singleton("RSPEC-S1314"); } @Override diff --git a/src/main/resources/META-INF/rewrite/static-analysis.yml b/src/main/resources/META-INF/rewrite/static-analysis.yml index 04b740bd8..17fb80e51 100644 --- a/src/main/resources/META-INF/rewrite/static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/static-analysis.yml @@ -50,7 +50,7 @@ recipeList: methodPattern: java.lang.Thread run() newMethodName: start tags: - - RSPEC-1217 + - RSPEC-S1217 --- type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.staticanalysis.CommonDeclarationSiteTypeVariances @@ -79,4 +79,4 @@ recipeList: excludeBounds: - java.lang.* tags: - - RSPEC-1217 + - RSPEC-S1217 From ccf33047326421f9b970fdf07656c4caefeaec16 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 23 Apr 2024 19:04:31 +0000 Subject: [PATCH 049/183] refactor: Empty new line at end of file Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/AlvZH9o21?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/RemoveUnusedPrivateFieldsTest.java | 1 - .../openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java | 2 +- .../org/openrewrite/staticanalysis/SimplifyTernaryTest.java | 2 +- .../staticanalysis/SortedSetStreamToLinkedHashSetTest.java | 2 +- .../staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java | 2 +- .../openrewrite/staticanalysis/UnnecessaryParenthesesTest.java | 1 - 6 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java index fb8346b7f..435daa72b 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFieldsTest.java @@ -441,4 +441,3 @@ class A { ); } } - diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java index 0a7b8bd50..40d02fd29 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYearTest.java @@ -258,4 +258,4 @@ public void formatDate() { ) ); } -} \ No newline at end of file +} diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java index c11cd2ffd..1917cdb49 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyTernaryTest.java @@ -105,4 +105,4 @@ boolean booleanExpression() { ) ); } -} \ No newline at end of file +} diff --git a/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java b/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java index 5b003f7ed..c63c3bd9b 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSetTest.java @@ -120,4 +120,4 @@ void method(Set set) { } """)); } -} \ No newline at end of file +} diff --git a/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java b/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java index 8fde85ee5..c04d036b2 100644 --- a/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNestedTest.java @@ -1363,4 +1363,4 @@ public Set makeASet() { } -} \ No newline at end of file +} diff --git a/src/test/java/org/openrewrite/staticanalysis/UnnecessaryParenthesesTest.java b/src/test/java/org/openrewrite/staticanalysis/UnnecessaryParenthesesTest.java index d46c1e32b..9950d2e94 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UnnecessaryParenthesesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UnnecessaryParenthesesTest.java @@ -961,4 +961,3 @@ String test(String s) { ); } } - From 981693ebca3acf9c12093148487caccadfdd9181 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 25 Apr 2024 12:22:58 +0000 Subject: [PATCH 050/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java | 2 +- .../org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java | 2 +- .../org/openrewrite/staticanalysis/SimplifyBooleanReturn.java | 1 - .../org/openrewrite/staticanalysis/JavaElementFactoryTest.java | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java index d7206b163..3a34e44fd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceOptionalIsPresentWithIfPresent.java @@ -247,4 +247,4 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) return mi; } } -} \ No newline at end of file +} diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java index ede5bed36..72c97ac1b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java @@ -122,4 +122,4 @@ public static String replaceY(String input) { return output.toString(); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java index eefb68bd6..daa2b29fe 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java @@ -211,4 +211,3 @@ private boolean hasElseWithComment(J.If.Else else_) { }; } } - diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java b/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java index 765efe3d8..a15981647 100644 --- a/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/JavaElementFactoryTest.java @@ -152,4 +152,4 @@ public J.MemberReference visitMemberReference(J.MemberReference memberRef, Atomi J.MemberReference methodReference = newStaticMethodReference(reference.getMethodType(), true, reference.getType()); assertThat(SemanticallyEqual.areEqual(reference, methodReference)).isTrue(); } -} \ No newline at end of file +} From 456f278dccb61e48db817cbe7c4122ba77f96727 Mon Sep 17 00:00:00 2001 From: Mike Solomon Date: Fri, 26 Apr 2024 09:23:31 -0700 Subject: [PATCH 051/183] Clarify display name + desc for builder recipe (#285) Fixes: https://github.com/openrewrite/rewrite-static-analysis/issues/263 Co-authored-by: timo-abele --- .../java/org/openrewrite/staticanalysis/UseAsBuilder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java b/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java index 2ca74676e..d9334c68d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java @@ -57,12 +57,12 @@ public class UseAsBuilder extends Recipe { @Override public String getDisplayName() { - return "Use the builder pattern where possible"; + return "Chain calls to builder methods"; } @Override public String getDescription() { - return "When an API has been designed as a builder, use it that way rather than as a series of setter calls."; + return "Chain calls to builder methods that are on separate lines into one chain of builder calls."; } @Override From c2fffaf2929a8eafbb050ba21cb55d1b0371d12c Mon Sep 17 00:00:00 2001 From: Michael Keppler Date: Fri, 26 Apr 2024 19:22:00 +0200 Subject: [PATCH 052/183] Avoid parenthesis only cleanup in SimplifyConstantIfBranchExecution (#287) Recognize when the recipe cannot optimize to a constant condition and return the if-condition as before all attempted optimizations. Fixes #286. --- .../SimplifyConstantIfBranchExecution.java | 3 ++- ...SimplifyConstantIfBranchExecutionTest.java | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java index 704a6201f..d57b98487 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java @@ -87,6 +87,7 @@ private E cleanupBooleanExpression( @Override public J visitIf(J.If if_, ExecutionContext ctx) { J.If if__ = (J.If) super.visitIf(if_, ctx); + J.If ifBeforeCleanup = if__; J.ControlParentheses cp = cleanupBooleanExpression(if__.getIfCondition(), ctx); if__ = if__.withIfCondition(cp); @@ -104,7 +105,7 @@ public J visitIf(J.If if_, ExecutionContext ctx) { // The simplification process did not result in resolving to a single 'true' or 'false' value if (!compileTimeConstantBoolean.isPresent()) { - return if__; // Return the visited `if` + return ifBeforeCleanup; // Return the visited `if` } else if (compileTimeConstantBoolean.get()) { // True branch // Only keep the `then` branch, and remove the `else` branch. diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java index 9393e24df..612da862e 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java @@ -49,6 +49,26 @@ public void test() { ); } + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/286") + void doNotChangeParenthesisOnly() { + rewriteRun( + //language=java + java( + """ + public class A { + public void test() { + boolean b = true; + if (!(b)) { + System.out.println("hello"); + } + } + } + """ + ) + ); + } + @DocumentExample @Test void simplifyConstantIfTrue() { From bb0aadd99a51e48b6c4cca60127ccd4eaf242f34 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 23 Mar 2024 09:38:06 +0100 Subject: [PATCH 053/183] Polish `NoEmptyCollectionWithRawType` --- .../staticanalysis/NoEmptyCollectionWithRawType.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index a3b22fcd8..0c058712f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -72,7 +72,7 @@ public TreeVisitor getVisitor() { @Override public J visitImport(J.Import anImport, ExecutionContext ctx) { J.Identifier name = anImport.getQualid().getName(); - if (anImport.isStatic() && name.getSimpleName().startsWith("EMPTY_") && + if (anImport.isStatic() && updateFields.containsKey(name.getSimpleName()) && TypeUtils.isOfClassType(anImport.getQualid().getTarget().getType(), "java.util.Collections")) { return anImport.withQualid(anImport.getQualid().withName(name.withSimpleName(updateFields.get(name.getSimpleName())))); } @@ -83,7 +83,7 @@ public J visitImport(J.Import anImport, ExecutionContext ctx) { public J visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { J.Identifier name = fieldAccess.getName(); JavaType.Variable varType = name.getFieldType(); - if (varType != null && varType.getName().startsWith("EMPTY_") && + if (varType != null && updateFields.containsKey(varType.getName()) && TypeUtils.isOfClassType(varType.getOwner(), "java.util.Collections")) { return JavaTemplate.builder("java.util.Collections." + updateFields.get(varType.getName()) + "()") .contextSensitive() // context sensitive due to generics @@ -97,7 +97,7 @@ public J visitFieldAccess(J.FieldAccess fieldAccess, ExecutionContext ctx) { @Override public J visitIdentifier(J.Identifier identifier, ExecutionContext ctx) { JavaType.Variable varType = identifier.getFieldType(); - if (varType != null && varType.getName().startsWith("EMPTY_") && + if (varType != null && updateFields.containsKey(varType.getName()) && TypeUtils.isOfClassType(varType.getOwner(), "java.util.Collections")) { return JavaTemplate.builder(updateFields.get(varType.getName()) + "()") From 1ed9c44b1413a388a7d436e8a7ca5bcacc40fc94 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Tue, 7 May 2024 11:53:36 +0200 Subject: [PATCH 054/183] Remove uses of `J.ClassDeclaration#getAnnotations()` This is also covered by `J.ClassDeclaration#getPadding()` --- .../java/org/openrewrite/staticanalysis/FinalClassVisitor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java index 0e7355701..b481e2a81 100755 --- a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java @@ -124,7 +124,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex // Temporary work around until issue https://github.com/openrewrite/rewrite/issues/2348 is implemented. if (!cd.getLeadingAnnotations().isEmpty()) { // Setting the prefix to empty will cause the `Spaces` visitor to fix the formatting. - cd = cd.getAnnotations().withKind(cd.getAnnotations().getKind().withPrefix(Space.EMPTY)); + cd = cd.getPadding().withKind(cd.getPadding().getKind().withPrefix(Space.EMPTY)); } assert getCursor().getParent() != null; From e15d17b811a380177dad277d0ccffa35d74178d1 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 9 May 2024 20:42:34 -0700 Subject: [PATCH 055/183] Fix RenameMethodsNamedHashcodeEqualOrToString class name casing --- ...ng.java => RenameMethodsNamedHashcodeEqualOrToString.java} | 2 +- .../resources/META-INF/rewrite/common-static-analysis.yml | 2 +- .../RenameMethodsNamedHashcodeEqualOrTostringTest.java | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) rename src/main/java/org/openrewrite/staticanalysis/{RenameMethodsNamedHashcodeEqualOrTostring.java => RenameMethodsNamedHashcodeEqualOrToString.java} (98%) diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java b/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java similarity index 98% rename from src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java rename to src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java index 9ad7044f4..23bb0f734 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostring.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java @@ -31,7 +31,7 @@ import java.util.Collections; import java.util.Set; -public class RenameMethodsNamedHashcodeEqualOrTostring extends Recipe { +public class RenameMethodsNamedHashcodeEqualOrToString extends Recipe { private static final MethodMatcher NO_ARGS = new MethodMatcher("*..* *()", true); private static final MethodMatcher OBJECT_ARG = new MethodMatcher("*..* *(java.lang.Object)", true); diff --git a/src/main/resources/META-INF/rewrite/common-static-analysis.yml b/src/main/resources/META-INF/rewrite/common-static-analysis.yml index 2c01c6bf8..598e11499 100644 --- a/src/main/resources/META-INF/rewrite/common-static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/common-static-analysis.yml @@ -70,7 +70,7 @@ recipeList: # - org.openrewrite.staticanalysis.RemoveUnusedLocalVariables # - org.openrewrite.staticanalysis.RemoveUnusedPrivateMethods - org.openrewrite.staticanalysis.RenameLocalVariablesToCamelCase - - org.openrewrite.staticanalysis.RenameMethodsNamedHashcodeEqualOrTostring + - org.openrewrite.staticanalysis.RenameMethodsNamedHashcodeEqualOrToString - org.openrewrite.staticanalysis.RenamePrivateFieldsToCamelCase - org.openrewrite.staticanalysis.ReplaceLambdaWithMethodReference - org.openrewrite.staticanalysis.ReplaceStringBuilderWithString diff --git a/src/test/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostringTest.java b/src/test/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostringTest.java index 1c53b2ccb..ed9542817 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostringTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrTostringTest.java @@ -23,11 +23,11 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings({"MethodMayBeStatic", "MisspelledEquals", "BooleanMethodNameMustStartWithQuestion"}) -class RenameMethodsNamedHashcodeEqualOrTostringTest implements RewriteTest { +class RenameMethodsNamedHashcodeEqualOrToStringTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new RenameMethodsNamedHashcodeEqualOrTostring()); + spec.recipe(new RenameMethodsNamedHashcodeEqualOrToString()); } @DocumentExample From a7ed18f6bb8e6d566ff3fdfded2c84914380330b Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 9 May 2024 20:43:22 -0700 Subject: [PATCH 056/183] Attempt to improve performance of ReplaceDuplicateStringLiterals so that it can be included back into common static analysis by default --- .../ReplaceDuplicateStringLiterals.java | 209 ++++++++---------- .../ReplaceDuplicateStringLiteralsTest.java | 12 +- 2 files changed, 97 insertions(+), 124 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index aafa8adce..052aa806b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -29,9 +29,9 @@ import java.time.Duration; import java.util.*; +import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; -import static java.util.Objects.requireNonNull; import static org.openrewrite.Tree.randomId; @Value @@ -51,12 +51,16 @@ public String getDisplayName() { @Override public String getDescription() { - return "Replaces `String` literals with a length of 5 or greater repeated a minimum of 3 times. Qualified `String` literals include final Strings, method invocations, and new class invocations. Adds a new `private static final String` or uses an existing equivalent class field. A new variable name will be generated based on the literal value if an existing field does not exist. The generated name will append a numeric value to the variable name if a name already exists in the compilation unit."; + return "Replaces `String` literals with a length of 5 or greater repeated a minimum of 3 times. " + + "Qualified `String` literals include final Strings, method invocations, and new class invocations. " + + "Adds a new `private static final String` or uses an existing equivalent class field. " + + "A new variable name will be generated based on the literal value if an existing field does not exist. " + + "The generated name will append a numeric value to the variable name if a name already exists in the compilation unit."; } @Override public Set getTags() { - return Collections.singleton("RSPEC-S1192"); + return new LinkedHashSet<>(asList("RSPEC-1192", "RSPEC-1889")); } @Override @@ -70,12 +74,11 @@ public TreeVisitor getVisitor() { @Override public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { - JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); + JavaSourceFile cu = (JavaSourceFile) tree; Optional sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class); - if (Boolean.TRUE.equals(includeTestSources) || (sourceSet.isPresent() && "main".equals(sourceSet.get().getName()))) { - return super.visit(cu, ctx); + if(!Boolean.TRUE.equals(includeTestSources) && !(sourceSet.isPresent() && "main".equals(sourceSet.get().getName()))) { + return cu; } - return cu; } return super.visit(tree, ctx); } @@ -86,14 +89,13 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct return classDecl; } - Map> duplicateLiteralsMap = FindDuplicateStringLiterals.find(classDecl); + DuplicateLiteralInfo duplicateLiteralInfo = DuplicateLiteralInfo.find(classDecl); + Map> duplicateLiteralsMap = duplicateLiteralInfo.getDuplicateLiterals(); if (duplicateLiteralsMap.isEmpty()) { return classDecl; } - - Set variableNames = FindVariableNames.find(classDecl); - Map fieldValueToFieldName = FindExistingPrivateStaticFinalFields.find(classDecl); - + Set variableNames = duplicateLiteralInfo.getVariableNames(); + Map fieldValueToFieldName = duplicateLiteralInfo.getFieldValueToFieldName(); String classFqn = classDecl.getType().getFullyQualifiedName(); for (String valueOfLiteral : duplicateLiteralsMap.keySet()) { String variableName; @@ -111,7 +113,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct if (StringUtils.isBlank(variableName)) { continue; } - J.Literal replaceLiteral = ((J.Literal) duplicateLiteralsMap.get(valueOfLiteral).toArray()[0]).withId(Tree.randomId()); + J.Literal replaceLiteral = ((J.Literal) duplicateLiteralsMap.get(valueOfLiteral).toArray()[0]).withId(randomId()); String insertStatement = "private static final String " + variableName + " = #{any(String)};"; if (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Enum) { J.EnumValueSet enumValueSet = classDecl.getBody().getStatements().stream() @@ -121,7 +123,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct .orElse(null); if (enumValueSet != null) { - // Temporary work around due to an issue in the JavaTemplate related to BlockStatementTemplateGenerator#enumClassDeclaration. + // "Temporary" work around due to an issue in the JavaTemplate related to BlockStatementTemplateGenerator#enumClassDeclaration. Space singleSpace = Space.build(" ", emptyList()); Expression literal = duplicateLiteralsMap.get(valueOfLiteral).toArray(new J.Literal[0])[0].withId(randomId()); J.Modifier privateModifier = new J.Modifier(randomId(), Space.build("\n", emptyList()), Markers.EMPTY, null, J.Modifier.Type.Private, emptyList()); @@ -174,7 +176,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct } } else { classDecl = classDecl.withBody( - JavaTemplate.builder(insertStatement).contextSensitive().build() + JavaTemplate.builder(insertStatement).build() .apply(new Cursor(getCursor(), classDecl.getBody()), classDecl.getBody().getCoordinates().firstStatement(), replaceLiteral)); } } @@ -230,18 +232,20 @@ private String transformToVariableName(String valueOfLiteral) { }); } - private static class FindDuplicateStringLiterals extends JavaIsoVisitor>> { + private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations declaration) { + return declaration.hasModifier(J.Modifier.Type.Private) && + declaration.hasModifier(J.Modifier.Type.Static) && + declaration.hasModifier(J.Modifier.Type.Final); + } + + @Value + private static class DuplicateLiteralInfo { + Set variableNames; + Map fieldValueToFieldName; + Map> literalsMap = new HashMap<>(); - /** - * Find duplicate `String` literals repeated 3 or more times and with a length of at least 3. - * - * @param inClass subtree to search in. - * @return `Map` of `String` literal values to the `J.Literal` AST elements. - */ - public static Map> find(J.ClassDeclaration inClass) { - Map> literalsMap = new HashMap<>(); + public Map> getDuplicateLiterals() { Map> filteredMap = new TreeMap<>(Comparator.reverseOrder()); - new FindDuplicateStringLiterals().visit(inClass, literalsMap); for (String valueOfLiteral : literalsMap.keySet()) { if (literalsMap.get(valueOfLiteral).size() >= 3) { filteredMap.put(valueOfLiteral, literalsMap.get(valueOfLiteral)); @@ -250,120 +254,89 @@ public static Map> find(J.ClassDeclaration inClass) { return filteredMap; } - @Override - public J.Literal visitLiteral(J.Literal literal, Map> literalsMap) { - if (JavaType.Primitive.String.equals(literal.getType()) && - literal.getValue() instanceof String && - ((String) literal.getValue()).length() >= 5) { + public static DuplicateLiteralInfo find(J.ClassDeclaration inClass) { + DuplicateLiteralInfo result = new DuplicateLiteralInfo(new LinkedHashSet<>(), new LinkedHashMap<>()); + new JavaIsoVisitor() { - Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || - is instanceof J.Annotation || - is instanceof J.VariableDeclarations || - is instanceof J.NewClass || - is instanceof J.MethodInvocation); - // EnumValue can accept constructor arguments, including string literals - // But the static field can't be placed before them, so these literals are ineligible for replacement - if (parent.getValue() instanceof J.NewClass && parent.firstEnclosing(J.EnumValueSet.class) != null) { - return literal; + @Override + public J.Annotation visitAnnotation(J.Annotation annotation, Integer integer) { + // Literals in annotations cannot be replaced with variables and should be ignored + return annotation; } - if ((parent.getValue() instanceof J.VariableDeclarations && - ((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Final) && - !(((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Private) && ((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Static))) || - parent.getValue() instanceof J.NewClass || - parent.getValue() instanceof J.MethodInvocation) { - - literalsMap.computeIfAbsent(((String) literal.getValue()), k -> new HashSet<>()); - literalsMap.get((String) literal.getValue()).add(literal); + @Override + public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, Integer integer) { + J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, integer); + Cursor parentScope = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.MethodDeclaration); + J.VariableDeclarations declaration = getCursor().firstEnclosing(J.VariableDeclarations.class); + if (parentScope.getValue() instanceof J.MethodDeclaration || + (parentScope.getValue() instanceof J.ClassDeclaration && declaration != null && + // `private static final String`(s) are handled separately by `FindExistingPrivateStaticFinalFields`. + !(isPrivateStaticFinalVariable(declaration) && v.getInitializer() instanceof J.Literal && + ((J.Literal) v.getInitializer()).getValue() instanceof String))) { + result.variableNames.add(v.getSimpleName()); + } + if (parentScope.getValue() instanceof J.ClassDeclaration && + declaration != null && isPrivateStaticFinalVariable(declaration) && + v.getInitializer() instanceof J.Literal && + ((J.Literal) v.getInitializer()).getValue() instanceof String) { + String value = (String) (((J.Literal) v.getInitializer()).getValue()); + result.fieldValueToFieldName.putIfAbsent(value, v.getSimpleName()); + } + return v; } - } - return literal; - } - } - - private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations declaration) { - return declaration.hasModifier(J.Modifier.Type.Private) && - declaration.hasModifier(J.Modifier.Type.Static) && - declaration.hasModifier(J.Modifier.Type.Final); - } - - private static class FindVariableNames extends JavaIsoVisitor> { - - /** - * Find all the variable names that exist in the provided subtree. - * - * @param inClass subtree to search in. - * @return variable names that exist in the subtree. - */ - public static Set find(J.ClassDeclaration inClass) { - Set variableNames = new HashSet<>(); - new FindVariableNames().visit(inClass, variableNames); - return variableNames; - } - @Override - public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, Set variableNames) { - Cursor parentScope = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.MethodDeclaration); - J.VariableDeclarations declaration = getCursor().firstEnclosing(J.VariableDeclarations.class); - if (parentScope.getValue() instanceof J.MethodDeclaration || - (parentScope.getValue() instanceof J.ClassDeclaration && declaration != null && - // `private static final String`(s) are handled separately by `FindExistingPrivateStaticFinalFields`. - !(isPrivateStaticFinalVariable(declaration) && variable.getInitializer() instanceof J.Literal && - ((J.Literal) variable.getInitializer()).getValue() instanceof String))) { - variableNames.add(variable.getSimpleName()); - } - return variable; - } - } + @Override + public J.Literal visitLiteral(J.Literal literal, Integer integer) { + if (JavaType.Primitive.String.equals(literal.getType()) && + literal.getValue() instanceof String && + ((String) literal.getValue()).length() >= 5) { + + Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || + is instanceof J.Annotation || + is instanceof J.VariableDeclarations || + is instanceof J.NewClass || + is instanceof J.MethodInvocation); + // EnumValue can accept constructor arguments, including string literals + // But the static field can't be placed before them, so these literals are ineligible for replacement + if (parent.getValue() instanceof J.NewClass && parent.firstEnclosing(J.EnumValueSet.class) != null) { + return literal; + } - private static class FindExistingPrivateStaticFinalFields extends JavaIsoVisitor> { + if ((parent.getValue() instanceof J.VariableDeclarations && + ((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Final) && + !(((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Private) && ((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Static))) || + parent.getValue() instanceof J.NewClass || + parent.getValue() instanceof J.MethodInvocation) { - /** - * Find existing `private static final String`(s) in a class. - */ - public static Map find(J j) { - Map fieldValueToFieldName = new LinkedHashMap<>(); - new FindExistingPrivateStaticFinalFields().visit(j, fieldValueToFieldName); - return fieldValueToFieldName; - } + result.literalsMap.computeIfAbsent(((String) literal.getValue()), k -> new HashSet<>()); + result.literalsMap.get((String) literal.getValue()).add(literal); + } + } + return literal; + } - @Override - public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, Map valueToVariable) { - Cursor parentScope = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || - // Prevent checks on most of the literals. - is instanceof J.MethodDeclaration); - J.VariableDeclarations declaration = getCursor().firstEnclosing(J.VariableDeclarations.class); - if (parentScope.getValue() instanceof J.ClassDeclaration && - declaration != null && isPrivateStaticFinalVariable(declaration) && - variable.getInitializer() instanceof J.Literal && - ((J.Literal) variable.getInitializer()).getValue() instanceof String) { - String value = (String) (((J.Literal) variable.getInitializer()).getValue()); - valueToVariable.putIfAbsent(value, variable.getSimpleName()); - } - return variable; + }.visit(inClass, 0); + return result; } } /** * ReplaceStringLiterals in a class with a reference to a `private static final String` with the provided variable name. */ + @Value + @EqualsAndHashCode(callSuper = false) private static class ReplaceStringLiterals extends JavaVisitor { - private final J.ClassDeclaration isClass; - private final String variableName; - private final Set literals; - - private ReplaceStringLiterals(J.ClassDeclaration isClass, String variableName, Set literals) { - this.isClass = isClass; - this.variableName = variableName; - this.literals = literals; - } + J.ClassDeclaration isClass; + String variableName; + Set literals; @Override public J visitLiteral(J.Literal literal, ExecutionContext ctx) { if (literals.contains(literal)) { assert isClass.getType() != null; return new J.Identifier( - Tree.randomId(), + randomId(), literal.getPrefix(), literal.getMarkers(), emptyList(), diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index 2a3f5876b..c58e48d5d 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -305,7 +305,7 @@ class A { final String val1 = "value"; final String val2 = "value"; final String val3 = "value"; - + private static class B { // Do not change inner class value. private static final String CONSTANT = "value"; @@ -318,7 +318,7 @@ class A { final String val1 = CONSTANT; final String val2 = CONSTANT; final String val3 = CONSTANT; - + private static class B { // Do not change inner class value. private static final String CONSTANT = "value"; @@ -531,13 +531,13 @@ void enumDefinition() { enum A { /**/ ONE, TWO, THREE; - + public void example() { final String val1 = "value"; final String val2 = "value"; final String val3 = "value"; } - + public void bar() {} } """, @@ -546,13 +546,13 @@ enum A { /**/ ONE, TWO, THREE; private static final String VALUE = "value"; - + public void example() { final String val1 = VALUE; final String val2 = VALUE; final String val3 = VALUE; } - + public void bar() {} } """ From 989faff56f0d94b1fb9d66a8e962e9f8e4077d89 Mon Sep 17 00:00:00 2001 From: Hoan Nguyen Date: Fri, 10 May 2024 00:39:41 -0700 Subject: [PATCH 057/183] Fix bug in `InstanceOfPatternMatch` getting the variable name from the name of a nested class (#291) Co-authored-by: Hoan Nguyen --- .../InstanceOfPatternMatch.java | 4 +-- .../InstanceOfPatternMatchTest.java | 35 +++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 0364fba44..35cc9e506 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -364,9 +364,7 @@ public String variableName(@Nullable JavaType type) { return name; } else if (type instanceof JavaType.FullyQualified) { String className = ((JavaType.FullyQualified) type).getClassName(); - if (className.indexOf('.') > 0) { - className = className.substring(className.lastIndexOf('.')); - } + className = className.substring(className.lastIndexOf('.') + 1); String baseName = null; switch (style) { case SHORT: diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index 0d29e191f..c0b668e0c 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -221,6 +221,41 @@ void test(Object o) { ); } + @Test + void conflictingVariableOfNestedType() { + rewriteRun( + //language=java + java( + """ + import java.util.Map; + + public class A { + void test(Object o) { + Map.Entry entry = null; + if (o instanceof Map.Entry) { + entry = (Map.Entry) o; + } + System.out.println(entry); + } + } + """, + """ + import java.util.Map; + + public class A { + void test(Object o) { + Map.Entry entry = null; + if (o instanceof Map.Entry entry1) { + entry = entry1; + } + System.out.println(entry); + } + } + """ + ) + ); + } + @Issue("https://github.com/openrewrite/rewrite/issues/2787") @Disabled @Test From e80248ca535d17950ccde5419508c6cb530fa4bd Mon Sep 17 00:00:00 2001 From: Mike Solomon Date: Fri, 10 May 2024 09:34:19 -0700 Subject: [PATCH 058/183] Adjusts MethdNameCasing name and description (#292) * Adjusts MethdNameCasing name and description To hopefully be clearer as to what it does. Based on feedback from the following issue: https://github.com/openrewrite/rewrite/issues/4180 * Update src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java Co-authored-by: Tim te Beek --------- Co-authored-by: timo-abele Co-authored-by: Tim te Beek --- .../org/openrewrite/staticanalysis/MethodNameCasing.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index ca0fef805..5c7156572 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -56,12 +56,14 @@ public class MethodNameCasing extends ScanningRecipe Date: Fri, 17 May 2024 22:19:12 -0700 Subject: [PATCH 059/183] Gradle Enterprise -> Develocity --- settings.gradle.kts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index bc0227384..1e62d5190 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -7,31 +7,28 @@ pluginManagement { } plugins { - id("com.gradle.enterprise") version "latest.release" + id("com.gradle.develocity") version "latest.release" id("com.gradle.common-custom-user-data-gradle-plugin") version "latest.release" } -gradleEnterprise { - val isCiServer = System.getenv("CI")?.equals("true") ?: false +develocity { server = "https://ge.openrewrite.org/" + val isCiServer = System.getenv("CI")?.equals("true") ?: false + val accessKey = System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY") + val authenticated = !accessKey.isNullOrBlank() buildCache { - remote(gradleEnterprise.buildCache) { + remote(develocity.buildCache) { isEnabled = true - val accessKey = System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY") - isPush = isCiServer && !accessKey.isNullOrBlank() + isPush = isCiServer && authenticated } } buildScan { capture { - isTaskInputFiles = true + fileFingerprints = true } - isUploadInBackground = !isCiServer - - publishAlways() - this as com.gradle.enterprise.gradleplugin.internal.extension.BuildScanExtensionWithHiddenFeatures - publishIfAuthenticated() + uploadInBackground = !isCiServer } } From 5a391c1c77320128e50ce92a81688fc663930734 Mon Sep 17 00:00:00 2001 From: Dinar Shagaliev <45518871+Dinozavvvr@users.noreply.github.com> Date: Tue, 21 May 2024 13:50:00 +0300 Subject: [PATCH 060/183] `RemoveUnusedPrivateMethods` should ignore class annotated with `@SupressWarning("unused")` (#293) * Fix #4188 org.openrewrite.staticanalysis.RemoveUnusedPrivateMethods issue. Added test * Fix #4188 org.openrewrite.staticanalysis.RemoveUnusedPrivateMethods issue. Added solution * Fix #4188 org.openrewrite.staticanalysis.RemoveUnusedPrivateMethods issue. Renamed tests * Fix #4188 org.openrewrite.staticanalysis.RemoveUnusedPrivateMethods issue. Some changes * Minor polish * Fix imports --------- Co-authored-by: Tim te Beek --- .../RemoveUnusedPrivateMethods.java | 29 ++++- .../RemoveUnusedPrivateMethodsTest.java | 104 +++++++++++++++++- 2 files changed, 128 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java index de4cf3090..4b2da88f4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java @@ -21,11 +21,13 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.NoMissingTypes; +import org.openrewrite.java.search.FindAnnotations; import org.openrewrite.java.service.AnnotationService; import org.openrewrite.java.tree.*; import java.time.Duration; import java.util.Collections; +import java.util.List; import java.util.Set; public class RemoveUnusedPrivateMethods extends Recipe { @@ -53,8 +55,32 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new NoMissingTypes(), new JavaIsoVisitor() { + @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclaration, ExecutionContext ctx) { + if (unusedWarningsSuppressed(classDeclaration)) { + return classDeclaration; + } + return super.visitClassDeclaration(classDeclaration, ctx); + } + + private boolean unusedWarningsSuppressed(J classDeclaration) { + for (J.Annotation annotation : FindAnnotations.find(classDeclaration, "java.lang.SuppressWarnings")) { + List arguments = annotation.getArguments(); + if (arguments != null) { + for (Expression argument : arguments) { + if (J.Literal.isLiteralValue(argument, "unused")) { + return true; + } + } + } + } + return false; + } + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, + ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); JavaType.Method methodType = method.getMethodType(); if (methodType != null && methodType.hasFlags(Flag.Private) && @@ -104,4 +130,5 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } }); } + } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java index 8e076ce8b..35ff9360c 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java @@ -42,22 +42,22 @@ void removeUnusedPrivateMethods() { class Test { private void unused() { } - + public void dontRemove() { dontRemove2(); } - + private void dontRemove2() { } } """, """ class Test { - + public void dontRemove() { dontRemove2(); } - + private void dontRemove2() { } } @@ -127,4 +127,100 @@ private static void checkMethodInUse(String arg0, T arg1) { ) ); } + + @Test + void removeMethodsOnNestedClass() { + rewriteRun( + //language=java + java( + """ + import java.util.stream.Stream; + + class Test { + void test(String input) { + } + private Stream unused() { + return null; + } + + class InnerTest { + void test(String input) { + } + private Stream unused() { + return null; + } + } + } + """, + """ + import java.util.stream.Stream; + + class Test { + void test(String input) { + } + + class InnerTest { + void test(String input) { + } + } + } + """ + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite/issues/4076") + void doNotRemoveMethodsWithUnusedSuppressWarningsOnClass() { + rewriteRun( + //language=java + java( + """ + import java.util.stream.Stream; + + @SuppressWarnings("unused") + class Test { + void test(String input) { + } + private Stream unused() { + return null; + } + private Stream anotherUnused() { + return null; + } + } + """ + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite/issues/4076") + void doNotRemoveMethodsWithUnusedSuppressWarningsOnClassNestedClass() { + rewriteRun( + //language=java + java( + """ + import java.util.stream.Stream; + + @SuppressWarnings("unused") + class Test { + void test(String input) { + } + private Stream unused() { + return null; + } + + class InnerTest { + void test(String input) { + } + private Stream unused() { + return null; + } + } + } + """ + ) + ); + } } From f4fb1271c98bc73bdad43ef709c00cf94ee7b703 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Fri, 31 May 2024 22:37:11 +0000 Subject: [PATCH 061/183] refactor: Update Gradle wrapper Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4089a1daa..cd409941f 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip -distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From 889c899da98fccb55905e8e101c98dc85b5b66e4 Mon Sep 17 00:00:00 2001 From: Alex Boyko Date: Mon, 3 Jun 2024 13:18:09 -0400 Subject: [PATCH 062/183] InstanceOfPatternMatch: Generic type without parameters (#298) * InstanceOfPatternMatch: Generic type without parameters * Possible easy fix * Polish * Correction --- .../InstanceOfPatternMatch.java | 21 ++++++ .../InstanceOfPatternMatchTest.java | 64 ++++++++++++++++++- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 35cc9e506..2a134265d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -19,6 +19,7 @@ import lombok.EqualsAndHashCode; import lombok.Value; import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.VariableNameUtils; @@ -33,6 +34,7 @@ import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import static java.util.Collections.emptyList; @@ -224,6 +226,25 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) { name, type, null)); + JavaType.FullyQualified fqType = TypeUtils.asFullyQualified(type); + if (fqType != null && !fqType.getTypeParameters().isEmpty() && !(instanceOf.getClazz() instanceof J.ParameterizedType)) { + TypedTree oldTypeTree = (TypedTree) instanceOf.getClazz(); + + // Each type parameter is turned into a wildcard, i.e. `List` -> `List` or `Map.Entry` -> `Map.Entry` + List wildcardsList = IntStream.range(0, fqType.getTypeParameters().size()) + .mapToObj(i -> new J.Wildcard(randomId(), Space.EMPTY, Markers.EMPTY, null, null)) + .collect(Collectors.toList()); + + J.ParameterizedType newTypeTree = new J.ParameterizedType( + randomId(), + oldTypeTree.getPrefix(), + Markers.EMPTY, + oldTypeTree.withPrefix(Space.EMPTY), + null, + oldTypeTree.getType() + ).withTypeParameters(wildcardsList); + result = result.withClazz(newTypeTree); + } // update entry in replacements to share the pattern variable name for (Map.Entry entry : replacements.entrySet()) { diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index c0b668e0c..9ea524856 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -133,6 +133,64 @@ void test(Object o) { ); } + @Test + void genericsWithoutParameters() { + rewriteRun( + //language=java + java( + """ + import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + public class A { + @SuppressWarnings("unchecked") + public static List> applyRoutesType(Object routes) { + if (routes instanceof List) { + List routesList = (List) routes; + if (routesList.isEmpty()) { + return Collections.emptyList(); + } + if (routesList.stream() + .anyMatch(route -> !(route instanceof Map))) { + return Collections.emptyList(); + } + return routesList.stream() + .map(route -> (Map) route) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + } + """, + """ + import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + public class A { + @SuppressWarnings("unchecked") + public static List> applyRoutesType(Object routes) { + if (routes instanceof List routesList) { + if (routesList.isEmpty()) { + return Collections.emptyList(); + } + if (routesList.stream() + .anyMatch(route -> !(route instanceof Map))) { + return Collections.emptyList(); + } + return routesList.stream() + .map(route -> (Map) route) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + } + """ + ) + ); + } + @Test void primitiveArray() { rewriteRun( @@ -245,7 +303,7 @@ void test(Object o) { public class A { void test(Object o) { Map.Entry entry = null; - if (o instanceof Map.Entry entry1) { + if (o instanceof Map.Entry entry1) { entry = entry1; } System.out.println(entry); @@ -700,7 +758,7 @@ Object test(Object o) { import java.util.List; public class A { Object test(Object o) { - return o instanceof List l ? l.get(0) : o.toString(); + return o instanceof List l ? l.get(0) : o.toString(); } } """ @@ -725,7 +783,7 @@ Object test(Object o) { import java.util.List; public class A { Object test(Object o) { - return o instanceof List l ? l.get(0) : o.toString(); + return o instanceof List l ? l.get(0) : o.toString(); } } """ From 8e493d816dd89f7b7266648b63503bb6029d79d8 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 6 Jun 2024 11:25:21 -0700 Subject: [PATCH 063/183] Fix NoDoubleBraceInitialization not handling "var" declarations correctly --- .../NoDoubleBraceInitialization.java | 24 ++-------- .../NoDoubleBraceInitializationTest.java | 47 +++++++++++++++++-- 2 files changed, 45 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java index ecd07ebe6..c5199ef78 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java @@ -141,11 +141,7 @@ public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { private List addSelectToInitStatements(List statements, J.Identifier identifier, ExecutionContext ctx) { AddSelectVisitor selectVisitor = new AddSelectVisitor(identifier); - List statementList = new ArrayList<>(); - for (Statement statement : statements) { - statementList.add((Statement) selectVisitor.visit(statement, ctx)); - } - return statementList; + return ListUtils.map(statements, statement -> (Statement) selectVisitor.visitNonNull(statement, ctx)); } private static class AddSelectVisitor extends JavaIsoVisitor { @@ -158,22 +154,8 @@ public AddSelectVisitor(J.Identifier identifier) { @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); - if (mi.getMethodType() != null && identifier.getFieldType() != null && mi.getSelect() == null - || (mi.getSelect() instanceof J.Identifier && "this".equals(((J.Identifier) mi.getSelect()).getSimpleName()))) { - if (identifier.getFieldType() == null) { - return mi; - } - JavaType rawFieldType = identifier.getFieldType().getType(); - rawFieldType = rawFieldType instanceof JavaType.Parameterized ? ((JavaType.Parameterized) rawFieldType).getType() : rawFieldType; - if (mi.getMethodType() == null) { - return mi; - } - JavaType rawMethodDeclaringType = mi.getMethodType().getDeclaringType(); - rawMethodDeclaringType = rawMethodDeclaringType instanceof JavaType.Parameterized ? ((JavaType.Parameterized) rawMethodDeclaringType).getType() : rawMethodDeclaringType; - - if (TypeUtils.isAssignableTo(rawFieldType, rawMethodDeclaringType)) { - return mi.withSelect(identifier); - } + if (mi.getSelect() == null || (mi.getSelect() instanceof J.Identifier && "this".equals(((J.Identifier) mi.getSelect()).getSimpleName()))) { + return mi.withSelect(identifier); } return mi; } diff --git a/src/test/java/org/openrewrite/staticanalysis/NoDoubleBraceInitializationTest.java b/src/test/java/org/openrewrite/staticanalysis/NoDoubleBraceInitializationTest.java index fc4bc9151..08776afb8 100644 --- a/src/test/java/org/openrewrite/staticanalysis/NoDoubleBraceInitializationTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/NoDoubleBraceInitializationTest.java @@ -170,7 +170,7 @@ void doubleBranchInitializationForNewClassArgIgnored() { """ package abc; import java.util.List; - + public class Thing { private final List stuff; public Thing(List stuff) { @@ -185,7 +185,7 @@ public Thing(List stuff) { package abc; import java.util.ArrayList; import java.util.List; - + class A { Thing t = new Thing(new ArrayList(){{add("abc"); add("def");}}); } @@ -268,7 +268,7 @@ void doubleBraceInitializationForFieldVar() { import java.util.List; import java.util.Map; import java.util.Set; - + class A { private static final Map map = new HashMap<>() {{put("a", "a");}}; private final List lst = new ArrayList<>() {{add("x");add("y");}}; @@ -282,7 +282,7 @@ class A { import java.util.List; import java.util.Map; import java.util.Set; - + class A { private static final Map map; static { @@ -354,7 +354,7 @@ void anonymousSubClassMethodInvoked() { """ import java.util.HashMap; import java.util.Map; - + class A { void example() { Map bMap = new HashMap() { @@ -373,6 +373,43 @@ void subClassMethod() { ); } + @Test + void implicitReceiver() { + rewriteRun( + //language=java + java( + """ + import java.util.HashMap; + import java.util.Map; + + class A { + void example() { + var m = new HashMap() { + { + put("a", "A"); + put("b", "B"); + } + }; + } + } + """, + """ + import java.util.HashMap; + import java.util.Map; + + class A { + void example() { + var m = new HashMap(); + m.put("a", "A"); + m.put("b", "B"); + } + } + """ + ) + ); + } + + @Test void selectIsThis() { rewriteRun( From f6fe18b4dfd569cb278d82b8e4c21e4572dfc15e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 7 Jun 2024 09:27:30 +0000 Subject: [PATCH 064/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/BftezDRNh?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java b/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java index ca260c8d6..cfc8781c3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/HiddenFieldVisitor.java @@ -262,4 +262,3 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations } } - From 667ee473fce5f8e8f178e78e6f14da884774aaf5 Mon Sep 17 00:00:00 2001 From: Oliver Kopp Date: Fri, 7 Jun 2024 23:30:41 +0200 Subject: [PATCH 065/183] Remove empty lines after rewrite (#300) * Add test case * Fix removal of empty JavaDoc * Remove resulting empty JavaDoc in RemoveEmptyJavaDocParameters * Slight speedup of RemoveJavaDocAuthorTag * Add trim() * Fix trim() * Add test for Windows EOL * Apply formatter * Restore single line no space tests --------- Co-authored-by: Tim te Beek --- .../RemoveEmptyJavaDocParameters.java | 36 +++++++ .../RemoveJavaDocAuthorTag.java | 5 +- .../RemoveEmptyJavaDocParametersTest.java | 93 +++++++++++++------ .../RemoveJavaDocAuthorTagTest.java | 24 ++++- 4 files changed, 125 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java b/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java index da5103a04..033d5060e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java @@ -154,12 +154,48 @@ public Javadoc visitDocComment(Javadoc.DocComment javadoc, ExecutionContext ctx) } if (useNewBody) { + trim(newBody); + if (newBody.isEmpty()) { + return null; + } javadoc = javadoc.withBody(newBody); } // No need to call super visitor, already covered all cases by adding an empty first element when needed. return javadoc; } + /** + * Removes all empty lines from body + */ + private void trim(List body) { + Javadoc currentDoc; + Javadoc.LineBreak firstLineBreak = null; + while (!body.isEmpty()) { + currentDoc = body.get(body.size() - 1); + boolean isLineBreak = currentDoc instanceof Javadoc.LineBreak; + if (isLineBreak && firstLineBreak == null) { + firstLineBreak = (Javadoc.LineBreak) currentDoc; + } + boolean isEmptyText = false; + if (currentDoc instanceof Javadoc.Text) { + String currentText = ((Javadoc.Text) currentDoc).getText().trim(); + isEmptyText = currentText.isEmpty(); + } + if (!isLineBreak && !isEmptyText) { + break; + } + body.remove(body.size() - 1); + } + if (!body.isEmpty() && firstLineBreak != null) { + // ensure proper "ending" of JavaDoc including OS-specific newlines + String margin = firstLineBreak.getMargin(); + if (margin.endsWith("*")) { + firstLineBreak = firstLineBreak.withMargin(margin.substring(0, margin.length() - 1)); + } + body.add(firstLineBreak); + } + } + public boolean isEmptyParameter(Javadoc.Parameter parameter) { return parameter.getDescription().stream().allMatch(Javadoc.LineBreak.class::isInstance); } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTag.java b/src/main/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTag.java index c0abfbd66..cdd6b2161 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTag.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTag.java @@ -70,10 +70,10 @@ public Javadoc visitDocComment(Javadoc.DocComment javadoc, ExecutionContext ctx) } if (isChanged) { - Collections.reverse(newBody); if (isBlank(getCursor(), newBody)) { return null; } + Collections.reverse(newBody); dc = dc.withBody(newBody); } return dc; @@ -87,7 +87,8 @@ static boolean isBlank(Cursor cursor, List newBody) { return newBody.stream().allMatch(jd -> { PrintOutputCapture p = new PrintOutputCapture<>(null); jd.printer(cursor).visit(jd, p); - return StringUtils.isBlank(p.getOut()); + String currentLine = p.getOut().trim(); + return StringUtils.isBlank(currentLine) || "*".equals(currentLine); }); } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParametersTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParametersTest.java index b2b66d4d7..ffc761332 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParametersTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParametersTest.java @@ -51,8 +51,6 @@ void method(int arg0) { """, """ class Test { - /** - */ void method(int arg0) { } } @@ -77,8 +75,6 @@ int method() { """, """ class Test { - /** - */ int method() { } } @@ -103,8 +99,6 @@ void method() throws IllegalStateException { """, """ class Test { - /** - */ void method() throws IllegalStateException { } } @@ -174,6 +168,69 @@ void method(int arg0, int arg1) { ); } + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/98") + void removeTrailingEmptyLines() { + rewriteRun( + //language=java + java( + """ + class Test { + /** + * Text text text + * + * @param arg0 + * @param arg1 + * + */ + void method(int arg0, int arg1) { + } + } + """, + """ + class Test { + /** + * Text text text + */ + void method(int arg0, int arg1) { + } + } + """ + ) + ); + } + + @Test + void removeTrailingEmptyLinesWindowsEOL() { + rewriteRun( + //language=java + java( + """ + class Test { + /** + * Text text text + * + * @param arg0 + * @param arg1 + * + */ + void method(int arg0, int arg1) { + } + } + """.replace("\n", "\r\n"), + """ + class Test { + /** + * Text text text + */ + void method(int arg0, int arg1) { + } + } + """.replace("\n", "\r\n") + ) + ); + } + @Nested class NoSpace { @Test @@ -192,8 +249,6 @@ void method(int arg0) { """, """ class Test { - /** - */ void method(int arg0) { } } @@ -218,8 +273,6 @@ int method() { """, """ class Test { - /** - */ int method() { } } @@ -244,8 +297,6 @@ void method() throws IllegalStateException { """, """ class Test { - /** - */ void method() throws IllegalStateException { } } @@ -272,7 +323,6 @@ void method(int arg0) { """, """ class Test { - /***/ void method(int arg0) { } } @@ -295,7 +345,6 @@ int method() { """, """ class Test { - /***/ int method() { } } @@ -318,7 +367,6 @@ void method() throws IllegalStateException { """, """ class Test { - /***/ void method() throws IllegalStateException { } } @@ -341,7 +389,6 @@ void method(int arg0) { """, """ class Test { - /***/ void method(int arg0) { } } @@ -364,7 +411,6 @@ int method() { """, """ class Test { - /***/ int method() { } } @@ -387,7 +433,6 @@ void method() throws IllegalStateException { """, """ class Test { - /***/ void method() throws IllegalStateException { } } @@ -414,8 +459,6 @@ void method(int arg0) { """, """ class Test { - /** - */ void method(int arg0) { } } @@ -439,8 +482,6 @@ int method() { """, """ class Test { - /** - */ int method() { } } @@ -464,8 +505,6 @@ int method() throws IllegalStateException { """, """ class Test { - /** - */ int method() throws IllegalStateException { } } @@ -489,8 +528,6 @@ void method(int arg0) { """, """ class Test { - /** - */ void method(int arg0) { } } @@ -514,8 +551,6 @@ int method() { """, """ class Test { - /** - */ int method() { } } @@ -539,8 +574,6 @@ int method() throws IllegalStateException { """, """ class Test { - /** - */ int method() throws IllegalStateException { } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTagTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTagTest.java index 17a8e2074..91fbd48a3 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTagTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveJavaDocAuthorTagTest.java @@ -46,8 +46,9 @@ class Test {} ) ); } - @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/119") + @Test void tagOnSecondLine() { rewriteRun( //language=java @@ -65,6 +66,27 @@ class Test {} ); } + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/119") + @Test + void tagOnSecondLineSourroundedByEmptyLines() { + rewriteRun( + //language=java + java( + """ + /** + * + * @author foo.bar + * + */ + class Test {} + """, + """ + class Test {} + """ + ) + ); + } + @Issue("https://github.com/openrewrite/rewrite/issues/1640") @Test @DocumentExample From 8a196e2a5d6236457d302e1df9fc7b41c000c9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Wed, 12 Jun 2024 18:12:30 +0000 Subject: [PATCH 066/183] refactor: Add a blank line around fields with annotations Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.recipes.BlankLinesAroundFieldsWithAnnotations?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../org/openrewrite/staticanalysis/InstanceOfPatternMatch.java | 2 ++ .../openrewrite/staticanalysis/RemoveUnusedPrivateFields.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 2a134265d..eb528ed5c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -352,8 +352,10 @@ public J visitVariableDeclarations(J.VariableDeclarations multiVariable, Integer private static class VariableNameStrategy { public static final Pattern NAME_SPLIT_PATTERN = Pattern.compile("[$._]*(?=\\p{Upper}+[\\p{Lower}\\p{Digit}]*)"); private final Style style; + @Nullable private final String name; + private final Set contextScopes; enum Style { diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java index 137342669..8e8cfd617 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java @@ -67,6 +67,7 @@ public TreeVisitor getVisitor() { @Value class CheckField { J.VariableDeclarations declarations; + @Nullable Statement nextStatement; } @@ -221,6 +222,7 @@ public J visitVariable(J.VariableDeclarations.NamedVariable variable, AtomicBool private static class MaybeRemoveComment extends JavaVisitor { @Nullable private final Statement statement; + private final J.ClassDeclaration classDeclaration; public MaybeRemoveComment(@Nullable Statement statement, J.ClassDeclaration classDeclaration) { From 4608489da8dcdab51e80163a18094264482d88bc Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 18 Jun 2024 20:32:17 +0000 Subject: [PATCH 067/183] refactor: Only publish build scans if authenticated Use this link to re-run the recipe: https://app.moderne.io/recipes/builder/kLJjXlflM?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- settings.gradle.kts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/settings.gradle.kts b/settings.gradle.kts index 1e62d5190..d77925408 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -29,6 +29,12 @@ develocity { fileFingerprints = true } + publishing { + onlyIf { + authenticated + } + } + uploadInBackground = !isCiServer } } From 1826c680d5c7ab76712cd1c7b3cde9b0948b4231 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 27 Jun 2024 15:32:40 -0700 Subject: [PATCH 068/183] Add recipe which masks the back half of string literals which look like credit card numbers with "X" --- .../staticanalysis/MaskCreditCardNumbers.java | 69 +++++++++++++++++++ .../MaskCreditCardNumbersTest.java | 65 +++++++++++++++++ 2 files changed, 134 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java b/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java new file mode 100644 index 000000000..8e5c45d66 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java @@ -0,0 +1,69 @@ +/* + * 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 lombok.EqualsAndHashCode; +import lombok.Value; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Value +@EqualsAndHashCode(callSuper = false) +public class MaskCreditCardNumbers extends Recipe { + + @Override + public String getDisplayName() { + return "Mask credit card numbers"; + } + + @Override + public String getDescription() { + return "When encountering string literals which appear to be credit card numbers, " + + "mask the last eight digits with the letter 'X'."; + } + + private static final Pattern CC_PATTERN = Pattern.compile("([0-9]{4} ?[0-9]{4} ?)([0-9]{4} ?[0-9]{4} ?)"); + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor() { + @Override + public J.Literal visitLiteral(J.Literal literal, ExecutionContext executionContext) { + J.Literal l = super.visitLiteral(literal, executionContext); + if(l.getValue() instanceof String) { + String value = (String) l.getValue(); + Matcher m = CC_PATTERN.matcher(value); + if(m.matches()) { + String masked = m.group(1) +maskDigits(m.group(2)); + l = l.withValue(masked) + .withValueSource("\"" + masked + "\""); + } + } + return l; + } + }; + } + + private static String maskDigits(String digits) { + return digits.replaceAll("[0-9]", "X"); + } +} diff --git a/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java b/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java new file mode 100644 index 000000000..b188f7242 --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java @@ -0,0 +1,65 @@ +/* + * 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 MaskCreditCardNumbersTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new MaskCreditCardNumbers()); + } + + @Test + void noSpaces() { + rewriteRun( + //language=java + java(""" + class A { + String cc = "1234567890123456"; + } + """, + """ + class A { + String cc = "12345678XXXXXXXX"; + } + """) + ); + } + + @Test + void withSpaces() { + rewriteRun( + //language=java + java(""" + class A { + String cc = "1234 5678 9012 3456"; + } + """, + """ + class A { + String cc = "1234 5678 XXXX XXXX"; + } + """) + ); + } +} From 14b5d7fc38f884a57edfe45ef085af022e84eb53 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 29 Jun 2024 21:30:19 +0200 Subject: [PATCH 069/183] Apply OpenRewrite best practices to MaskCreditCardNumbers --- .../staticanalysis/MaskCreditCardNumbers.java | 4 +- .../MaskCreditCardNumbersTest.java | 44 +++++++++++-------- 2 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java b/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java index 8e5c45d66..2743bc47f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java +++ b/src/main/java/org/openrewrite/staticanalysis/MaskCreditCardNumbers.java @@ -47,8 +47,8 @@ public String getDescription() { public TreeVisitor getVisitor() { return new JavaIsoVisitor() { @Override - public J.Literal visitLiteral(J.Literal literal, ExecutionContext executionContext) { - J.Literal l = super.visitLiteral(literal, executionContext); + public J.Literal visitLiteral(J.Literal literal, ExecutionContext ctx) { + J.Literal l = super.visitLiteral(literal, ctx); if(l.getValue() instanceof String) { String value = (String) l.getValue(); Matcher m = CC_PATTERN.matcher(value); diff --git a/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java b/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java index b188f7242..c6368a680 100644 --- a/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/MaskCreditCardNumbersTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -29,20 +30,23 @@ public void defaults(RecipeSpec spec) { spec.recipe(new MaskCreditCardNumbers()); } + @DocumentExample @Test void noSpaces() { rewriteRun( //language=java - java(""" - class A { - String cc = "1234567890123456"; - } - """, + java( """ - class A { - String cc = "12345678XXXXXXXX"; - } - """) + class A { + String cc = "1234567890123456"; + } + """, + """ + class A { + String cc = "12345678XXXXXXXX"; + } + """ + ) ); } @@ -50,16 +54,18 @@ class A { void withSpaces() { rewriteRun( //language=java - java(""" - class A { - String cc = "1234 5678 9012 3456"; - } - """, - """ - class A { - String cc = "1234 5678 XXXX XXXX"; - } - """) + java( + """ + class A { + String cc = "1234 5678 9012 3456"; + } + """, + """ + class A { + String cc = "1234 5678 XXXX XXXX"; + } + """ + ) ); } } From 7f1ec7fac848bdb6117487b0ca157bc3100cbc4d Mon Sep 17 00:00:00 2001 From: Niklas Gustavsson Date: Sat, 29 Jun 2024 22:02:36 +0200 Subject: [PATCH 070/183] Do not rewrite String.replaceAll with special chars in replacement string (#306) * Do not rewrite String.replaceAll with special chars in replacement string If the replacement string of String.replaceAll contains $ or \, we should not rewrite it as these indicate special replacements: https://docs.oracle.com/en/java/javase/22/docs/api/java.base/java/util/regex/Matcher.html#replaceAll(java.lang.String) Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/301 * Only replace if second argument is also a literal --------- Co-authored-by: Sam Snyder Co-authored-by: Tim te Beek --- .../staticanalysis/UseStringReplace.java | 30 +++++++++----- .../staticanalysis/UseStringReplaceTest.java | 41 +++++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java index 6caad84dc..3801bdea0 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java @@ -52,7 +52,7 @@ public String getDisplayName() { @Override public String getDescription() { return "When `String::replaceAll` is used, the first argument should be a real regular expression. " + - "If it’s not the case, `String::replace` does exactly the same thing as `String::replaceAll` without the performance drawback of the regex."; + "If it’s not the case, `String::replace` does exactly the same thing as `String::replaceAll` without the performance drawback of the regex."; } @Override @@ -81,19 +81,27 @@ private static class UseStringReplaceVisitor extends JavaVisitor ((J.Literal) arg) diff --git a/src/test/java/org/openrewrite/staticanalysis/UseStringReplaceTest.java b/src/test/java/org/openrewrite/staticanalysis/UseStringReplaceTest.java index 84caa9573..1b5291ae3 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseStringReplaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseStringReplaceTest.java @@ -146,4 +146,45 @@ public void method() { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/301") + @DisplayName("String#replaceAll is not replaced by String#replace, because second argument has a backslash in it") + void replaceAllUnchangedIfBackslashInReplacementString() { + rewriteRun( + //language=java + java( + """ + class Test { + public String method() { + return "abc".replaceAll("b", "\\\\\\\\\\\\\\\\"); + } + } + """ + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/301") + @DisplayName("String#replaceAll is not replaced by String#replace, because second argument has a dollar sign in it") + void replaceAllUnchangedIfDollarInReplacementString() { + rewriteRun( + //language=java + java( + """ + class Test { + public String method1() { + return "abc".replaceAll("b", "$0"); + } + + public String method2() { + String s = "$0"; + return "abc".replaceAll("b", s); + } + } + """ + ) + ); + } } From 045bd00edc307fd585e8892fc1f297e95606b074 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 3 Jul 2024 13:34:16 +0200 Subject: [PATCH 071/183] Push up disabled failing test for #309 --- .../UseLambdaForFunctionalInterfaceTest.java | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java b/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java index 38981f9e9..dd30a98a2 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java @@ -45,7 +45,7 @@ void castingAmbiguity() { import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; - + class Test { void test() { AccessController.doPrivileged(new PrivilegedAction() { @@ -65,7 +65,7 @@ void test() { import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; - + class Test { void test() { AccessController.doPrivileged((PrivilegedAction) () -> 0); @@ -95,7 +95,7 @@ void gson() { import com.google.gson.JsonSerializer; import java.time.LocalDateTime; import java.lang.reflect.Type; - + class Test { void test() { new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new JsonSerializer() { @@ -112,7 +112,7 @@ public JsonElement serialize(LocalDateTime object, Type type, JsonSerializationC import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializer; import java.time.LocalDateTime; - + class Test { void test() { new GsonBuilder().registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (object, type, context) -> new JsonPrimitive(object.format(null))); @@ -163,14 +163,14 @@ public void run() { Test.this.execute(); } }; - + void execute() {} } """, """ class Test { Runnable r = Test.this::execute; - + void execute() {} } """ @@ -239,7 +239,7 @@ void emptyLambda() { java( """ import java.util.function.Consumer; - + class Test { void foo() { Consumer s; @@ -253,7 +253,7 @@ public void accept(Integer i) { """, """ import java.util.function.Consumer; - + class Test { void foo() { Consumer s; @@ -274,7 +274,7 @@ void nestedLambdaInMethodArgument() { java( """ import java.util.function.Consumer; - + class Test { void bar(Consumer c) { } @@ -294,7 +294,7 @@ public void accept(Integer i2) { """, """ import java.util.function.Consumer; - + class Test { void bar(Consumer c) { } @@ -646,7 +646,7 @@ public void run() { } }); } - + int j = 0; while (j < 20) { run(new Runnable() { @@ -668,13 +668,14 @@ public void run() { @Issue("https://github.com/moderneinc/support-app/issues/17") void lambdaWithComplexTypeInference() { rewriteRun( + //language=java java( """ import java.util.LinkedHashMap; import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; - + class Test { void method() { Object o = new MapDropdownChoice( @@ -697,7 +698,7 @@ public Map getObject() { }); } } - + class MapDropdownChoice { public MapDropdownChoice(Supplier> choiceMap) { } @@ -708,7 +709,7 @@ public MapDropdownChoice(Supplier> choiceMap) { import java.util.Map; import java.util.function.Supplier; import java.util.stream.Collectors; - + class Test { void method() { Object o = new MapDropdownChoice( @@ -725,7 +726,7 @@ void method() { }); } } - + class MapDropdownChoice { public MapDropdownChoice(Supplier> choiceMap) { } @@ -734,4 +735,44 @@ public MapDropdownChoice(Supplier> choiceMap) { ) ); } + + @Disabled + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/309") + void dontUseLambdaForMethodWithTypeParameter() { + //language=java + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().dependsOn( + """ + package com.helloworld; + + import java.util.List; + + public interface I { + List call(); + } + """ + )), + java( + // can't transform to lambda because of the type argument of I#call() + """ + package com.helloworld; + + import java.util.List; + + class Hello { + public void hello() { + final I i = new I() { + @Override + public List call() { + return null; + } + }; + final List list = i.call(); + } + } + """ + ) + ); + } } From d5a3753068ca7bf47e17deb58f6a86c122c91fc7 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 4 Jul 2024 08:05:26 +0200 Subject: [PATCH 072/183] `ReplaceDuplicateStringLiterals`: Improve runtime characteristics Reduce the peak memory consumption as well as improve runtime performance --- .../ReplaceDuplicateStringLiterals.java | 57 ++++++++++--------- 1 file changed, 30 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 052aa806b..8e94b1d6b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -17,6 +17,7 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import lombok.experimental.NonFinal; import org.openrewrite.*; import org.openrewrite.internal.StringUtils; import org.openrewrite.internal.lang.Nullable; @@ -76,7 +77,7 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) tree; Optional sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class); - if(!Boolean.TRUE.equals(includeTestSources) && !(sourceSet.isPresent() && "main".equals(sourceSet.get().getName()))) { + if (!Boolean.TRUE.equals(includeTestSources) && !(sourceSet.isPresent() && "main".equals(sourceSet.get().getName()))) { return cu; } } @@ -90,17 +91,20 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct } DuplicateLiteralInfo duplicateLiteralInfo = DuplicateLiteralInfo.find(classDecl); - Map> duplicateLiteralsMap = duplicateLiteralInfo.getDuplicateLiterals(); + Map> duplicateLiteralsMap = duplicateLiteralInfo.getDuplicateLiterals(); if (duplicateLiteralsMap.isEmpty()) { return classDecl; } Set variableNames = duplicateLiteralInfo.getVariableNames(); Map fieldValueToFieldName = duplicateLiteralInfo.getFieldValueToFieldName(); String classFqn = classDecl.getType().getFullyQualifiedName(); - for (String valueOfLiteral : duplicateLiteralsMap.keySet()) { + Map replacements = new HashMap<>(); + for (Map.Entry> entry : duplicateLiteralsMap.entrySet()) { + String valueOfLiteral = entry.getKey(); + List duplicateLiterals = duplicateLiteralsMap.get(valueOfLiteral); + String classFieldName = fieldValueToFieldName.get(valueOfLiteral); String variableName; - if (fieldValueToFieldName.containsKey(valueOfLiteral)) { - String classFieldName = fieldValueToFieldName.get(valueOfLiteral); + if (classFieldName != null) { variableName = getNameWithoutShadow(classFieldName, variableNames); if (StringUtils.isBlank(variableName)) { continue; @@ -113,7 +117,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct if (StringUtils.isBlank(variableName)) { continue; } - J.Literal replaceLiteral = ((J.Literal) duplicateLiteralsMap.get(valueOfLiteral).toArray()[0]).withId(randomId()); + J.Literal replaceLiteral = duplicateLiterals.get(0).withId(randomId()); String insertStatement = "private static final String " + variableName + " = #{any(String)};"; if (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Enum) { J.EnumValueSet enumValueSet = classDecl.getBody().getStatements().stream() @@ -125,7 +129,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct if (enumValueSet != null) { // "Temporary" work around due to an issue in the JavaTemplate related to BlockStatementTemplateGenerator#enumClassDeclaration. Space singleSpace = Space.build(" ", emptyList()); - Expression literal = duplicateLiteralsMap.get(valueOfLiteral).toArray(new J.Literal[0])[0].withId(randomId()); + Expression literal = duplicateLiterals.get(0).withId(randomId()); J.Modifier privateModifier = new J.Modifier(randomId(), Space.build("\n", emptyList()), Markers.EMPTY, null, J.Modifier.Type.Private, emptyList()); J.Modifier staticModifier = new J.Modifier(randomId(), singleSpace, Markers.EMPTY, null, J.Modifier.Type.Static, emptyList()); J.Modifier finalModifier = new J.Modifier(randomId(), singleSpace, Markers.EMPTY, null, J.Modifier.Type.Final, emptyList()); @@ -181,9 +185,10 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct } } variableNames.add(variableName); - doAfterVisit(new ReplaceStringLiterals(classDecl, variableName, duplicateLiteralsMap.get(valueOfLiteral))); + entry.getValue().forEach(v -> replacements.put(v, variableName)); } - return classDecl; + return replacements.isEmpty() ? classDecl : + new ReplaceStringLiterals(classDecl, replacements).visitNonNull(classDecl, ctx, getCursor().getParent()); } /** @@ -242,20 +247,11 @@ private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations decla private static class DuplicateLiteralInfo { Set variableNames; Map fieldValueToFieldName; - Map> literalsMap = new HashMap<>(); - - public Map> getDuplicateLiterals() { - Map> filteredMap = new TreeMap<>(Comparator.reverseOrder()); - for (String valueOfLiteral : literalsMap.keySet()) { - if (literalsMap.get(valueOfLiteral).size() >= 3) { - filteredMap.put(valueOfLiteral, literalsMap.get(valueOfLiteral)); - } - } - return filteredMap; - } + @NonFinal + Map> duplicateLiterals; public static DuplicateLiteralInfo find(J.ClassDeclaration inClass) { - DuplicateLiteralInfo result = new DuplicateLiteralInfo(new LinkedHashSet<>(), new LinkedHashMap<>()); + DuplicateLiteralInfo result = new DuplicateLiteralInfo(new LinkedHashSet<>(), new LinkedHashMap<>(), new HashMap<>()); new JavaIsoVisitor() { @Override @@ -309,14 +305,21 @@ public J.Literal visitLiteral(J.Literal literal, Integer integer) { parent.getValue() instanceof J.NewClass || parent.getValue() instanceof J.MethodInvocation) { - result.literalsMap.computeIfAbsent(((String) literal.getValue()), k -> new HashSet<>()); - result.literalsMap.get((String) literal.getValue()).add(literal); + result.duplicateLiterals.computeIfAbsent(((String) literal.getValue()), k -> new ArrayList<>(1)).add(literal); } } return literal; } }.visit(inClass, 0); + Map> filteredMap = new TreeMap<>(Comparator.reverseOrder()); + for (Map.Entry> entry : result.duplicateLiterals.entrySet()) { + if (entry.getValue().size() >= 3) { + filteredMap.put(entry.getKey(), entry.getValue()); + } + } + result.duplicateLiterals = filteredMap; + return result; } } @@ -328,12 +331,12 @@ public J.Literal visitLiteral(J.Literal literal, Integer integer) { @EqualsAndHashCode(callSuper = false) private static class ReplaceStringLiterals extends JavaVisitor { J.ClassDeclaration isClass; - String variableName; - Set literals; + Map replacements; @Override public J visitLiteral(J.Literal literal, ExecutionContext ctx) { - if (literals.contains(literal)) { + String variableName = replacements.get(literal); + if (variableName != null) { assert isClass.getType() != null; return new J.Identifier( randomId(), @@ -344,7 +347,7 @@ public J visitLiteral(J.Literal literal, ExecutionContext ctx) { JavaType.Primitive.String, new JavaType.Variable( null, - Flag.flagsToBitMap(new HashSet<>(Arrays.asList(Flag.Private, Flag.Static, Flag.Final))), + Flag.flagsToBitMap(EnumSet.of(Flag.Private, Flag.Static, Flag.Final)), variableName, isClass.getType(), JavaType.Primitive.String, From fc7ed29838cc092056658e61238ecc49a4ea45ce Mon Sep 17 00:00:00 2001 From: Marc Bruggmann Date: Thu, 4 Jul 2024 17:12:51 +0200 Subject: [PATCH 073/183] Don't use lambdas for methods with type parameters. (#310) See the re-enabled testcase. Fixes #309 --- .../staticanalysis/UseLambdaForFunctionalInterface.java | 5 +++++ .../staticanalysis/UseLambdaForFunctionalInterfaceTest.java | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java index bd6367896..ba3296dfd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java @@ -96,6 +96,11 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) { StringBuilder templateBuilder = new StringBuilder(); J.MethodDeclaration methodDeclaration = (J.MethodDeclaration) n.getBody().getStatements().get(0); + // If the functional interface method has type parameters, we can't replace it with a lambda. + if (methodDeclaration.getTypeParameters() != null && !methodDeclaration.getTypeParameters().isEmpty()) { + return n; + } + if (methodDeclaration.getParameters().get(0) instanceof J.Empty) { templateBuilder.append("() -> {"); } else { diff --git a/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java b/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java index dd30a98a2..06bc31140 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java @@ -736,7 +736,6 @@ public MapDropdownChoice(Supplier> choiceMap) { ); } - @Disabled @Test @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/309") void dontUseLambdaForMethodWithTypeParameter() { From d5601cabd0d227883cbf47ef9ca1b8c70e1f2065 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 4 Jul 2024 17:37:17 +0200 Subject: [PATCH 074/183] Add missing newline on annotated field --- .../staticanalysis/ReplaceDuplicateStringLiterals.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 8e94b1d6b..78d9434d7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -247,6 +247,7 @@ private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations decla private static class DuplicateLiteralInfo { Set variableNames; Map fieldValueToFieldName; + @NonFinal Map> duplicateLiterals; From b82b177537d4f5c1f95f5a55e7a1b377f98ee799 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Thu, 11 Jul 2024 12:05:53 -0500 Subject: [PATCH 075/183] refactor: Update Gradle wrapper (#311) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- gradlew | 4 +++- gradlew.bat | 2 ++ 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch delta 8703 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 Ojj7^59?Sdh$^Qpp*~F>< delta 8662 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cd409941f..02abc8b4c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip -distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip +distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index b740cf133..f3b75f3b0 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -84,7 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e46..9b42019c7 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## From 7686b538b23323d662a83ecdc47c456b7da5f557 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 13 Jul 2024 00:09:53 +0200 Subject: [PATCH 076/183] `ReplaceDuplicateStringLiterals`: More tweaks Should slightly improve performance and readability. --- .../ReplaceDuplicateStringLiterals.java | 105 +++++------------- 1 file changed, 26 insertions(+), 79 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 78d9434d7..8d6b0267f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -25,14 +25,12 @@ import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.*; -import org.openrewrite.marker.Markers; import java.time.Duration; import java.util.*; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; -import static java.util.Collections.singletonList; import static org.openrewrite.Tree.randomId; @Value @@ -84,6 +82,7 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { return super.visit(tree, ctx); } + @SuppressWarnings("UnusedAssignment") @Override public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { if (classDecl.getType() == null) { @@ -118,75 +117,24 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct continue; } J.Literal replaceLiteral = duplicateLiterals.get(0).withId(randomId()); - String insertStatement = "private static final String " + variableName + " = #{any(String)};"; + JavaTemplate template = JavaTemplate.builder("private static final String " + variableName + " = #{any(String)};").build(); if (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Enum) { - J.EnumValueSet enumValueSet = classDecl.getBody().getStatements().stream() - .filter(J.EnumValueSet.class::isInstance) - .map(J.EnumValueSet.class::cast) - .findFirst() - .orElse(null); - - if (enumValueSet != null) { - // "Temporary" work around due to an issue in the JavaTemplate related to BlockStatementTemplateGenerator#enumClassDeclaration. - Space singleSpace = Space.build(" ", emptyList()); - Expression literal = duplicateLiterals.get(0).withId(randomId()); - J.Modifier privateModifier = new J.Modifier(randomId(), Space.build("\n", emptyList()), Markers.EMPTY, null, J.Modifier.Type.Private, emptyList()); - J.Modifier staticModifier = new J.Modifier(randomId(), singleSpace, Markers.EMPTY, null, J.Modifier.Type.Static, emptyList()); - J.Modifier finalModifier = new J.Modifier(randomId(), singleSpace, Markers.EMPTY, null, J.Modifier.Type.Final, emptyList()); - J.VariableDeclarations variableDeclarations = autoFormat(new J.VariableDeclarations( - randomId(), - Space.EMPTY, - Markers.EMPTY, - emptyList(), - Arrays.asList(privateModifier, staticModifier, finalModifier), - new J.Identifier( - randomId(), - singleSpace, - Markers.EMPTY, - emptyList(), - "String", - JavaType.ShallowClass.build("java.lang.String"), - null), - null, - emptyList(), - singletonList(JRightPadded.build(new J.VariableDeclarations.NamedVariable( - randomId(), - Space.EMPTY, - Markers.EMPTY, - new J.Identifier( - randomId(), - Space.EMPTY, - Markers.EMPTY, - emptyList(), - variableName, - JavaType.ShallowClass.build("java.lang.String"), - null), - emptyList(), - JLeftPadded.build(literal).withBefore(singleSpace), - null))) - ), ctx, new Cursor(getCursor(), classDecl.getBody())); - - // Insert the new statement after the EnumValueSet. - List statements = new ArrayList<>(classDecl.getBody().getStatements().size() + 1); - boolean addedNewStatement = false; - for (Statement statement : classDecl.getBody().getStatements()) { - if (!(statement instanceof J.EnumValueSet) && !addedNewStatement) { - statements.add(variableDeclarations); - addedNewStatement = true; - } - statements.add(statement); - } - classDecl = classDecl.withBody(classDecl.getBody().withStatements(statements)); - } + J.Block applied = template + .apply(new Cursor(getCursor(), classDecl.getBody()), classDecl.getBody().getCoordinates().lastStatement(), replaceLiteral); + List statements = applied.getStatements(); + statements.add(1, statements.remove(statements.size() - 1)); + classDecl = classDecl.withBody(applied.withStatements(statements)); } else { classDecl = classDecl.withBody( - JavaTemplate.builder(insertStatement).build() + template .apply(new Cursor(getCursor(), classDecl.getBody()), classDecl.getBody().getCoordinates().firstStatement(), replaceLiteral)); } } variableNames.add(variableName); entry.getValue().forEach(v -> replacements.put(v, variableName)); } + duplicateLiteralInfo = null; + duplicateLiteralsMap = null; return replacements.isEmpty() ? classDecl : new ReplaceStringLiterals(classDecl, replacements).visitNonNull(classDecl, ctx, getCursor().getParent()); } @@ -218,19 +166,18 @@ private String transformToVariableName(String valueOfLiteral) { StringBuilder newName = new StringBuilder(); for (int i = 0; i < valueOfLiteral.length(); i++) { char c = valueOfLiteral.charAt(i); - if (i > 0 && newName.lastIndexOf("_") != newName.length() - 1 && - (Character.isUpperCase(c) && prevIsLower || !prevIsCharacter)) { - newName.append("_"); + if (i > 0 && (Character.isUpperCase(c) && prevIsLower || !prevIsCharacter) && + newName.length() > 0 && newName.charAt(newName.length() - 1) != '_') { + newName.append('_'); } prevIsCharacter = Character.isLetterOrDigit(c); - if (!prevIsCharacter) { - continue; - } - if (newName.length() == 0 && Character.isDigit(c)) { - newName.append("A_"); + if (prevIsCharacter) { + if (newName.length() == 0 && Character.isDigit(c)) { + newName.append("A_"); + } + newName.append(Character.toUpperCase(c)); + prevIsLower = Character.isLowerCase(c); } - newName.append(Character.toUpperCase(c)); - prevIsLower = Character.isLowerCase(c); } return VariableNameUtils.normalizeName(newName.toString()); } @@ -265,17 +212,17 @@ public J.Annotation visitAnnotation(J.Annotation annotation, Integer integer) { public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, Integer integer) { J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, integer); Cursor parentScope = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.MethodDeclaration); - J.VariableDeclarations declaration = getCursor().firstEnclosing(J.VariableDeclarations.class); + J.VariableDeclarations declaration = getCursor().firstEnclosingOrThrow(J.VariableDeclarations.class); + boolean privateStaticFinalVariable = isPrivateStaticFinalVariable(declaration); + // `private static final String`(s) are handled separately by `FindExistingPrivateStaticFinalFields`. if (parentScope.getValue() instanceof J.MethodDeclaration || - (parentScope.getValue() instanceof J.ClassDeclaration && declaration != null && - // `private static final String`(s) are handled separately by `FindExistingPrivateStaticFinalFields`. - !(isPrivateStaticFinalVariable(declaration) && v.getInitializer() instanceof J.Literal && - ((J.Literal) v.getInitializer()).getValue() instanceof String))) { + parentScope.getValue() instanceof J.ClassDeclaration && + !(privateStaticFinalVariable && v.getInitializer() instanceof J.Literal && + ((J.Literal) v.getInitializer()).getValue() instanceof String)) { result.variableNames.add(v.getSimpleName()); } if (parentScope.getValue() instanceof J.ClassDeclaration && - declaration != null && isPrivateStaticFinalVariable(declaration) && - v.getInitializer() instanceof J.Literal && + privateStaticFinalVariable && v.getInitializer() instanceof J.Literal && ((J.Literal) v.getInitializer()).getValue() instanceof String) { String value = (String) (((J.Literal) v.getInitializer()).getValue()); result.fieldValueToFieldName.putIfAbsent(value, v.getSimpleName()); From a8270e4398c0fd3a55dd5b2fc5a9f6fbc8102b70 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 13 Jul 2024 00:50:27 +0200 Subject: [PATCH 077/183] `ReplaceDuplicateStringLiterals`: More tweaks Should slightly improve performance and readability. --- .../ReplaceDuplicateStringLiterals.java | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 8d6b0267f..7e9d8d65a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -184,10 +184,8 @@ private String transformToVariableName(String valueOfLiteral) { }); } - private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations declaration) { - return declaration.hasModifier(J.Modifier.Type.Private) && - declaration.hasModifier(J.Modifier.Type.Static) && - declaration.hasModifier(J.Modifier.Type.Final); + private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations.NamedVariable variable) { + return variable.getVariableType() != null && variable.getVariableType().hasFlags(Flag.Private, Flag.Static, Flag.Final); } @Value @@ -212,8 +210,7 @@ public J.Annotation visitAnnotation(J.Annotation annotation, Integer integer) { public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, Integer integer) { J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, integer); Cursor parentScope = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.MethodDeclaration); - J.VariableDeclarations declaration = getCursor().firstEnclosingOrThrow(J.VariableDeclarations.class); - boolean privateStaticFinalVariable = isPrivateStaticFinalVariable(declaration); + boolean privateStaticFinalVariable = isPrivateStaticFinalVariable(variable); // `private static final String`(s) are handled separately by `FindExistingPrivateStaticFinalFields`. if (parentScope.getValue() instanceof J.MethodDeclaration || parentScope.getValue() instanceof J.ClassDeclaration && @@ -238,7 +235,7 @@ public J.Literal visitLiteral(J.Literal literal, Integer integer) { Cursor parent = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.Annotation || - is instanceof J.VariableDeclarations || + is instanceof J.VariableDeclarations.NamedVariable || is instanceof J.NewClass || is instanceof J.MethodInvocation); // EnumValue can accept constructor arguments, including string literals @@ -247,11 +244,9 @@ public J.Literal visitLiteral(J.Literal literal, Integer integer) { return literal; } - if ((parent.getValue() instanceof J.VariableDeclarations && - ((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Final) && - !(((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Private) && ((J.VariableDeclarations) parent.getValue()).hasModifier(J.Modifier.Type.Static))) || - parent.getValue() instanceof J.NewClass || - parent.getValue() instanceof J.MethodInvocation) { + if ((parent.getValue() instanceof J.VariableDeclarations.NamedVariable && !isPrivateStaticFinalVariable(parent.getValue())) || + parent.getValue() instanceof J.NewClass || + parent.getValue() instanceof J.MethodInvocation) { result.duplicateLiterals.computeIfAbsent(((String) literal.getValue()), k -> new ArrayList<>(1)).add(literal); } From 9dc027c0b96a43ade363d95f3b751e4c1e4a3339 Mon Sep 17 00:00:00 2001 From: jbessels <69236284+jbessels@users.noreply.github.com> Date: Sat, 13 Jul 2024 02:52:28 +0200 Subject: [PATCH 078/183] Recipe AddSerialAnnotationToserialVersionUID (#247) * Added file for AddSerialAnnotationToserialVersionUID * Ran gradlew licenseFormat * Apply suggestions from code review * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply formatter and remove offending elements * Complete implementation for fields * Add missing newline at end of file * Only annotate in serializable classes --------- Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- ...AddSerialAnnotationToserialVersionUID.java | 93 ++++++++++ ...erialAnnotationToserialVersionUIDTest.java | 166 ++++++++++++++++++ 2 files changed, 259 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java new file mode 100644 index 000000000..9c209d93c --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java @@ -0,0 +1,93 @@ +/* + * 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.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.lang.NonNull; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.search.FindAnnotations; +import org.openrewrite.java.search.UsesJavaVersion; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; + +import java.time.Duration; +import java.util.Comparator; + +public class AddSerialAnnotationToserialVersionUID extends Recipe { + @Override + public String getDisplayName() { + return "Add `@Serial` annotation to `serialVersionUID`"; + } + + @Override + public String getDescription() { + return "Annotation any `serialVersionUID` fields with `@Serial` to indicate it's part of the serialization mechanism."; + } + + @Override + public Duration getEstimatedEffortPerOccurrence() { + return Duration.ofMinutes(1); + } + + @Override + @NonNull + public TreeVisitor getVisitor() { + return Preconditions.check( + Preconditions.and( + new UsesJavaVersion<>(14), + new UsesType<>("java.io.Serializable", true) + ), + new JavaIsoVisitor() { + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) { + if (TypeUtils.isAssignableTo("java.io.Serializable", classDecl.getType())) { + return super.visitClassDeclaration(classDecl, executionContext); + } + return classDecl; + } + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + J.VariableDeclarations vd = super.visitVariableDeclarations(multiVariable, ctx); + if (isPrivateStaticFinalLongSerialVersionUID(vd) && + FindAnnotations.find(vd, "@java.io.Serial").isEmpty()) { + maybeAddImport("java.io.Serial"); + return JavaTemplate.builder("@Serial") + .imports("java.io.Serial") + .build() + .apply(getCursor(), vd.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); + } + return vd; + } + + private boolean isPrivateStaticFinalLongSerialVersionUID(J.VariableDeclarations vd) { + return vd.hasModifier(J.Modifier.Type.Private) && + vd.hasModifier(J.Modifier.Type.Static) && + vd.hasModifier(J.Modifier.Type.Final) && + TypeUtils.asPrimitive(vd.getType()) == JavaType.Primitive.Long && + vd.getVariables().size() == 1 && + "serialVersionUID".equals(vd.getVariables().get(0).getSimpleName()); + } + } + ); + } +} diff --git a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java new file mode 100644 index 000000000..5b18adc2d --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java @@ -0,0 +1,166 @@ +/* + * 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.DocumentExample; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; + +class AddSerialAnnotationToserialVersionUIDTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new AddSerialAnnotationToserialVersionUID()) + .allSources(sourceSpec -> sourceSpec.markers(javaVersion(17))); + } + + @DocumentExample + @Test + void addSerialAnnotation() { + rewriteRun( + //language=java + java( + """ + import java.io.Serializable; + + class Example implements Serializable { + private static final long serialVersionUID = 1L; + } + """, + """ + import java.io.Serial; + import java.io.Serializable; + + class Example implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + } + """ + ) + ); + } + + @Test + void shouldNoopIfAlreadyPresent() { + rewriteRun( + //language=java + java( + """ + import java.io.Serializable; + import java.io.Serial; + + class Example implements Serializable { + String var1 = "first variable"; + @Serial + private static final long serialVersionUID = 1L; + int var3 = 666; + } + """ + ) + ); + } + + @Test + void shouldNotAnnotateNonSerializableClass() { + rewriteRun( + //language=java + java( + """ + class Example { + private static final long serialVersionUID = 1L; + } + """ + ) + ); + } + + @Test + void shouldNotAnnotateOnJava11() { + rewriteRun( + //language=java + java( + """ + import java.io.Serializable; + + class Example implements Serializable { + private static final long serialVersionUID = 1L; + } + """, + spec -> spec.markers(javaVersion(11)) + ) + ); + } + + @Test + void shouldNotAnnotateOtherFields() { + rewriteRun( + //language=java + java( + """ + import java.io.Serializable; + + class Example implements Serializable { + static final long serialVersionUID = 1L; + private final long serialVersionUID = 1L; + private static long serialVersionUID = 1L; + private static final int serialVersionUID = 1L; + private static final long foo = 1L; + + void doSomething() { + long serialVersionUID = 1L; + } + } + """ + ) + ); + } + + @Test + void shouldAnnotatedFieldsInInnerClasses() { + rewriteRun( + //language=java + java( + """ + import java.io.Serializable; + + class Outer implements Serializable { + private static final long serialVersionUID = 1; + static class Inner implements Serializable { + private static final long serialVersionUID = 1; + } + } + """, + """ + import java.io.Serial; + import java.io.Serializable; + + class Outer implements Serializable { + @Serial + private static final long serialVersionUID = 1; + static class Inner implements Serializable { + @Serial + private static final long serialVersionUID = 1; + } + } + """ + ) + ); + } +} From 48c94b5f4ebf27f3ab589096018c15dd78b61fb7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 13 Jul 2024 02:56:02 +0200 Subject: [PATCH 079/183] refactor: OpenRewrite best practices --- .../staticanalysis/AddSerialAnnotationToserialVersionUID.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java index 9c209d93c..846685341 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java @@ -58,9 +58,9 @@ public TreeVisitor getVisitor() { ), new JavaIsoVisitor() { @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext executionContext) { + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { if (TypeUtils.isAssignableTo("java.io.Serializable", classDecl.getType())) { - return super.visitClassDeclaration(classDecl, executionContext); + return super.visitClassDeclaration(classDecl, ctx); } return classDecl; } From ab9825a79643ab6e91ac6c011c93a8b9f8f17401 Mon Sep 17 00:00:00 2001 From: Ko Turk Date: Mon, 15 Jul 2024 01:27:48 +0200 Subject: [PATCH 080/183] Duplicate variable name in if statement with two different instanceof (#175) * Added failing JUnit test for issue "Duplicate variable name in if statement with two different instanceof" * Add issue link * Move and update test; include #265 * Update expectations & handle binary separately * Demonstrate known failure --------- Co-authored-by: Ko Turk Co-authored-by: Tim te Beek --- .../InstanceOfPatternMatch.java | 27 ++++-- .../InstanceOfPatternMatchTest.java | 94 ++++++++++++++++++- 2 files changed, 114 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index eb528ed5c..ce994926a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -19,7 +19,6 @@ import lombok.EqualsAndHashCode; import lombok.Value; import org.openrewrite.*; -import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.VariableNameUtils; @@ -265,7 +264,7 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) { } else { strategy = VariableNameStrategy.short_(); } - String baseName = variableBaseName((TypeTree) instanceOf.getClazz(), strategy); + String baseName = strategy.variableName(((TypeTree) instanceOf.getClazz()).getType()); return VariableNameUtils.generateVariableName(baseName, cursor, INCREMENT_NUMBER); } @@ -295,10 +294,6 @@ public J processVariableDeclarations(J.VariableDeclarations multiVariable) { } } - private static String variableBaseName(TypeTree typeTree, VariableNameStrategy nameStrategy) { - return nameStrategy.variableName(typeTree.getType()); - } - private static class UseInstanceOfPatternMatching extends JavaVisitor { private final InstanceOfPatternReplacements replacements; @@ -312,6 +307,26 @@ static J refactor(@Nullable J tree, InstanceOfPatternReplacements replacements, return new UseInstanceOfPatternMatching(replacements).visit(tree, 0, cursor); } + @Override + public J visitBinary(J.Binary original, Integer integer) { + Expression newLeft = (Expression) super.visitNonNull(original.getLeft(), integer); + if (newLeft != original.getLeft()) { + // The left side changed, so the right side should see any introduced variable names + J.Binary replacement = original.withLeft(newLeft); + Cursor widenedCursor = updateCursor(replacement); + + Expression newRight; + if (original.getRight() instanceof J.InstanceOf) { + newRight = replacements.processInstanceOf((J.InstanceOf) original.getRight(), widenedCursor); + } else { + newRight = (Expression) super.visitNonNull(original.getRight(), integer, widenedCursor); + } + return replacement.withRight(newRight); + } + // The left side didn't change, so the right side doesn't need to see any introduced variable names + return super.visitBinary(original, integer); + } + @Override public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, Integer executionContext) { instanceOf = (J.InstanceOf) super.visitInstanceOf(instanceOf, executionContext); diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index 9ea524856..bcc35611b 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -37,7 +37,6 @@ public void defaults(RecipeSpec spec) { @SuppressWarnings({"ImplicitArrayToString", "PatternVariableCanBeUsed", "UnnecessaryLocalVariable"}) @Nested class If { - @Test void ifConditionWithoutPattern() { rewriteRun( @@ -468,6 +467,71 @@ void test(Object o) { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/174") + void ifTwoDifferentInstanceOf() { + rewriteRun( + version( + //language=java + java( + """ + class A { + int combinedLength(Object o, Object o2) { + if (o instanceof String && o2 instanceof String) { + return ((String) o).length() + ((String) o2).length(); + } + return -1; + } + } + """, + """ + class A { + int combinedLength(Object o, Object o2) { + if (o instanceof String string && o2 instanceof String string1) { + return string.length() + string1.length(); + } + return -1; + } + } + """ + ), 17 + ) + ); + } + + @Disabled("Not handled correctly yet") + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/174") + void ifTwoDifferentInstanceOfWithParentheses() { + rewriteRun( + version( + //language=java + java( + """ + class A { + int combinedLength(Object o, Object o2) { + if (o instanceof String && (o2 instanceof String)) { + return ((String) o).length() + ((String) o2).length(); + } + return -1; + } + } + """, + """ + class A { + int combinedLength(Object o, Object o2) { + if (o instanceof String string && (o2 instanceof String string1)) { + return string.length() + string1.length(); + } + return -1; + } + } + """ + ), 17 + ) + ); + } } @SuppressWarnings({"CastCanBeRemovedNarrowingVariableType", "ClassInitializerMayBeStatic"}) @@ -570,6 +634,34 @@ String test(Object o) { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/pull/265") + void multipleCastsInDifferentOperands() { + rewriteRun( + //language=java + java( + """ + import java.util.Comparator; + public class A { + Comparator comparator() { + return (a, b) -> + a instanceof String && b instanceof String ? ((String) a).compareTo((String) b) : 0; + } + } + """, + """ + import java.util.Comparator; + public class A { + Comparator comparator() { + return (a, b) -> + a instanceof String s && b instanceof String s1 ? s.compareTo(s1) : 0; + } + } + """ + ) + ); + } } @Nested From b1b711619552c98f3c50f032871d1da0ee13ce49 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 15 Jul 2024 10:20:43 +0200 Subject: [PATCH 081/183] Support parentheses in binary for InstanceOfPatternMatch --- .../staticanalysis/InstanceOfPatternMatch.java | 7 ++++++- .../staticanalysis/InstanceOfPatternMatchTest.java | 9 ++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index ce994926a..3a4e53fc8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -318,6 +318,11 @@ public J visitBinary(J.Binary original, Integer integer) { Expression newRight; if (original.getRight() instanceof J.InstanceOf) { newRight = replacements.processInstanceOf((J.InstanceOf) original.getRight(), widenedCursor); + } else if (original.getRight() instanceof J.Parentheses && + ((J.Parentheses) original.getRight()).getTree() instanceof J.InstanceOf) { + @SuppressWarnings("unchecked") + J.Parentheses originalRight = (J.Parentheses) original.getRight(); + newRight = originalRight.withTree(replacements.processInstanceOf(originalRight.getTree(), widenedCursor)); } else { newRight = (Expression) super.visitNonNull(original.getRight(), integer, widenedCursor); } @@ -443,7 +448,7 @@ public String variableName(@Nullable JavaType type) { OUTER: while (true) { for (Cursor scope : contextScopes) { - String newCandidate = VariableNameUtils.generateVariableName(candidate, scope, VariableNameUtils.GenerationStrategy.INCREMENT_NUMBER); + String newCandidate = VariableNameUtils.generateVariableName(candidate, scope, INCREMENT_NUMBER); if (!newCandidate.equals(candidate)) { candidate = newCandidate; continue OUTER; diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index bcc35611b..b7343ff1a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -385,7 +385,7 @@ void test(Object o) { } } } - """ + """ ) ); } @@ -405,7 +405,7 @@ void test(Object o) { } } } - """ + """ ) ); } @@ -423,7 +423,7 @@ void test(Object o) { } } } - """ + """ ) ); } @@ -500,7 +500,6 @@ int combinedLength(Object o, Object o2) { ); } - @Disabled("Not handled correctly yet") @Test @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/174") void ifTwoDifferentInstanceOfWithParentheses() { @@ -799,7 +798,7 @@ boolean test(Object o) { } } - @SuppressWarnings({"rawtypes", "unchecked"}) + @SuppressWarnings("unchecked") @Nested class Generics { @Test From ad28bb1232bb4f56bdef751ac5015b3470961b96 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 15 Jul 2024 23:57:31 +0200 Subject: [PATCH 082/183] Retain prefix from removed parentheses Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/307 --- .../InstanceOfPatternMatch.java | 2 +- .../InstanceOfPatternMatchTest.java | 61 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 3a4e53fc8..53f674ec2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -344,7 +344,7 @@ public J visitParentheses(J.Parentheses parens, Integer executi if (parens.getTree() instanceof J.TypeCast) { J replacement = replacements.processTypeCast((J.TypeCast) parens.getTree(), getCursor()); if (replacement != null) { - return replacement; + return replacement.withPrefix(parens.getPrefix()); } } return super.visitParentheses(parens, executionContext); diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index b7343ff1a..8279511d9 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -976,4 +976,65 @@ String test(Object o) { ); } } + + @Nested + class Throws { + @Test + void throwsException() { + rewriteRun( + //language=java + java( + """ + class A { + void test(Throwable t) { + if (t instanceof RuntimeException) { + throw (RuntimeException) t; + } + } + } + """, + """ + class A { + void test(Throwable t) { + if (t instanceof RuntimeException exception) { + throw exception; + } + } + } + """ + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/307") + void throwsExceptionWithExtraParentheses() { + rewriteRun( + //language=java + java( + """ + class A { + void test(Throwable t) { + if (t instanceof Exception) { + // Extra parentheses trips up the replacement + throw ((Exception) t); + } + } + } + """, + """ + class A { + void test(Throwable t) { + if (t instanceof Exception exception) { + // Extra parentheses trips up the replacement + throw exception; + } + } + } + """ + ) + ); + } + + } } From c65d885d5100bbab8d87255723ef435536f10519 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Tue, 16 Jul 2024 16:09:24 -0600 Subject: [PATCH 083/183] NullableOnMethodReturnType --- .../NullableOnMethodReturnType.java | 78 +++++++++++++++++++ .../NullableOnMethodReturnTypeTest.java | 70 +++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java 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 000000000..c6d415606 --- /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 000000000..f8308f944 --- /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) { + } + } + """ + ) + ); + } +} From c29138fad04224433b50ee66930e9e8a8329ede3 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Tue, 16 Jul 2024 16:15:43 -0600 Subject: [PATCH 084/183] Fix test --- .../openrewrite/staticanalysis/NullableOnMethodReturnType.java | 2 +- .../staticanalysis/NullableOnMethodReturnTypeTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java b/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java index c6d415606..8c63a1ee2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java @@ -63,7 +63,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, - Collections.singletonList(nullable.getTree().withPrefix(Space.SINGLE_SPACE)), + Collections.singletonList(nullable.getTree().withPrefix(Space.EMPTY)), m2.getReturnTypeExpression() )); m2 = autoFormat(m2, m2.getReturnTypeExpression(), ctx, getCursor().getParentOrThrow()); diff --git a/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java index f8308f944..4fa9734d3 100644 --- a/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java @@ -44,6 +44,7 @@ public String test() { """ import org.openrewrite.internal.lang.Nullable; class Test { + public @Nullable String test() { } } From 6f251ab96ecd337fed6395f8047c3f30ab42a9d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Tue, 16 Jul 2024 22:27:53 +0000 Subject: [PATCH 085/183] refactor: Move `@Nullable` method annotations to the return type Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.staticanalysis.NullableOnMethodReturnType?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../BigDecimalRoundingConstantsToEnums.java | 3 +-- .../ChainStringBuilderAppendCalls.java | 6 ++---- .../staticanalysis/ExplicitLambdaArgumentTypes.java | 6 ++---- .../staticanalysis/FinalizePrivateFields.java | 4 ++-- .../staticanalysis/ForLoopIncrementInUpdate.java | 4 ++-- .../openrewrite/staticanalysis/InlineVariable.java | 3 +-- .../staticanalysis/InstanceOfPatternMatch.java | 12 ++++-------- .../staticanalysis/JavaElementFactory.java | 6 ++---- .../NoPrimitiveWrappersForToStringOrCompareTo.java | 3 +-- .../staticanalysis/RemoveMethodCallVisitor.java | 9 +++------ .../staticanalysis/RenameToCamelCase.java | 3 +-- .../SimplifyConsecutiveAssignments.java | 12 ++++-------- .../SimplifyDurationCreationUnits.java | 3 +-- .../TernaryOperatorsShouldNotBeNested.java | 3 +-- .../staticanalysis/UnnecessaryThrows.java | 4 ++-- .../staticanalysis/UpperCaseLiteralSuffixes.java | 3 +-- .../staticanalysis/UseDiamondOperator.java | 3 +-- .../UseLambdaForFunctionalInterface.java | 6 ++---- 18 files changed, 33 insertions(+), 60 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java b/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java index ce690768a..077f06070 100644 --- a/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java +++ b/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java @@ -114,8 +114,7 @@ private boolean isConvertibleBigDecimalConstant(J elem) { return false; } - @Nullable - private String getTemplateText(J elem) { + private @Nullable String getTemplateText(J elem) { String roundingName = null; if (elem instanceof J.FieldAccess && ((J.FieldAccess) elem).getTarget().getType() instanceof JavaType.FullyQualified) { J.FieldAccess fa = (J.FieldAccess) elem; diff --git a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java index cdd0c930a..6907d8f4a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java +++ b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java @@ -132,8 +132,7 @@ public static J.Binary concatAdditionBinary(Expression left, Expression right) { /** * Concat expressions to an expression with '+' connected. */ - @Nullable - public static Expression additiveExpression(Expression... expressions) { + public static @Nullable Expression additiveExpression(Expression... expressions) { Expression expression = null; for (Expression element : expressions) { if (element != null) { @@ -143,8 +142,7 @@ public static Expression additiveExpression(Expression... expressions) { return expression; } - @Nullable - public static Expression additiveExpression(List expressions) { + public static @Nullable Expression additiveExpression(List expressions) { return additiveExpression(expressions.toArray(new Expression[0])); } diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java index 9bf1ae0a3..eddb95980 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java @@ -122,8 +122,7 @@ private J.VariableDeclarations maybeAddTypeExpression(J.VariableDeclarations mul return multiVariable; } - @Nullable - private TypeTree buildTypeTree(@Nullable JavaType type, Space space) { + private @Nullable TypeTree buildTypeTree(@Nullable JavaType type, Space space) { if (type == null || type instanceof JavaType.Unknown) { return null; } else if (type instanceof JavaType.Primitive) { @@ -209,8 +208,7 @@ private TypeTree buildTypeTree(@Nullable JavaType type, Space space) { return null; } - @Nullable - private JContainer buildTypeParameters(List typeParameters) { + private @Nullable JContainer buildTypeParameters(List typeParameters) { List> typeExpressions = new ArrayList<>(); for (JavaType type : typeParameters) { diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java index 4da74bc9d..914ae99cf 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java @@ -299,14 +299,14 @@ private static boolean isInLambda(Cursor cursor) { @Value @EqualsAndHashCode(callSuper = false) private static class FindLastIdentifier extends JavaIsoVisitor> { + /** * Find the last identifier in a J.FieldAccess. The purpose is to check whether it's a private field. * * @param j the subtree to search, supposed to be a J.FieldAccess * @return the last Identifier if found, otherwise null. */ - @Nullable - static J.Identifier getLastIdentifier(J j) { + static @Nullable J.Identifier getLastIdentifier(J j) { List ids = new FindLastIdentifier().reduce(j, new ArrayList<>()); return !ids.isEmpty() ? ids.get(ids.size() - 1) : null; } diff --git a/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java b/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java index 110fac692..8a635ac9f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java +++ b/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java @@ -89,9 +89,9 @@ public J visitForLoop(J.ForLoop forLoop, ExecutionContext ctx) { //noinspection ConstantConditions f = f.withBody((Statement) new JavaVisitor() { - @Nullable + @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { return tree == unary ? null : super.visit(tree, ctx); } }.visit(f.getBody(), ctx)); diff --git a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java index 8e3714663..6a0fd9c47 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java +++ b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java @@ -94,8 +94,7 @@ public J.Block visitBlock(J.Block block, ExecutionContext ctx) { return bl; } - @Nullable - private String identReturned(List stats) { + private @Nullable String identReturned(List stats) { Statement lastStatement = stats.get(stats.size() - 1); if (lastStatement instanceof J.Return) { J.Return return_ = (J.Return) lastStatement; diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 53f674ec2..8bd449292 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -268,8 +268,7 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) { return VariableNameUtils.generateVariableName(baseName, cursor, INCREMENT_NUMBER); } - @Nullable - public J processTypeCast(J.TypeCast typeCast, Cursor cursor) { + public @Nullable J processTypeCast(J.TypeCast typeCast, Cursor cursor) { J.InstanceOf instanceOf = replacements.get(typeCast); if (instanceOf != null && instanceOf.getPattern() != null) { String name = ((J.Identifier) instanceOf.getPattern()).getSimpleName(); @@ -288,8 +287,7 @@ public J processTypeCast(J.TypeCast typeCast, Cursor cursor) { return null; } - @Nullable - public J processVariableDeclarations(J.VariableDeclarations multiVariable) { + public @Nullable J processVariableDeclarations(J.VariableDeclarations multiVariable) { return multiVariable.getVariables().stream().anyMatch(variablesToDelete::containsValue) ? null : multiVariable; } } @@ -302,8 +300,7 @@ public UseInstanceOfPatternMatching(InstanceOfPatternReplacements replacements) this.replacements = replacements; } - @Nullable - static J refactor(@Nullable J tree, InstanceOfPatternReplacements replacements, Cursor cursor) { + static @Nullable J refactor(@Nullable J tree, InstanceOfPatternReplacements replacements, Cursor cursor) { return new UseInstanceOfPatternMatching(replacements).visit(tree, 0, cursor); } @@ -362,8 +359,7 @@ public J visitTypeCast(J.TypeCast typeCast, Integer executionContext) { @SuppressWarnings("NullableProblems") @Override - @Nullable - public J visitVariableDeclarations(J.VariableDeclarations multiVariable, Integer integer) { + public @Nullable J visitVariableDeclarations(J.VariableDeclarations multiVariable, Integer integer) { multiVariable = (J.VariableDeclarations) super.visitVariableDeclarations(multiVariable, integer); return replacements.processVariableDeclarations(multiVariable); } diff --git a/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java b/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java index b0e3f1412..26a4283bd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java +++ b/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java @@ -120,8 +120,7 @@ static J.MemberReference newInstanceMethodReference(Expression containing, Strin ); } - @Nullable - static J.FieldAccess newClassLiteral(@Nullable JavaType type, boolean qualified) { + static @Nullable J.FieldAccess newClassLiteral(@Nullable JavaType type, boolean qualified) { JavaType.Class classType = getClassType(type); if (classType == null) { return null; @@ -142,8 +141,7 @@ static J.FieldAccess newClassLiteral(@Nullable JavaType type, boolean qualified) ); } - @Nullable - private static JavaType.Class getClassType(@Nullable JavaType type) { + private static @Nullable JavaType.Class getClassType(@Nullable JavaType type) { if (type instanceof JavaType.Class) { JavaType.Class classType = (JavaType.Class) type; if (classType.getFullyQualifiedName().equals("java.lang.Class")) { diff --git a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java index 4e32ff2ff..37e4b4848 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java @@ -123,8 +123,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu return mi; } - @Nullable - private Expression getSingleArg(@Nullable List args) { + private @Nullable Expression getSingleArg(@Nullable List args) { if (args != null && args.size() == 1 && !(args.get(0) instanceof J.Empty)) { return args.get(0); } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java index 2ae53e2ff..7423225d2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java @@ -46,21 +46,18 @@ public class RemoveMethodCallVisitor

    extends JavaIsoVisitor

    { private final BiPredicate argumentPredicate; @SuppressWarnings("NullableProblems") - @Nullable @Override - public J.NewClass visitNewClass(J.NewClass newClass, P p) { + public @Nullable J.NewClass visitNewClass(J.NewClass newClass, P p) { return visitMethodCall(newClass, () -> super.visitNewClass(newClass, p)); } @SuppressWarnings("NullableProblems") - @Nullable @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) { + public @Nullable J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) { return visitMethodCall(method, () -> super.visitMethodInvocation(method, p)); } - @Nullable - private M visitMethodCall(M methodCall, Supplier visitSuper) { + private @Nullable M visitMethodCall(M methodCall, Supplier visitSuper) { if (!methodMatcher.matches(methodCall)) { return visitSuper.get(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java index 52ee5f7b1..4b4463951 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java @@ -85,8 +85,7 @@ protected String computeKey(String identifier, J context) { return identifier; } - @Nullable - protected JavaType.Variable getFieldType(J tree) { + protected @Nullable JavaType.Variable getFieldType(J tree) { if (tree instanceof J.Identifier) { return ((J.Identifier) tree).getFieldType(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java index d68202f86..9579a4934 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java @@ -101,8 +101,7 @@ public J.Block visitBlock(J.Block block, ExecutionContext ctx) { * @return The name of a numeric variable being assigned or null if not a numeric * variable assignment. */ - @Nullable - private String numericVariableName(Statement s) { + private @Nullable String numericVariableName(Statement s) { if (s instanceof J.Assignment) { return singleVariableName(((J.Assignment) s).getVariable()); } else if (s instanceof J.VariableDeclarations) { @@ -114,8 +113,7 @@ private String numericVariableName(Statement s) { return null; } - @Nullable - private Expression numericVariableAccumulation(Statement s, String name) { + private @Nullable Expression numericVariableAccumulation(Statement s, String name) { if (s instanceof J.Unary) { if (name.equals(singleVariableName(((J.Unary) s).getExpression()))) { return new J.Literal(Tree.randomId(), Space.EMPTY, Markers.EMPTY, 1, "1", null, @@ -130,8 +128,7 @@ private Expression numericVariableAccumulation(Statement s, String name) { return null; } - @Nullable - private String numericVariableOperator(Statement s, String name) { + private @Nullable String numericVariableOperator(Statement s, String name) { if (s instanceof J.Unary) { if (name.equals(singleVariableName(((J.Unary) s).getExpression()))) { switch (((J.Unary) s).getOperator()) { @@ -175,8 +172,7 @@ private String numericVariableOperator(Statement s, String name) { return null; } - @Nullable - private String singleVariableName(Expression e) { + private @Nullable String singleVariableName(Expression e) { JavaType.Primitive type = TypeUtils.asPrimitive(e.getType()); return type != null && type.isNumeric() && e instanceof J.Identifier ? ((J.Identifier) e).getSimpleName() : diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java index 89421d564..336dde90c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java @@ -112,8 +112,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu }); } - @Nullable - public static Long getConstantIntegralValue(Expression expression) { + public static @Nullable Long getConstantIntegralValue(Expression expression) { if (expression instanceof J.Literal) { J.Literal literal = (J.Literal) expression; if (literal.getType() != JavaType.Primitive.Int && literal.getType() != JavaType.Primitive.Long) { diff --git a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java index f81a59ecc..d8ec002ee 100644 --- a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java +++ b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java @@ -299,8 +299,7 @@ private Optional findConditionIdentifier(final J.Ternary ternary) } - @Nullable - private static J.Identifier xorVariable(J first, J second) { + private static @Nullable J.Identifier xorVariable(J first, J second) { J.Identifier result = null; if (isVariable(first) && isVariable(second)) { return null; diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java index 1d63357ce..253cdf202 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java @@ -67,9 +67,9 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex if (!unusedThrows.isEmpty()) { new JavaIsoVisitor() { - @Nullable + @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { if (unusedThrows.isEmpty()) { return (J) tree; } diff --git a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java index 23006a5bb..d43837de7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java +++ b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java @@ -77,8 +77,7 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations return nv; } - @Nullable - private String upperCaseSuffix(@Nullable String valueSource) { + private @Nullable String upperCaseSuffix(@Nullable String valueSource) { if (valueSource == null || valueSource.length() < 2) { return valueSource; } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java index 22f339b73..5212481c7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java @@ -198,8 +198,7 @@ public J.Return visitReturn(J.Return _return, ExecutionContext ctx) { return return_; } - @Nullable - private List parameterizedTypes(J.ParameterizedType parameterizedType) { + private @Nullable List parameterizedTypes(J.ParameterizedType parameterizedType) { if (parameterizedType.getTypeParameters() == null) { return null; } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java index ba3296dfd..c85e0978e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java @@ -385,9 +385,8 @@ public J visitNewClass(J.NewClass newClass, List variables) { return newClass; } - @Nullable @Override - public J visit(@Nullable Tree tree, List variables) { + public @Nullable J visit(@Nullable Tree tree, List variables) { if (getCursor().getNearestMessage("stop") != null) { return (J) tree; } @@ -427,8 +426,7 @@ public J visitMethodInvocation(J.MethodInvocation method, AtomicBoolean atomicBo } // TODO consider moving to TypeUtils - @Nullable - private static JavaType.Method getSamCompatible(@Nullable JavaType type) { + private static @Nullable JavaType.Method getSamCompatible(@Nullable JavaType type) { JavaType.Method sam = null; JavaType.FullyQualified fullyQualified = TypeUtils.asFullyQualified(type); if (fullyQualified == null) { From aa561cdfa2065bfbd6c04bcf87052cc039e4b83a Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 19 Jul 2024 12:53:52 +0000 Subject: [PATCH 086/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/NullableOnMethodReturnTypeTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java index 4fa9734d3..8fec2e882 100644 --- a/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/NullableOnMethodReturnTypeTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -28,6 +29,7 @@ public void defaults(RecipeSpec spec) { spec.recipe(new NullableOnMethodReturnType()); } + @DocumentExample @Test void nullableOnMethodReturnType() { rewriteRun( From 6d04813574c6b02c8bf7d6e2f66b2bdaa881854c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Sun, 4 Aug 2024 15:12:33 -0400 Subject: [PATCH 087/183] Sweep preconditions to prepare for C# running (#319) * Sweep preconditions to prepare for C# running --- .../ControlFlowIndentation.java | 2 +- .../staticanalysis/CovariantEquals.java | 110 ++++++++++++++- .../CovariantEqualsVisitor.java | 125 ------------------ .../staticanalysis/DefaultComesLast.java | 3 +- .../staticanalysis/EmptyBlock.java | 2 +- .../staticanalysis/EqualsAvoidsNull.java | 2 +- .../ExplicitInitialization.java | 2 +- .../staticanalysis/FallThrough.java | 2 +- .../staticanalysis/FallThroughVisitor.java | 2 +- .../staticanalysis/HiddenField.java | 2 +- .../HideUtilityClassConstructor.java | 2 +- .../LambdaBlockToExpression.java | 11 +- .../staticanalysis/MethodNameCasing.java | 2 +- .../NestedEnumsAreNotStatic.java | 1 + .../NoDoubleBraceInitialization.java | 2 +- .../NoEmptyCollectionWithRawType.java | 2 +- .../NoToStringOnStringType.java | 4 +- .../staticanalysis/NoValueOfOnStringType.java | 2 +- ...meMethodsNamedHashcodeEqualOrToString.java | 3 +- .../ReplaceStringBuilderWithString.java | 4 +- .../staticanalysis/SimplifyBooleanReturn.java | 9 +- .../SimplifyConstantIfBranchExecution.java | 2 +- .../staticanalysis/StaticMethodNotFinal.java | 2 +- .../staticanalysis/StringLiteralEquality.java | 9 +- .../UnnecessaryCloseInTryWithResources.java | 13 +- .../UnnecessaryExplicitTypeArguments.java | 3 +- .../UnnecessaryPrimitiveAnnotations.java | 4 +- .../UpperCaseLiteralSuffixes.java | 63 +++++---- .../staticanalysis/UseDiamondOperator.java | 7 +- .../ReplaceLambdaWithMethodReferenceTest.java | 39 +++--- 30 files changed, 213 insertions(+), 223 deletions(-) delete mode 100644 src/main/java/org/openrewrite/staticanalysis/CovariantEqualsVisitor.java diff --git a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java index 4d784dc9f..9ea4c749d 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java +++ b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java @@ -66,7 +66,7 @@ public TreeVisitor getVisitor() { public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - TabsAndIndentsStyle style = ((SourceFile) cu).getStyle(TabsAndIndentsStyle.class); + TabsAndIndentsStyle style = cu.getStyle(TabsAndIndentsStyle.class); if (style == null) { style = IntelliJ.tabsAndIndents(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java b/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java index 9ed4e4f6a..3c31d1f39 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java @@ -15,14 +15,20 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Incubating; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; +import org.openrewrite.*; +import org.openrewrite.java.AnnotationMatcher; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.service.AnnotationService; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; import java.time.Duration; import java.util.Collections; +import java.util.Comparator; import java.util.Set; +import java.util.stream.Stream; @Incubating(since = "7.0.0") public class CovariantEquals extends Recipe { @@ -35,7 +41,7 @@ public String getDisplayName() { @Override public String getDescription() { return "Checks that classes and records which define a covariant `equals()` method also override method `equals(Object)`. " + - "Covariant `equals()` means a method that is similar to `equals(Object)`, but with a covariant parameter type (any subtype of `Object`)."; + "Covariant `equals()` means a method that is similar to `equals(Object)`, but with a covariant parameter type (any subtype of `Object`)."; } @Override @@ -50,6 +56,98 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return new CovariantEqualsVisitor<>(); + MethodMatcher objectEquals = new MethodMatcher("* equals(java.lang.Object)"); + return new JavaIsoVisitor() { + + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); + Stream mds = cd.getBody().getStatements().stream() + .filter(J.MethodDeclaration.class::isInstance) + .map(J.MethodDeclaration.class::cast); + if (cd.getKind() != J.ClassDeclaration.Kind.Type.Interface && mds.noneMatch(m -> objectEquals.matches(m, classDecl))) { + cd = (J.ClassDeclaration) new ChangeCovariantEqualsMethodVisitor(cd).visit(cd, ctx, getCursor().getParentOrThrow()); + assert cd != null; + } + return cd; + } + + class ChangeCovariantEqualsMethodVisitor extends JavaIsoVisitor { + private final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override"); + + private final J.ClassDeclaration enclosingClass; + + public ChangeCovariantEqualsMethodVisitor(J.ClassDeclaration enclosingClass) { + this.enclosingClass = enclosingClass; + } + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + updateCursor(m); + + /* + * Looking for "public boolean equals(EnclosingClassType)" as the method signature match. + * We'll replace it with "public boolean equals(Object)" + */ + JavaType.FullyQualified type = enclosingClass.getType(); + if (type == null || type instanceof JavaType.Unknown) { + return m; + } + + String ecfqn = type.getFullyQualifiedName(); + if (m.hasModifier(J.Modifier.Type.Public) && + m.getReturnTypeExpression() != null && + JavaType.Primitive.Boolean.equals(m.getReturnTypeExpression().getType()) && + new MethodMatcher(ecfqn + " equals(" + ecfqn + ")").matches(m, enclosingClass)) { + + if (!service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)) { + m = JavaTemplate.builder("@Override").build() + .apply(updateCursor(m), + m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); + } + + /* + * Change parameter type to Object, and maybe change input parameter name representing the other object. + * This is because we prepend these type-checking replacement statements to the existing "equals(..)" body. + * Therefore we don't want to collide with any existing variable names. + */ + J.VariableDeclarations.NamedVariable oldParamName = ((J.VariableDeclarations) m.getParameters().get(0)).getVariables().get(0); + String paramName = "obj".equals(oldParamName.getSimpleName()) ? "other" : "obj"; + m = JavaTemplate.builder("Object #{}").build() + .apply(updateCursor(m), + m.getCoordinates().replaceParameters(), + paramName); + + /* + * We'll prepend this type-check and type-cast to the beginning of the existing + * equals(..) method body statements, and let the existing equals(..) method definition continue + * with the logic doing what it was doing. + */ + String equalsBodyPrefixTemplate = "if (#{} == this) return true;\n" + + "if (#{} == null || getClass() != #{}.getClass()) return false;\n" + + "#{} #{} = (#{}) #{};\n"; + JavaTemplate equalsBodySnippet = JavaTemplate.builder(equalsBodyPrefixTemplate).contextSensitive().build(); + + assert m.getBody() != null; + Object[] params = new Object[]{ + paramName, + paramName, + paramName, + enclosingClass.getSimpleName(), + oldParamName.getSimpleName(), + enclosingClass.getSimpleName(), + paramName + }; + + m = equalsBodySnippet.apply(new Cursor(getCursor().getParent(), m), + m.getBody().getStatements().get(0).getCoordinates().before(), + params); + } + + return m; + } + } + }; } } diff --git a/src/main/java/org/openrewrite/staticanalysis/CovariantEqualsVisitor.java b/src/main/java/org/openrewrite/staticanalysis/CovariantEqualsVisitor.java deleted file mode 100644 index 6ac7c5b0b..000000000 --- a/src/main/java/org/openrewrite/staticanalysis/CovariantEqualsVisitor.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2020 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.Cursor; -import org.openrewrite.Incubating; -import org.openrewrite.java.AnnotationMatcher; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.service.AnnotationService; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; - -import java.util.Comparator; -import java.util.stream.Stream; - -@Incubating(since = "7.0.0") -public class CovariantEqualsVisitor

    extends JavaIsoVisitor

    { - private static final MethodMatcher OBJECT_EQUALS = new MethodMatcher("* equals(java.lang.Object)"); - - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P p) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, p); - Stream mds = cd.getBody().getStatements().stream() - .filter(J.MethodDeclaration.class::isInstance) - .map(J.MethodDeclaration.class::cast); - if (cd.getKind() != J.ClassDeclaration.Kind.Type.Interface && mds.noneMatch(m -> OBJECT_EQUALS.matches(m, classDecl))) { - cd = (J.ClassDeclaration) new ChangeCovariantEqualsMethodVisitor<>(cd).visit(cd, p, getCursor().getParentOrThrow()); - assert cd != null; - } - return cd; - } - - private static class ChangeCovariantEqualsMethodVisitor

    extends JavaIsoVisitor

    { - private static final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override"); - private static final String EQUALS_BODY_PREFIX_TEMPLATE = - "if (#{} == this) return true;\n" + - "if (#{} == null || getClass() != #{}.getClass()) return false;\n" + - "#{} #{} = (#{}) #{};\n"; - - private final J.ClassDeclaration enclosingClass; - - public ChangeCovariantEqualsMethodVisitor(J.ClassDeclaration enclosingClass) { - this.enclosingClass = enclosingClass; - } - - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, P p) { - J.MethodDeclaration m = super.visitMethodDeclaration(method, p); - updateCursor(m); - - /* - * Looking for "public boolean equals(EnclosingClassType)" as the method signature match. - * We'll replace it with "public boolean equals(Object)" - */ - JavaType.FullyQualified type = enclosingClass.getType(); - if (type == null || type instanceof JavaType.Unknown) { - return m; - } - - String ecfqn = type.getFullyQualifiedName(); - if (m.hasModifier(J.Modifier.Type.Public) && - m.getReturnTypeExpression() != null && - JavaType.Primitive.Boolean.equals(m.getReturnTypeExpression().getType()) && - new MethodMatcher(ecfqn + " equals(" + ecfqn + ")").matches(m, enclosingClass)) { - - if (!service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)) { - m = JavaTemplate.builder("@Override").build() - .apply(updateCursor(m), - m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); - } - - /* - * Change parameter type to Object, and maybe change input parameter name representing the other object. - * This is because we prepend these type-checking replacement statements to the existing "equals(..)" body. - * Therefore we don't want to collide with any existing variable names. - */ - J.VariableDeclarations.NamedVariable oldParamName = ((J.VariableDeclarations) m.getParameters().get(0)).getVariables().get(0); - String paramName = "obj".equals(oldParamName.getSimpleName()) ? "other" : "obj"; - m = JavaTemplate.builder("Object #{}").build() - .apply(updateCursor(m), - m.getCoordinates().replaceParameters(), - paramName); - - /* - * We'll prepend this type-check and type-cast to the beginning of the existing - * equals(..) method body statements, and let the existing equals(..) method definition continue - * with the logic doing what it was doing. - */ - JavaTemplate equalsBodySnippet = JavaTemplate.builder(EQUALS_BODY_PREFIX_TEMPLATE).contextSensitive().build(); - - assert m.getBody() != null; - Object[] params = new Object[]{ - paramName, - paramName, - paramName, - enclosingClass.getSimpleName(), - oldParamName.getSimpleName(), - enclosingClass.getSimpleName(), - paramName - }; - - m = equalsBodySnippet.apply(new Cursor(getCursor().getParent(), m), - m.getBody().getStatements().get(0).getCoordinates().before(), - params); - } - - return m; - } - } -} diff --git a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java index a8f85e1dc..bf04bcf8d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java +++ b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java @@ -61,7 +61,7 @@ private static class DefaultComesLastFromCompilationUnitStyle extends JavaIsoVis public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - DefaultComesLastStyle style = ((SourceFile) cu).getStyle(DefaultComesLastStyle.class); + DefaultComesLastStyle style = cu.getStyle(DefaultComesLastStyle.class); if (style == null) { style = Checkstyle.defaultComesLast(); } @@ -70,5 +70,4 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { return (J) tree; } } - } diff --git a/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java b/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java index 0f636271a..1604096c8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java +++ b/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java @@ -61,7 +61,7 @@ private static class EmptyBlockFromCompilationUnitStyle extends JavaIsoVisitor getVisitor() { public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - ExplicitInitializationStyle style = ((SourceFile) cu).getStyle(ExplicitInitializationStyle.class); + ExplicitInitializationStyle style = cu.getStyle(ExplicitInitializationStyle.class); if (style == null) { style = Checkstyle.explicitInitialization(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/FallThrough.java b/src/main/java/org/openrewrite/staticanalysis/FallThrough.java index ba23f852f..130feb734 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FallThrough.java +++ b/src/main/java/org/openrewrite/staticanalysis/FallThrough.java @@ -60,7 +60,7 @@ private static class FallThroughFromCompilationUnitStyle extends JavaIsoVisitor< public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - FallThroughStyle style = ((SourceFile) cu).getStyle(FallThroughStyle.class); + FallThroughStyle style = cu.getStyle(FallThroughStyle.class); if (style == null) { style = Checkstyle.fallThrough(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java index 00b699646..fb29e0dd1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FallThroughVisitor.java @@ -54,7 +54,7 @@ public J.Case visitCase(J.Case case_, P p) { J.Switch switch_ = getCursor().dropParentUntil(J.Switch.class::isInstance).getValue(); if (Boolean.TRUE.equals(style.getCheckLastCaseGroup()) || !isLastCase(case_, switch_)) { if (FindLastLineBreaksOrFallsThroughComments.find(switch_, c).isEmpty()) { - c = (J.Case) new AddBreak<>(c).visit(c, p, getCursor().getParent()); + c = (J.Case) new AddBreak<>(c).visitNonNull(c, p, getCursor().getParentOrThrow()); } } } diff --git a/src/main/java/org/openrewrite/staticanalysis/HiddenField.java b/src/main/java/org/openrewrite/staticanalysis/HiddenField.java index 0a2b13286..49873b765 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HiddenField.java +++ b/src/main/java/org/openrewrite/staticanalysis/HiddenField.java @@ -61,7 +61,7 @@ private static class HiddenFieldFromCompilationUnitStyle extends JavaIsoVisitor< public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - HiddenFieldStyle style = ((SourceFile) cu).getStyle(HiddenFieldStyle.class); + HiddenFieldStyle style = cu.getStyle(HiddenFieldStyle.class); if (style == null) { style = Checkstyle.hiddenFieldStyle(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java index 43026d399..9f04f9b8c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java @@ -62,7 +62,7 @@ private static class HideUtilityClassConstructorFromCompilationUnitStyle extends public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - HideUtilityClassConstructorStyle style = ((SourceFile) cu).getStyle(HideUtilityClassConstructorStyle.class); + HideUtilityClassConstructorStyle style = cu.getStyle(HideUtilityClassConstructorStyle.class); if (style == null) { style = Checkstyle.hideUtilityClassConstructorStyle(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java b/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java index 936f1b2a3..a68f3f98a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java +++ b/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java @@ -51,11 +51,12 @@ public J.Lambda visitLambda(J.Lambda lambda, ExecutionContext ctx) { Space prefix = statement.getPrefix(); if (statement instanceof J.Return) { Expression expression = ((J.Return) statement).getExpression(); - if (prefix.getComments().isEmpty()) { - return l.withBody(expression); - } else { - return l.withBody(expression.withPrefix(prefix)); - } + if (prefix.getComments().isEmpty()) { + //noinspection DataFlowIssue + return l.withBody(expression); + } else if (expression != null) { + return l.withBody(expression.withPrefix(prefix)); + } } else if (statement instanceof J.MethodInvocation) { if (prefix.getComments().isEmpty()) { return l.withBody(statement); diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index 5c7156572..cca1935db 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -187,7 +187,7 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { } @Value - static class MethodNameChange { + public static class MethodNameChange { UUID scope; boolean privateMethod; ChangeMethodName recipe; diff --git a/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java b/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java index 180e9c84f..941cb8e5d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java +++ b/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java @@ -67,6 +67,7 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex if (cd.getKind() == J.ClassDeclaration.Kind.Type.Enum && cd.getType() != null && cd.getType().getOwningClass() != null) { if (J.Modifier.hasModifier(cd.getModifiers(), J.Modifier.Type.Static)) { J.Block enumBody = cd.getBody(); + //noinspection DataFlowIssue cd = cd.withBody(null); cd = maybeAutoFormat(cd, cd.withModifiers(ListUtils.map(cd.getModifiers(), mod -> diff --git a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java index c5199ef78..171b504d8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java @@ -229,7 +229,7 @@ public J.NewClass visitNewClass(J.NewClass newClass, String methodName) { private static class FindMethodInvocationInDoubleBrace extends JavaIsoVisitor { /** - * Find whether any collection content initialization method(e.g add() or put()) is invoked in the double brace. + * Find whether any collection content initialization method(e.g. add() or put()) is invoked in the double brace. * * @param j The subtree to search, supposed to be the 2nd brace (J.Block) * @return true if any method invocation found in the double brace, otherwise false. diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java index 0c058712f..70c6a420b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEmptyCollectionWithRawType.java @@ -42,7 +42,7 @@ public String getDisplayName() { @Override public String getDescription() { - return "Replaces `Collections#EMPTY_..` with methods that return generic types."; + return "Replaces `Collections#EMPTY_...` with methods that return generic types."; } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java b/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java index cd45f50a8..b012fee96 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoToStringOnStringType.java @@ -33,12 +33,12 @@ public class NoToStringOnStringType extends Recipe { @Override public String getDisplayName() { - return "Unnecessary String#toString()"; + return "Unnecessary `String#toString`"; } @Override public String getDescription() { - return "Remove unnecessary `String#toString()` invocations on objects which are already a string."; + return "Remove unnecessary `String#toString` invocations on objects which are already a string."; } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java b/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java index 123801262..f50fbe145 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java @@ -37,7 +37,7 @@ public class NoValueOfOnStringType extends Recipe { @Override public String getDisplayName() { - return "Unnecessary String#valueOf(..)"; + return "Unnecessary `String#valueOf(..)`"; } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java b/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java index 23bb0f734..ece5adb94 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameMethodsNamedHashcodeEqualOrToString.java @@ -26,6 +26,7 @@ import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; +import org.openrewrite.staticanalysis.java.JavaFileChecker; import java.time.Duration; import java.util.Collections; @@ -58,7 +59,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(Preconditions.or(new DeclaresMethod<>(NO_ARGS), new DeclaresMethod<>(OBJECT_ARG)), new JavaIsoVisitor() { + return Preconditions.check(Preconditions.and(new JavaFileChecker<>(), Preconditions.or(new DeclaresMethod<>(NO_ARGS), new DeclaresMethod<>(OBJECT_ARG))), new JavaIsoVisitor() { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { if (method.getMethodType() != null && method.getReturnTypeExpression() != null) { diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java index 2879b22f8..92b61a331 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java @@ -37,7 +37,7 @@ public class ReplaceStringBuilderWithString extends Recipe { @Override public String getDisplayName() { - return "Replace StringBuilder.append() with String"; + return "Replace `StringBuilder#append` with `String`"; } @Override @@ -88,7 +88,7 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) return m; } - // Check if a method call is a select of another method call + // Check if a method call is a "select" of another method call private boolean isAMethodSelect(J.MethodInvocation method) { Cursor parent = getCursor().getParent(2); // 2 means skip right padded cursor if (parent == null || !(parent.getValue() instanceof J.MethodInvocation)) { diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java index daa2b29fe..12d34eafa 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java @@ -192,7 +192,7 @@ private Optional getReturnExprIfOnlyStatementInElseThen(J.If iff2) { return Optional.empty(); } - private boolean hasElseWithComment(J.If.Else else_) { + private boolean hasElseWithComment(@Nullable J.If.Else else_) { if (else_ == null || else_.getBody() == null) { return false; } @@ -202,11 +202,8 @@ private boolean hasElseWithComment(J.If.Else else_) { if (!else_.getBody().getComments().isEmpty()) { return true; } - if (else_.getBody() instanceof J.Block - && !((J.Block) else_.getBody()).getStatements().get(0).getComments().isEmpty()) { - return true; - } - return false; + return else_.getBody() instanceof J.Block + && !((J.Block) else_.getBody()).getStatements().get(0).getComments().isEmpty(); } }; } diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java index d57b98487..960c98c1e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java @@ -57,7 +57,7 @@ public J visitBlock(J.Block block, ExecutionContext ctx) { if (bl != block) { bl = (J.Block) new RemoveUnneededBlock.RemoveUnneededBlockStatementVisitor() .visitNonNull(bl, ctx, getCursor().getParentOrThrow()); - EmptyBlockStyle style = ((SourceFile) getCursor().firstEnclosingOrThrow(JavaSourceFile.class)) + EmptyBlockStyle style = getCursor().firstEnclosingOrThrow(JavaSourceFile.class) .getStyle(EmptyBlockStyle.class); if (style == null) { style = Checkstyle.emptyBlock(); diff --git a/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java b/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java index 8d9e613a6..c2bd31b03 100644 --- a/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java +++ b/src/main/java/org/openrewrite/staticanalysis/StaticMethodNotFinal.java @@ -29,7 +29,7 @@ public class StaticMethodNotFinal extends Recipe { @Override public String getDisplayName() { - return "Static methods not final"; + return "Static methods need not be final"; } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java b/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java index 87540c7f1..a74847846 100644 --- a/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java +++ b/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java @@ -20,8 +20,7 @@ import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; -import org.openrewrite.staticanalysis.groovy.GroovyFileChecker; -import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker; +import org.openrewrite.staticanalysis.java.JavaFileChecker; import java.time.Duration; import java.util.Collections; @@ -33,7 +32,7 @@ public class StringLiteralEquality extends Recipe { @Override public String getDisplayName() { - return "Use `String.equals()` on String literals"; + return "Use `String.equals()` on `String` literals"; } @Override @@ -59,9 +58,7 @@ public TreeVisitor getVisitor() { // Don't change for Kotlin because In Kotlin, `==` means structural equality, so it's redundant to call equals(). // see https://rules.sonarsource.com/kotlin/RSPEC-S6519/ TreeVisitor preconditions = Preconditions.and( - Preconditions.and( - Preconditions.not(new KotlinFileChecker<>()), - Preconditions.not(new GroovyFileChecker<>())), + new JavaFileChecker<>(), new UsesType<>("java.lang.String", false)); return Preconditions.check(preconditions, new JavaVisitor() { private final JavaType.FullyQualified TYPE_STRING = TypeUtils.asFullyQualified(JavaType.buildType("java.lang.String")); diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java index 2f35fdb0f..7bfd0fb7b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java @@ -16,12 +16,16 @@ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; 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.staticanalysis.groovy.GroovyFileChecker; +import org.openrewrite.staticanalysis.java.JavaFileChecker; +import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker; import java.time.Duration; import java.util.Collections; @@ -50,7 +54,14 @@ public Set getTags() { @Override public TreeVisitor getVisitor() { - return new UnnecessaryAutoCloseableVisitor(); + return Preconditions.check( + Preconditions.or( + new JavaFileChecker<>(), + new KotlinFileChecker<>(), + new GroovyFileChecker<>() + ), + new UnnecessaryAutoCloseableVisitor() + ); } private static class UnnecessaryAutoCloseableVisitor extends JavaIsoVisitor { diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java index 9c7003e44..f728f2fde 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java @@ -18,6 +18,7 @@ import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; +import org.openrewrite.staticanalysis.java.JavaFileChecker; import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker; public class UnnecessaryExplicitTypeArguments extends Recipe { @@ -34,7 +35,7 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(Preconditions.not(new KotlinFileChecker<>()), new JavaIsoVisitor() { + return Preconditions.check(new JavaFileChecker<>(), new JavaIsoVisitor() { @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation m = super.visitMethodInvocation(method, ctx); diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java index 83cd834f9..adb5d1063 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java @@ -37,12 +37,12 @@ public class UnnecessaryPrimitiveAnnotations extends Recipe { @Override public String getDisplayName() { - return "Remove Nullable and CheckForNull annotations from primitives"; + return "Remove `@Nullable` and `@CheckForNull` annotations from primitives"; } @Override public String getDescription() { - return "Remove `@Nullable` and `@CheckForNull` annotations from primitives since they can't be null."; + return "Primitives can't be null anyway, so these annotations are not useful in this context."; } @Override diff --git a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java index d43837de7..92d4ffa01 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java +++ b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java @@ -24,6 +24,7 @@ import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; +import org.openrewrite.staticanalysis.java.JavaFileChecker; import java.time.Duration; import java.util.Collections; @@ -52,37 +53,41 @@ public Set getTags() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(Preconditions.or( - new UsesType<>("long", false), - new UsesType<>("java.lang.Long", false), - new UsesType<>("double", false), - new UsesType<>("java.lang.Double", false), - new UsesType<>("float", false), - new UsesType<>("java.lang.Float", false) - ), new JavaIsoVisitor() { - @Override - public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { - J.VariableDeclarations.NamedVariable nv = super.visitVariable(variable, ctx); - if (nv.getInitializer() instanceof J.Literal && nv.getInitializer().getType() != null) { - J.Literal initializer = (J.Literal) nv.getInitializer(); - if (initializer.getType() == JavaType.Primitive.Double - || initializer.getType() == JavaType.Primitive.Float - || initializer.getType() == JavaType.Primitive.Long) { - String upperValueSource = upperCaseSuffix(initializer.getValueSource()); - if (upperValueSource != null && !upperValueSource.equals(initializer.getValueSource())) { - nv = nv.withInitializer(initializer.withValueSource(upperValueSource)); + return Preconditions.check( + Preconditions.and( + new JavaFileChecker<>(), + Preconditions.or( + new UsesType<>("long", false), + new UsesType<>("java.lang.Long", false), + new UsesType<>("double", false), + new UsesType<>("java.lang.Double", false), + new UsesType<>("float", false), + new UsesType<>("java.lang.Float", false) + ) + ), new JavaIsoVisitor() { + @Override + public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { + J.VariableDeclarations.NamedVariable nv = super.visitVariable(variable, ctx); + if (nv.getInitializer() instanceof J.Literal && nv.getInitializer().getType() != null) { + J.Literal initializer = (J.Literal) nv.getInitializer(); + if (initializer.getType() == JavaType.Primitive.Double + || initializer.getType() == JavaType.Primitive.Float + || initializer.getType() == JavaType.Primitive.Long) { + String upperValueSource = upperCaseSuffix(initializer.getValueSource()); + if (upperValueSource != null && !upperValueSource.equals(initializer.getValueSource())) { + nv = nv.withInitializer(initializer.withValueSource(upperValueSource)); + } + } } + return nv; } - } - return nv; - } - private @Nullable String upperCaseSuffix(@Nullable String valueSource) { - if (valueSource == null || valueSource.length() < 2) { - return valueSource; - } - return valueSource.substring(0, valueSource.length() - 1) + valueSource.substring(valueSource.length() - 1).toUpperCase(); - } - }); + private @Nullable String upperCaseSuffix(@Nullable String valueSource) { + if (valueSource == null || valueSource.length() < 2) { + return valueSource; + } + return valueSource.substring(0, valueSource.length() - 1) + valueSource.substring(valueSource.length() - 1).toUpperCase(); + } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java index 5212481c7..eb5c6a093 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java @@ -35,12 +35,15 @@ public class UseDiamondOperator extends Recipe { @Override public String getDisplayName() { - return "Use diamond operator"; + return "Use the diamond operator"; } @Override public String getDescription() { - return "The diamond operator (`<>`) should be used. Java 7 introduced the diamond operator (<>) to reduce the verbosity of generics code. For instance, instead of having to declare a List's type in both its declaration and its constructor, you can now simplify the constructor declaration with `<>`, and the compiler will infer the type."; + return "The diamond operator (`<>`) should be used. Java 7 introduced the diamond operator (<>) to " + + "reduce the verbosity of generics code. For instance, instead of having to declare a `List`'s " + + "type in both its declaration and its constructor, you can now simplify the constructor declaration " + + "with `<>`, and the compiler will infer the type."; } @Override diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java index 0403d02d6..90f945e7d 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReferenceTest.java @@ -29,7 +29,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.java.Assertions.java; -@SuppressWarnings({"unchecked", "RedundantCast", "SimplifyStreamApiCallChains", "Convert2MethodRef", "CodeBlock2Expr", "RedundantOperationOnEmptyContainer", "ResultOfMethodCallIgnored", "rawtypes", "UnusedAssignment"}) +@SuppressWarnings({"unchecked", "RedundantCast", "SimplifyStreamApiCallChains", "Convert2MethodRef", "CodeBlock2Expr", "RedundantOperationOnEmptyContainer", "ResultOfMethodCallIgnored", "rawtypes", "UnusedAssignment", "OptionalGetWithoutIsPresent"}) class ReplaceLambdaWithMethodReferenceTest implements RewriteTest { @Override @@ -87,7 +87,7 @@ void ignoreAmbiguousMethodReference() { java( """ import java.util.stream.Stream; - + class Test { Stream method() { return Stream.of(1, 32, 12, 15, 23).map(x -> Integer.toString(x)); @@ -242,7 +242,7 @@ List method(List input) { //language=java """ import org.test.CheckType; - + import java.util.List; import java.util.stream.Collectors; @@ -525,16 +525,16 @@ void method(List input) { @Test void castType() { rewriteRun( - //language=java java( + //language=java """ package org.test; public class CheckType { } """ ), - //language=java java( + //language=java """ import java.util.List; import java.util.stream.Collectors; @@ -550,6 +550,7 @@ List filter(List l) { } } """, + //language=java """ import java.util.List; import java.util.stream.Collectors; @@ -606,7 +607,7 @@ List filter(List l) { //language=java """ import org.test.CheckType; - + import java.util.List; import java.util.stream.Collectors; @@ -1335,21 +1336,21 @@ void groupingByGetClass() { //language=java java( """ - import java.util.*; - import java.util.stream.*; - - class Animal {} - class Cat extends Animal {} - class Dog extends Animal {} + import java.util.*; + import java.util.stream.*; + + class Animal {} + class Cat extends Animal {} + class Dog extends Animal {} - class Test { - public void groupOnGetClass() { - List animals = List.of(new Cat(), new Dog()); - Map, List> collect; - collect = animals.stream().collect(Collectors.groupingBy(a -> a.getClass())); + class Test { + public void groupOnGetClass() { + List animals = List.of(new Cat(), new Dog()); + Map, List> collect; + collect = animals.stream().collect(Collectors.groupingBy(a -> a.getClass())); + } } - } - """ + """ ) ); } From 79f4dfaf4dd5a283cfeaeba3e51c70d04a40a70f Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 8 Aug 2024 09:21:17 -0700 Subject: [PATCH 088/183] Prevent EqualsAvoidsNull from rewriting aString.equals(null) into null.equals(aString) --- .../staticanalysis/EqualsAvoidsNullVisitor.java | 7 ++++--- .../staticanalysis/EqualsAvoidsNullTest.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index 30d28769a..f6703c82b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -47,15 +47,16 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) } if ((STRING_EQUALS.matches(m) || (!Boolean.TRUE.equals(style.getIgnoreEqualsIgnoreCase()) && STRING_EQUALS_IGNORE_CASE.matches(m))) && - m.getArguments().get(0) instanceof J.Literal && - !(m.getSelect() instanceof J.Literal)) { + m.getArguments().get(0) instanceof J.Literal && + m.getArguments().get(0).getType() != JavaType.Primitive.Null && + !(m.getSelect() instanceof J.Literal)) { Tree parent = getCursor().getParentTreeCursor().getValue(); if (parent instanceof J.Binary) { J.Binary binary = (J.Binary) parent; if (binary.getOperator() == J.Binary.Type.And && binary.getLeft() instanceof J.Binary) { J.Binary potentialNullCheck = (J.Binary) binary.getLeft(); if ((isNullLiteral(potentialNullCheck.getLeft()) && matchesSelect(potentialNullCheck.getRight(), m.getSelect())) || - (isNullLiteral(potentialNullCheck.getRight()) && matchesSelect(potentialNullCheck.getLeft(), m.getSelect()))) { + (isNullLiteral(potentialNullCheck.getRight()) && matchesSelect(potentialNullCheck.getLeft(), m.getSelect()))) { doAfterVisit(new RemoveUnnecessaryNullCheck<>(binary)); } } diff --git a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java index 5f87cbe42..bc370a862 100644 --- a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java @@ -84,4 +84,19 @@ public class A { ) ); } + + @Test + void nullLiteral() { + rewriteRun( + //language=java + java(""" + public class A { + void foo(String s) { + if(s.equals(null)) { + } + } + } + """) + ); + } } From 567dd0da57d44d8c8c59dc3819ae3b83dd184d5c Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 8 Aug 2024 09:37:45 -0700 Subject: [PATCH 089/183] Replace aString.equals(null) with aString == null --- .../EqualsAvoidsNullVisitor.java | 31 ++++++++++++------- .../staticanalysis/EqualsAvoidsNullTest.java | 9 ++++++ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index f6703c82b..f2d6bfb2b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -19,36 +19,35 @@ import lombok.Value; import org.openrewrite.Tree; import org.openrewrite.internal.lang.Nullable; -import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.style.EqualsAvoidsNullStyle; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.JavaType; -import org.openrewrite.java.tree.Space; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; import static java.util.Collections.singletonList; @Value @EqualsAndHashCode(callSuper = false) -public class EqualsAvoidsNullVisitor

    extends JavaIsoVisitor

    { +public class EqualsAvoidsNullVisitor

    extends JavaVisitor

    { private static final MethodMatcher STRING_EQUALS = new MethodMatcher("String equals(java.lang.Object)"); private static final MethodMatcher STRING_EQUALS_IGNORE_CASE = new MethodMatcher("String equalsIgnoreCase(java.lang.String)"); EqualsAvoidsNullStyle style; @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) { - J.MethodInvocation m = super.visitMethodInvocation(method, p); - + public J visitMethodInvocation(J.MethodInvocation method, P p) { + J j = super.visitMethodInvocation(method, p); + if (!(j instanceof J.MethodInvocation)) { + return j; + } + J.MethodInvocation m = (J.MethodInvocation) j; if (m.getSelect() == null) { return m; } if ((STRING_EQUALS.matches(m) || (!Boolean.TRUE.equals(style.getIgnoreEqualsIgnoreCase()) && STRING_EQUALS_IGNORE_CASE.matches(m))) && m.getArguments().get(0) instanceof J.Literal && - m.getArguments().get(0).getType() != JavaType.Primitive.Null && !(m.getSelect() instanceof J.Literal)) { Tree parent = getCursor().getParentTreeCursor().getValue(); if (parent instanceof J.Binary) { @@ -62,8 +61,16 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) } } - m = m.withSelect(((J.Literal) m.getArguments().get(0)).withPrefix(m.getSelect().getPrefix())) - .withArguments(singletonList(m.getSelect().withPrefix(Space.EMPTY))); + if (m.getArguments().get(0).getType() == JavaType.Primitive.Null) { + return new J.Binary(Tree.randomId(), m.getPrefix(), Markers.EMPTY, + m.getSelect(), + JLeftPadded.build(J.Binary.Type.Equal).withBefore(Space.SINGLE_SPACE), + m.getArguments().get(0).withPrefix(Space.SINGLE_SPACE), + JavaType.Primitive.Boolean); + } else { + m = m.withSelect(((J.Literal) m.getArguments().get(0)).withPrefix(m.getSelect().getPrefix())) + .withArguments(singletonList(m.getSelect().withPrefix(Space.EMPTY))); + } } return m; diff --git a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java index bc370a862..68bb55236 100644 --- a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java @@ -96,6 +96,15 @@ void foo(String s) { } } } + """, + """ + + public class A { + void foo(String s) { + if(s == null) { + } + } + } """) ); } From f74a5071f0fa12193433e6311b7e75afb606d10b Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Wed, 14 Aug 2024 09:17:13 -0500 Subject: [PATCH 090/183] refactor: Update Gradle wrapper (#324) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.jar | Bin 43504 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 2c3521197d7c4586c843d1d3e9090525f1898cde..a4b76b9530d66f5e68d973ea569d8e19de379189 100644 GIT binary patch delta 3990 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 02abc8b4c..fc4850027 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip -distributionSha256Sum=d725d707bfabd4dfdc958c624003b3c80accc03f7037b5122c4b1d0ef15cecab +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip +distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 5555d0fd0b65785ba0a4fe54bda94729bf1b2257 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Wed, 14 Aug 2024 22:28:21 -0400 Subject: [PATCH 091/183] MoveFieldAnnotationToType --- .../java/MoveFieldAnnotationToType.java | 140 ++++++++++++++++++ .../staticanalysis/java/package-info.java | 19 +++ .../java/MoveFieldAnnotationToTypeTest.java | 90 +++++++++++ 3 files changed, 249 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java create mode 100644 src/main/java/org/openrewrite/staticanalysis/java/package-info.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java new file mode 100644 index 000000000..6d066ccb7 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -0,0 +1,140 @@ +/* + * 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.java; + +import lombok.EqualsAndHashCode; +import lombok.Value; +import org.jspecify.annotations.Nullable; +import org.openrewrite.*; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.internal.StringUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.*; + +import java.util.concurrent.atomic.AtomicReference; +import java.util.regex.Pattern; + +@Value +@EqualsAndHashCode(callSuper = false) +public class MoveFieldAnnotationToType extends Recipe { + + @Option(displayName = "Annotation type", + description = "The type of annotation to move.", + example = "org.openrewrite..*", + required = false) + @Nullable + String annotationType; + + @Override + public String getDisplayName() { + return "Move annotation to type instead of field"; + } + + @Override + public String getDescription() { + return "Annotations that could be applied to either a field or a " + + "type are better applied to the type, because similar annotations " + + "may be more restrictive, leading to compile errors like " + + "'scoping construct cannot be annotated with type-use annotation' " + + "when migrating later."; + } + + @Override + public TreeVisitor getVisitor() { + String annotationTypeInput = annotationType == null ? "org.openrewrite..*" : annotationType; + + return Preconditions.check(new UsesType<>(annotationTypeInput, null), new JavaIsoVisitor() { + final Pattern typePattern = Pattern.compile(StringUtils.aspectjNameToPattern(annotationTypeInput)); + + @Override + public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { + J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); + + if (isStaticInnerClass(mv.getTypeExpression())) { + AtomicReference matchingAnnotation = new AtomicReference<>(); + mv = mv.withLeadingAnnotations(ListUtils.map(mv.getLeadingAnnotations(), a -> { + if (matchesType(a)) { + matchingAnnotation.set(a); + return null; + } + return a; + })); + if (mv.getTypeExpression() != null && matchingAnnotation.get() != null) { + TypeTree te = annotateInnerClass(mv.getTypeExpression(), matchingAnnotation.get()); + mv = mv.withTypeExpression(te); + // auto format should handle this, but evidently doesn't + if (mv.getLeadingAnnotations().isEmpty()) { + mv = mv.withTypeExpression(te.withPrefix(te.getPrefix().withWhitespace(""))); + } + mv = autoFormat(mv, mv.getTypeExpression(), ctx, getCursor().getParentOrThrow()); + } + } + return mv; + } + + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); + + if (isStaticInnerClass(md.getReturnTypeExpression())) { + AtomicReference matchingAnnotation = new AtomicReference<>(); + md = md.withLeadingAnnotations(ListUtils.map(md.getLeadingAnnotations(), a -> { + if (matchesType(a)) { + matchingAnnotation.set(a); + return null; + } + return a; + })); + if (md.getReturnTypeExpression() != null && matchingAnnotation.get() != null) { + TypeTree te = annotateInnerClass(md.getReturnTypeExpression(), matchingAnnotation.get()); + md = md.withReturnTypeExpression(te); + // auto format should handle this, but evidently doesn't + if (md.getLeadingAnnotations().isEmpty()) { + md = md.withReturnTypeExpression(te.withPrefix(te.getPrefix().withWhitespace(""))); + } + md = autoFormat(md, md.getReturnTypeExpression(), ctx, getCursor().getParentOrThrow()); + } + } + return md; + } + + private boolean matchesType(J.Annotation ann) { + JavaType.FullyQualified fq = TypeUtils.asFullyQualified(ann.getType()); + return fq != null && typePattern.matcher(fq.getFullyQualifiedName()).matches(); + } + + private boolean isStaticInnerClass(@Nullable TypeTree tree) { + if (!(tree instanceof J.FieldAccess)) { + return false; + } + JavaType.FullyQualified fq = TypeUtils.asFullyQualified(tree.getType()); + return fq != null && fq.getOwningClass() != null && + fq.hasFlags(Flag.Static); + } + + private TypeTree annotateInnerClass(TypeTree staticInnerClassRef, J.Annotation annotation) { + J.FieldAccess s = (J.FieldAccess) staticInnerClassRef; + s = s.withName(s.getName().withAnnotations( + ListUtils.concat(annotation.withPrefix(Space.EMPTY), s.getName().getAnnotations()))); + if (s.getName().getPrefix().getWhitespace().isEmpty()) { + s = s.withName(s.getName().withPrefix(s.getName().getPrefix().withWhitespace(" "))); + } + return s; + } + }); + } +} diff --git a/src/main/java/org/openrewrite/staticanalysis/java/package-info.java b/src/main/java/org/openrewrite/staticanalysis/java/package-info.java new file mode 100644 index 000000000..05807bdc6 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/java/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2020 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. + */ +@NonNullApi +package org.openrewrite.staticanalysis.java; + +import org.openrewrite.internal.lang.NonNullApi; diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java new file mode 100644 index 000000000..ff1020d00 --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -0,0 +1,90 @@ +/* + * 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.java; + +import org.junit.jupiter.api.Test; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +@SuppressWarnings("deprecation") +public class MoveFieldAnnotationToTypeTest implements RewriteTest { + + @Test + void alreadyOnInnerClass() { + rewriteRun( + spec -> spec.recipe(new MoveFieldAnnotationToType("*..*")), + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + Xml.@Nullable Tag tag; + } + """ + ) + ); + } + + @Test + void fieldAnnotation() { + rewriteRun( + spec -> spec.recipe(new MoveFieldAnnotationToType("org.openrewrite..*")), + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + @Nullable Xml.Tag tag; + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + Xml.@Nullable Tag tag; + } + """ + ) + ); + } + + @Test + void methodAnnotation() { + rewriteRun( + spec -> spec.recipe(new MoveFieldAnnotationToType(null)), + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + interface Test { + @Nullable Xml.Tag tag(); + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + interface Test { + Xml.@Nullable Tag tag(); + } + """ + ) + ); + } +} From b48d17ec1caefbe392cd6300156d4112a3ce2d33 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Wed, 14 Aug 2024 23:17:03 -0400 Subject: [PATCH 092/183] Handle annotations on fields/methods that contain modifiers before the annotation --- .../java/MoveFieldAnnotationToType.java | 26 +++++++++ .../java/MoveFieldAnnotationToTypeTest.java | 55 ++++++++++++++++++- 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index 6d066ccb7..d1bfd183e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -60,6 +60,32 @@ public TreeVisitor getVisitor() { return Preconditions.check(new UsesType<>(annotationTypeInput, null), new JavaIsoVisitor() { final Pattern typePattern = Pattern.compile(StringUtils.aspectjNameToPattern(annotationTypeInput)); + @Override + public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, ExecutionContext ctx) { + J.AnnotatedType at = super.visitAnnotatedType(annotatedType, ctx); + + if (isStaticInnerClass(at.getTypeExpression())) { + AtomicReference matchingAnnotation = new AtomicReference<>(); + at = at.withAnnotations(ListUtils.map(at.getAnnotations(), a -> { + if (matchesType(a)) { + matchingAnnotation.set(a); + return null; + } + return a; + })); + if (at.getTypeExpression() != null && matchingAnnotation.get() != null) { + TypeTree te = annotateInnerClass(at.getTypeExpression(), matchingAnnotation.get()); + at = at.withTypeExpression(te); + // auto format should handle this, but evidently doesn't + if (at.getAnnotations().isEmpty() && !(getCursor().getParentTreeCursor().getValue() instanceof J.MethodDeclaration)) { + at = at.withTypeExpression(te.withPrefix(te.getPrefix().withWhitespace(""))); + } + at = autoFormat(at, at.getTypeExpression(), ctx, getCursor().getParentOrThrow()); + } + } + return at; + } + @Override public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index ff1020d00..d6d804a37 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis.java; import org.junit.jupiter.api.Test; +import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; @@ -23,10 +24,14 @@ @SuppressWarnings("deprecation") public class MoveFieldAnnotationToTypeTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new MoveFieldAnnotationToType("org.openrewrite..*")); + } + @Test void alreadyOnInnerClass() { rewriteRun( - spec -> spec.recipe(new MoveFieldAnnotationToType("*..*")), //language=java java( """ @@ -43,7 +48,6 @@ class Test { @Test void fieldAnnotation() { rewriteRun( - spec -> spec.recipe(new MoveFieldAnnotationToType("org.openrewrite..*")), //language=java java( """ @@ -64,10 +68,32 @@ class Test { ); } + @Test + void publicFieldAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + public @Nullable Xml.Tag tag; + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + public Xml.@Nullable Tag tag; + } + """ + ) + ); + } + @Test void methodAnnotation() { rewriteRun( - spec -> spec.recipe(new MoveFieldAnnotationToType(null)), //language=java java( """ @@ -87,4 +113,27 @@ interface Test { ) ); } + + @Test + void publicMethodAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + interface Test { + synchronized @Nullable Xml.Tag tag(); + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + interface Test { + synchronized Xml.@Nullable Tag tag(); + } + """ + ) + ); + } } From e83ddf9a44c5202d8ba656cbdb14f3c219d7c8b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schn=C3=A9ider?= Date: Thu, 15 Aug 2024 00:55:45 -0400 Subject: [PATCH 093/183] Migrate to JSpecify from OpenRewrite JSR-305 meta-annotations (#327) * Migrate to JSpecify from OpenRewrite JSR-305 meta-annotations Use this link to re-run the recipe: https://app.moderne.io/builder/gW3xaSLWV?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../AddSerialAnnotationToserialVersionUID.java | 2 +- .../staticanalysis/AddSerialVersionUidToSerializable.java | 2 +- .../staticanalysis/AtomicPrimitiveEqualsUsesGet.java | 2 +- .../staticanalysis/BigDecimalRoundingConstantsToEnums.java | 2 +- .../CaseInsensitiveComparisonsDoNotChangeCase.java | 2 +- .../staticanalysis/ChainStringBuilderAppendCalls.java | 2 +- .../staticanalysis/CombineSemanticallyEqualCatchBlocks.java | 2 +- .../openrewrite/staticanalysis/ControlFlowIndentation.java | 4 ++-- .../staticanalysis/DeclarationSiteTypeVariance.java | 2 +- .../org/openrewrite/staticanalysis/DefaultComesLast.java | 2 +- .../java/org/openrewrite/staticanalysis/EmptyBlock.java | 2 +- .../org/openrewrite/staticanalysis/EmptyBlockVisitor.java | 2 +- .../org/openrewrite/staticanalysis/EqualsAvoidsNull.java | 2 +- .../openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java | 2 +- .../staticanalysis/ExplicitCharsetOnStringGetBytes.java | 2 +- .../openrewrite/staticanalysis/ExplicitInitialization.java | 2 +- .../staticanalysis/ExplicitLambdaArgumentTypes.java | 2 +- .../java/org/openrewrite/staticanalysis/FallThrough.java | 2 +- .../org/openrewrite/staticanalysis/FinalClassVisitor.java | 2 +- .../openrewrite/staticanalysis/FinalizePrivateFields.java | 4 ++-- .../staticanalysis/ForLoopIncrementInUpdate.java | 2 +- .../java/org/openrewrite/staticanalysis/HiddenField.java | 2 +- .../staticanalysis/HideUtilityClassConstructor.java | 2 +- .../java/org/openrewrite/staticanalysis/InlineVariable.java | 2 +- .../openrewrite/staticanalysis/InstanceOfPatternMatch.java | 2 +- .../org/openrewrite/staticanalysis/JavaElementFactory.java | 6 +++--- .../org/openrewrite/staticanalysis/LowercasePackage.java | 2 +- .../org/openrewrite/staticanalysis/MethodNameCasing.java | 2 +- .../staticanalysis/MissingOverrideAnnotation.java | 2 +- .../java/org/openrewrite/staticanalysis/NeedBraces.java | 2 +- .../NoPrimitiveWrappersForToStringOrCompareTo.java | 2 +- .../staticanalysis/NullableOnMethodReturnType.java | 6 +++--- .../java/org/openrewrite/staticanalysis/OperatorWrap.java | 2 +- .../staticanalysis/ReferentialEqualityToObjectEquals.java | 4 ++-- .../openrewrite/staticanalysis/RemoveMethodCallVisitor.java | 6 +++--- .../staticanalysis/RemoveUnusedLocalVariables.java | 2 +- .../staticanalysis/RemoveUnusedPrivateFields.java | 2 +- .../staticanalysis/RenameExceptionInEmptyCatch.java | 2 +- .../org/openrewrite/staticanalysis/RenameToCamelCase.java | 4 ++-- .../staticanalysis/ReplaceDuplicateStringLiterals.java | 2 +- .../staticanalysis/ReplaceLambdaWithMethodReference.java | 2 +- .../staticanalysis/ReplaceRedundantFormatWithPrintf.java | 2 +- .../staticanalysis/SimplifyBooleanExpression.java | 2 +- .../openrewrite/staticanalysis/SimplifyBooleanReturn.java | 4 ++-- .../openrewrite/staticanalysis/SimplifyCompoundVisitor.java | 2 +- .../staticanalysis/SimplifyConsecutiveAssignments.java | 2 +- .../staticanalysis/SimplifyConstantIfBranchExecution.java | 2 +- .../staticanalysis/SimplifyDurationCreationUnits.java | 2 +- .../staticanalysis/TernaryOperatorsShouldNotBeNested.java | 4 ++-- .../org/openrewrite/staticanalysis/TypecastParenPad.java | 2 +- .../org/openrewrite/staticanalysis/UnnecessaryThrows.java | 6 +++--- .../staticanalysis/UpperCaseLiteralSuffixes.java | 2 +- .../java/org/openrewrite/staticanalysis/UseAsBuilder.java | 2 +- .../openrewrite/staticanalysis/UseCollectionInterfaces.java | 2 +- .../org/openrewrite/staticanalysis/UseDiamondOperator.java | 2 +- .../staticanalysis/UseJavaStyleArrayDeclarations.java | 2 +- .../staticanalysis/UseLambdaForFunctionalInterface.java | 6 +++--- .../staticanalysis/groovy/GroovyFileChecker.java | 2 +- .../openrewrite/staticanalysis/java/JavaFileChecker.java | 2 +- .../staticanalysis/kotlin/KotlinFileChecker.java | 2 +- .../java/org/openrewrite/staticanalysis/package-info.java | 4 ++-- .../org/openrewrite/staticanalysis/groovy/package-info.java | 4 ++-- 62 files changed, 80 insertions(+), 80 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java index 846685341..ec9ac01fd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.NonNull; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.NonNull; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.search.FindAnnotations; diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java index 6b75b8511..98092bf33 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.tree.J; diff --git a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java index 99649ef95..252446436 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java +++ b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java b/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java index 077f06070..fc79687c4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java +++ b/src/main/java/org/openrewrite/staticanalysis/BigDecimalRoundingConstantsToEnums.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java b/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java index 46d1967af..672a4c8f5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java @@ -15,12 +15,12 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; diff --git a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java index 6907d8f4a..2d1495df4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java +++ b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaParser; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java index b68c76cab..9976e1c21 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java +++ b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.search.SemanticallyEqual; diff --git a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java index 9ea4c749d..1dfa7271e 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java +++ b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.format.TabsAndIndentsVisitor; import org.openrewrite.java.style.IntelliJ; @@ -108,7 +108,7 @@ boolean shouldReformat(J.If s) { return shouldReformat(s.getElsePart()); } - boolean shouldReformat(@Nullable J.If.Else s) { + boolean shouldReformat(J.If.@Nullable Else s) { if (s == null) { return false; } diff --git a/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java b/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java index 9d51a63d4..127db40de 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java +++ b/src/main/java/org/openrewrite/staticanalysis/DeclarationSiteTypeVariance.java @@ -17,10 +17,10 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; diff --git a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java index bf04bcf8d..27f9e3da8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java +++ b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.DefaultComesLastStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java b/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java index 1604096c8..351d955f9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java +++ b/src/main/java/org/openrewrite/staticanalysis/EmptyBlock.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.EmptyBlockStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java index cdd8764f9..44460fc09 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EmptyBlockVisitor.java @@ -17,8 +17,8 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.DeleteStatement; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java index f8f07dcc3..9ba2b97f6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.EqualsAvoidsNullStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index f2d6bfb2b..420d4eacb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -17,8 +17,8 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.style.EqualsAvoidsNullStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitCharsetOnStringGetBytes.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitCharsetOnStringGetBytes.java index 0e24c4ade..e78c817ea 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitCharsetOnStringGetBytes.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitCharsetOnStringGetBytes.java @@ -17,8 +17,8 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java index 4424117d1..3f2db4102 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitialization.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.ExplicitInitializationStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java index eddb95980..84d1eb922 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java @@ -15,12 +15,12 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; 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.*; import org.openrewrite.marker.Markers; diff --git a/src/main/java/org/openrewrite/staticanalysis/FallThrough.java b/src/main/java/org/openrewrite/staticanalysis/FallThrough.java index 130feb734..7a20d61f1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FallThrough.java +++ b/src/main/java/org/openrewrite/staticanalysis/FallThrough.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.FallThroughStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java index b481e2a81..e03c0b2e8 100755 --- a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Tree; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java index 914ae99cf..7420b554a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java @@ -17,9 +17,9 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.service.AnnotationService; import org.openrewrite.java.tree.*; @@ -306,7 +306,7 @@ private static class FindLastIdentifier extends JavaIsoVisitor ids = new FindLastIdentifier().reduce(j, new ArrayList<>()); return !ids.isEmpty() ? ids.get(ids.size() - 1) : null; } diff --git a/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java b/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java index 8a635ac9f..57f6370f1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java +++ b/src/main/java/org/openrewrite/staticanalysis/ForLoopIncrementInUpdate.java @@ -15,12 +15,12 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; 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.JavaVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.Space; diff --git a/src/main/java/org/openrewrite/staticanalysis/HiddenField.java b/src/main/java/org/openrewrite/staticanalysis/HiddenField.java index 49873b765..0d0a5459a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HiddenField.java +++ b/src/main/java/org/openrewrite/staticanalysis/HiddenField.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.HiddenFieldStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java index 9f04f9b8c..5a36b54fb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructor.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.HideUtilityClassConstructorStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java index 6a0fd9c47..5757f1c4f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java +++ b/src/main/java/org/openrewrite/staticanalysis/InlineVariable.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; 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.Expression; import org.openrewrite.java.tree.J; diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 8bd449292..c8b207e9b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -18,8 +18,8 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.VariableNameUtils; import org.openrewrite.java.search.SemanticallyEqual; diff --git a/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java b/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java index 26a4283bd..46bce8afd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java +++ b/src/main/java/org/openrewrite/staticanalysis/JavaElementFactory.java @@ -15,7 +15,7 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.internal.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; @@ -120,7 +120,7 @@ static J.MemberReference newInstanceMethodReference(Expression containing, Strin ); } - static @Nullable J.FieldAccess newClassLiteral(@Nullable JavaType type, boolean qualified) { + static J.@Nullable FieldAccess newClassLiteral(@Nullable JavaType type, boolean qualified) { JavaType.Class classType = getClassType(type); if (classType == null) { return null; @@ -141,7 +141,7 @@ static J.MemberReference newInstanceMethodReference(Expression containing, Strin ); } - private static @Nullable JavaType.Class getClassType(@Nullable JavaType type) { + private static JavaType.@Nullable Class getClassType(@Nullable JavaType type) { if (type instanceof JavaType.Class) { JavaType.Class classType = (JavaType.Class) type; if (classType.getFullyQualifiedName().equals("java.lang.Class")) { diff --git a/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java b/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java index f465ccb92..bc4ae4866 100644 --- a/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java +++ b/src/main/java/org/openrewrite/staticanalysis/LowercasePackage.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.ChangePackage; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.J; diff --git a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java index cca1935db..3efa9acd5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java +++ b/src/main/java/org/openrewrite/staticanalysis/MethodNameCasing.java @@ -17,10 +17,10 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.NameCaseConvention; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.ChangeMethodName; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java b/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java index c9c157b0c..4bf09e61f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java +++ b/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java @@ -17,8 +17,8 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.AnnotationMatcher; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; diff --git a/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java b/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java index 9c9adf6ed..925f4d763 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java +++ b/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.NeedBracesStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java index 37e4b4848..62cc54506 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; diff --git a/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java b/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java index 8c63a1ee2..b0eafde14 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NullableOnMethodReturnType.java @@ -20,7 +20,6 @@ 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; @@ -28,6 +27,7 @@ import java.util.Collections; +import static java.util.Objects.requireNonNull; import static org.openrewrite.java.trait.Traits.annotated; public class NullableOnMethodReturnType extends Recipe { @@ -48,7 +48,7 @@ public TreeVisitor getVisitor() { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - return annotated(Nullable.class) + return requireNonNull(annotated("*..Nullable") .lower(getCursor()) .findFirst() .map(nullable -> { @@ -71,7 +71,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex } return m2; }) - .orElse(m); + .orElse(m)); } }; } diff --git a/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java b/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java index 5233e6db2..2e3297992 100755 --- a/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java +++ b/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.OperatorWrapStyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java b/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java index f1ce4e83c..c21037697 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; @@ -64,7 +64,7 @@ public TreeVisitor getVisitor() { private static class ReferentialEqualityToObjectEqualityVisitor extends JavaVisitor { private static final JavaType TYPE_OBJECT = JavaType.buildType("java.lang.Object"); - private static J.MethodInvocation asEqualsMethodInvocation(J.Binary binary, @Nullable JavaType.FullyQualified selectType) { + private static J.MethodInvocation asEqualsMethodInvocation(J.Binary binary, JavaType.@Nullable FullyQualified selectType) { return new J.MethodInvocation( Tree.randomId(), binary.getPrefix(), diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java index 7423225d2..774c63000 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java @@ -16,7 +16,7 @@ package org.openrewrite.staticanalysis; import lombok.AllArgsConstructor; -import org.openrewrite.internal.lang.Nullable; +import org.jspecify.annotations.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.tree.Expression; @@ -47,13 +47,13 @@ public class RemoveMethodCallVisitor

    extends JavaIsoVisitor

    { @SuppressWarnings("NullableProblems") @Override - public @Nullable J.NewClass visitNewClass(J.NewClass newClass, P p) { + public J.@Nullable NewClass visitNewClass(J.NewClass newClass, P p) { return visitMethodCall(newClass, () -> super.visitNewClass(newClass, p)); } @SuppressWarnings("NullableProblems") @Override - public @Nullable J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) { + public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) { return visitMethodCall(method, () -> super.visitMethodInvocation(method, p)); } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index 5f297ed16..b8d15e14b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -17,9 +17,9 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.DeleteStatement; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java index 8e8cfd617..b3872fcea 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java @@ -17,11 +17,11 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.AnnotationMatcher; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameExceptionInEmptyCatch.java b/src/main/java/org/openrewrite/staticanalysis/RenameExceptionInEmptyCatch.java index c1fc9777b..68305db3e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameExceptionInEmptyCatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameExceptionInEmptyCatch.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.RenameVariable; import org.openrewrite.java.tree.J; diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java index 4b4463951..90c587470 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameToCamelCase.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.RenameVariable; import org.openrewrite.java.tree.J; @@ -85,7 +85,7 @@ protected String computeKey(String identifier, J context) { return identifier; } - protected @Nullable JavaType.Variable getFieldType(J tree) { + protected JavaType.@Nullable Variable getFieldType(J tree) { if (tree instanceof J.Identifier) { return ((J.Identifier) tree).getFieldType(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 7e9d8d65a..3932c31a3 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -18,9 +18,9 @@ import lombok.EqualsAndHashCode; import lombok.Value; import lombok.experimental.NonFinal; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.StringUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.*; import org.openrewrite.java.marker.JavaSourceSet; import org.openrewrite.java.search.UsesType; diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java index 873117f44..3690ebb20 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceLambdaWithMethodReference.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.service.ImportService; diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintf.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintf.java index a00dc1804..c5a9e0060 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintf.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceRedundantFormatWithPrintf.java @@ -15,12 +15,12 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java index 5216ad9bc..b778f19b1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.cleanup.SimplifyBooleanExpressionVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.kotlin.marker.IsNullSafe; diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java index 12d34eafa..f42c5d590 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.DeleteStatement; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; @@ -192,7 +192,7 @@ private Optional getReturnExprIfOnlyStatementInElseThen(J.If iff2) { return Optional.empty(); } - private boolean hasElseWithComment(@Nullable J.If.Else else_) { + private boolean hasElseWithComment(J.If.@Nullable Else else_) { if (else_ == null || else_.getBody() == null) { return false; } diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java index ef512a92b..e386db1af 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Tree; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.cleanup.SimplifyBooleanExpressionVisitor; import org.openrewrite.java.tree.Expression; diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java index 9579a4934..0e4a10c86 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConsecutiveAssignments.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.cleanup.UnnecessaryParenthesesVisitor; diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java index 960c98c1e..0c634cd66 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.SourceFile; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.cleanup.SimplifyBooleanExpressionVisitor; import org.openrewrite.java.cleanup.UnnecessaryParenthesesVisitor; diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java index 336dde90c..9b6d01aaa 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyDurationCreationUnits.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; diff --git a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java index d8ec002ee..5036d36d6 100644 --- a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java +++ b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.marker.JavaVersion; @@ -299,7 +299,7 @@ private Optional findConditionIdentifier(final J.Ternary ternary) } - private static @Nullable J.Identifier xorVariable(J first, J second) { + private static J.@Nullable Identifier xorVariable(J first, J second) { J.Identifier result = null; if (isVariable(first) && isVariable(second)) { return null; diff --git a/src/main/java/org/openrewrite/staticanalysis/TypecastParenPad.java b/src/main/java/org/openrewrite/staticanalysis/TypecastParenPad.java index d1b360f80..41b2cf9b4 100755 --- a/src/main/java/org/openrewrite/staticanalysis/TypecastParenPad.java +++ b/src/main/java/org/openrewrite/staticanalysis/TypecastParenPad.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.format.SpacesVisitor; import org.openrewrite.java.style.Checkstyle; diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java index 253cdf202..76c6be992 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java @@ -15,12 +15,12 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; 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.JavaVisitor; import org.openrewrite.java.JavadocVisitor; @@ -113,7 +113,7 @@ public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { return super.visitNewClass(newClass, ctx); } - private void removeThrownTypes(@Nullable JavaType.Method type) { + private void removeThrownTypes(JavaType.@Nullable Method type) { if (type != null) { for (JavaType.FullyQualified thrownException : type.getThrownExceptions()) { unusedThrows.removeIf(t -> TypeUtils.isAssignableTo(t, thrownException)); @@ -140,7 +140,7 @@ private void removeThrownTypes(@Nullable JavaType.Method type) { } - private Set findExceptionCandidates(@Nullable J.MethodDeclaration method) { + private Set findExceptionCandidates(J.@Nullable MethodDeclaration method) { if (method == null || method.getMethodType() == null || method.isAbstract()) { return Collections.emptySet(); diff --git a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java index 92d4ffa01..8a9bbc899 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java +++ b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.J; diff --git a/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java b/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java index d9334c68d..681361baa 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseAsBuilder.java @@ -17,9 +17,9 @@ import lombok.EqualsAndHashCode; import lombok.Value; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesMethod; diff --git a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java index a087b51b0..e31d3df49 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java @@ -15,10 +15,10 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.groovy.tree.G; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; diff --git a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java index eb5c6a093..1302e9ecf 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseDiamondOperator.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.UsesJavaVersion; import org.openrewrite.java.tree.*; diff --git a/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java b/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java index 2ee425af3..d4faa321c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java @@ -15,11 +15,11 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; 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.VariableDeclarations; import org.openrewrite.java.tree.JLeftPadded; diff --git a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java index c85e0978e..693a6c15f 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.*; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; @@ -200,7 +200,7 @@ private boolean methodArgumentRequiresCast(J.Lambda lambda, MethodCall method, i return hasGenerics(lambda); } - private boolean areMethodsAmbiguous(@Nullable JavaType.Method m1, @Nullable JavaType.Method m2) { + private boolean areMethodsAmbiguous(JavaType.@Nullable Method m1, JavaType.@Nullable Method m2) { if (m1 == null || m2 == null) { return false; } @@ -426,7 +426,7 @@ public J visitMethodInvocation(J.MethodInvocation method, AtomicBoolean atomicBo } // TODO consider moving to TypeUtils - private static @Nullable JavaType.Method getSamCompatible(@Nullable JavaType type) { + private static JavaType.@Nullable Method getSamCompatible(@Nullable JavaType type) { JavaType.Method sam = null; JavaType.FullyQualified fullyQualified = TypeUtils.asFullyQualified(type); if (fullyQualified == null) { diff --git a/src/main/java/org/openrewrite/staticanalysis/groovy/GroovyFileChecker.java b/src/main/java/org/openrewrite/staticanalysis/groovy/GroovyFileChecker.java index 2476b20ac..acfe60807 100644 --- a/src/main/java/org/openrewrite/staticanalysis/groovy/GroovyFileChecker.java +++ b/src/main/java/org/openrewrite/staticanalysis/groovy/GroovyFileChecker.java @@ -15,10 +15,10 @@ */ package org.openrewrite.staticanalysis.groovy; +import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; import org.openrewrite.groovy.tree.G; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.marker.SearchResult; /** diff --git a/src/main/java/org/openrewrite/staticanalysis/java/JavaFileChecker.java b/src/main/java/org/openrewrite/staticanalysis/java/JavaFileChecker.java index bf5edaf68..e7e05a8c7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/JavaFileChecker.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/JavaFileChecker.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis.java; +import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.tree.J; import org.openrewrite.marker.SearchResult; diff --git a/src/main/java/org/openrewrite/staticanalysis/kotlin/KotlinFileChecker.java b/src/main/java/org/openrewrite/staticanalysis/kotlin/KotlinFileChecker.java index 5ceb261c4..ccecc2edc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/kotlin/KotlinFileChecker.java +++ b/src/main/java/org/openrewrite/staticanalysis/kotlin/KotlinFileChecker.java @@ -15,9 +15,9 @@ */ package org.openrewrite.staticanalysis.kotlin; +import org.jspecify.annotations.Nullable; import org.openrewrite.Tree; import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.lang.Nullable; import org.openrewrite.kotlin.tree.K; import org.openrewrite.marker.SearchResult; diff --git a/src/main/java/org/openrewrite/staticanalysis/package-info.java b/src/main/java/org/openrewrite/staticanalysis/package-info.java index ba8619542..2ffda05b2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/package-info.java +++ b/src/main/java/org/openrewrite/staticanalysis/package-info.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@NonNullApi +@NullMarked package org.openrewrite.staticanalysis; -import org.openrewrite.internal.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/org/openrewrite/staticanalysis/groovy/package-info.java b/src/test/java/org/openrewrite/staticanalysis/groovy/package-info.java index 9cd948e05..d0fe440f2 100644 --- a/src/test/java/org/openrewrite/staticanalysis/groovy/package-info.java +++ b/src/test/java/org/openrewrite/staticanalysis/groovy/package-info.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@NonNullApi +@NullMarked package org.openrewrite.staticanalysis.groovy; -import org.openrewrite.internal.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; From 43839f4ddebf19c6767182941265abdc79db1e2b Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Thu, 15 Aug 2024 09:27:14 -0400 Subject: [PATCH 094/183] More cases in MoveFieldAnnotationToType --- .../java/MoveFieldAnnotationToType.java | 41 ++++++++++------- .../java/MoveFieldAnnotationToTypeTest.java | 46 +++++++++++++++++++ 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index d1bfd183e..b38525d3e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -64,7 +64,7 @@ public TreeVisitor getVisitor() { public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, ExecutionContext ctx) { J.AnnotatedType at = super.visitAnnotatedType(annotatedType, ctx); - if (isStaticInnerClass(at.getTypeExpression())) { + if (isQualifiedClass(at.getTypeExpression())) { AtomicReference matchingAnnotation = new AtomicReference<>(); at = at.withAnnotations(ListUtils.map(at.getAnnotations(), a -> { if (matchesType(a)) { @@ -90,7 +90,7 @@ public J.AnnotatedType visitAnnotatedType(J.AnnotatedType annotatedType, Executi public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations multiVariable, ExecutionContext ctx) { J.VariableDeclarations mv = super.visitVariableDeclarations(multiVariable, ctx); - if (isStaticInnerClass(mv.getTypeExpression())) { + if (isQualifiedClass(mv.getTypeExpression())) { AtomicReference matchingAnnotation = new AtomicReference<>(); mv = mv.withLeadingAnnotations(ListUtils.map(mv.getLeadingAnnotations(), a -> { if (matchesType(a)) { @@ -116,7 +116,7 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); - if (isStaticInnerClass(md.getReturnTypeExpression())) { + if (isQualifiedClass(md.getReturnTypeExpression())) { AtomicReference matchingAnnotation = new AtomicReference<>(); md = md.withLeadingAnnotations(ListUtils.map(md.getLeadingAnnotations(), a -> { if (matchesType(a)) { @@ -143,23 +143,30 @@ private boolean matchesType(J.Annotation ann) { return fq != null && typePattern.matcher(fq.getFullyQualifiedName()).matches(); } - private boolean isStaticInnerClass(@Nullable TypeTree tree) { - if (!(tree instanceof J.FieldAccess)) { - return false; - } - JavaType.FullyQualified fq = TypeUtils.asFullyQualified(tree.getType()); - return fq != null && fq.getOwningClass() != null && - fq.hasFlags(Flag.Static); + private boolean isQualifiedClass(@Nullable TypeTree tree) { + return tree instanceof J.FieldAccess || tree instanceof J.ParameterizedType || + tree instanceof J.ArrayType; } - private TypeTree annotateInnerClass(TypeTree staticInnerClassRef, J.Annotation annotation) { - J.FieldAccess s = (J.FieldAccess) staticInnerClassRef; - s = s.withName(s.getName().withAnnotations( - ListUtils.concat(annotation.withPrefix(Space.EMPTY), s.getName().getAnnotations()))); - if (s.getName().getPrefix().getWhitespace().isEmpty()) { - s = s.withName(s.getName().withPrefix(s.getName().getPrefix().withWhitespace(" "))); + private TypeTree annotateInnerClass(TypeTree qualifiedClassRef, J.Annotation annotation) { + if (qualifiedClassRef instanceof J.FieldAccess) { + J.FieldAccess q = (J.FieldAccess) qualifiedClassRef; + q = q.withName(q.getName().withAnnotations( + ListUtils.concat(annotation.withPrefix(Space.EMPTY), q.getName().getAnnotations()))); + if (q.getName().getPrefix().getWhitespace().isEmpty()) { + q = q.withName(q.getName().withPrefix(q.getName().getPrefix().withWhitespace(" "))); + } + return q; + } else if (qualifiedClassRef instanceof J.ParameterizedType && + ((J.ParameterizedType) qualifiedClassRef).getClazz() instanceof TypeTree) { + J.ParameterizedType pt = (J.ParameterizedType) qualifiedClassRef; + return pt.withClazz(annotateInnerClass((TypeTree) pt.getClazz(), annotation)); + } else if (qualifiedClassRef instanceof J.ArrayType) { + J.ArrayType at = (J.ArrayType) qualifiedClassRef; + at = at.withAnnotations(ListUtils.concat(annotation.withPrefix(Space.SINGLE_SPACE), at.getAnnotations())); + return at; } - return s; + return qualifiedClassRef; } }); } diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index d6d804a37..3b14e0a07 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -68,6 +68,52 @@ class Test { ); } + @Test + void fullyQualifiedFieldAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + @Nullable java.util.List l; + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + java.util.@Nullable List l; + } + """ + ) + ); + } + + @Test + void arrayFieldAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + @Nullable String[] l; + } + """, + """ + import org.openrewrite.internal.lang.Nullable; + import org.openrewrite.xml.tree.Xml; + class Test { + String @Nullable[] l; + } + """ + ) + ); + } + @Test void publicFieldAnnotation() { rewriteRun( From ebc0965ca638bfaaef083e9a5d8d0636dd9e8ca7 Mon Sep 17 00:00:00 2001 From: Jonathan Schneider Date: Thu, 15 Aug 2024 14:08:52 -0400 Subject: [PATCH 095/183] MoveFieldAnnotationToType handling parameterized types that are not qualified --- .../java/MoveFieldAnnotationToType.java | 3 ++- .../java/MoveFieldAnnotationToTypeTest.java | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index b38525d3e..44b0e1b57 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -144,7 +144,8 @@ private boolean matchesType(J.Annotation ann) { } private boolean isQualifiedClass(@Nullable TypeTree tree) { - return tree instanceof J.FieldAccess || tree instanceof J.ParameterizedType || + return tree instanceof J.FieldAccess || + (tree instanceof J.ParameterizedType && ((J.ParameterizedType) tree).getClazz() instanceof J.FieldAccess) || tree instanceof J.ArrayType; } diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index 3b14e0a07..f47137c4a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -75,14 +75,12 @@ void fullyQualifiedFieldAnnotation() { java( """ import org.openrewrite.internal.lang.Nullable; - import org.openrewrite.xml.tree.Xml; class Test { @Nullable java.util.List l; } """, """ import org.openrewrite.internal.lang.Nullable; - import org.openrewrite.xml.tree.Xml; class Test { java.util.@Nullable List l; } @@ -98,14 +96,12 @@ void arrayFieldAnnotation() { java( """ import org.openrewrite.internal.lang.Nullable; - import org.openrewrite.xml.tree.Xml; class Test { @Nullable String[] l; } """, """ import org.openrewrite.internal.lang.Nullable; - import org.openrewrite.xml.tree.Xml; class Test { String @Nullable[] l; } @@ -114,6 +110,22 @@ class Test { ); } + @Test + void parameterizedFieldAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.openrewrite.internal.lang.Nullable; + import java.util.List; + class Test { + @Nullable List l; + } + """ + ) + ); + } + @Test void publicFieldAnnotation() { rewriteRun( From 12b323a8a14f66a422963603c18ea39b9989c211 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 16 Aug 2024 11:48:46 +0200 Subject: [PATCH 096/183] OpenRewrite best practices in MoveFieldAnnotationToTypeTest --- .../staticanalysis/java/MoveFieldAnnotationToTypeTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index f47137c4a..08169745a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -16,6 +16,7 @@ package org.openrewrite.staticanalysis.java; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -46,6 +47,7 @@ class Test { } @Test + @DocumentExample void fieldAnnotation() { rewriteRun( //language=java From 3baf39a93f49b52e01398591e10363022097eb66 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 16 Aug 2024 11:53:33 +0200 Subject: [PATCH 097/183] Convert additional lambdas to expressions based on number of args (#329) --- .../LambdaBlockToExpression.java | 4 +- .../LambdaBlockToExpressionTest.java | 39 ++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java b/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java index a68f3f98a..4d33cd88b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java +++ b/src/main/java/org/openrewrite/staticanalysis/LambdaBlockToExpression.java @@ -89,6 +89,7 @@ private static boolean hasMethodOverloading(J.MethodInvocation method) { // TODO this is actually more complex in the presence of generics and inheritance static boolean hasMethodOverloading(JavaType.Method methodType) { String methodName = methodType.getName(); + int numberOfParameters = methodType.getParameterNames().size(); return Optional.of(methodType) .map(JavaType.Method::getDeclaringType) .filter(JavaType.Class.class::isInstance) @@ -97,7 +98,8 @@ static boolean hasMethodOverloading(JavaType.Method methodType) { .map(methods -> { int overloadingCount = 0; for (JavaType.Method dm : methods) { - if (dm.getName().equals(methodName)) { + if (methodName.equals(dm.getName()) && + numberOfParameters == dm.getParameterNames().size()) { if (++overloadingCount > 1) { return true; } diff --git a/src/test/java/org/openrewrite/staticanalysis/LambdaBlockToExpressionTest.java b/src/test/java/org/openrewrite/staticanalysis/LambdaBlockToExpressionTest.java index 7cdcac996..b6d20273d 100644 --- a/src/test/java/org/openrewrite/staticanalysis/LambdaBlockToExpressionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/LambdaBlockToExpressionTest.java @@ -75,7 +75,7 @@ class Test { """ import java.util.function.Function; class Test { - Function f = n -> + Function f = n -> // The buttonType will always be "cancel", even if we pressed one of the entry type buttons n + 1; } @@ -145,4 +145,41 @@ public void run() { ); } + @Test + @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/pull/582") + void simplifyAssertThrows() { + rewriteRun( + spec-> spec.parser(JavaParser.fromJavaVersion().classpath("junit")), + //language=java + java( + """ + import static org.junit.jupiter.api.Assertions.assertThrows; + + class Test { + void test() { + assertThrows(IllegalArgumentException.class, () -> { + foo(); + }); + } + void foo() { + throw new IllegalArgumentException("boom"); + } + } + """, + """ + import static org.junit.jupiter.api.Assertions.assertThrows; + + class Test { + void test() { + assertThrows(IllegalArgumentException.class, () -> + foo()); + } + void foo() { + throw new IllegalArgumentException("boom"); + } + } + """ + ) + ); + } } From 7291fd93d43c06d5fd26a63607b21b5e69207211 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 17 Aug 2024 10:51:42 +0200 Subject: [PATCH 098/183] Fix uncovered missing type issues After https://github.com/openrewrite/rewrite/pull/4406 --- .../ExplicitLambdaArgumentTypesTest.java | 18 +++++++++--------- .../UseLambdaForFunctionalInterfaceTest.java | 1 + 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java b/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java index 5623e8cd1..4577076c0 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java @@ -40,7 +40,7 @@ public void defaults(RecipeSpec spec) { @Test void unknownArgumentType() { rewriteRun( - spec -> spec.typeValidationOptions(TypeValidation.builder().identifiers(false).build()), + spec -> spec.typeValidationOptions(TypeValidation.builder().identifiers(false).methodDeclarations(true).build()), //language=java java( """ @@ -564,7 +564,7 @@ public interface ObservableValue { java( """ package javafx.beans.value; - + @FunctionalInterface public interface ChangeListener { void changed(ObservableValue observable, T oldValue, T newValue); @@ -575,7 +575,7 @@ public interface ChangeListener { java( """ package example; - + import javafx.beans.value.ObservableValue; import javafx.beans.value.ChangeListener; @@ -590,7 +590,7 @@ public void addListener(ChangeListener listener) { """ import javafx.beans.value.ObservableValue; import example.Fred; - + class Test { void foo() { Fred fred = new Fred(); @@ -602,7 +602,7 @@ void foo() { """ import javafx.beans.value.ObservableValue; import example.Fred; - + class Test { void foo() { Fred fred = new Fred(); @@ -623,9 +623,9 @@ void extendsConstraint() { java( """ package com.test; - + import java.util.List; - + class A { void foo(List a) { a.forEach(it -> { }); @@ -634,9 +634,9 @@ void foo(List a) { """, """ package com.test; - + import java.util.List; - + class A { void foo(List a) { a.forEach((A it) -> { }); diff --git a/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java b/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java index 06bc31140..7931e383f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterfaceTest.java @@ -91,6 +91,7 @@ void gson() { """ import com.google.gson.JsonSerializationContext; import com.google.gson.GsonBuilder; + import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; import com.google.gson.JsonSerializer; import java.time.LocalDateTime; From 195fbf806ce214305d59d92e81bf2bef7401cf9c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 17 Aug 2024 10:58:20 +0200 Subject: [PATCH 099/183] Disable type validation on `methodDeclarations` in missing type test --- .../staticanalysis/ExplicitLambdaArgumentTypesTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java b/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java index 4577076c0..9b37f3a62 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypesTest.java @@ -40,7 +40,7 @@ public void defaults(RecipeSpec spec) { @Test void unknownArgumentType() { rewriteRun( - spec -> spec.typeValidationOptions(TypeValidation.builder().identifiers(false).methodDeclarations(true).build()), + spec -> spec.typeValidationOptions(TypeValidation.builder().identifiers(false).methodDeclarations(false).build()), //language=java java( """ From 38e00480fbb4e5356bce48eefc3b1c79d5361474 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 17 Aug 2024 13:38:29 +0200 Subject: [PATCH 100/183] Fix typo in UseStringReplace --- .../java/org/openrewrite/staticanalysis/UseStringReplace.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java index 3801bdea0..17bee652c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java @@ -46,7 +46,7 @@ public class UseStringReplace extends Recipe { @Override public String getDisplayName() { - return "Use `String::replace()` when fist parameter is not a real regular expression"; + return "Use `String::replace()` when first parameter is not a real regular expression"; } @Override From c9607c03cc34707f8229cbece765afb6360ff843 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 17 Aug 2024 14:24:51 +0200 Subject: [PATCH 101/183] Also skip RemoveUnusedPrivateMethods when `all` suppressed Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/331 --- .../openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java index 4b2da88f4..fb7325b86 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java @@ -69,7 +69,8 @@ private boolean unusedWarningsSuppressed(J classDeclaration) { List arguments = annotation.getArguments(); if (arguments != null) { for (Expression argument : arguments) { - if (J.Literal.isLiteralValue(argument, "unused")) { + if (J.Literal.isLiteralValue(argument, "all") || + J.Literal.isLiteralValue(argument, "unused")) { return true; } } From 4c7c842adaba802c83d03bc3fda74f5891eb5569 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 18 Aug 2024 08:52:55 +0000 Subject: [PATCH 102/183] refactor: Remove unused imports Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.RemoveUnusedImports?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../openrewrite/staticanalysis/ControlFlowIndentation.java | 5 ++++- .../staticanalysis/UnnecessaryExplicitTypeArguments.java | 1 - .../AddSerialAnnotationToserialVersionUIDTest.java | 1 - 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java index 1dfa7271e..c9d09fd45 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java +++ b/src/main/java/org/openrewrite/staticanalysis/ControlFlowIndentation.java @@ -16,7 +16,10 @@ package org.openrewrite.staticanalysis; import org.jspecify.annotations.Nullable; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.format.TabsAndIndentsVisitor; diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java index f728f2fde..90affc4ac 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryExplicitTypeArguments.java @@ -19,7 +19,6 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.staticanalysis.java.JavaFileChecker; -import org.openrewrite.staticanalysis.kotlin.KotlinFileChecker; public class UnnecessaryExplicitTypeArguments extends Recipe { diff --git a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java index 5b18adc2d..46a5e48be 100644 --- a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java @@ -17,7 +17,6 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; -import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; From 717fbb193659a655f62ae569cf768d9ea20c4c5c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 21 Aug 2024 07:25:18 +0000 Subject: [PATCH 103/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/java/MoveFieldAnnotationToTypeTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index 08169745a..ea7e19c64 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -23,7 +23,7 @@ import static org.openrewrite.java.Assertions.java; @SuppressWarnings("deprecation") -public class MoveFieldAnnotationToTypeTest implements RewriteTest { +class MoveFieldAnnotationToTypeTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { From 5aaf59ffb610dfb805ea1273f35dcd794d29ec5b Mon Sep 17 00:00:00 2001 From: Martin Ross Date: Wed, 21 Aug 2024 04:51:16 -0400 Subject: [PATCH 104/183] Fix overly cautious regexp for = UseStringReplace (#332) * Fix overly cautious * Add missing semicolon * Pass java SourceSpec into rewriteRun, and negative cases * Use different class names for each source --------- Co-authored-by: Tim te Beek --- .../staticanalysis/UseStringReplace.java | 2 +- .../staticanalysis/UseStringReplaceTest.java | 42 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java index 17bee652c..a38a487cb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java @@ -74,7 +74,7 @@ private static class UseStringReplaceVisitor extends JavaVisitor Date: Wed, 21 Aug 2024 11:04:57 +0200 Subject: [PATCH 105/183] Work around upstream change to how generics are passed down - https://github.com/openrewrite/rewrite/pull/4427 --- .../staticanalysis/ExplicitLambdaArgumentTypes.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java index 84d1eb922..2d01e5b47 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java @@ -172,7 +172,8 @@ private J.VariableDeclarations maybeAddTypeExpression(J.VariableDeclarations mul } else if (type instanceof JavaType.GenericTypeVariable) { JavaType.GenericTypeVariable genericType = (JavaType.GenericTypeVariable) type; - if (!genericType.getName().equals("?")) { + if (!"?".equals(genericType.getName()) && + !"".equals(genericType.getName())) { return new J.Identifier(Tree.randomId(), space, Markers.EMPTY, From cf3dcedfe12989119fcc01fd5273b77cba4d41fa Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 24 Aug 2024 00:01:25 +0200 Subject: [PATCH 106/183] Revert 3a5399eaebcdcd8f5b5ed34293e891b18fa46070 --- .../staticanalysis/ExplicitLambdaArgumentTypes.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java index 2d01e5b47..84d1eb922 100755 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitLambdaArgumentTypes.java @@ -172,8 +172,7 @@ private J.VariableDeclarations maybeAddTypeExpression(J.VariableDeclarations mul } else if (type instanceof JavaType.GenericTypeVariable) { JavaType.GenericTypeVariable genericType = (JavaType.GenericTypeVariable) type; - if (!"?".equals(genericType.getName()) && - !"".equals(genericType.getName())) { + if (!genericType.getName().equals("?")) { return new J.Identifier(Tree.randomId(), space, Markers.EMPTY, From 8e693d2763263e7e72508bfafa2bdd69a06ca225 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 24 Aug 2024 17:33:26 +0200 Subject: [PATCH 107/183] Blunt adaptation for C# in `FinalizePrivateFields` Tests are not that easy yet. --- build.gradle.kts | 1 + .../staticanalysis/FinalizePrivateFields.java | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b430f94f5..9349d8739 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -17,6 +17,7 @@ dependencies { implementation("org.openrewrite:rewrite-java") implementation("org.openrewrite:rewrite-groovy:${rewriteVersion}") implementation("org.openrewrite:rewrite-kotlin:${rewriteVersion}") + implementation("org.openrewrite:rewrite-csharp:${rewriteVersion}") implementation("org.openrewrite.meta:rewrite-analysis:${rewriteVersion}") implementation("org.apache.commons:commons-text:latest.release") diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java index 7420b554a..d378e532c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java @@ -19,6 +19,7 @@ import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; +import org.openrewrite.csharp.tree.Cs; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.service.AnnotationService; @@ -53,6 +54,17 @@ public TreeVisitor getVisitor() { return new JavaIsoVisitor() { private Set privateFieldsToBeFinalized = new HashSet<>(); + @Nullable + private SourceFile topLevel; + + @Override + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { + if (topLevel == null && tree instanceof SourceFile) { + topLevel = (SourceFile) tree; + } + return super.visit(tree, ctx); + } + @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { if (!service(AnnotationService.class).getAllAnnotations(getCursor()).isEmpty()) { @@ -99,8 +111,8 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m return type != null ? v.withVariableType(type.withFlags( Flag.bitMapToFlags(type.getFlagsBitMap() | Flag.Final.getBitMask()))) : null; })).withModifiers(ListUtils.concat(mv.getModifiers(), - new J.Modifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, - J.Modifier.Type.Final, emptyList()))), ctx); + new J.Modifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, topLevel instanceof Cs ? "readonly" : "final", + topLevel instanceof Cs ? J.Modifier.Type.LanguageExtension : J.Modifier.Type.Final, emptyList()))), ctx); } return mv; @@ -124,6 +136,7 @@ private List collectPrivateFields(Cursor c .map(J.VariableDeclarations.class::cast) .filter(mv -> mv.hasModifier(J.Modifier.Type.Private) && !mv.hasModifier(J.Modifier.Type.Final) + && !(topLevel instanceof Cs || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()))) && !mv.hasModifier(J.Modifier.Type.Volatile)) .filter(mv -> !anyAnnotationApplied(new Cursor(bodyCursor, mv))) .map(J.VariableDeclarations::getVariables) From 30cb79f8bcf5fa1650fd7bc7fdd1eee04f7435f9 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 24 Aug 2024 17:39:42 +0200 Subject: [PATCH 108/183] Bugfix --- .../org/openrewrite/staticanalysis/FinalizePrivateFields.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java index d378e532c..dab6427d5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java @@ -136,7 +136,7 @@ private List collectPrivateFields(Cursor c .map(J.VariableDeclarations.class::cast) .filter(mv -> mv.hasModifier(J.Modifier.Type.Private) && !mv.hasModifier(J.Modifier.Type.Final) - && !(topLevel instanceof Cs || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()))) + && (!(topLevel instanceof Cs) || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()))) && !mv.hasModifier(J.Modifier.Type.Volatile)) .filter(mv -> !anyAnnotationApplied(new Cursor(bodyCursor, mv))) .map(J.VariableDeclarations::getVariables) From 7c6c2187edf0105314897cf18e393cef767bb388 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sat, 24 Aug 2024 17:51:30 +0200 Subject: [PATCH 109/183] Also ignore C# `const` fields in `FinalizePrivateFields` --- .../org/openrewrite/staticanalysis/FinalizePrivateFields.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java index dab6427d5..52e76c0f9 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java @@ -136,7 +136,7 @@ private List collectPrivateFields(Cursor c .map(J.VariableDeclarations.class::cast) .filter(mv -> mv.hasModifier(J.Modifier.Type.Private) && !mv.hasModifier(J.Modifier.Type.Final) - && (!(topLevel instanceof Cs) || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()))) + && (!(topLevel instanceof Cs) || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()) || "const".equals(m.getKeyword()))) && !mv.hasModifier(J.Modifier.Type.Volatile)) .filter(mv -> !anyAnnotationApplied(new Cursor(bodyCursor, mv))) .map(J.VariableDeclarations::getVariables) From 4bd4ec1cf53ff55e74a0316d3347c56b657841b0 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Sun, 25 Aug 2024 05:23:50 +0000 Subject: [PATCH 110/183] Migrate a few more OpenRewrite nullability annotations Use this link to re-run the recipe: https://app.moderne.io/builder/rCfr27H6r?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../org/openrewrite/staticanalysis/java/package-info.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/package-info.java b/src/main/java/org/openrewrite/staticanalysis/java/package-info.java index 05807bdc6..8e6b6aca0 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/package-info.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/package-info.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -@NonNullApi +@NullMarked package org.openrewrite.staticanalysis.java; -import org.openrewrite.internal.lang.NonNullApi; +import org.jspecify.annotations.NullMarked; From 397e325a7d236e706dfc92a248a4d9d85e0573b3 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 29 Aug 2024 13:22:06 +0200 Subject: [PATCH 111/183] Make the array itself nullable, not the elements Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/333 --- .../openrewrite/staticanalysis/RemoveUnusedLocalVariables.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index b8d15e14b..49296fb04 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -41,8 +41,7 @@ public class RemoveUnusedLocalVariables extends Recipe { description = "An array of variable identifier names for local variables to ignore, even if the local variable is unused.", required = false, example = "[unused, notUsed, IGNORE_ME]") - @Nullable - String[] ignoreVariablesNamed; + String @Nullable [] ignoreVariablesNamed; @Override public String getDisplayName() { From fe4ba634ee5961d6a969e65469921084cd6d2edd Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Thu, 29 Aug 2024 15:02:25 -0700 Subject: [PATCH 112/183] Add regression guard for combination of SimplifyBooleanExpression and SimplifyBooleanReturn --- .../SimplifyBooleanExpression.java | 2 +- .../SimplifyBooleanExpressionTest.java | 30 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java index b778f19b1..a8fb898fc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanExpression.java @@ -42,7 +42,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-S1125"); + return Collections.singleton("RSPEC-1125"); } @Override diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java index 8bdbbbb98..d825e8570 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java @@ -379,4 +379,34 @@ public class A { ) ); } + + @Test + void nullCheck() { + rewriteRun( + spec -> spec + .recipes( + new SimplifyBooleanReturn(), + new SimplifyBooleanExpression() + ), + //language=java + java(""" + class A { + String name; + boolean notOne(A a) { + if (a != null ? !name.equals(a.name) : a.name != null) return false; + return true; + } + } + """, + """ + class A { + String name; + boolean notOne(A a) { + return a == null ? a.name != null : !name.equals(a.name); + } + } + """ + ) + ); + } } From 28f2b41c2bc3da4a3221b98b72390e0b839d833c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 30 Aug 2024 11:41:56 +0000 Subject: [PATCH 113/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/SimplifyBooleanExpressionTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java index d825e8570..039136621 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyBooleanExpressionTest.java @@ -389,7 +389,8 @@ void nullCheck() { new SimplifyBooleanExpression() ), //language=java - java(""" + java( + """ class A { String name; boolean notOne(A a) { From 88abd41ecddb64bfed597718712f8b3a64cf316e Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Mon, 9 Sep 2024 09:21:23 -0500 Subject: [PATCH 114/183] refactor: Update Gradle wrapper (#335) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fc4850027..f95442d59 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip -distributionSha256Sum=5b9c5eb3f9fc2c94abaea57d90bd78747ca117ddbbf96c859d3741181a12bf2a +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1 networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From abb033844c2cb28303d140dc822de407aea4352d Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Mon, 9 Sep 2024 11:27:16 -0700 Subject: [PATCH 115/183] Python doesn't need braces --- .../staticanalysis/NeedBraces.java | 16 +++++-- .../staticanalysis/NeedBracesTest.java | 48 +++++++++---------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java b/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java index 925f4d763..4302e2e40 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java +++ b/src/main/java/org/openrewrite/staticanalysis/NeedBraces.java @@ -60,6 +60,8 @@ public TreeVisitor getVisitor() { } private static class NeedBracesVisitor extends JavaIsoVisitor { + + @SuppressWarnings("NotNullFieldNotInitialized") NeedBracesStyle needBracesStyle; /** @@ -92,10 +94,16 @@ private J.Block buildBlock(Statement owner, T element) { } @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof JavaSourceFile) { + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { + if (tree instanceof SourceFile) { SourceFile cu = (SourceFile) requireNonNull(tree); - needBracesStyle = cu.getStyle(NeedBracesStyle.class) == null ? Checkstyle.needBracesStyle() : cu.getStyle(NeedBracesStyle.class); + // Python don't need none of your curly braces + if (cu.getSourcePath().toString().endsWith(".py")) { + return (J) tree; + } + needBracesStyle = cu.getStyle(NeedBracesStyle.class) == null ? + Checkstyle.needBracesStyle() : + cu.getStyle(NeedBracesStyle.class, new NeedBracesStyle(false, false)); } return super.visit(tree, ctx); } @@ -201,6 +209,4 @@ public J.ForLoop visitForLoop(J.ForLoop forLoop, ExecutionContext ctx) { return elem; } } - - ; } diff --git a/src/test/java/org/openrewrite/staticanalysis/NeedBracesTest.java b/src/test/java/org/openrewrite/staticanalysis/NeedBracesTest.java index adc4e6b3d..59bb36013 100644 --- a/src/test/java/org/openrewrite/staticanalysis/NeedBracesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/NeedBracesTest.java @@ -40,8 +40,8 @@ "UnusedAssignment", "ConstantConditions", "ClassInitializerMayBeStatic", - "UnnecessaryReturnStatement" -}) + "UnnecessaryReturnStatement", + "DuplicateCondition"}) class NeedBracesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { @@ -68,31 +68,31 @@ class Test { static void addToWhile() { while (true) ; } - + static void addToWhileWithBody() { while (true) return; } - + static void addToIf(int n) { if (n == 1) return; // foo } - + static void addToIfElse(int n) { if (n == 1) return; else return; } - + static void addToIfElseIfElse(int n) { if (n == 1) return; else if (n == 2) return; else return; } - + static void addToDoWhile(Object obj) { do obj.notify(); while (true); } - + static void addToIterativeFor(Object obj) { for (int i = 0; ; ) obj.notify(); } @@ -104,20 +104,20 @@ static void addToWhile() { while (true) { } } - + static void addToWhileWithBody() { while (true) { return; } } - + static void addToIf(int n) { if (n == 1) { return; } // foo } - + static void addToIfElse(int n) { if (n == 1) { return; @@ -125,7 +125,7 @@ static void addToIfElse(int n) { return; } } - + static void addToIfElseIfElse(int n) { if (n == 1) { return; @@ -135,13 +135,13 @@ static void addToIfElseIfElse(int n) { return; } } - + static void addToDoWhile(Object obj) { do { obj.notify(); } while (true); } - + static void addToIterativeFor(Object obj) { for (int i = 0; ; ) { obj.notify(); @@ -164,7 +164,7 @@ class Test { static void emptyWhile() { while (true) ; } - + static void emptyForIterative() { for (int i = 0; i < 10; i++) ; } @@ -185,26 +185,26 @@ class Test { static void allowIf(int n) { if (n == 1) return; } - + static void allowIfElse(int n) { if (n == 1) return; else return; } - + static void allowIfElseIfElse(int n) { if (n == 1) return; else if (n == 2) return; else return; } - + static void allowWhileWithBody() { while (true) return; } - + static void allowDoWhileWithBody(Object obj) { do obj.notify(); while (true); } - + static void allowForIterativeWithBody(Object obj) { for (int i = 0; ; ) obj.notify(); } @@ -225,11 +225,11 @@ class Test { static void doNotAllowWhileWithEmptyBody() { while (true) ; } - + static void doNotAllowDoWhileWithEmptyBody(Object obj) { do ; while (true); } - + static void doNotAllowForIterativeWithEmptyBody(Object obj) { for (int i = 0; ; ) ; } @@ -241,12 +241,12 @@ static void doNotAllowWhileWithEmptyBody() { while (true) { } } - + static void doNotAllowDoWhileWithEmptyBody(Object obj) { do { } while (true); } - + static void doNotAllowForIterativeWithEmptyBody(Object obj) { for (int i = 0; ; ) { } From 337235e6d43c07ab52f185321c4fe241387e586c Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 16 Sep 2024 21:28:13 +0000 Subject: [PATCH 116/183] refactor: Order imports Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.java.OrderImports?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sidmFsdWUiOiJUcnVlIiwibmFtZSI6InJlbW92ZVVudXNlZCJ9XQ== Co-authored-by: Moderne --- .../org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java index 72c97ac1b..cf6509fd5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceWeekYearWithYear.java @@ -19,7 +19,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.tree.J; import java.util.Collections; import java.util.Set; From 11851269a964b276d8600621c1bc28f43e24d2fb Mon Sep 17 00:00:00 2001 From: Marc Bruggmann Date: Thu, 19 Sep 2024 17:46:16 +0200 Subject: [PATCH 117/183] Support fluent chains in RemoveMethodCallVisitor (#340) * refactor: Pull out conditions from RemoveMethodCallVisitor * feat: Support removing method invocations from fluent chains * refactor: Use new methods to streamline visitNewClass also * Slight polish * Inline call to MethodMatcher --------- Co-authored-by: Tim te Beek --- .../RemoveMethodCallVisitor.java | 58 +++++++++++-------- .../RemoveMethodCallVisitorTest.java | 42 ++++++++++++-- 2 files changed, 71 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java index 774c63000..52e9dd2ad 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java @@ -19,12 +19,9 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.MethodCall; +import org.openrewrite.java.tree.*; import java.util.function.BiPredicate; -import java.util.function.Supplier; /** * Removes all {@link MethodCall} matching both the @@ -48,33 +45,48 @@ public class RemoveMethodCallVisitor

    extends JavaIsoVisitor

    { @SuppressWarnings("NullableProblems") @Override public J.@Nullable NewClass visitNewClass(J.NewClass newClass, P p) { - return visitMethodCall(newClass, () -> super.visitNewClass(newClass, p)); + if (methodMatcher.matches(newClass) && predicateMatchesAllArguments(newClass) && isStatementInParentBlock(newClass)) { + if (newClass.getMethodType() != null) { + maybeRemoveImport(newClass.getMethodType().getDeclaringType()); + } + return null; + } + return super.visitNewClass(newClass, p); } @SuppressWarnings("NullableProblems") @Override public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, P p) { - return visitMethodCall(method, () -> super.visitMethodInvocation(method, p)); - } + // Find method invocations that match the specified method and arguments + if (methodMatcher.matches(method) && predicateMatchesAllArguments(method)) { + // If the method invocation is a standalone statement, remove it altogether + if (isStatementInParentBlock(method)) { + if (method.getMethodType() != null) { + maybeRemoveImport(method.getMethodType().getDeclaringType()); + } + return null; + } - private @Nullable M visitMethodCall(M methodCall, Supplier visitSuper) { - if (!methodMatcher.matches(methodCall)) { - return visitSuper.get(); - } - J.Block parentBlock = getCursor().firstEnclosing(J.Block.class); - //noinspection SuspiciousMethodCalls - if (parentBlock != null && !parentBlock.getStatements().contains(methodCall)) { - return visitSuper.get(); - } - // Remove the method invocation when the argumentMatcherPredicate is true for all arguments - for (int i = 0; i < methodCall.getArguments().size(); i++) { - if (!argumentPredicate.test(i, methodCall.getArguments().get(i))) { - return visitSuper.get(); + // If the method invocation is in a fluent chain, remove just the current invocation + if (method.getSelect() instanceof J.MethodInvocation && + TypeUtils.isOfType(method.getType(), method.getSelect().getType())) { + return super.visitMethodInvocation((J.MethodInvocation) method.getSelect(), p); } } - if (methodCall.getMethodType() != null) { - maybeRemoveImport(methodCall.getMethodType().getDeclaringType()); + return super.visitMethodInvocation(method, p); + } + + private boolean predicateMatchesAllArguments(MethodCall method) { + for (int i = 0; i < method.getArguments().size(); i++) { + if (!argumentPredicate.test(i, method.getArguments().get(i))) { + return false; + } } - return null; + return true; + } + + private boolean isStatementInParentBlock(Statement method) { + J.Block parentBlock = getCursor().firstEnclosing(J.Block.class); + return parentBlock == null || parentBlock.getStatements().contains(method); } } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitorTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitorTest.java index a8ad6a73f..21ac36efe 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitorTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitorTest.java @@ -43,7 +43,7 @@ void assertTrueIsRemoved() { """ abstract class Test { abstract void assertTrue(boolean condition); - + void test() { System.out.println("Hello"); assertTrue(true); @@ -53,7 +53,7 @@ void test() { """, """ abstract class Test { abstract void assertTrue(boolean condition); - + void test() { System.out.println("Hello"); System.out.println("World"); @@ -72,7 +72,7 @@ void assertTrueFalseIsNotRemoved() { """ abstract class Test { abstract void assertTrue(boolean condition); - + void test() { System.out.println("Hello"); assertTrue(false); @@ -95,7 +95,7 @@ void assertTrueTwoArgIsRemoved() { """ abstract class Test { abstract void assertTrue(String message, boolean condition); - + void test() { System.out.println("Hello"); assertTrue("message", true); @@ -106,7 +106,7 @@ void test() { """ abstract class Test { abstract void assertTrue(String message, boolean condition); - + void test() { System.out.println("Hello"); System.out.println("World"); @@ -125,7 +125,7 @@ void doesNotRemoveAssertTrueIfReturnValueIsUsed() { """ abstract class Test { abstract int assertTrue(boolean condition); - + void test() { System.out.println("Hello"); int value = assertTrue(true); @@ -136,4 +136,34 @@ void test() { ) ); } + + @Test + void removeMethodCallFromFluentChain() { + rewriteRun( + spec -> spec.recipe(RewriteTest.toRecipe(() -> new RemoveMethodCallVisitor<>( + new MethodMatcher("java.lang.StringBuilder append(..)"), (i, e) -> true))), + // language=java + java( + """ + class Main { + void hello() { + final String s = new StringBuilder("hello") + .delete(1, 2) + .append("world") + .toString(); + } + } + """, + """ + class Main { + void hello() { + final String s = new StringBuilder("hello") + .delete(1, 2) + .toString(); + } + } + """ + ) + ); + } } From 811af2ddfa8ee91d8d0db1a998cdf5d264983471 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 21 Sep 2024 13:39:40 +0200 Subject: [PATCH 118/183] Add WrapOption to OperatorWrap to change detected style easily after running once (#341) --- .../staticanalysis/OperatorWrap.java | 597 ++++++++-------- .../staticanalysis/OperatorWrapTest.java | 676 ++++++++++++++++++ 2 files changed, 981 insertions(+), 292 deletions(-) create mode 100644 src/test/java/org/openrewrite/staticanalysis/OperatorWrapTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java b/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java index 2e3297992..991662c19 100755 --- a/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java +++ b/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java @@ -15,6 +15,8 @@ */ package org.openrewrite.staticanalysis; +import lombok.EqualsAndHashCode; +import lombok.Value; import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.internal.ListUtils; @@ -28,7 +30,16 @@ import static java.util.Objects.requireNonNull; +@Value +@EqualsAndHashCode(callSuper = false) public class OperatorWrap extends Recipe { + + @Option(displayName = "Operator wrapping style", + description = "The operator wrapping style to enforce, which may differ from the configured or detected style.", + valid = {"EOL", "NL"}, + example = "NL") + OperatorWrapStyle.@Nullable WrapOption wrapOption; + @Override public String getDisplayName() { return "Operator wrapping"; @@ -41,382 +52,384 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { - return new OperatorWrapVisitor(); - } + return new JavaIsoVisitor() { + OperatorWrapStyle operatorWrapStyle; - private static class OperatorWrapVisitor extends JavaIsoVisitor { - OperatorWrapStyle operatorWrapStyle; + @Override + public J visit(@Nullable Tree tree, ExecutionContext ctx) { + if (tree instanceof JavaSourceFile) { + SourceFile cu = (SourceFile) requireNonNull(tree); + operatorWrapStyle = cu.getStyle(OperatorWrapStyle.class) == null ? Checkstyle.operatorWrapStyle() : cu.getStyle(OperatorWrapStyle.class); - @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof JavaSourceFile) { - SourceFile cu = (SourceFile) requireNonNull(tree); - operatorWrapStyle = cu.getStyle(OperatorWrapStyle.class) == null ? Checkstyle.operatorWrapStyle() : cu.getStyle(OperatorWrapStyle.class); + if (wrapOption != null) { + // Convenience override, to bypass having to configure a style once to change detected style + operatorWrapStyle = operatorWrapStyle.withWrapOption(wrapOption); + } + } + return super.visit(tree, ctx); } - return super.visit(tree, ctx); - } - @Override - public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { - J.Binary b = super.visitBinary(binary, ctx); - J.Binary.Type op = b.getOperator(); - if ((Boolean.TRUE.equals(operatorWrapStyle.getDiv()) && op == J.Binary.Type.Division) || - (Boolean.TRUE.equals(operatorWrapStyle.getStar()) && op == J.Binary.Type.Multiplication) || - (Boolean.TRUE.equals(operatorWrapStyle.getPlus()) && op == J.Binary.Type.Addition) || - (Boolean.TRUE.equals(operatorWrapStyle.getMinus()) && op == J.Binary.Type.Subtraction) || - (Boolean.TRUE.equals(operatorWrapStyle.getMod()) && op == J.Binary.Type.Modulo) || - (Boolean.TRUE.equals(operatorWrapStyle.getSr()) && op == J.Binary.Type.RightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getSl()) && op == J.Binary.Type.LeftShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getBsr()) && op == J.Binary.Type.UnsignedRightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getEqual()) && op == J.Binary.Type.Equal) || - (Boolean.TRUE.equals(operatorWrapStyle.getNotEqual()) && op == J.Binary.Type.NotEqual) || - (Boolean.TRUE.equals(operatorWrapStyle.getGt()) && op == J.Binary.Type.GreaterThan) || - (Boolean.TRUE.equals(operatorWrapStyle.getGe()) && op == J.Binary.Type.GreaterThanOrEqual) || - (Boolean.TRUE.equals(operatorWrapStyle.getLt()) && op == J.Binary.Type.LessThan) || - (Boolean.TRUE.equals(operatorWrapStyle.getLe()) && op == J.Binary.Type.LessThanOrEqual) || - (Boolean.TRUE.equals(operatorWrapStyle.getBand()) && op == J.Binary.Type.BitAnd) || - (Boolean.TRUE.equals(operatorWrapStyle.getBxor()) && op == J.Binary.Type.BitXor) || - (Boolean.TRUE.equals(operatorWrapStyle.getBor()) && op == J.Binary.Type.BitOr) || - (Boolean.TRUE.equals(operatorWrapStyle.getLand()) && op == J.Binary.Type.And) || - (Boolean.TRUE.equals(operatorWrapStyle.getLor()) && op == J.Binary.Type.Or)) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (b.getRight().getPrefix().getWhitespace().contains("\n")) { - b = b.getPadding().withOperator( - b.getPadding().getOperator().withBefore( - b.getRight().getPrefix() - ) - ); + @Override + public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { + J.Binary b = super.visitBinary(binary, ctx); + J.Binary.Type op = b.getOperator(); + if ((Boolean.TRUE.equals(operatorWrapStyle.getDiv()) && op == J.Binary.Type.Division) || + (Boolean.TRUE.equals(operatorWrapStyle.getStar()) && op == J.Binary.Type.Multiplication) || + (Boolean.TRUE.equals(operatorWrapStyle.getPlus()) && op == J.Binary.Type.Addition) || + (Boolean.TRUE.equals(operatorWrapStyle.getMinus()) && op == J.Binary.Type.Subtraction) || + (Boolean.TRUE.equals(operatorWrapStyle.getMod()) && op == J.Binary.Type.Modulo) || + (Boolean.TRUE.equals(operatorWrapStyle.getSr()) && op == J.Binary.Type.RightShift) || + (Boolean.TRUE.equals(operatorWrapStyle.getSl()) && op == J.Binary.Type.LeftShift) || + (Boolean.TRUE.equals(operatorWrapStyle.getBsr()) && op == J.Binary.Type.UnsignedRightShift) || + (Boolean.TRUE.equals(operatorWrapStyle.getEqual()) && op == J.Binary.Type.Equal) || + (Boolean.TRUE.equals(operatorWrapStyle.getNotEqual()) && op == J.Binary.Type.NotEqual) || + (Boolean.TRUE.equals(operatorWrapStyle.getGt()) && op == J.Binary.Type.GreaterThan) || + (Boolean.TRUE.equals(operatorWrapStyle.getGe()) && op == J.Binary.Type.GreaterThanOrEqual) || + (Boolean.TRUE.equals(operatorWrapStyle.getLt()) && op == J.Binary.Type.LessThan) || + (Boolean.TRUE.equals(operatorWrapStyle.getLe()) && op == J.Binary.Type.LessThanOrEqual) || + (Boolean.TRUE.equals(operatorWrapStyle.getBand()) && op == J.Binary.Type.BitAnd) || + (Boolean.TRUE.equals(operatorWrapStyle.getBxor()) && op == J.Binary.Type.BitXor) || + (Boolean.TRUE.equals(operatorWrapStyle.getBor()) && op == J.Binary.Type.BitOr) || + (Boolean.TRUE.equals(operatorWrapStyle.getLand()) && op == J.Binary.Type.And) || + (Boolean.TRUE.equals(operatorWrapStyle.getLor()) && op == J.Binary.Type.Or)) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (b.getRight().getPrefix().getWhitespace().contains("\n")) { + b = b.getPadding().withOperator( + b.getPadding().getOperator().withBefore( + b.getRight().getPrefix() + ) + ); + b = b.withRight( + b.getRight().withPrefix( + b.getRight().getPrefix().withWhitespace(" ") + ) + ); + } + } else if (b.getPadding().getOperator().getBefore().getWhitespace().contains("\n")) { b = b.withRight( b.getRight().withPrefix( + b.getPadding().getOperator().getBefore() + ) + ); + b = b.getPadding().withOperator( + b.getPadding().getOperator().withBefore( b.getRight().getPrefix().withWhitespace(" ") ) ); } - } else if (b.getPadding().getOperator().getBefore().getWhitespace().contains("\n")) { - b = b.withRight( - b.getRight().withPrefix( - b.getPadding().getOperator().getBefore() - ) - ); - b = b.getPadding().withOperator( - b.getPadding().getOperator().withBefore( - b.getRight().getPrefix().withWhitespace(" ") - ) - ); } + return b; } - return b; - } - @Override - public J.TypeParameter visitTypeParameter(J.TypeParameter typeParam, ExecutionContext ctx) { - J.TypeParameter tp = super.visitTypeParameter(typeParam, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getTypeExtensionAnd()) && tp.getPadding().getBounds() != null) { - int typeBoundsSize = tp.getPadding().getBounds().getPadding().getElements().size(); - tp = tp.getPadding().withBounds( - tp.getPadding().getBounds().getPadding().withElements( - ListUtils.map(tp.getPadding().getBounds().getPadding().getElements(), - (index, elemContainer) -> { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (index != typeBoundsSize - 1 && typeParam.getPadding().getBounds() != null) { - JRightPadded next = typeParam.getPadding().getBounds().getPadding().getElements().get(index + 1); - if (next.getElement().getPrefix().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withAfter( - next.getElement().getPrefix() - ); + @Override + public J.TypeParameter visitTypeParameter(J.TypeParameter typeParam, ExecutionContext ctx) { + J.TypeParameter tp = super.visitTypeParameter(typeParam, ctx); + if (Boolean.TRUE.equals(operatorWrapStyle.getTypeExtensionAnd()) && tp.getPadding().getBounds() != null) { + int typeBoundsSize = tp.getPadding().getBounds().getPadding().getElements().size(); + tp = tp.getPadding().withBounds( + tp.getPadding().getBounds().getPadding().withElements( + ListUtils.map(tp.getPadding().getBounds().getPadding().getElements(), + (index, elemContainer) -> { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (index != typeBoundsSize - 1 && typeParam.getPadding().getBounds() != null) { + JRightPadded next = typeParam.getPadding().getBounds().getPadding().getElements().get(index + 1); + if (next.getElement().getPrefix().getWhitespace().contains("\n")) { + elemContainer = elemContainer.withAfter( + next.getElement().getPrefix() + ); + } + } else { + if (elemContainer.getElement().getPrefix().getWhitespace().contains("\n")) { + elemContainer = elemContainer.withElement( + elemContainer.getElement().withPrefix( + elemContainer.getElement().getPrefix().withWhitespace(" ") + ) + ); + } } } else { - if (elemContainer.getElement().getPrefix().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withElement( - elemContainer.getElement().withPrefix( - elemContainer.getElement().getPrefix().withWhitespace(" ") - ) - ); - } - } - } else { - if (index != typeBoundsSize - 1) { - if (elemContainer.getAfter().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withAfter( - elemContainer.getAfter().withWhitespace(" ") - ); - } - } else if (typeBoundsSize > 1 && typeParam.getPadding().getBounds() != null) { - JRightPadded previous = typeParam.getPadding().getBounds().getPadding().getElements().get(index - 1); - if (previous.getAfter().getWhitespace().contains("\n")) { - elemContainer = elemContainer.withElement( - elemContainer.getElement().withPrefix( - previous.getAfter() - ) - ); + if (index != typeBoundsSize - 1) { + if (elemContainer.getAfter().getWhitespace().contains("\n")) { + elemContainer = elemContainer.withAfter( + elemContainer.getAfter().withWhitespace(" ") + ); + } + } else if (typeBoundsSize > 1 && typeParam.getPadding().getBounds() != null) { + JRightPadded previous = typeParam.getPadding().getBounds().getPadding().getElements().get(index - 1); + if (previous.getAfter().getWhitespace().contains("\n")) { + elemContainer = elemContainer.withElement( + elemContainer.getElement().withPrefix( + previous.getAfter() + ) + ); + } } } + return elemContainer; } - return elemContainer; - } - ) - ) - ); + ) + ) + ); + } + return tp; } - return tp; - } - @Override - public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, ExecutionContext ctx) { - J.InstanceOf i = super.visitInstanceOf(instanceOf, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getLiteralInstanceof())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (i.getClazz().getPrefix().getWhitespace().contains("\n")) { - i = i.getPadding().withExpr( - i.getPadding().getExpr().withAfter( - i.getClazz().getPrefix() - ) - ); + @Override + public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, ExecutionContext ctx) { + J.InstanceOf i = super.visitInstanceOf(instanceOf, ctx); + if (Boolean.TRUE.equals(operatorWrapStyle.getLiteralInstanceof())) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (i.getClazz().getPrefix().getWhitespace().contains("\n")) { + i = i.getPadding().withExpr( + i.getPadding().getExpr().withAfter( + i.getClazz().getPrefix() + ) + ); + i = i.withClazz( + i.getClazz().withPrefix( + i.getClazz().getPrefix().withWhitespace(" ") + ) + ); + } + } else if (i.getPadding().getExpr().getAfter().getWhitespace().contains("\n")) { i = i.withClazz( i.getClazz().withPrefix( - i.getClazz().getPrefix().withWhitespace(" ") + i.getPadding().getExpr().getAfter() + ) + ); + i = i.getPadding().withExpr( + i.getPadding().getExpr().withAfter( + i.getPadding().getExpr().getAfter().withWhitespace(" ") ) ); } - } else if (i.getPadding().getExpr().getAfter().getWhitespace().contains("\n")) { - i = i.withClazz( - i.getClazz().withPrefix( - i.getPadding().getExpr().getAfter() - ) - ); - i = i.getPadding().withExpr( - i.getPadding().getExpr().withAfter( - i.getPadding().getExpr().getAfter().withWhitespace(" ") - ) - ); } + return i; } - return i; - } - @Override - public J.Ternary visitTernary(J.Ternary ternary, ExecutionContext ctx) { - J.Ternary t = super.visitTernary(ternary, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getQuestion())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (t.getTruePart().getPrefix().getWhitespace().contains("\n")) { - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withBefore( - t.getPadding().getTruePart().getElement().getPrefix() - ) - ); + @Override + public J.Ternary visitTernary(J.Ternary ternary, ExecutionContext ctx) { + J.Ternary t = super.visitTernary(ternary, ctx); + if (Boolean.TRUE.equals(operatorWrapStyle.getQuestion())) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (t.getTruePart().getPrefix().getWhitespace().contains("\n")) { + t = t.getPadding().withTruePart( + t.getPadding().getTruePart().withBefore( + t.getPadding().getTruePart().getElement().getPrefix() + ) + ); + t = t.getPadding().withTruePart( + t.getPadding().getTruePart().withElement( + t.getPadding().getTruePart().getElement().withPrefix( + t.getPadding().getTruePart().getElement().getPrefix().withWhitespace(" ") + ) + ) + ); + } + } else if (t.getPadding().getTruePart().getBefore().getWhitespace().contains("\n")) { t = t.getPadding().withTruePart( t.getPadding().getTruePart().withElement( t.getPadding().getTruePart().getElement().withPrefix( - t.getPadding().getTruePart().getElement().getPrefix().withWhitespace(" ") + t.getPadding().getTruePart().getBefore() ) ) ); - } - } else if (t.getPadding().getTruePart().getBefore().getWhitespace().contains("\n")) { - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withElement( - t.getPadding().getTruePart().getElement().withPrefix( - t.getPadding().getTruePart().getBefore() - ) - ) - ); - t = t.getPadding().withTruePart( - t.getPadding().getTruePart().withBefore( - t.getPadding().getTruePart().getElement().getPrefix().withWhitespace(" ") - ) - ); - } - } - if (Boolean.TRUE.equals(operatorWrapStyle.getColon())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (t.getPadding().getFalsePart().getElement().getPrefix().getWhitespace().contains("\n")) { - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withBefore( - t.getPadding().getFalsePart().getElement().getPrefix() + t = t.getPadding().withTruePart( + t.getPadding().getTruePart().withBefore( + t.getPadding().getTruePart().getElement().getPrefix().withWhitespace(" ") ) ); + } + } + if (Boolean.TRUE.equals(operatorWrapStyle.getColon())) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (t.getPadding().getFalsePart().getElement().getPrefix().getWhitespace().contains("\n")) { + t = t.getPadding().withFalsePart( + t.getPadding().getFalsePart().withBefore( + t.getPadding().getFalsePart().getElement().getPrefix() + ) + ); + t = t.getPadding().withFalsePart( + t.getPadding().getFalsePart().withElement( + t.getPadding().getFalsePart().getElement().withPrefix( + t.getPadding().getFalsePart().getElement().getPrefix().withWhitespace(" ") + ) + ) + ); + } + } else if (t.getPadding().getFalsePart().getBefore().getWhitespace().contains("\n")) { t = t.getPadding().withFalsePart( t.getPadding().getFalsePart().withElement( t.getPadding().getFalsePart().getElement().withPrefix( - t.getPadding().getFalsePart().getElement().getPrefix().withWhitespace(" ") + t.getPadding().getFalsePart().getBefore() ) ) ); + t = t.getPadding().withFalsePart( + t.getPadding().getFalsePart().withBefore( + t.getPadding().getFalsePart().getElement().getPrefix().withWhitespace(" ") + ) + ); } - } else if (t.getPadding().getFalsePart().getBefore().getWhitespace().contains("\n")) { - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withElement( - t.getPadding().getFalsePart().getElement().withPrefix( - t.getPadding().getFalsePart().getBefore() - ) - ) - ); - t = t.getPadding().withFalsePart( - t.getPadding().getFalsePart().withBefore( - t.getPadding().getFalsePart().getElement().getPrefix().withWhitespace(" ") - ) - ); } + return t; } - return t; - } - @Override - public J.MemberReference visitMemberReference(J.MemberReference memberRef, ExecutionContext ctx) { - J.MemberReference m = super.visitMemberReference(memberRef, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getMethodRef())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (m.getPadding().getReference().getBefore().getWhitespace().contains("\n")) { - m = m.getPadding().withContaining( - m.getPadding().getContaining().withAfter( - m.getPadding().getReference().getBefore() - ) - ); + @Override + public J.MemberReference visitMemberReference(J.MemberReference memberRef, ExecutionContext ctx) { + J.MemberReference m = super.visitMemberReference(memberRef, ctx); + if (Boolean.TRUE.equals(operatorWrapStyle.getMethodRef())) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (m.getPadding().getReference().getBefore().getWhitespace().contains("\n")) { + m = m.getPadding().withContaining( + m.getPadding().getContaining().withAfter( + m.getPadding().getReference().getBefore() + ) + ); + m = m.getPadding().withReference( + m.getPadding().getReference().withBefore( + m.getPadding().getReference().getBefore().withWhitespace("") + ) + ); + } + } else if (m.getPadding().getContaining().getAfter().getWhitespace().contains("\n")) { m = m.getPadding().withReference( m.getPadding().getReference().withBefore( + m.getPadding().getContaining().getAfter() + ) + ); + m = m.getPadding().withContaining( + m.getPadding().getContaining().withAfter( m.getPadding().getReference().getBefore().withWhitespace("") ) ); } - } else if (m.getPadding().getContaining().getAfter().getWhitespace().contains("\n")) { - m = m.getPadding().withReference( - m.getPadding().getReference().withBefore( - m.getPadding().getContaining().getAfter() - ) - ); - m = m.getPadding().withContaining( - m.getPadding().getContaining().withAfter( - m.getPadding().getReference().getBefore().withWhitespace("") - ) - ); } + return m; } - return m; - } - @Override - public J.Assignment visitAssignment(J.Assignment assignment, ExecutionContext ctx) { - J.Assignment a = super.visitAssignment(assignment, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getAssign())) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (a.getPadding().getAssignment().getElement().getPrefix().getWhitespace().contains("\n")) { - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withBefore( - a.getPadding().getAssignment().getElement().getPrefix() - ) - ); + @Override + public J.Assignment visitAssignment(J.Assignment assignment, ExecutionContext ctx) { + J.Assignment a = super.visitAssignment(assignment, ctx); + if (Boolean.TRUE.equals(operatorWrapStyle.getAssign())) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (a.getPadding().getAssignment().getElement().getPrefix().getWhitespace().contains("\n")) { + a = a.getPadding().withAssignment( + a.getPadding().getAssignment().withBefore( + a.getPadding().getAssignment().getElement().getPrefix() + ) + ); + a = a.getPadding().withAssignment( + a.getPadding().getAssignment().withElement( + a.getPadding().getAssignment().getElement().withPrefix( + a.getPadding().getAssignment().getElement().getPrefix().withWhitespace(" ") + ) + ) + ); + } + } else if (a.getPadding().getAssignment().getBefore().getWhitespace().contains("\n")) { a = a.getPadding().withAssignment( a.getPadding().getAssignment().withElement( a.getPadding().getAssignment().getElement().withPrefix( - a.getPadding().getAssignment().getElement().getPrefix().withWhitespace(" ") + a.getPadding().getAssignment().getBefore() ) ) ); + a = a.getPadding().withAssignment( + a.getPadding().getAssignment().withBefore( + a.getPadding().getAssignment().getBefore().withWhitespace(" ") + ) + ); } - } else if (a.getPadding().getAssignment().getBefore().getWhitespace().contains("\n")) { - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withElement( - a.getPadding().getAssignment().getElement().withPrefix( - a.getPadding().getAssignment().getBefore() - ) - ) - ); - a = a.getPadding().withAssignment( - a.getPadding().getAssignment().withBefore( - a.getPadding().getAssignment().getBefore().withWhitespace(" ") - ) - ); } + return a; } - return a; - } - @Override - public J.AssignmentOperation visitAssignmentOperation(J.AssignmentOperation assignOp, ExecutionContext ctx) { - J.AssignmentOperation a = super.visitAssignmentOperation(assignOp, ctx); - J.AssignmentOperation.Type op = a.getOperator(); - if ((Boolean.TRUE.equals(operatorWrapStyle.getPlusAssign()) && op == J.AssignmentOperation.Type.Addition) || - (Boolean.TRUE.equals(operatorWrapStyle.getMinusAssign()) && op == J.AssignmentOperation.Type.Subtraction) || - (Boolean.TRUE.equals(operatorWrapStyle.getStarAssign()) && op == J.AssignmentOperation.Type.Multiplication) || - (Boolean.TRUE.equals(operatorWrapStyle.getDivAssign()) && op == J.AssignmentOperation.Type.Division) || - (Boolean.TRUE.equals(operatorWrapStyle.getModAssign()) && op == J.AssignmentOperation.Type.Modulo) || - (Boolean.TRUE.equals(operatorWrapStyle.getSrAssign()) && op == J.AssignmentOperation.Type.RightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getSlAssign()) && op == J.AssignmentOperation.Type.LeftShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getBsrAssign()) && op == J.AssignmentOperation.Type.UnsignedRightShift) || - (Boolean.TRUE.equals(operatorWrapStyle.getBandAssign()) && op == J.AssignmentOperation.Type.BitAnd) || - (Boolean.TRUE.equals(operatorWrapStyle.getBxorAssign()) && op == J.AssignmentOperation.Type.BitXor) || - (Boolean.TRUE.equals(operatorWrapStyle.getBorAssign()) && op == J.AssignmentOperation.Type.BitOr)) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (a.getAssignment().getPrefix().getWhitespace().contains("\n")) { - a = a.getPadding().withOperator( - a.getPadding().getOperator().withBefore( - a.getAssignment().getPrefix() - ) - ); + @Override + public J.AssignmentOperation visitAssignmentOperation(J.AssignmentOperation assignOp, ExecutionContext ctx) { + J.AssignmentOperation a = super.visitAssignmentOperation(assignOp, ctx); + J.AssignmentOperation.Type op = a.getOperator(); + if ((Boolean.TRUE.equals(operatorWrapStyle.getPlusAssign()) && op == J.AssignmentOperation.Type.Addition) || + (Boolean.TRUE.equals(operatorWrapStyle.getMinusAssign()) && op == J.AssignmentOperation.Type.Subtraction) || + (Boolean.TRUE.equals(operatorWrapStyle.getStarAssign()) && op == J.AssignmentOperation.Type.Multiplication) || + (Boolean.TRUE.equals(operatorWrapStyle.getDivAssign()) && op == J.AssignmentOperation.Type.Division) || + (Boolean.TRUE.equals(operatorWrapStyle.getModAssign()) && op == J.AssignmentOperation.Type.Modulo) || + (Boolean.TRUE.equals(operatorWrapStyle.getSrAssign()) && op == J.AssignmentOperation.Type.RightShift) || + (Boolean.TRUE.equals(operatorWrapStyle.getSlAssign()) && op == J.AssignmentOperation.Type.LeftShift) || + (Boolean.TRUE.equals(operatorWrapStyle.getBsrAssign()) && op == J.AssignmentOperation.Type.UnsignedRightShift) || + (Boolean.TRUE.equals(operatorWrapStyle.getBandAssign()) && op == J.AssignmentOperation.Type.BitAnd) || + (Boolean.TRUE.equals(operatorWrapStyle.getBxorAssign()) && op == J.AssignmentOperation.Type.BitXor) || + (Boolean.TRUE.equals(operatorWrapStyle.getBorAssign()) && op == J.AssignmentOperation.Type.BitOr)) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (a.getAssignment().getPrefix().getWhitespace().contains("\n")) { + a = a.getPadding().withOperator( + a.getPadding().getOperator().withBefore( + a.getAssignment().getPrefix() + ) + ); + a = a.withAssignment( + a.getAssignment().withPrefix( + a.getAssignment().getPrefix().withWhitespace(" ") + ) + ); + } + } else if (a.getPadding().getOperator().getBefore().getWhitespace().contains("\n")) { a = a.withAssignment( a.getAssignment().withPrefix( + a.getPadding().getOperator().getBefore() + ) + ); + a = a.getPadding().withOperator( + a.getPadding().getOperator().withBefore( a.getAssignment().getPrefix().withWhitespace(" ") ) ); } - } else if (a.getPadding().getOperator().getBefore().getWhitespace().contains("\n")) { - a = a.withAssignment( - a.getAssignment().withPrefix( - a.getPadding().getOperator().getBefore() - ) - ); - a = a.getPadding().withOperator( - a.getPadding().getOperator().withBefore( - a.getAssignment().getPrefix().withWhitespace(" ") - ) - ); } + return a; } - return a; - } - @Override - public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { - J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, ctx); - if (Boolean.TRUE.equals(operatorWrapStyle.getAssign()) && v.getPadding().getInitializer() != null) { - if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { - if (v.getPadding().getInitializer().getElement().getPrefix().getWhitespace().contains("\n")) { - v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withBefore( - v.getPadding().getInitializer().getElement().getPrefix() - ) - ); - if (v.getPadding().getInitializer() != null && v.getPadding().getInitializer().getElement() != null) { + @Override + public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { + J.VariableDeclarations.NamedVariable v = super.visitVariable(variable, ctx); + if (Boolean.TRUE.equals(operatorWrapStyle.getAssign()) && v.getPadding().getInitializer() != null) { + if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { + if (v.getPadding().getInitializer().getElement().getPrefix().getWhitespace().contains("\n")) { v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withElement( - v.getPadding().getInitializer().getElement().withPrefix( - v.getPadding().getInitializer().getElement().getPrefix().withWhitespace(" ") - ) + v.getPadding().getInitializer().withBefore( + v.getPadding().getInitializer().getElement().getPrefix() ) ); + if (v.getPadding().getInitializer() != null && v.getPadding().getInitializer().getElement() != null) { + v = v.getPadding().withInitializer( + v.getPadding().getInitializer().withElement( + v.getPadding().getInitializer().getElement().withPrefix( + v.getPadding().getInitializer().getElement().getPrefix().withWhitespace(" ") + ) + ) + ); + } } - } - } else if (v.getPadding().getInitializer().getBefore().getWhitespace().contains("\n")) { - v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withElement( - v.getPadding().getInitializer().getElement().withPrefix( - v.getPadding().getInitializer().getBefore() - ) - ) - ); - if (v.getPadding().getInitializer() != null && v.getPadding().getInitializer().getBefore() != null) { + } else if (v.getPadding().getInitializer().getBefore().getWhitespace().contains("\n")) { v = v.getPadding().withInitializer( - v.getPadding().getInitializer().withBefore( - v.getPadding().getInitializer().getElement().getPrefix().withWhitespace(" ") + v.getPadding().getInitializer().withElement( + v.getPadding().getInitializer().getElement().withPrefix( + v.getPadding().getInitializer().getBefore() + ) ) ); + if (v.getPadding().getInitializer() != null && v.getPadding().getInitializer().getBefore() != null) { + v = v.getPadding().withInitializer( + v.getPadding().getInitializer().withBefore( + v.getPadding().getInitializer().getElement().getPrefix().withWhitespace(" ") + ) + ); + } } } + return v; } - return v; - } + }; } - } diff --git a/src/test/java/org/openrewrite/staticanalysis/OperatorWrapTest.java b/src/test/java/org/openrewrite/staticanalysis/OperatorWrapTest.java new file mode 100644 index 000000000..97e6d88aa --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/OperatorWrapTest.java @@ -0,0 +1,676 @@ +/* + * 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.staticanalysis; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.Tree; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.format.AutoFormatVisitor; +import org.openrewrite.java.style.Checkstyle; +import org.openrewrite.java.style.OperatorWrapStyle; +import org.openrewrite.java.tree.J; +import org.openrewrite.style.NamedStyles; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; + +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; + +import static java.util.Collections.emptySet; +import static java.util.Collections.singletonList; +import static org.openrewrite.java.Assertions.java; + +@SuppressWarnings({"StringConcatenationMissingWhitespace", "ConstantConditions", "CStyleArrayDeclaration"}) +class OperatorWrapTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new OperatorWrap(null)); + } + + private static List operatorWrapStyle() { + return operatorWrapStyle(style -> style); + } + + private static List operatorWrapStyle(UnaryOperator with) { + return Collections.singletonList( + new NamedStyles( + Tree.randomId(), "test", "test", "test", emptySet(), + singletonList(with.apply(Checkstyle.operatorWrapStyle())) + ) + ); + } + + @DocumentExample + @Test + void binaryOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), + //language=java + java( + """ + class Test { + static void method() { + String s = "aaa" + + "b" + "c"; + } + } + """, + """ + class Test { + static void method() { + String s = "aaa" + + "b" + "c"; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void binaryOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), + //language=java + java( + """ + class Test { + static void method() { + String s = "aaa" + + "b" + "c"; + } + } + """, + """ + class Test { + static void method() { + String s = "aaa" + + "b" + "c"; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void typeParameterOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), + //language=java + java( + """ + import java.io.Serializable; + + class Test { + static > T method0() { + return null; + } + + static T method1() { + return null; + } + } + """, + """ + import java.io.Serializable; + + class Test { + static > T method0() { + return null; + } + + static T method1() { + return null; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void typeParameterOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), + //language=java + java( + """ + import java.io.Serializable; + + class Test { + static > T method0() { + return null; + } + + static T method1() { + return null; + } + } + """, + """ + import java.io.Serializable; + + class Test { + static > T method0() { + return null; + } + + static T method1() { + return null; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void instanceOfOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), + //language=java + java( + """ + class Test { + static Object method(Object s) { + if (s instanceof + String) { + return null; + } + return s; + } + } + """, + """ + class Test { + static Object method(Object s) { + if (s + instanceof String) { + return null; + } + return s; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void instanceOfOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), + //language=java + java( + """ + class Test { + static Object method(Object s) { + if (s + instanceof String) { + return null; + } + return s; + } + } + """, + """ + class Test { + static Object method(Object s) { + if (s instanceof + String) { + return null; + } + return s; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void ternaryOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), + //language=java + java( + """ + class Test { + static String method(String s) { + return s.contains("a") ? + "truePart" : + "falsePart"; + } + } + """, + """ + class Test { + static String method(String s) { + return s.contains("a") + ? "truePart" + : "falsePart"; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void ternaryOnNewlineIgnoringColon() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withColon(false)))), + //language=java + java( + """ + class Test { + static String method(String s) { + return s.contains("a") ? + "truePart" : + "falsePart"; + } + } + """, + """ + class Test { + static String method(String s) { + return s.contains("a") + ? "truePart" : + "falsePart"; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void ternaryOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL)))), + //language=java + java( + """ + class Test { + static String method(String s) { + return s.contains("a") + ? "truePart" + : "falsePart"; + } + } + """, + """ + class Test { + static String method(String s) { + return s.contains("a") ? + "truePart" : + "falsePart"; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void assignmentOperatorOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withAssign(true) + .withDivAssign(true) + .withPlusAssign(true) + .withMinusAssign(true) + .withStarAssign(true) + .withModAssign(true) + .withSrAssign(true) + .withBsrAssign(true) + .withSlAssign(true) + .withBxorAssign(true) + .withBorAssign(true) + .withBandAssign(true) + ))), + //language=java + java( + """ + class Test { + static int method() { + int a = 0; + a /= + 1; + a += + 1; + return a; + } + } + """, + """ + class Test { + static int method() { + int a = 0; + a + /= 1; + a + += 1; + return a; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void assignmentOperatorOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL) + .withAssign(true) + .withDivAssign(true) + .withPlusAssign(true) + .withMinusAssign(true) + .withStarAssign(true) + .withModAssign(true) + .withSrAssign(true) + .withBsrAssign(true) + .withSlAssign(true) + .withBxorAssign(true) + .withBorAssign(true) + .withBandAssign(true) + ))), + //language=java + java( + """ + class Test { + static int method() { + int a = 0; + a + /= 1; + a + += 1; + return a; + } + } + """, + """ + class Test { + static int method() { + int a = 0; + a /= + 1; + a += + 1; + return a; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void memberReferenceOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withMethodRef(true)))), + //language=java + java( + """ + import java.util.stream.Stream; + + class Test { + static void methodStream(Stream stream) { + stream.forEach(System.out:: + println); + } + } + """, + """ + import java.util.stream.Stream; + + class Test { + static void methodStream(Stream stream) { + stream.forEach(System.out + ::println); + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void memberReferenceOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL).withMethodRef(true)))), + //language=java + java( + """ + import java.util.stream.Stream; + + class Test { + static void methodStream(Stream stream) { + stream.forEach(System.out + ::println); + } + } + """, + """ + import java.util.stream.Stream; + + class Test { + static void methodStream(Stream stream) { + stream.forEach(System.out:: + println); + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void assignmentOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withAssign(true)))), + //language=java + java( + """ + class Test { + static int method() { + int n; + n = + 1; + return n; + } + } + """, + """ + class Test { + static int method() { + int n; + n + = 1; + return n; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void assignmentOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL).withAssign(true)))), + //language=java + java( + """ + class Test { + static int method() { + int n; + n + = 1; + return n; + } + } + """, + """ + class Test { + static int method() { + int n; + n = + 1; + return n; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void variableOnNewline() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withAssign(true)))), + //language=java + java( + """ + class Test { + static void method() { + int n = + 1; + int nArr[] = + new int[0]; + } + } + """, + """ + class Test { + static void method() { + int n + = 1; + int nArr[] + = new int[0]; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + @Test + void variableOnEndOfLine() { + rewriteRun( + spec -> spec.parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle(style -> + style.withWrapOption(OperatorWrapStyle.WrapOption.EOL).withAssign(true)))), + //language=java + java( + """ + class Test { + static void method() { + int n + = 1; + int nArr[] + = new int[0]; + } + } + """, + """ + class Test { + static void method() { + int n = + 1; + int nArr[] = + new int[0]; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } + + private static Consumer> autoFormatIsIdempotent() { + return spec -> spec.afterRecipe(cu -> + Assertions.assertThat(new AutoFormatVisitor<>().visit(cu, 0)).isEqualTo(cu)); + } + + @Test + void allowOverrideOfDetectedStyle() { + rewriteRun( + spec -> spec + .recipe(new OperatorWrap(OperatorWrapStyle.WrapOption.EOL)) + .parser(JavaParser.fromJavaVersion().styles(operatorWrapStyle())), + //language=java + java( + """ + class Test { + static void method() { + String s = "aaa" + + "b" + + "c"; + } + } + """, + """ + class Test { + static void method() { + String s = "aaa" + + "b" + + "c"; + } + } + """, + autoFormatIsIdempotent() + ) + ); + } +} From 606ce4b12db170029dc633b9a786ef634ced4d82 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 21 Sep 2024 17:08:40 +0200 Subject: [PATCH 119/183] Mark `OperatorWrap.wrapOption` as optional --- .../openrewrite/staticanalysis/OperatorWrap.java | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java b/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java index 991662c19..7dd83e4fb 100755 --- a/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java +++ b/src/main/java/org/openrewrite/staticanalysis/OperatorWrap.java @@ -37,7 +37,8 @@ public class OperatorWrap extends Recipe { @Option(displayName = "Operator wrapping style", description = "The operator wrapping style to enforce, which may differ from the configured or detected style.", valid = {"EOL", "NL"}, - example = "NL") + example = "NL", + required = false) OperatorWrapStyle.@Nullable WrapOption wrapOption; @Override @@ -181,7 +182,7 @@ public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, ExecutionContext ct if (OperatorWrapStyle.WrapOption.NL.equals(operatorWrapStyle.getWrapOption())) { if (i.getClazz().getPrefix().getWhitespace().contains("\n")) { i = i.getPadding().withExpr( - i.getPadding().getExpr().withAfter( + i.getPadding().getExpression().withAfter( i.getClazz().getPrefix() ) ); @@ -191,15 +192,15 @@ public J.InstanceOf visitInstanceOf(J.InstanceOf instanceOf, ExecutionContext ct ) ); } - } else if (i.getPadding().getExpr().getAfter().getWhitespace().contains("\n")) { + } else if (i.getPadding().getExpression().getAfter().getWhitespace().contains("\n")) { i = i.withClazz( i.getClazz().withPrefix( - i.getPadding().getExpr().getAfter() + i.getPadding().getExpression().getAfter() ) ); i = i.getPadding().withExpr( - i.getPadding().getExpr().withAfter( - i.getPadding().getExpr().getAfter().withWhitespace(" ") + i.getPadding().getExpression().withAfter( + i.getPadding().getExpression().getAfter().withWhitespace(" ") ) ); } From b913516b17a31164f194475c1ba17eb7ffb34f98 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 21 Sep 2024 17:24:17 +0200 Subject: [PATCH 120/183] refactor: Operator wrapping on end of line (#342) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.staticanalysis.OperatorWrap?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sidmFsdWUiOiJFT0wiLCJuYW1lIjoid3JhcE9wdGlvbiJ9XQ== Co-authored-by: Moderne --- .../AddSerialVersionUidToSerializable.java | 6 ++-- .../AtomicPrimitiveEqualsUsesGet.java | 4 +-- .../ChainStringBuilderAppendCalls.java | 12 +++---- .../staticanalysis/EmptyBlockVisitor.java | 4 +-- .../ExplicitInitializationVisitor.java | 6 ++-- .../ExternalizableHasNoArgsConstructor.java | 4 +-- .../staticanalysis/FinalClassVisitor.java | 8 ++--- .../FinalizeMethodArguments.java | 4 +-- .../staticanalysis/FinalizePrivateFields.java | 18 +++++----- .../HideUtilityClassConstructorVisitor.java | 4 +-- .../InstanceOfPatternMatch.java | 22 ++++++------ .../IsEmptyCallOnCollections.java | 14 ++++---- .../staticanalysis/MinimumSwitchCases.java | 4 +-- .../MissingOverrideAnnotation.java | 12 +++---- .../NoDoubleBraceInitialization.java | 24 ++++++------- .../ReferentialEqualityToObjectEquals.java | 34 +++++++++---------- .../RemoveCallsToObjectFinalize.java | 4 +-- ...avingVarargsWithObjectsRequireNonNull.java | 6 ++-- .../staticanalysis/SimplifyBooleanReturn.java | 4 +-- .../SortedSetStreamToLinkedHashSet.java | 6 ++-- .../TernaryOperatorsShouldNotBeNested.java | 18 +++++----- .../UnnecessaryPrimitiveAnnotations.java | 6 ++-- .../staticanalysis/UnnecessaryThrows.java | 8 ++--- .../UpperCaseLiteralSuffixes.java | 6 ++-- .../UseCollectionInterfaces.java | 4 +-- ...UseForEachRemoveInsteadOfSetRemoveAll.java | 14 ++++---- .../UseLambdaForFunctionalInterface.java | 14 ++++---- 27 files changed, 135 insertions(+), 135 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java index 98092bf33..67e25a039 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialVersionUidToSerializable.java @@ -97,9 +97,9 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex private J.VariableDeclarations maybeFixVariableDeclarations(J.VariableDeclarations varDecls) { List modifiers = varDecls.getModifiers(); - if (!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Private) - || !J.Modifier.hasModifier(modifiers, J.Modifier.Type.Static) - || !J.Modifier.hasModifier(modifiers, J.Modifier.Type.Final)) { + if (!J.Modifier.hasModifier(modifiers, J.Modifier.Type.Private) || + !J.Modifier.hasModifier(modifiers, J.Modifier.Type.Static) || + !J.Modifier.hasModifier(modifiers, J.Modifier.Type.Final)) { varDecls = varDecls.withModifiers(Arrays.asList( new J.Modifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, J.Modifier.Type.Private, Collections.emptyList()), new J.Modifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, null, J.Modifier.Type.Static, Collections.emptyList()), diff --git a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java index 252446436..d4a42c3b0 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java +++ b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java @@ -68,8 +68,8 @@ public TreeVisitor getVisitor() { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - if (mi.getSelect() != null && isAtomicEqualsType(mi.getSelect().getType()) && aiMethodMatcher.matches(mi) - && TypeUtils.isOfType(mi.getSelect().getType(), mi.getArguments().get(0).getType())) { + if (mi.getSelect() != null && isAtomicEqualsType(mi.getSelect().getType()) && aiMethodMatcher.matches(mi) && + TypeUtils.isOfType(mi.getSelect().getType(), mi.getArguments().get(0).getType())) { JavaType.FullyQualified fqt = TypeUtils.asFullyQualified(mi.getSelect().getType()); if (fqt != null) { String templateString = "#{any(" + fqt.getFullyQualifiedName() + ")}.get() == #{any(" + fqt.getFullyQualifiedName() + ")}.get()"; diff --git a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java index 2d1495df4..9ba0679ad 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java +++ b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java @@ -78,8 +78,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu boolean appendToString = false; for (Expression exp : flattenExpressions) { if (appendToString) { - if (exp instanceof J.Literal - && (((J.Literal) exp).getType() == JavaType.Primitive.String) + if (exp instanceof J.Literal && + (((J.Literal) exp).getType() == JavaType.Primitive.String) ) { group.add(exp); } else { @@ -87,8 +87,8 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu groups.add(exp); } } else { - if (exp instanceof J.Literal - && (((J.Literal) exp).getType() == JavaType.Primitive.String)) { + if (exp instanceof J.Literal && + (((J.Literal) exp).getType() == JavaType.Primitive.String)) { addToGroups(group, groups); appendToString = true; } else if ((exp instanceof J.Identifier || exp instanceof J.MethodInvocation) && exp.getType() != null) { @@ -182,8 +182,8 @@ public static boolean flatAdditiveExpressions(Expression expression, List(tryable)); - } else if (Boolean.TRUE.equals(emptyBlockStyle.getLiteralFinally()) && t.getFinally() != null - && !t.getCatches().isEmpty() && isEmptyBlock(t.getFinally())) { + } else if (Boolean.TRUE.equals(emptyBlockStyle.getLiteralFinally()) && t.getFinally() != null && + !t.getCatches().isEmpty() && isEmptyBlock(t.getFinally())) { t = t.withFinally(null); } diff --git a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java index 854a859aa..ad0583922 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExplicitInitializationVisitor.java @@ -67,9 +67,9 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations if (service(AnnotationService.class).matches(variableDeclsCursor, LOMBOK_BUILDER_DEFAULT)) { return v; } - J.Literal literalInit = variable.getInitializer() instanceof J.Literal - ? (J.Literal) variable.getInitializer() - : null; + J.Literal literalInit = variable.getInitializer() instanceof J.Literal ? + (J.Literal) variable.getInitializer() : + null; if (literalInit != null && !variableDecls.hasModifier(J.Modifier.Type.Final)) { if (TypeUtils.asFullyQualified(variable.getType()) != null && JavaType.Primitive.Null.equals(literalInit.getType())) { diff --git a/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java b/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java index 65a833099..1e84ca290 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java @@ -75,8 +75,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex Statement statement = statements.get(i); if (statement instanceof J.VariableDeclarations) { J.VariableDeclarations varDecls = (J.VariableDeclarations) statement; - if (J.Modifier.hasModifier(varDecls.getModifiers(), J.Modifier.Type.Final) - && varDecls.getVariables().stream().anyMatch(v -> v.getInitializer() == null)) { + if (J.Modifier.hasModifier(varDecls.getModifiers(), J.Modifier.Type.Final) && + varDecls.getVariables().stream().anyMatch(v -> v.getInitializer() == null)) { hasFinalUninitializedFieldVar = true; break; } diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java index e03c0b2e8..ddab4312b 100755 --- a/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalClassVisitor.java @@ -60,8 +60,8 @@ public class FinalClassVisitor extends JavaIsoVisitor { public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclaration, ExecutionContext ctx) { J.ClassDeclaration cd = super.visitClassDeclaration(classDeclaration, ctx); - if (cd.getKind() != J.ClassDeclaration.Kind.Type.Class || cd.hasModifier(J.Modifier.Type.Abstract) - || cd.hasModifier(J.Modifier.Type.Final) || cd.getType() == null) { + if (cd.getKind() != J.ClassDeclaration.Kind.Type.Class || cd.hasModifier(J.Modifier.Type.Abstract) || + cd.hasModifier(J.Modifier.Type.Final) || cd.getType() == null) { return cd; } @@ -90,8 +90,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclarat } private void excludeSupertypes(JavaType.FullyQualified type) { - if (type.getSupertype() != null && type.getOwningClass() != null - && typesToNotFinalize.add(type.getSupertype().getFullyQualifiedName())) { + if (type.getSupertype() != null && type.getOwningClass() != null && + typesToNotFinalize.add(type.getSupertype().getFullyQualifiedName())) { excludeSupertypes(type.getSupertype()); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java index d1f388f55..7e8c3adec 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizeMethodArguments.java @@ -198,8 +198,8 @@ private boolean hasFinalModifiers(final List parameters) { return parameters.stream().allMatch(p -> { if (p instanceof J.VariableDeclarations) { final List modifiers = ((J.VariableDeclarations) p).getModifiers(); - return !modifiers.isEmpty() - && modifiers.stream() + return !modifiers.isEmpty() && + modifiers.stream() .allMatch(m -> m.getType().equals(J.Modifier.Type.Final)); } return false; diff --git a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java index 52e76c0f9..45e9fd74e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/FinalizePrivateFields.java @@ -119,8 +119,8 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m } private boolean anyAnnotationApplied(Cursor variableCursor) { - return !service(AnnotationService.class).getAllAnnotations(variableCursor).isEmpty() - || variableCursor.getValue().getTypeExpression() instanceof J.AnnotatedType; + return !service(AnnotationService.class).getAllAnnotations(variableCursor).isEmpty() || + variableCursor.getValue().getTypeExpression() instanceof J.AnnotatedType; } /** @@ -134,10 +134,10 @@ private List collectPrivateFields(Cursor c .stream() .filter(J.VariableDeclarations.class::isInstance) .map(J.VariableDeclarations.class::cast) - .filter(mv -> mv.hasModifier(J.Modifier.Type.Private) - && !mv.hasModifier(J.Modifier.Type.Final) - && (!(topLevel instanceof Cs) || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()) || "const".equals(m.getKeyword()))) - && !mv.hasModifier(J.Modifier.Type.Volatile)) + .filter(mv -> mv.hasModifier(J.Modifier.Type.Private) && + !mv.hasModifier(J.Modifier.Type.Final) && + (!(topLevel instanceof Cs) || mv.getModifiers().stream().noneMatch(m -> "readonly".equals(m.getKeyword()) || "const".equals(m.getKeyword()))) && + !mv.hasModifier(J.Modifier.Type.Volatile)) .filter(mv -> !anyAnnotationApplied(new Cursor(bodyCursor, mv))) .map(J.VariableDeclarations::getVariables) .flatMap(Collection::stream) @@ -249,9 +249,9 @@ private static boolean isInLoop(Cursor cursor) { * @return true if the cursor is in a constructor or an initializer block (both static or non-static) */ private static boolean isInitializedByClass(Cursor cursor, boolean privateFieldIsStatic) { - Object parent = cursor.dropParentWhile(p -> (p instanceof J.Block && !((J.Block) p).isStatic()) - || p instanceof JRightPadded - || p instanceof JLeftPadded) + Object parent = cursor.dropParentWhile(p -> (p instanceof J.Block && !((J.Block) p).isStatic()) || + p instanceof JRightPadded || + p instanceof JLeftPadded) .getValue(); if (parent instanceof J.Block) { return privateFieldIsStatic; diff --git a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorVisitor.java b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorVisitor.java index fcef79ad5..5205562b4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/HideUtilityClassConstructorVisitor.java @@ -124,8 +124,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, P @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, P p) { J.MethodDeclaration md = super.visitMethodDeclaration(method, p); - if (md.getMethodType() == null || !md.isConstructor() - || (md.hasModifier(J.Modifier.Type.Private) || md.hasModifier(J.Modifier.Type.Protected) || md.getMethodType().getDeclaringType().getKind().equals(JavaType.Class.Kind.Enum))) { + if (md.getMethodType() == null || !md.isConstructor() || + (md.hasModifier(J.Modifier.Type.Private) || md.hasModifier(J.Modifier.Type.Protected) || md.getMethodType().getDeclaringType().getKind().equals(JavaType.Class.Kind.Enum))) { return md; } diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index c8b207e9b..aafef2431 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -52,8 +52,8 @@ public String getDisplayName() { @Override public String getDescription() { - return "Adds pattern variables to `instanceof` expressions wherever the same (side effect free) expression is referenced in a corresponding type cast expression within the flow scope of the `instanceof`." - + " Currently, this recipe supports `if` statements and ternary operator expressions."; + return "Adds pattern variables to `instanceof` expressions wherever the same (side effect free) expression is referenced in a corresponding type cast expression within the flow scope of the `instanceof`." + + " Currently, this recipe supports `if` statements and ternary operator expressions."; } @Override @@ -167,8 +167,8 @@ public void registerInstanceOf(J.InstanceOf instanceOf, Set contexts) { JavaType type = ((TypedTree) instanceOf.getClazz()).getType(); Optional existing = instanceOfs.keySet().stream() - .filter(k -> TypeUtils.isAssignableTo(type, k.getType()) - && SemanticallyEqual.areEqual(k.getExpression(), expression)) + .filter(k -> TypeUtils.isAssignableTo(type, k.getType()) && + SemanticallyEqual.areEqual(k.getExpression(), expression)) .findAny(); if (!existing.isPresent()) { instanceOfs.put(new ExpressionAndType(expression, type), instanceOf); @@ -182,8 +182,8 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) { JavaType type = typeCast.getClazz().getTree().getType(); Optional match = instanceOfs.keySet().stream() - .filter(k -> TypeUtils.isAssignableTo(type, k.getType()) - && SemanticallyEqual.areEqual(k.getExpression(), expression)) + .filter(k -> TypeUtils.isAssignableTo(type, k.getType()) && + SemanticallyEqual.areEqual(k.getExpression(), expression)) .findAny(); if (match.isPresent()) { Cursor parent = cursor.getParentTreeCursor(); @@ -192,8 +192,8 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) { for (Iterator it = cursor.getPath(); it.hasNext(); ) { Object next = it.next(); if (validContexts.contains(next)) { - if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable - && !variablesToDelete.containsKey(instanceOf)) { + if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable && + !variablesToDelete.containsKey(instanceOf)) { variablesToDelete.put(instanceOf, parent.getValue()); } else { replacements.put(typeCast, instanceOf); @@ -258,9 +258,9 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) { VariableNameStrategy strategy; if (root instanceof J.If) { J.VariableDeclarations.NamedVariable variable = variablesToDelete.get(instanceOf); - strategy = variable != null - ? VariableNameStrategy.exact(variable.getSimpleName()) - : VariableNameStrategy.normal(contextScopes.get(instanceOf)); + strategy = variable != null ? + VariableNameStrategy.exact(variable.getSimpleName()) : + VariableNameStrategy.normal(contextScopes.get(instanceOf)); } else { strategy = VariableNameStrategy.short_(); } diff --git a/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java b/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java index 9b2bb5eb9..024f920ae 100755 --- a/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java +++ b/src/main/java/org/openrewrite/staticanalysis/IsEmptyCallOnCollections.java @@ -64,9 +64,9 @@ public TreeVisitor getVisitor() { public J visitBinary(J.Binary binary, ExecutionContext ctx) { if (isZero(binary.getLeft()) || isZero(binary.getRight())) { boolean zeroRight = isZero(binary.getRight()); - if (binary.getOperator() == J.Binary.Type.Equal || binary.getOperator() == J.Binary.Type.NotEqual - || zeroRight && binary.getOperator() == J.Binary.Type.GreaterThan - || !zeroRight && binary.getOperator() == J.Binary.Type.LessThan) { + if (binary.getOperator() == J.Binary.Type.Equal || binary.getOperator() == J.Binary.Type.NotEqual || + zeroRight && binary.getOperator() == J.Binary.Type.GreaterThan || + !zeroRight && binary.getOperator() == J.Binary.Type.LessThan) { J maybeSizeCall = zeroRight ? binary.getLeft() : binary.getRight(); if (maybeSizeCall instanceof J.MethodInvocation) { J.MethodInvocation maybeSizeCallMethod = (J.MethodInvocation) maybeSizeCall; @@ -78,10 +78,10 @@ public J visitBinary(J.Binary binary, ExecutionContext ctx) { } } else if (isOne(binary.getLeft()) || isOne(binary.getRight())) { boolean oneRight = isOne(binary.getRight()); - if ((oneRight && binary.getOperator() == J.Binary.Type.LessThan) - || (!oneRight && binary.getOperator() == J.Binary.Type.GreaterThan) - || (oneRight && binary.getOperator() == J.Binary.Type.GreaterThanOrEqual) - || (!oneRight && binary.getOperator() == J.Binary.Type.LessThanOrEqual)) { + if ((oneRight && binary.getOperator() == J.Binary.Type.LessThan) || + (!oneRight && binary.getOperator() == J.Binary.Type.GreaterThan) || + (oneRight && binary.getOperator() == J.Binary.Type.GreaterThanOrEqual) || + (!oneRight && binary.getOperator() == J.Binary.Type.LessThanOrEqual)) { J maybeSizeCall = oneRight ? binary.getLeft() : binary.getRight(); if (maybeSizeCall instanceof J.MethodInvocation) { J.MethodInvocation maybeSizeCallMethod = (J.MethodInvocation) maybeSizeCall; diff --git a/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java b/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java index 02a0d9ee9..e98c6c1fb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java +++ b/src/main/java/org/openrewrite/staticanalysis/MinimumSwitchCases.java @@ -243,8 +243,8 @@ private boolean isDefault(J.Case case_) { private boolean switchesOnEnum(J.Switch switch_) { JavaType selectorType = switch_.getSelector().getTree().getType(); - return selectorType instanceof JavaType.Class - && ((JavaType.Class) selectorType).getKind() == JavaType.Class.Kind.Enum; + return selectorType instanceof JavaType.Class && + ((JavaType.Class) selectorType).getKind() == JavaType.Class.Kind.Enum; } }; diff --git a/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java b/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java index 4bf09e61f..251cd9846 100644 --- a/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java +++ b/src/main/java/org/openrewrite/staticanalysis/MissingOverrideAnnotation.java @@ -83,12 +83,12 @@ private Cursor getCursorToParentScope(Cursor cursor) { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - if (!method.hasModifier(J.Modifier.Type.Static) - && !method.isConstructor() - && !service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION) - && TypeUtils.isOverride(method.getMethodType()) - && !(Boolean.TRUE.equals(ignoreAnonymousClassMethods) - && getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) { + if (!method.hasModifier(J.Modifier.Type.Static) && + !method.isConstructor() && + !service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION) && + TypeUtils.isOverride(method.getMethodType()) && + !(Boolean.TRUE.equals(ignoreAnonymousClassMethods) && + getCursorToParentScope(getCursor()).getValue() instanceof J.NewClass)) { method = JavaTemplate.apply("@Override", getCursor(), method.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); } diff --git a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java index 171b504d8..3d3498634 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoDoubleBraceInitialization.java @@ -69,18 +69,18 @@ public TreeVisitor getVisitor() { private static class NoDoubleBraceInitializationVisitor extends JavaIsoVisitor { private boolean isSupportedDoubleBraceInitialization(J.NewClass nc) { - if (getCursor().getParent() == null - || getCursor().getParent().firstEnclosing(J.class) instanceof J.MethodInvocation - || getCursor().getParent().firstEnclosing(J.class) instanceof J.NewClass) { + if (getCursor().getParent() == null || + getCursor().getParent().firstEnclosing(J.class) instanceof J.MethodInvocation || + getCursor().getParent().firstEnclosing(J.class) instanceof J.NewClass) { return false; } - if (nc.getBody() != null && !nc.getBody().getStatements().isEmpty() - && nc.getBody().getStatements().size() == 1 - && nc.getBody().getStatements().get(0) instanceof J.Block - && getCursor().getParent(3) != null) { - return TypeUtils.isAssignableTo(MAP_TYPE, nc.getType()) - || TypeUtils.isAssignableTo(LIST_TYPE, nc.getType()) - || TypeUtils.isAssignableTo(SET_TYPE, nc.getType()); + if (nc.getBody() != null && !nc.getBody().getStatements().isEmpty() && + nc.getBody().getStatements().size() == 1 && + nc.getBody().getStatements().get(0) instanceof J.Block && + getCursor().getParent(3) != null) { + return TypeUtils.isAssignableTo(MAP_TYPE, nc.getType()) || + TypeUtils.isAssignableTo(LIST_TYPE, nc.getType()) || + TypeUtils.isAssignableTo(SET_TYPE, nc.getType()); } return false; } @@ -96,8 +96,8 @@ public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { J.Block secondBlock = (J.Block) nc.getBody().getStatements().get(0); List initStatements = secondBlock.getStatements(); - boolean maybeMistakenlyMissedAddingElement = !initStatements.isEmpty() - && initStatements.stream().allMatch(J.NewClass.class::isInstance); + boolean maybeMistakenlyMissedAddingElement = !initStatements.isEmpty() && + initStatements.stream().allMatch(J.NewClass.class::isInstance); if (maybeMistakenlyMissedAddingElement) { JavaType newClassType = nc.getType(); diff --git a/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java b/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java index c21037697..9db0dbf51 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReferentialEqualityToObjectEquals.java @@ -126,8 +126,8 @@ public J visitBinary(J.Binary binary, ExecutionContext ctx) { } private boolean isExcludedBinary(J.Binary binary) { - return isInEqualsOverrideMethod() || isPrimitiveNull(binary.getRight()) || hasThisIdentifier(binary) || isBoxedTypeComparison(binary) - || TypeUtils.isOfClassType(binary.getLeft().getType(), "java.lang.Enum") || TypeUtils.isOfClassType(binary.getRight().getType(), "java.lang.Enum"); + return isInEqualsOverrideMethod() || isPrimitiveNull(binary.getRight()) || hasThisIdentifier(binary) || isBoxedTypeComparison(binary) || + TypeUtils.isOfClassType(binary.getLeft().getType(), "java.lang.Enum") || TypeUtils.isOfClassType(binary.getRight().getType(), "java.lang.Enum"); } private boolean isInEqualsOverrideMethod() { @@ -143,28 +143,28 @@ private boolean isPrimitiveNull(Expression expression) { } private boolean hasThisIdentifier(J.Binary binary) { - return ((binary.getRight() instanceof J.Identifier && "this".equals(((J.Identifier) binary.getRight()).getSimpleName())) - || ((binary.getLeft() instanceof J.Identifier && "this".equals(((J.Identifier) binary.getLeft()).getSimpleName())))); + return ((binary.getRight() instanceof J.Identifier && "this".equals(((J.Identifier) binary.getRight()).getSimpleName())) || + ((binary.getLeft() instanceof J.Identifier && "this".equals(((J.Identifier) binary.getLeft()).getSimpleName())))); } private boolean isBoxedTypeComparison(J.Binary binary) { - if (binary.getLeft() != null && binary.getLeft().getType() != null - && binary.getRight() != null && binary.getRight().getType() != null) { - return isBoxed(binary.getRight().getType()) - && isBoxed(binary.getLeft().getType()); + if (binary.getLeft() != null && binary.getLeft().getType() != null && + binary.getRight() != null && binary.getRight().getType() != null) { + return isBoxed(binary.getRight().getType()) && + isBoxed(binary.getLeft().getType()); } return false; } private boolean isBoxed(JavaType type) { - return type instanceof JavaType.Primitive - || TypeUtils.isOfClassType(type, "java.lang.Byte") - || TypeUtils.isOfClassType(type, "java.lang.Character") - || TypeUtils.isOfClassType(type, "java.lang.Short") - || TypeUtils.isOfClassType(type, "java.lang.Integer") - || TypeUtils.isOfClassType(type, "java.lang.Long") - || TypeUtils.isOfClassType(type, "java.lang.Float") - || TypeUtils.isOfClassType(type, "java.lang.Double") - || TypeUtils.isOfClassType(type, "java.lang.Boolean"); + return type instanceof JavaType.Primitive || + TypeUtils.isOfClassType(type, "java.lang.Byte") || + TypeUtils.isOfClassType(type, "java.lang.Character") || + TypeUtils.isOfClassType(type, "java.lang.Short") || + TypeUtils.isOfClassType(type, "java.lang.Integer") || + TypeUtils.isOfClassType(type, "java.lang.Long") || + TypeUtils.isOfClassType(type, "java.lang.Float") || + TypeUtils.isOfClassType(type, "java.lang.Double") || + TypeUtils.isOfClassType(type, "java.lang.Boolean"); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java index 958faafa3..067fa55dc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java @@ -59,8 +59,8 @@ public TreeVisitor getVisitor() { public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation invocation = super.visitMethodInvocation(method, ctx); - if (invocation.getMethodType() != null && "finalize".equals(invocation.getMethodType().getName()) - && (invocation.getMethodType().getDeclaringType().getSupertype() != null && Object.class.getName().equals(invocation.getMethodType().getDeclaringType().getSupertype().getFullyQualifiedName()))) { + if (invocation.getMethodType() != null && "finalize".equals(invocation.getMethodType().getName()) && + (invocation.getMethodType().getDeclaringType().getSupertype() != null && Object.class.getName().equals(invocation.getMethodType().getDeclaringType().getSupertype().getFullyQualifiedName()))) { //noinspection DataFlowIssue return null; } diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceValidateNotNullHavingVarargsWithObjectsRequireNonNull.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceValidateNotNullHavingVarargsWithObjectsRequireNonNull.java index f0513ca5a..5e269cebc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceValidateNotNullHavingVarargsWithObjectsRequireNonNull.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceValidateNotNullHavingVarargsWithObjectsRequireNonNull.java @@ -56,9 +56,9 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) } List arguments = mi.getArguments(); - String template = arguments.size() == 2 - ? "Objects.requireNonNull(#{any()}, #{any(java.lang.String)})" - : String.format("Objects.requireNonNull(#{any()}, () -> String.format(#{any(java.lang.String)}, %s))", + String template = arguments.size() == 2 ? + "Objects.requireNonNull(#{any()}, #{any(java.lang.String)})" : + String.format("Objects.requireNonNull(#{any()}, () -> String.format(#{any(java.lang.String)}, %s))", String.join(", ", Collections.nCopies(arguments.size() - 2, "#{any()}"))); diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java index f42c5d590..9526ccc07 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyBooleanReturn.java @@ -202,8 +202,8 @@ private boolean hasElseWithComment(J.If.@Nullable Else else_) { if (!else_.getBody().getComments().isEmpty()) { return true; } - return else_.getBody() instanceof J.Block - && !((J.Block) else_.getBody()).getStatements().get(0).getComments().isEmpty(); + return else_.getBody() instanceof J.Block && + !((J.Block) else_.getBody()).getStatements().get(0).getComments().isEmpty(); } }; } diff --git a/src/main/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSet.java b/src/main/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSet.java index 7339e09f3..490845b92 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSet.java +++ b/src/main/java/org/openrewrite/staticanalysis/SortedSetStreamToLinkedHashSet.java @@ -55,9 +55,9 @@ public TreeVisitor getVisitor() { @Override public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); - if (STREAM_COLLECT_METHOD_MATCHER.matches(mi) - && STREAM_SORTED_METHOD_MATCHER.matches(mi.getSelect()) - && COLLECTORS_TO_SET_METHOD_MATCHER.matches(mi.getArguments().get(0))) { + if (STREAM_COLLECT_METHOD_MATCHER.matches(mi) && + STREAM_SORTED_METHOD_MATCHER.matches(mi.getSelect()) && + COLLECTORS_TO_SET_METHOD_MATCHER.matches(mi.getArguments().get(0))) { maybeRemoveImport("java.util.stream.Collectors.toSet"); maybeAddImport("java.util.LinkedHashSet"); maybeAddImport("java.util.stream.Collectors"); diff --git a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java index 5036d36d6..82d15c064 100644 --- a/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java +++ b/src/main/java/org/openrewrite/staticanalysis/TernaryOperatorsShouldNotBeNested.java @@ -220,19 +220,19 @@ private J.Case toCase(final J.Identifier switchVar, final J.Ternary ternary) { J.MethodInvocation inv = ((J.MethodInvocation) ternary.getCondition()); if (isObjectsEquals(inv)) { maybeRemoveImport("java.util.Objects"); - compare = isVariable(inv.getArguments().get(0)) - ? inv.getArguments().get(1) - : inv.getArguments().get(0); + compare = isVariable(inv.getArguments().get(0)) ? + inv.getArguments().get(1) : + inv.getArguments().get(0); } else { - compare = isEqualVariable(switchVar, inv.getSelect()) - ? inv.getArguments().get(0) - : inv.getSelect(); + compare = isEqualVariable(switchVar, inv.getSelect()) ? + inv.getArguments().get(0) : + inv.getSelect(); } } else if (isEqualsBinary(ternary.getCondition())) { J.Binary bin = ((J.Binary) ternary.getCondition()); - compare = isEqualVariable(switchVar, bin.getLeft()) - ? bin.getRight() - : bin.getLeft(); + compare = isEqualVariable(switchVar, bin.getLeft()) ? + bin.getRight() : + bin.getLeft(); } else { throw new IllegalArgumentException( "Only J.Binary or J.MethodInvocation are expected as ternary conditions when creating a switch case"); diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java index adb5d1063..31b839a05 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryPrimitiveAnnotations.java @@ -63,9 +63,9 @@ public TreeVisitor getVisitor() { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx); - if (md.getReturnTypeExpression() != null - && !(md.getReturnTypeExpression() instanceof J.ArrayType) - && md.getReturnTypeExpression().getType() instanceof JavaType.Primitive) { + if (md.getReturnTypeExpression() != null && + !(md.getReturnTypeExpression() instanceof J.ArrayType) && + md.getReturnTypeExpression().getType() instanceof JavaType.Primitive) { md = maybeAutoFormat(md, md.withLeadingAnnotations(filterAnnotations(md.getLeadingAnnotations())), ctx); } return md; diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java index 76c6be992..4cf7f844e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryThrows.java @@ -165,10 +165,10 @@ private Set findExceptionCandidates(J.@Nullable MethodD } //noinspection ConstantConditions - if ((method.getMethodType().getDeclaringType() != null && method.getMethodType().getDeclaringType().getFlags().contains(Flag.Final)) - || method.isAbstract() || method.hasModifier(J.Modifier.Type.Static) - || method.hasModifier(J.Modifier.Type.Private) - || method.hasModifier(J.Modifier.Type.Final)) { + if ((method.getMethodType().getDeclaringType() != null && method.getMethodType().getDeclaringType().getFlags().contains(Flag.Final)) || + method.isAbstract() || method.hasModifier(J.Modifier.Type.Static) || + method.hasModifier(J.Modifier.Type.Private) || + method.hasModifier(J.Modifier.Type.Final)) { //Consider all checked exceptions as candidates if the type/method are final or the method is private or static. return candidates; } diff --git a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java index 8a9bbc899..581a45deb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java +++ b/src/main/java/org/openrewrite/staticanalysis/UpperCaseLiteralSuffixes.java @@ -70,9 +70,9 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations J.VariableDeclarations.NamedVariable nv = super.visitVariable(variable, ctx); if (nv.getInitializer() instanceof J.Literal && nv.getInitializer().getType() != null) { J.Literal initializer = (J.Literal) nv.getInitializer(); - if (initializer.getType() == JavaType.Primitive.Double - || initializer.getType() == JavaType.Primitive.Float - || initializer.getType() == JavaType.Primitive.Long) { + if (initializer.getType() == JavaType.Primitive.Double || + initializer.getType() == JavaType.Primitive.Float || + initializer.getType() == JavaType.Primitive.Long) { String upperValueSource = upperCaseSuffix(initializer.getValueSource()); if (upperValueSource != null && !upperValueSource.equals(initializer.getValueSource())) { nv = nv.withInitializer(initializer.withValueSource(upperValueSource)); diff --git a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java index e31d3df49..236e552fa 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java @@ -113,8 +113,8 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - if ((m.hasModifier(J.Modifier.Type.Public) || m.hasModifier(J.Modifier.Type.Private) || m.getModifiers().isEmpty()) - && m.getReturnTypeExpression() != null) { + if ((m.hasModifier(J.Modifier.Type.Public) || m.hasModifier(J.Modifier.Type.Private) || m.getModifiers().isEmpty()) && + m.getReturnTypeExpression() != null) { JavaType.FullyQualified originalType = TypeUtils.asFullyQualified(m.getReturnTypeExpression().getType()); if (originalType != null && rspecRulesReplaceTypeMap.containsKey(originalType.getFullyQualifiedName())) { diff --git a/src/main/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAll.java b/src/main/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAll.java index ed53a92ff..91bd4ba86 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAll.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseForEachRemoveInsteadOfSetRemoveAll.java @@ -54,14 +54,14 @@ private boolean returnValueIsUsed() { Iterator cIterator = getCursor().getPathAsCursors(); while (cIterator.hasNext()) { Cursor p = cIterator.next(); - if (p.getValue() instanceof J.ClassDeclaration - || p.getValue() instanceof J.Block - || p.getValue() instanceof J.Lambda) { + if (p.getValue() instanceof J.ClassDeclaration || + p.getValue() instanceof J.Block || + p.getValue() instanceof J.Lambda) { return false; - } else if (p.getValue() instanceof J.ControlParentheses - || p.getValue() instanceof J.Return - || p.getValue() instanceof J.VariableDeclarations - || p.getValue() instanceof J.Assignment) { + } else if (p.getValue() instanceof J.ControlParentheses || + p.getValue() instanceof J.Return || + p.getValue() instanceof J.VariableDeclarations || + p.getValue() instanceof J.Assignment) { return true; } } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java index 693a6c15f..7ea4aa90b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseLambdaForFunctionalInterface.java @@ -76,10 +76,10 @@ public J visitNewClass(J.NewClass newClass, ExecutionContext ctx) { return n; } - if (usesThis(getCursor()) - || shadowsLocalVariable(getCursor()) - || usedAsStatement(getCursor()) - || fieldInitializerReferencingUninitializedField(getCursor())) { + if (usesThis(getCursor()) || + shadowsLocalVariable(getCursor()) || + usedAsStatement(getCursor()) || + fieldInitializerReferencingUninitializedField(getCursor())) { return n; } @@ -329,9 +329,9 @@ public J.Identifier visitIdentifier(J.Identifier ident, Integer integer) { if (referencesUninitializedFinalField.get()) { return ident; } - if (ident.getFieldType() != null && ident.getFieldType().hasFlags(Flag.Final) - && !ident.getFieldType().hasFlags(Flag.HasInit) - && owner.equals(ident.getFieldType().getOwner())) { + if (ident.getFieldType() != null && ident.getFieldType().hasFlags(Flag.Final) && + !ident.getFieldType().hasFlags(Flag.HasInit) && + owner.equals(ident.getFieldType().getOwner())) { referencesUninitializedFinalField.set(true); } return super.visitIdentifier(ident, integer); From 6ccce06eec50260005bc08fcdc09bb8fddc46216 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 21 Sep 2024 18:01:38 +0200 Subject: [PATCH 121/183] Correct casing on AddSerialAnnotationToSerialVersionUID Context: https://github.com/openrewrite/rewrite-migrate-java/issues/96 --- ...ionUID.java => AddSerialAnnotationToSerialVersionUID.java} | 2 +- ...st.java => AddSerialAnnotationToSerialVersionUIDTest.java} | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename src/main/java/org/openrewrite/staticanalysis/{AddSerialAnnotationToserialVersionUID.java => AddSerialAnnotationToSerialVersionUID.java} (98%) rename src/test/java/org/openrewrite/staticanalysis/{AddSerialAnnotationToserialVersionUIDTest.java => AddSerialAnnotationToSerialVersionUIDTest.java} (97%) diff --git a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUID.java similarity index 98% rename from src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java rename to src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUID.java index ec9ac01fd..746399b09 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUID.java +++ b/src/main/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUID.java @@ -32,7 +32,7 @@ import java.time.Duration; import java.util.Comparator; -public class AddSerialAnnotationToserialVersionUID extends Recipe { +public class AddSerialAnnotationToSerialVersionUID extends Recipe { @Override public String getDisplayName() { return "Add `@Serial` annotation to `serialVersionUID`"; diff --git a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java similarity index 97% rename from src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java rename to src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java index 46a5e48be..ea1e39ec5 100644 --- a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToserialVersionUIDTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java @@ -23,10 +23,10 @@ import static org.openrewrite.java.Assertions.java; import static org.openrewrite.java.Assertions.javaVersion; -class AddSerialAnnotationToserialVersionUIDTest implements RewriteTest { +class AddSerialAnnotationToSerialVersionUIDTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new AddSerialAnnotationToserialVersionUID()) + spec.recipe(new AddSerialAnnotationToSerialVersionUID()) .allSources(sourceSpec -> sourceSpec.markers(javaVersion(17))); } From 56503f5c3d749432201610749f64018677e72fd4 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Mon, 23 Sep 2024 23:23:02 -0500 Subject: [PATCH 122/183] refactor: Update Gradle wrapper (#343) Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sibmFtZSI6ImRpc3RyaWJ1dGlvbiIsInZhbHVlIjoiYmluIn1d Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f95442d59..6077047a1 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip -distributionSha256Sum=1541fa36599e12857140465f3c91a97409b4512501c26f9631fb113e392c5bd1 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 5c43d9487304d136f612c3273d5fb09b4756197e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Tue, 24 Sep 2024 17:49:33 +0200 Subject: [PATCH 123/183] Show `@Serial` addition when chained with adding field --- ...erialAnnotationToSerialVersionUIDTest.java | 43 +++++++++++++++---- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java index ea1e39ec5..cb3beed75 100644 --- a/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/AddSerialAnnotationToSerialVersionUIDTest.java @@ -38,7 +38,7 @@ void addSerialAnnotation() { java( """ import java.io.Serializable; - + class Example implements Serializable { private static final long serialVersionUID = 1L; } @@ -46,7 +46,7 @@ class Example implements Serializable { """ import java.io.Serial; import java.io.Serializable; - + class Example implements Serializable { @Serial private static final long serialVersionUID = 1L; @@ -57,14 +57,41 @@ class Example implements Serializable { } @Test - void shouldNoopIfAlreadyPresent() { + void shouldAddToNewFieldWhenChained() { rewriteRun( + spec -> spec.recipes( + new AddSerialVersionUidToSerializable(), + new AddSerialAnnotationToSerialVersionUID()), //language=java java( """ import java.io.Serializable; + + class Example implements Serializable { + } + """, + """ import java.io.Serial; + import java.io.Serializable; + + class Example implements Serializable { + @Serial + private static final long serialVersionUID = 1; + } + """ + ) + ); + } + @Test + void shouldNoopIfAlreadyPresent() { + rewriteRun( + //language=java + java( + """ + import java.io.Serializable; + import java.io.Serial; + class Example implements Serializable { String var1 = "first variable"; @Serial @@ -97,7 +124,7 @@ void shouldNotAnnotateOnJava11() { java( """ import java.io.Serializable; - + class Example implements Serializable { private static final long serialVersionUID = 1L; } @@ -114,14 +141,14 @@ void shouldNotAnnotateOtherFields() { java( """ import java.io.Serializable; - + class Example implements Serializable { static final long serialVersionUID = 1L; private final long serialVersionUID = 1L; private static long serialVersionUID = 1L; private static final int serialVersionUID = 1L; private static final long foo = 1L; - + void doSomething() { long serialVersionUID = 1L; } @@ -138,7 +165,7 @@ void shouldAnnotatedFieldsInInnerClasses() { java( """ import java.io.Serializable; - + class Outer implements Serializable { private static final long serialVersionUID = 1; static class Inner implements Serializable { @@ -149,7 +176,7 @@ static class Inner implements Serializable { """ import java.io.Serial; import java.io.Serializable; - + class Outer implements Serializable { @Serial private static final long serialVersionUID = 1; From 07e416e1db4b7c3cd0eaa02e6b1dbaf97f259dd5 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Fri, 27 Sep 2024 11:05:55 -0700 Subject: [PATCH 124/183] Fix adding private/static/final modifiers to interface fields --- .../ReplaceDuplicateStringLiterals.java | 8 ++++--- .../ReplaceDuplicateStringLiteralsTest.java | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 3932c31a3..1f4138dd7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -31,6 +31,7 @@ import static java.util.Arrays.asList; import static java.util.Collections.emptyList; +import static java.util.Objects.requireNonNull; import static org.openrewrite.Tree.randomId; @Value @@ -71,7 +72,7 @@ public Duration getEstimatedEffortPerOccurrence() { public TreeVisitor getVisitor() { return Preconditions.check(new UsesType<>("java.lang.String", false), new JavaVisitor() { @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) tree; Optional sourceSet = cu.getMarkers().findFirst(JavaSourceSet.class); @@ -117,7 +118,8 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct continue; } J.Literal replaceLiteral = duplicateLiterals.get(0).withId(randomId()); - JavaTemplate template = JavaTemplate.builder("private static final String " + variableName + " = #{any(String)};").build(); + String modifiers = (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Interface) ? "" : "private static final "; + JavaTemplate template = JavaTemplate.builder(modifiers + "String " + variableName + " = #{any(String)};").build(); if (classDecl.getKind() == J.ClassDeclaration.Kind.Type.Enum) { J.Block applied = template .apply(new Cursor(getCursor(), classDecl.getBody()), classDecl.getBody().getCoordinates().lastStatement(), replaceLiteral); @@ -136,7 +138,7 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct duplicateLiteralInfo = null; duplicateLiteralsMap = null; return replacements.isEmpty() ? classDecl : - new ReplaceStringLiterals(classDecl, replacements).visitNonNull(classDecl, ctx, getCursor().getParent()); + new ReplaceStringLiterals(classDecl, replacements).visitNonNull(classDecl, ctx, requireNonNull(getCursor().getParent())); } /** diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index c58e48d5d..63b3f7a8f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -577,4 +577,28 @@ enum Scratch { ) ); } + + @Test + void interfaceLiteralsCannotBePrivate() { + rewriteRun( + //language=java + java( + """ + interface A { + String val1 = "value"; + String val2 = "value"; + String val3 = "value"; + } + """, + """ + interface A { + String VALUE = "value"; + String val1 = VALUE; + String val2 = VALUE; + String val3 = VALUE; + } + """ + ) + ); + } } From 72d7f5cb93a2d5a64a57665e4d781679eadb00dc Mon Sep 17 00:00:00 2001 From: aboyko Date: Fri, 13 Sep 2024 12:18:21 -0400 Subject: [PATCH 125/183] InstanceOf: no action if type casts with specific type params used Merge missing change --- .../InstanceOfPatternMatch.java | 94 ++++-- .../InstanceOfPatternMatchTest.java | 271 +++++++++++++++++- 2 files changed, 325 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index aafef2431..032eeebe8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -153,6 +153,12 @@ private static class ExpressionAndType { private final JavaType type; } + @Data + private static class VariableAndTypeTree { + private final J.VariableDeclarations.NamedVariable variable; + private final TypeTree type; + } + @Data private static class InstanceOfPatternReplacements { private final J root; @@ -160,7 +166,7 @@ private static class InstanceOfPatternReplacements { private final Map> contexts = new HashMap<>(); private final Map> contextScopes = new HashMap<>(); private final Map replacements = new HashMap<>(); - private final Map variablesToDelete = new HashMap<>(); + private final Map variablesToDelete = new HashMap<>(); public void registerInstanceOf(J.InstanceOf instanceOf, Set contexts) { Expression expression = instanceOf.getExpression(); @@ -192,13 +198,21 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) { for (Iterator it = cursor.getPath(); it.hasNext(); ) { Object next = it.next(); if (validContexts.contains(next)) { - if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable && - !variablesToDelete.containsKey(instanceOf)) { - variablesToDelete.put(instanceOf, parent.getValue()); + if (isAcceptableTypeCast(typeCast) && isTheSameAsOtherTypeCasts(typeCast, instanceOf)) { + if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable && + !variablesToDelete.containsKey(instanceOf)) { + variablesToDelete.put(instanceOf, new VariableAndTypeTree(parent.getValue(), parent.firstEnclosing(J.VariableDeclarations.class).getTypeExpression())); + } else { + replacements.put(typeCast, instanceOf); + } + contextScopes.computeIfAbsent(instanceOf, k -> new HashSet<>()).add(cursor); } else { - replacements.put(typeCast, instanceOf); + replacements.entrySet().removeIf(e -> e.getValue() == instanceOf); + variablesToDelete.remove(instanceOf); + contextScopes.remove(instanceOf); + contexts.remove(instanceOf); + instanceOfs.entrySet().removeIf(e -> e.getValue() == instanceOf); } - contextScopes.computeIfAbsent(instanceOf, k -> new HashSet<>()).add(cursor); break; } else if (root == next) { break; @@ -207,6 +221,24 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) { } } + private boolean isAcceptableTypeCast(J.TypeCast typeCast) { + TypeTree typeTree = typeCast.getClazz().getTree(); + if (typeTree instanceof J.ParameterizedType) { + return ((J.ParameterizedType) typeTree).getTypeParameters().stream().allMatch(J.Wildcard.class::isInstance); + } + return true; + } + + private boolean isTheSameAsOtherTypeCasts(J.TypeCast typeCast, J.InstanceOf instanceOf) { + return replacements + .entrySet() + .stream() + .filter(e -> e.getValue() == instanceOf) + .findFirst() + .map(e -> e.getKey().getType().equals(typeCast.getType())) + .orElse(true); + } + public boolean isEmpty() { return replacements.isEmpty() && variablesToDelete.isEmpty(); } @@ -225,24 +257,13 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) { name, type, null)); - JavaType.FullyQualified fqType = TypeUtils.asFullyQualified(type); - if (fqType != null && !fqType.getTypeParameters().isEmpty() && !(instanceOf.getClazz() instanceof J.ParameterizedType)) { - TypedTree oldTypeTree = (TypedTree) instanceOf.getClazz(); - - // Each type parameter is turned into a wildcard, i.e. `List` -> `List` or `Map.Entry` -> `Map.Entry` - List wildcardsList = IntStream.range(0, fqType.getTypeParameters().size()) - .mapToObj(i -> new J.Wildcard(randomId(), Space.EMPTY, Markers.EMPTY, null, null)) - .collect(Collectors.toList()); - J.ParameterizedType newTypeTree = new J.ParameterizedType( - randomId(), - oldTypeTree.getPrefix(), - Markers.EMPTY, - oldTypeTree.withPrefix(Space.EMPTY), - null, - oldTypeTree.getType() - ).withTypeParameters(wildcardsList); - result = result.withClazz(newTypeTree); + J currentTypeTree = instanceOf.getClazz(); + TypeTree typeCastTypeTree = computeTypeTreeFromTypeCasts(instanceOf); + // If type tree from typa cast is not parameterized then NVM. Instance of should already have proper type + if (typeCastTypeTree != null && typeCastTypeTree instanceof J.ParameterizedType) { + J.ParameterizedType parameterizedType = (J.ParameterizedType) typeCastTypeTree; + result = result.withClazz(parameterizedType.withId(Tree.randomId()).withPrefix(currentTypeTree.getPrefix())); } // update entry in replacements to share the pattern variable name @@ -254,13 +275,30 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) { return result; } + private TypeTree computeTypeTreeFromTypeCasts(J.InstanceOf instanceOf) { + TypeTree typeCastTypeTree = replacements + .entrySet() + .stream() + .filter(e -> e.getValue() == instanceOf) + .findFirst() + .map(e -> e.getKey().getClazz().getTree()) + .orElse(null); + if (typeCastTypeTree == null) { + VariableAndTypeTree variable = variablesToDelete.get(instanceOf); + if (variable != null) { + typeCastTypeTree = variable.getType(); + } + } + return typeCastTypeTree; + } + private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) { VariableNameStrategy strategy; if (root instanceof J.If) { - J.VariableDeclarations.NamedVariable variable = variablesToDelete.get(instanceOf); - strategy = variable != null ? - VariableNameStrategy.exact(variable.getSimpleName()) : - VariableNameStrategy.normal(contextScopes.get(instanceOf)); + VariableAndTypeTree variableData = variablesToDelete.get(instanceOf); + strategy = variableData != null + ? VariableNameStrategy.exact(variableData.getVariable().getSimpleName()) + : VariableNameStrategy.normal(contextScopes.get(instanceOf)); } else { strategy = VariableNameStrategy.short_(); } @@ -288,7 +326,7 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) { } public @Nullable J processVariableDeclarations(J.VariableDeclarations multiVariable) { - return multiVariable.getVariables().stream().anyMatch(variablesToDelete::containsValue) ? null : multiVariable; + return multiVariable.getVariables().stream().anyMatch(v -> variablesToDelete.values().stream().anyMatch(vd -> vd.getVariable() == v)) ? null : multiVariable; } } diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index 8279511d9..cfcbc9d06 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -133,7 +133,7 @@ void test(Object o) { } @Test - void genericsWithoutParameters() { + void typeParameters_1() { rewriteRun( //language=java java( @@ -142,21 +142,48 @@ void genericsWithoutParameters() { import java.util.List; import java.util.Map; import java.util.stream.Collectors; + import java.util.stream.Stream; public class A { @SuppressWarnings("unchecked") - public static List> applyRoutesType(Object routes) { + public static Stream> applyRoutesType(Object routes) { if (routes instanceof List) { List routesList = (List) routes; if (routesList.isEmpty()) { - return Collections.emptyList(); + return Stream.empty(); } if (routesList.stream() .anyMatch(route -> !(route instanceof Map))) { - return Collections.emptyList(); + return Stream.empty(); } return routesList.stream() - .map(route -> (Map) route) - .collect(Collectors.toList()); + .map(route -> (Map) route); + } + return Stream.empty(); + } + } + """ + ) + ); + } + + @Test + void typeParameters_2() { + rewriteRun( + //language=java + java( + """ + import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + public class A { + @SuppressWarnings("unchecked") + public static List> applyRoutesType(Object routes) { + if (routes instanceof List) { + List routesList = (List) routes; + if (routesList.isEmpty()) { + return Collections.emptyList(); + } } return Collections.emptyList(); } @@ -170,7 +197,111 @@ public static List> applyRoutesType(Object routes) { public class A { @SuppressWarnings("unchecked") public static List> applyRoutesType(Object routes) { - if (routes instanceof List routesList) { + if (routes instanceof List routesList) { + if (routesList.isEmpty()) { + return Collections.emptyList(); + } + } + return Collections.emptyList(); + } + } + """ + ) + ); + } + + @Test + void typeParameters_3() { + rewriteRun( + //language=java + java( + """ + import java.util.Collections; + import java.util.List; + public class A { + @SuppressWarnings("unchecked") + public static void applyRoutesType(Object routes) { + if (routes instanceof List) { + List routesList = (List) routes; + String.join(",", (List) routes); + } + } + } + """ + ) + ); + } + + @Test + void typeParameters_4() { + rewriteRun( + //language=java + java( + """ + import java.util.Collections; + import java.util.List; + public class A { + @SuppressWarnings("unchecked") + public static void applyRoutesType(Object routes) { + if (routes instanceof List) { + String.join(",", (List) routes); + } + } + } + """, """ + import java.util.Collections; + import java.util.List; + public class A { + @SuppressWarnings("unchecked") + public static void applyRoutesType(Object routes) { + if (routes instanceof List list) { + String.join(",", list); + } + } + } + """ + ) + ); + } + + @Test + void typeParameters_5() { + rewriteRun( + //language=java + java( + """ + import java.util.Arrays; + import java.util.Collection; + import java.util.List; + public class A { + @SuppressWarnings("unchecked") + private Collection addValueToList(List previousValues, Object value) { + if (previousValues == null) { + return (value instanceof Collection) ? (Collection) value : Arrays.asList(value); + } + return List.of(); + } + } + """ + ) + ); + } + + @Test + void typeParameters_6() { + rewriteRun( + //language=java + java( + """ + import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + public class A { + @SuppressWarnings("unchecked") + public static List> applyRoutesType(Object routes) { + if (routes instanceof List) { + List routesList = (List) routes; if (routesList.isEmpty()) { return Collections.emptyList(); } @@ -190,6 +321,76 @@ public static List> applyRoutesType(Object routes) { ); } + @Test + void typeParameters_7() { + rewriteRun( + //language=java + java( + """ + import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + public class A { + @SuppressWarnings("unchecked") + public static List> applyRoutesType(Object routes) { + if (routes instanceof List) { + return ((List) routes).stream() + .map(route -> (Map) route) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + } + """, """ + import java.util.Collections; + import java.util.List; + import java.util.Map; + import java.util.stream.Collectors; + public class A { + @SuppressWarnings("unchecked") + public static List> applyRoutesType(Object routes) { + if (routes instanceof List list) { + return list.stream() + .map(route -> (Map) route) + .collect(Collectors.toList()); + } + return Collections.emptyList(); + } + } + """ + ) + ); + } + + @Test + void typeParameters_8() { + rewriteRun( + //language=java + java( + """ + import java.util.Arrays; + import java.util.Collection; + import java.util.List; + public class A { + @SuppressWarnings("unchecked") + private Collection addValueToList(List previousValues, Object value) { + Collection cl = List.of(); + if (previousValues == null) { + if (value instanceof Collection) { + cl = (Collection) value; + } else { + cl = Arrays.asList(value.toString()); + } + } + return cl; + } + } + """ + ) + ); + } + @Test void primitiveArray() { rewriteRun( @@ -256,7 +457,7 @@ void conflictingVariableInBody() { public class A { void test(Object o) { if (o instanceof String) { - String string = 'x'; + String string = "x"; System.out.println((String) o); // String string1 = "y"; } @@ -267,7 +468,7 @@ void test(Object o) { public class A { void test(Object o) { if (o instanceof String string1) { - String string = 'x'; + String string = "x"; System.out.println(string1); // String string1 = "y"; } @@ -302,7 +503,7 @@ void test(Object o) { public class A { void test(Object o) { Map.Entry entry = null; - if (o instanceof Map.Entry entry1) { + if (o instanceof Map.Entry entry1) { entry = entry1; } System.out.println(entry); @@ -869,7 +1070,7 @@ Object test(Object o) { return o instanceof List ? ((List) o).get(0) : o.toString(); } } - """, + """/*, """ import java.util.List; public class A { @@ -877,7 +1078,7 @@ Object test(Object o) { return o instanceof List l ? l.get(0) : o.toString(); } } - """ + """*/ ) ); } @@ -975,6 +1176,52 @@ String test(Object o) { ) ); } + @Test + void iterableParameter() { + rewriteRun( + //language=java + java( + """ + import java.util.HashMap; + import java.util.List; + import java.util.Map; + + public class ApplicationSecurityGroupsParameterHelper { + + static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; + + public Map transformGatewayParameters(Map parameters) { + Map environment = new HashMap<>(); + Object applicationSecurityGroups = parameters.get(APPLICATION_SECURITY_GROUPS); + if (applicationSecurityGroups instanceof List) { + environment.put(APPLICATION_SECURITY_GROUPS, String.join(",", (List) applicationSecurityGroups)); + } + return environment; + } + } + """, + """ + import java.util.HashMap; + import java.util.List; + import java.util.Map; + + public class ApplicationSecurityGroupsParameterHelper { + + static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; + + public Map transformGatewayParameters(Map parameters) { + Map environment = new HashMap<>(); + Object applicationSecurityGroups = parameters.get(APPLICATION_SECURITY_GROUPS); + if (applicationSecurityGroups instanceof List list) { + environment.put(APPLICATION_SECURITY_GROUPS, String.join(",", list)); + } + return environment; + } + } + """ + ) + ); + } } @Nested From 87431507ab92ffb38e39ee85087cce38b5dc8522 Mon Sep 17 00:00:00 2001 From: Nate Date: Tue, 1 Oct 2024 16:09:19 -0700 Subject: [PATCH 126/183] update to latest error prone to remove protobuf-java@3.19.2 vulnerability --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 9349d8739..c6bb64e08 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { annotationProcessor("org.openrewrite:rewrite-templating:${rewriteVersion}") implementation("org.openrewrite:rewrite-templating:${rewriteVersion}") - compileOnly("com.google.errorprone:error_prone_core:2.19.1:with-dependencies") { + compileOnly("com.google.errorprone:error_prone_core:2.+:with-dependencies") { exclude("com.google.auto.service", "auto-service-annotations") } From acc1af761e1c14d1f09ea94d55d766857c430591 Mon Sep 17 00:00:00 2001 From: Sam Snyder Date: Tue, 1 Oct 2024 16:45:05 -0700 Subject: [PATCH 127/183] Fix tests which broke because a new version of annotations was released. Cleanup test warnings & whitespace --- .../staticanalysis/EqualsAvoidsNull.java | 3 +- .../InstanceOfPatternMatch.java | 18 ++++--- .../InstanceOfPatternMatchTest.java | 48 +++++++++---------- .../UseCollectionInterfacesTest.java | 12 ++--- .../UseDiamondOperatorTest.java | 22 ++++----- .../UseObjectNotifyAllTest.java | 16 +++---- 6 files changed, 57 insertions(+), 62 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java index 9ba2b97f6..e82365732 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java @@ -65,8 +65,9 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (style == null) { style = Checkstyle.equalsAvoidsNull(); } - return new EqualsAvoidsNullVisitor<>(style).visit(cu, ctx); + return new EqualsAvoidsNullVisitor<>(style).visitNonNull(cu, ctx); } + //noinspection DataFlowIssue return (J) tree; } } diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index 032eeebe8..bff256afa 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -33,10 +33,10 @@ import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import static java.util.Collections.emptyList; +import static java.util.Objects.requireNonNull; import static org.openrewrite.Tree.randomId; import static org.openrewrite.java.VariableNameUtils.GenerationStrategy.INCREMENT_NUMBER; @@ -171,6 +171,9 @@ private static class InstanceOfPatternReplacements { public void registerInstanceOf(J.InstanceOf instanceOf, Set contexts) { Expression expression = instanceOf.getExpression(); JavaType type = ((TypedTree) instanceOf.getClazz()).getType(); + if (type == null) { + return; + } Optional existing = instanceOfs.keySet().stream() .filter(k -> TypeUtils.isAssignableTo(type, k.getType()) && @@ -200,8 +203,9 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) { if (validContexts.contains(next)) { if (isAcceptableTypeCast(typeCast) && isTheSameAsOtherTypeCasts(typeCast, instanceOf)) { if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable && - !variablesToDelete.containsKey(instanceOf)) { - variablesToDelete.put(instanceOf, new VariableAndTypeTree(parent.getValue(), parent.firstEnclosing(J.VariableDeclarations.class).getTypeExpression())); + !variablesToDelete.containsKey(instanceOf)) { + variablesToDelete.put(instanceOf, new VariableAndTypeTree(parent.getValue(), + requireNonNull(parent.firstEnclosing(J.VariableDeclarations.class).getTypeExpression()))); } else { replacements.put(typeCast, instanceOf); } @@ -224,7 +228,7 @@ public void registerTypeCast(J.TypeCast typeCast, Cursor cursor) { private boolean isAcceptableTypeCast(J.TypeCast typeCast) { TypeTree typeTree = typeCast.getClazz().getTree(); if (typeTree instanceof J.ParameterizedType) { - return ((J.ParameterizedType) typeTree).getTypeParameters().stream().allMatch(J.Wildcard.class::isInstance); + return requireNonNull(((J.ParameterizedType) typeTree).getTypeParameters()).stream().allMatch(J.Wildcard.class::isInstance); } return true; } @@ -247,7 +251,7 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) { if (!contextScopes.containsKey(instanceOf)) { return instanceOf; } - @Nullable JavaType type = ((TypedTree) instanceOf.getClazz()).getType(); + JavaType type = ((TypedTree) instanceOf.getClazz()).getType(); String name = patternVariableName(instanceOf, cursor); J.InstanceOf result = instanceOf.withPattern(new J.Identifier( randomId(), @@ -260,8 +264,8 @@ public J.InstanceOf processInstanceOf(J.InstanceOf instanceOf, Cursor cursor) { J currentTypeTree = instanceOf.getClazz(); TypeTree typeCastTypeTree = computeTypeTreeFromTypeCasts(instanceOf); - // If type tree from typa cast is not parameterized then NVM. Instance of should already have proper type - if (typeCastTypeTree != null && typeCastTypeTree instanceof J.ParameterizedType) { + // If type tree from type cast is not parameterized then NVM. Instance of should already have proper type + if (typeCastTypeTree instanceof J.ParameterizedType) { J.ParameterizedType parameterizedType = (J.ParameterizedType) typeCastTypeTree; result = result.withClazz(parameterizedType.withId(Tree.randomId()).withPrefix(currentTypeTree.getPrefix())); } diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index cfcbc9d06..fb23b21bc 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -25,7 +25,7 @@ import static org.openrewrite.java.Assertions.java; import static org.openrewrite.java.Assertions.version; -@SuppressWarnings({"RedundantCast", "DataFlowIssue", "ConstantValue"}) +@SuppressWarnings({"RedundantCast", "DataFlowIssue", "ConstantValue", "ImplicitArrayToString", "PatternVariableCanBeUsed", "UnnecessaryLocalVariable", "SizeReplaceableByIsEmpty", "rawtypes", "ResultOfMethodCallIgnored", "ArraysAsListWithZeroOrOneArgument", "DuplicateCondition"}) class InstanceOfPatternMatchTest implements RewriteTest { @Override @@ -34,7 +34,7 @@ public void defaults(RecipeSpec spec) { .allSources(sourceSpec -> version(sourceSpec, 17)); } - @SuppressWarnings({"ImplicitArrayToString", "PatternVariableCanBeUsed", "UnnecessaryLocalVariable"}) + @Nested class If { @Test @@ -177,7 +177,6 @@ void typeParameters_2() { import java.util.Map; import java.util.stream.Collectors; public class A { - @SuppressWarnings("unchecked") public static List> applyRoutesType(Object routes) { if (routes instanceof List) { List routesList = (List) routes; @@ -195,7 +194,6 @@ public static List> applyRoutesType(Object routes) { import java.util.Map; import java.util.stream.Collectors; public class A { - @SuppressWarnings("unchecked") public static List> applyRoutesType(Object routes) { if (routes instanceof List routesList) { if (routesList.isEmpty()) { @@ -386,7 +384,7 @@ private Collection addValueToList(List previousValues, Object va return cl; } } - """ + """ ) ); } @@ -1121,6 +1119,7 @@ void test(Object t) { } } + @SuppressWarnings({"unchecked", "rawtypes"}) @Nested class Various { @Test @@ -1176,39 +1175,36 @@ String test(Object o) { ) ); } + @Test void iterableParameter() { rewriteRun( //language=java java( """ - import java.util.HashMap; - import java.util.List; - import java.util.Map; - - public class ApplicationSecurityGroupsParameterHelper { - - static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; + import java.util.HashMap; + import java.util.List; + import java.util.Map; - public Map transformGatewayParameters(Map parameters) { - Map environment = new HashMap<>(); - Object applicationSecurityGroups = parameters.get(APPLICATION_SECURITY_GROUPS); - if (applicationSecurityGroups instanceof List) { - environment.put(APPLICATION_SECURITY_GROUPS, String.join(",", (List) applicationSecurityGroups)); - } - return environment; - } - } - """, - """ + public class ApplicationSecurityGroupsParameterHelper { + static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; + public Map transformGatewayParameters(Map parameters) { + Map environment = new HashMap<>(); + Object applicationSecurityGroups = parameters.get(APPLICATION_SECURITY_GROUPS); + if (applicationSecurityGroups instanceof List) { + environment.put(APPLICATION_SECURITY_GROUPS, String.join(",", (List) applicationSecurityGroups)); + } + return environment; + } + } + """, + """ import java.util.HashMap; import java.util.List; import java.util.Map; - + public class ApplicationSecurityGroupsParameterHelper { - static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; - public Map transformGatewayParameters(Map parameters) { Map environment = new HashMap<>(); Object applicationSecurityGroups = parameters.get(APPLICATION_SECURITY_GROUPS); diff --git a/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java b/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java index ed52a9331..19e86a31a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseCollectionInterfacesTest.java @@ -30,7 +30,7 @@ import static org.openrewrite.java.Assertions.java; import static org.openrewrite.java.Assertions.javaVersion; -@SuppressWarnings("rawtypes") +@SuppressWarnings({"rawtypes", "StatementWithEmptyBody"}) class UseCollectionInterfacesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { @@ -45,7 +45,7 @@ void noTargetInUse() { """ import java.util.Collections; import java.util.Set; - + class Test { Set method() { return Collections.emptySet(); @@ -138,7 +138,7 @@ void annotatedReturnType() { rewriteRun( spec -> spec .allSources(s -> s.markers(javaVersion(9))) - .parser(JavaParser.fromJavaVersion().classpath("annotations-24.1.0")), + .parser(JavaParser.fromJavaVersion().classpath("annotations")), //language=java java( """ @@ -266,7 +266,7 @@ void annotatedFieldType() { rewriteRun( spec -> spec .allSources(s -> s.markers(javaVersion(9))) - .parser(JavaParser.fromJavaVersion().classpath("annotations-24.1.0")), + .parser(JavaParser.fromJavaVersion().classpath("annotations")), //language=java java( """ @@ -741,7 +741,7 @@ void enumSetHasDifferentGenericTypeThanSet() { java( """ import java.util.EnumSet; - + class Test { public EnumSet values = EnumSet.allOf(A.class); void iterate() { @@ -993,11 +993,9 @@ void groovyDefVariable() { //language=groovy """ library('other-library') - def myMap = [ myEntry: [[ key: value ]] ] - runPipeline(myMap: myMap) """ ) diff --git a/src/test/java/org/openrewrite/staticanalysis/UseDiamondOperatorTest.java b/src/test/java/org/openrewrite/staticanalysis/UseDiamondOperatorTest.java index 72c19b9a0..f6986b6b9 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseDiamondOperatorTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseDiamondOperatorTest.java @@ -115,14 +115,14 @@ void useDiamondOperatorTest2() { import java.util.function.Predicate; import java.util.List; import java.util.Map; - + class Foo { Map map; Map unknownMap; public Foo(Predicate p) {} public void something(Foo> foos){} public void somethingEasy(List> l){} - + Foo getFoo() { // variable type initializer Foo> f = new Foo>(it -> it.stream().anyMatch(baz -> true)); @@ -137,7 +137,7 @@ Foo getFoo() { // return type unknown return new Foo>(it -> it.stream().anyMatch(baz -> true)); } - + Foo> getFoo2() { // return type expression return new Foo>(it -> it.stream().anyMatch(baz -> true)); @@ -150,14 +150,14 @@ Foo> getFoo2() { import java.util.function.Predicate; import java.util.List; import java.util.Map; - + class Foo { Map map; Map unknownMap; public Foo(Predicate p) {} public void something(Foo> foos){} public void somethingEasy(List> l){} - + Foo getFoo() { // variable type initializer Foo> f = new Foo<>(it -> it.stream().anyMatch(baz -> true)); @@ -172,7 +172,7 @@ Foo getFoo() { // return type unknown return new Foo>(it -> it.stream().anyMatch(baz -> true)); } - + Foo> getFoo2() { // return type expression return new Foo<>(it -> it.stream().anyMatch(baz -> true)); @@ -193,13 +193,13 @@ void returnTypeParamsDoNotMatchNewClassParams() { """ import java.util.List; import java.util.function.Predicate; - + class Test { interface MyInterface { } class MyClass implements MyInterface{ public MyClass(Predicate p, T check) {} } - + public MyInterface a() { return new MyClass, Integer>(l -> l.stream().anyMatch(String::isEmpty), 0); } @@ -211,13 +211,13 @@ public MyClass, Integer> b() { """ import java.util.List; import java.util.function.Predicate; - + class Test { interface MyInterface { } class MyClass implements MyInterface{ public MyClass(Predicate p, T check) {} } - + public MyInterface a() { return new MyClass, Integer>(l -> l.stream().anyMatch(String::isEmpty), 0); } @@ -525,7 +525,7 @@ void doNotChangeAnnotatedTypeParameters() { rewriteRun( spec -> spec .allSources(s -> s.markers(javaVersion(9))) - .parser(JavaParser.fromJavaVersion().classpath("annotations-24.1.0")), + .parser(JavaParser.fromJavaVersion().classpath("annotations")), //language=java java( """ diff --git a/src/test/java/org/openrewrite/staticanalysis/UseObjectNotifyAllTest.java b/src/test/java/org/openrewrite/staticanalysis/UseObjectNotifyAllTest.java index e1887d51c..58aef0384 100644 --- a/src/test/java/org/openrewrite/staticanalysis/UseObjectNotifyAllTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/UseObjectNotifyAllTest.java @@ -43,14 +43,13 @@ public final class ProcessStep implements Runnable { // reaches this value public ProcessStep(int step) { this.step = step; - } - + } @Override public void run() { try { synchronized (lock) { while (time != step) { lock.wait(); - } + } time++; lock.notify(); Thread.notify(); @@ -65,7 +64,7 @@ public static void main(String[] args) { } } } - """, + """, """ public final class ProcessStep implements Runnable { private static final Object lock = new Object(); @@ -74,14 +73,13 @@ public final class ProcessStep implements Runnable { // reaches this value public ProcessStep(int step) { this.step = step; - } - + } @Override public void run() { try { synchronized (lock) { while (time != step) { lock.wait(); - } + } time++; lock.notifyAll(); Thread.notifyAll(); @@ -96,10 +94,8 @@ public static void main(String[] args) { } } } - """ + """ ) ); } - - } From 6c2116406dc41ef30bf5d7ca69248e4ad99dbc9a Mon Sep 17 00:00:00 2001 From: Niels de Bruin Date: Wed, 2 Oct 2024 20:03:05 +0200 Subject: [PATCH 128/183] Fix: CatchClauseOnlyRethrows does not handle multi catch correctly --- .../CatchClauseOnlyRethrows.java | 11 ++++++-- .../CatchClauseOnlyRethrowsTest.java | 25 +++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index 9b05e49e8..e947b8a21 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -77,8 +77,7 @@ public J.Try visitTry(J.Try tryable, ExecutionContext ctx) { // keep this one for (int j = i + 1; j < tryable.getCatches().size(); j++) { J.Try.Catch next = tryable.getCatches().get(j); - if (!onlyRethrows(next) && TypeUtils.isAssignableTo(next.getParameter().getType(), - aCatch.getParameter().getType())) { + if (!onlyRethrows(next) && hasWiderExceptionType(aCatch, next)) { return aCatch; } } @@ -88,6 +87,14 @@ public J.Try visitTry(J.Try tryable, ExecutionContext ctx) { })); } + private boolean hasWiderExceptionType(J.Try.Catch aCatch, J.Try.Catch next) { + if (next.getParameter().getType() instanceof JavaType.MultiCatch) { + JavaType.MultiCatch multiCatch = (JavaType.MultiCatch) next.getParameter().getType(); + return multiCatch.getThrowableTypes().stream().anyMatch(alt -> TypeUtils.isAssignableTo(alt, aCatch.getParameter().getType())); + } + return TypeUtils.isAssignableTo(next.getParameter().getType(), aCatch.getParameter().getType()); + } + private boolean onlyRethrows(J.Try.Catch aCatch) { if (aCatch.getBody().getStatements().size() != 1 || !(aCatch.getBody().getStatements().get(0) instanceof J.Throw)) { diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index 0a8b24315..5a483f971 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -82,6 +82,31 @@ void foo() throws IOException { ); } + @Test + void catchShouldBePreservedBecauseLessSpecificCatchFollowsWithMultiCast() { + rewriteRun( + //language=java + java( + """ + import java.io.FileReader; + import java.io.IOException; + + class A { + void foo() throws IOException { + try { + new FileReader("").read(); + } catch (IOException e) { + throw e; + } catch(Exception | Throwable t) { + t.printStackTrace(); + } + } + } + """ + ) + ); + } + @DocumentExample @Test void tryCanBeRemoved() { From 771e21deba886d2efd42e3168914bc2e5570706f Mon Sep 17 00:00:00 2001 From: Niels de Bruin Date: Thu, 3 Oct 2024 14:27:12 +0200 Subject: [PATCH 129/183] Change stream to foreach loop --- .../staticanalysis/CatchClauseOnlyRethrows.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index e947b8a21..87eb10a8d 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -90,7 +90,12 @@ public J.Try visitTry(J.Try tryable, ExecutionContext ctx) { private boolean hasWiderExceptionType(J.Try.Catch aCatch, J.Try.Catch next) { if (next.getParameter().getType() instanceof JavaType.MultiCatch) { JavaType.MultiCatch multiCatch = (JavaType.MultiCatch) next.getParameter().getType(); - return multiCatch.getThrowableTypes().stream().anyMatch(alt -> TypeUtils.isAssignableTo(alt, aCatch.getParameter().getType())); + for (JavaType throwableType : multiCatch.getThrowableTypes()) { + if (TypeUtils.isAssignableTo(throwableType, aCatch.getParameter().getType())) { + return true; + } + } + return false; } return TypeUtils.isAssignableTo(next.getParameter().getType(), aCatch.getParameter().getType()); } From 7bffbcae65dea13c8b6926053f738bcc70397671 Mon Sep 17 00:00:00 2001 From: Niels de Bruin Date: Sat, 5 Oct 2024 14:42:42 +0200 Subject: [PATCH 130/183] Fix check order of if statement --- .../openrewrite/staticanalysis/CatchClauseOnlyRethrows.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index 87eb10a8d..d6a577d9b 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -77,7 +77,10 @@ public J.Try visitTry(J.Try tryable, ExecutionContext ctx) { // keep this one for (int j = i + 1; j < tryable.getCatches().size(); j++) { J.Try.Catch next = tryable.getCatches().get(j); - if (!onlyRethrows(next) && hasWiderExceptionType(aCatch, next)) { + if (hasWiderExceptionType(aCatch, next)) { + if (onlyRethrows(next)) { + return null; + } return aCatch; } } From 34b5938ff4f2610a92ddef9063ed223b8768f659 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 6 Oct 2024 13:07:50 +0200 Subject: [PATCH 131/183] Remove unused imports after remove unused private methods Fixes https://github.com/openrewrite/rewrite-static-analysis/issues/347 --- .../openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java | 2 ++ .../staticanalysis/RemoveUnusedPrivateMethodsTest.java | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java index fb7325b86..6b422b900 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java @@ -21,6 +21,7 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.NoMissingTypes; +import org.openrewrite.java.RemoveUnusedImports; import org.openrewrite.java.search.FindAnnotations; import org.openrewrite.java.service.AnnotationService; import org.openrewrite.java.tree.*; @@ -123,6 +124,7 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, } } + doAfterVisit(new RemoveUnusedImports().getVisitor()); //noinspection ConstantConditions return null; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java index 35ff9360c..c98b33897 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethodsTest.java @@ -153,8 +153,6 @@ private Stream unused() { } """, """ - import java.util.stream.Stream; - class Test { void test(String input) { } From 6646f06a29dd7dee48674dc3389959036fcfe47d Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 7 Oct 2024 14:28:41 +0200 Subject: [PATCH 132/183] Apply best practices to InstanceOfPatternMatch --- .../openrewrite/staticanalysis/InstanceOfPatternMatch.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java index bff256afa..5975d072b 100644 --- a/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java +++ b/src/main/java/org/openrewrite/staticanalysis/InstanceOfPatternMatch.java @@ -300,9 +300,9 @@ private String patternVariableName(J.InstanceOf instanceOf, Cursor cursor) { VariableNameStrategy strategy; if (root instanceof J.If) { VariableAndTypeTree variableData = variablesToDelete.get(instanceOf); - strategy = variableData != null - ? VariableNameStrategy.exact(variableData.getVariable().getSimpleName()) - : VariableNameStrategy.normal(contextScopes.get(instanceOf)); + strategy = variableData != null ? + VariableNameStrategy.exact(variableData.getVariable().getSimpleName()) : + VariableNameStrategy.normal(contextScopes.get(instanceOf)); } else { strategy = VariableNameStrategy.short_(); } From ae3f1d05cba8e4133eeb660d557a16409768f2e3 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Fri, 11 Oct 2024 22:16:47 +0200 Subject: [PATCH 133/183] Make `UseCollectionInterfaces.rspecRulesReplaceTypeMap` final --- .../org/openrewrite/staticanalysis/UseCollectionInterfaces.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java index 236e552fa..99c55fd44 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseCollectionInterfaces.java @@ -56,7 +56,7 @@ public Duration getEstimatedEffortPerOccurrence() { return Duration.ofMinutes(10); } - public static Map rspecRulesReplaceTypeMap = new HashMap<>(); + public static final Map rspecRulesReplaceTypeMap = new HashMap<>(); static { rspecRulesReplaceTypeMap.put("java.util.ArrayDeque", "java.util.Deque"); From 2163f0f05dd6fe7f4762e3486c8f16a9a5787036 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 13 Oct 2024 18:15:42 +0200 Subject: [PATCH 134/183] Stop `NoMutableStaticFieldsInRecipes` suggestions on `ChainStringBuilderAppendCalls.additiveBinaryTemplate` --- .../staticanalysis/ChainStringBuilderAppendCalls.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java index 9ba0679ad..557ea7bc1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java +++ b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java @@ -32,6 +32,7 @@ public class ChainStringBuilderAppendCalls extends Recipe { private static final MethodMatcher STRING_BUILDER_APPEND = new MethodMatcher("java.lang.StringBuilder append(String)"); + @SuppressWarnings("ALL") // Stop NoMutableStaticFieldsInRecipes from suggesting to remove this mutable static field private static J.Binary additiveBinaryTemplate = null; @Override From 12689e71eaa76982b04b6fb9ee68bc774c31516b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 16 Oct 2024 09:51:43 +0000 Subject: [PATCH 135/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/ChainStringBuilderAppendCalls.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java index 557ea7bc1..911d39ffd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java +++ b/src/main/java/org/openrewrite/staticanalysis/ChainStringBuilderAppendCalls.java @@ -32,6 +32,7 @@ public class ChainStringBuilderAppendCalls extends Recipe { private static final MethodMatcher STRING_BUILDER_APPEND = new MethodMatcher("java.lang.StringBuilder append(String)"); + @SuppressWarnings("ALL") // Stop NoMutableStaticFieldsInRecipes from suggesting to remove this mutable static field private static J.Binary additiveBinaryTemplate = null; From 5b19b47bc670dc7862839156bd304f79698f95b6 Mon Sep 17 00:00:00 2001 From: Leanne Kerford Date: Tue, 22 Oct 2024 00:35:50 -0700 Subject: [PATCH 136/183] Cap the lengths of generate names in the ReplaceDuplicateStringLiterals recipe (#374) * Cap the lengths of generate names We could generate some unreasonably long variable names. By default, we will cap variable names at 100 characters but people can change this was desired. We will crop the generated variable name to the nearest "_" if there is one available * Apply suggestions from code review * Use ternary for `.substring` instead of similar if/else --------- Co-authored-by: Tim te Beek --- .../ReplaceDuplicateStringLiterals.java | 13 +++++++++- .../ReplaceDuplicateStringLiteralsTest.java | 26 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 1f4138dd7..404739882 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -44,6 +44,12 @@ public class ReplaceDuplicateStringLiterals extends Recipe { @Nullable Boolean includeTestSources; + @Option(displayName = "Maximum length of the generate variable names", + description = "By default this is set to 100 characters", + required = false) + @Nullable + Integer maxVariableLength = 100; + @Override public String getDisplayName() { return "Replace duplicate `String` literals"; @@ -181,7 +187,12 @@ private String transformToVariableName(String valueOfLiteral) { prevIsLower = Character.isLowerCase(c); } } - return VariableNameUtils.normalizeName(newName.toString()); + String newNameString = newName.toString(); + while (newNameString.length() > maxVariableLength){ + int indexOf = newNameString.lastIndexOf("_"); + newNameString = newNameString.substring(0, indexOf > -1 ? indexOf : maxVariableLength); + } + return VariableNameUtils.normalizeName(newNameString); } }); } diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index 63b3f7a8f..849e7f377 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -148,6 +148,32 @@ class A { ); } + @Test + void generatedNameIsVeryLong() { + rewriteRun( + //language=java + java( + """ + package org.foo; + class A { + final String val1 = "ThisIsAnUnreasonablyLongVariableNameItGoesOnAndOnForAVeryLongTimeItMightNeverEndWhoIsToKnowHowLongItWillKeepGoingAndGoing"; + final String val2 = "ThisIsAnUnreasonablyLongVariableNameItGoesOnAndOnForAVeryLongTimeItMightNeverEndWhoIsToKnowHowLongItWillKeepGoingAndGoing"; + final String val3 = "ThisIsAnUnreasonablyLongVariableNameItGoesOnAndOnForAVeryLongTimeItMightNeverEndWhoIsToKnowHowLongItWillKeepGoingAndGoing"; + } + """, + """ + package org.foo; + class A { + private static final String THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END = "ThisIsAnUnreasonablyLongVariableNameItGoesOnAndOnForAVeryLongTimeItMightNeverEndWhoIsToKnowHowLongItWillKeepGoingAndGoing"; + final String val1 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END; + final String val2 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END; + final String val3 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END; + } + """ + ) + ); + } + @Test void replaceRedundantLiteralInMethodInvocation() { rewriteRun( From 07686d2942ccdf742f75b0db1ea3552457e9ad26 Mon Sep 17 00:00:00 2001 From: punkratz312 Date: Tue, 22 Oct 2024 09:59:59 +0200 Subject: [PATCH 137/183] address: LiteralsFirstInComparisons PMD rule (#368) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * wip * wip BAR_CONTENT_EQUALS * add compareToIgnoreCase * wip * undo * fix name for direct matching * EQUALS_IGNORE_CASE * add CONTENT_EQUALS * add compareToInverted * tmp fix * wip * fix working * wiß * wip * wip * wip * wip * finalize * sort * add doc * undo * Update src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitorTest.java Co-authored-by: Tim te Beek * fix Cast after // instanceof check * Update src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitorTest.java Co-authored-by: Tim te Beek * Revert "fix Cast after" This reverts commit 2c2b33a08b806069902e1ec122fccde8865c5730. * wip: error: pattern matching in instanceof is not supported in -source 8 * cast * Revert build.gradle changes; rewrite-bom manages rewrite-java * format * condense * condense * apply style * wip * format fin * fix * isStringComparisonMethod * remove null * undo * Update src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * add doc * fix getSuperIfSelectNull * paranthese * fix dry * fix if * fix if * fix literalsFirstInComparisons * order * dry firstArgument * naming * Update src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Fix earlier incorrect bot suggestion * Place binary operators on same line; remove nested ternaries * Restore more deliberate naming of `potentialNullCheck` * Collapse imports as per what's common elsewhere * Make the method matchers static final again * add leftover * Use JSpecify nullable annotations --------- Co-authored-by: I753089 Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../EqualsAvoidsNullVisitor.java | 117 ++++++++++++------ .../staticanalysis/UseStringReplace.java | 5 +- .../staticanalysis/EqualsAvoidsNullTest.java | 6 + 3 files changed, 86 insertions(+), 42 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index 420d4eacb..8ddc19a33 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -26,54 +26,91 @@ import org.openrewrite.marker.Markers; import static java.util.Collections.singletonList; +import static java.util.Objects.requireNonNull; +/** + * A visitor that identifies and addresses potential issues related to + * the use of {@code equals} methods in Java, particularly to avoid + * null pointer exceptions when comparing strings. + *

    + * This visitor looks for method invocations of {@code equals}, + * {@code equalsIgnoreCase}, {@code compareTo}, and {@code contentEquals}, + * and performs optimizations to ensure null checks are correctly applied. + *

    + * For more details, refer to the PMD best practices: + * Literals First in Comparisons + * + * @param

    The type of the parent context used for visiting the AST. + */ @Value @EqualsAndHashCode(callSuper = false) public class EqualsAvoidsNullVisitor

    extends JavaVisitor

    { - private static final MethodMatcher STRING_EQUALS = new MethodMatcher("String equals(java.lang.Object)"); - private static final MethodMatcher STRING_EQUALS_IGNORE_CASE = new MethodMatcher("String equalsIgnoreCase(java.lang.String)"); + + private static final MethodMatcher EQUALS = new MethodMatcher("java.lang.String " + "equals(java.lang.Object)"); + private static final MethodMatcher EQUALS_IGNORE_CASE = new MethodMatcher("java.lang.String " + "equalsIgnoreCase(java.lang.String)"); + private static final MethodMatcher COMPARE_TO = new MethodMatcher("java.lang.String " + "compareTo(java.lang.String)"); + private static final MethodMatcher COMPARE_TO_IGNORE_CASE = new MethodMatcher("java.lang.String " + "compareToIgnoreCase(java.lang.String)"); + private static final MethodMatcher CONTENT_EQUALS = new MethodMatcher("java.lang.String " + "contentEquals(java.lang.CharSequence)"); EqualsAvoidsNullStyle style; @Override public J visitMethodInvocation(J.MethodInvocation method, P p) { - J j = super.visitMethodInvocation(method, p); - if (!(j instanceof J.MethodInvocation)) { - return j; + J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, p); + if (m.getSelect() != null && + !(m.getSelect() instanceof J.Literal) && + m.getArguments().get(0) instanceof J.Literal && + isStringComparisonMethod(m)) { + return literalsFirstInComparisonsBinaryCheck(m, getCursor().getParentTreeCursor().getValue()); } - J.MethodInvocation m = (J.MethodInvocation) j; - if (m.getSelect() == null) { - return m; + return m; + } + + private boolean isStringComparisonMethod(J.MethodInvocation methodInvocation) { + return EQUALS.matches(methodInvocation) || + !style.getIgnoreEqualsIgnoreCase() && + EQUALS_IGNORE_CASE.matches(methodInvocation) || + COMPARE_TO.matches(methodInvocation) || + COMPARE_TO_IGNORE_CASE.matches(methodInvocation) || + CONTENT_EQUALS.matches(methodInvocation); + } + + private Expression literalsFirstInComparisonsBinaryCheck(J.MethodInvocation m, P parent) { + if (parent instanceof J.Binary) { + handleBinaryExpression(m, (J.Binary) parent); } + return getExpression(m, m.getArguments().get(0)); + } - if ((STRING_EQUALS.matches(m) || (!Boolean.TRUE.equals(style.getIgnoreEqualsIgnoreCase()) && STRING_EQUALS_IGNORE_CASE.matches(m))) && - m.getArguments().get(0) instanceof J.Literal && - !(m.getSelect() instanceof J.Literal)) { - Tree parent = getCursor().getParentTreeCursor().getValue(); - if (parent instanceof J.Binary) { - J.Binary binary = (J.Binary) parent; - if (binary.getOperator() == J.Binary.Type.And && binary.getLeft() instanceof J.Binary) { - J.Binary potentialNullCheck = (J.Binary) binary.getLeft(); - if ((isNullLiteral(potentialNullCheck.getLeft()) && matchesSelect(potentialNullCheck.getRight(), m.getSelect())) || - (isNullLiteral(potentialNullCheck.getRight()) && matchesSelect(potentialNullCheck.getLeft(), m.getSelect()))) { - doAfterVisit(new RemoveUnnecessaryNullCheck<>(binary)); - } - } - } + private static Expression getExpression(J.MethodInvocation m, Expression firstArgument) { + return firstArgument.getType() == JavaType.Primitive.Null ? + literalsFirstInComparisonsNull(m, firstArgument) : + literalsFirstInComparisons(m, firstArgument); + } + + private static J.Binary literalsFirstInComparisonsNull(J.MethodInvocation m, Expression firstArgument) { + return new J.Binary(Tree.randomId(), + m.getPrefix(), + Markers.EMPTY, + requireNonNull(m.getSelect()), + JLeftPadded.build(J.Binary.Type.Equal).withBefore(Space.SINGLE_SPACE), + firstArgument.withPrefix(Space.SINGLE_SPACE), + JavaType.Primitive.Boolean); + } - if (m.getArguments().get(0).getType() == JavaType.Primitive.Null) { - return new J.Binary(Tree.randomId(), m.getPrefix(), Markers.EMPTY, - m.getSelect(), - JLeftPadded.build(J.Binary.Type.Equal).withBefore(Space.SINGLE_SPACE), - m.getArguments().get(0).withPrefix(Space.SINGLE_SPACE), - JavaType.Primitive.Boolean); - } else { - m = m.withSelect(((J.Literal) m.getArguments().get(0)).withPrefix(m.getSelect().getPrefix())) - .withArguments(singletonList(m.getSelect().withPrefix(Space.EMPTY))); + private static J.MethodInvocation literalsFirstInComparisons(J.MethodInvocation m, Expression firstArgument) { + return m.withSelect(firstArgument.withPrefix(requireNonNull(m.getSelect()).getPrefix())) + .withArguments(singletonList(m.getSelect().withPrefix(Space.EMPTY))); + } + + private void handleBinaryExpression(J.MethodInvocation m, J.Binary binary) { + if (binary.getOperator() == J.Binary.Type.And && binary.getLeft() instanceof J.Binary) { + J.Binary potentialNullCheck = (J.Binary) binary.getLeft(); + if (isNullLiteral(potentialNullCheck.getLeft()) && matchesSelect(potentialNullCheck.getRight(), requireNonNull(m.getSelect())) || + isNullLiteral(potentialNullCheck.getRight()) && matchesSelect(potentialNullCheck.getLeft(), requireNonNull(m.getSelect()))) { + doAfterVisit(new RemoveUnnecessaryNullCheck<>(binary)); } } - - return m; } private boolean isNullLiteral(Expression expression) { @@ -81,13 +118,20 @@ private boolean isNullLiteral(Expression expression) { } private boolean matchesSelect(Expression expression, Expression select) { - return expression.printTrimmed(getCursor()).replaceAll("\\s", "").equals(select.printTrimmed(getCursor()).replaceAll("\\s", "")); + return expression.printTrimmed(getCursor()).replaceAll("\\s", "") + .equals(select.printTrimmed(getCursor()).replaceAll("\\s", "")); } private static class RemoveUnnecessaryNullCheck

    extends JavaVisitor

    { + private final J.Binary scope; + boolean done; + public RemoveUnnecessaryNullCheck(J.Binary scope) { + this.scope = scope; + } + @Override public @Nullable J visit(@Nullable Tree tree, P p) { if (done) { @@ -96,17 +140,12 @@ private static class RemoveUnnecessaryNullCheck

    extends JavaVisitor

    { return super.visit(tree, p); } - public RemoveUnnecessaryNullCheck(J.Binary scope) { - this.scope = scope; - } - @Override public J visitBinary(J.Binary binary, P p) { if (scope.isScope(binary)) { done = true; return binary.getRight().withPrefix(Space.EMPTY); } - return super.visitBinary(binary, p); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java index a38a487cb..ee21d9ace 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseStringReplace.java @@ -28,7 +28,6 @@ import java.time.Duration; import java.util.Collections; -import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; @@ -90,7 +89,7 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) return invocation; // Might contain special characters; unsafe to replace } String secondValue = (String) ((J.Literal) secondArgument).getValue(); - if (Objects.nonNull(secondValue) && (secondValue.contains("$") || secondValue.contains("\\"))) { + if (secondValue != null && (secondValue.contains("$") || secondValue.contains("\\"))) { return invocation; // Does contain special characters; unsafe to replace } @@ -100,7 +99,7 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) // Checks if the String literal may not be a regular expression, // if so, then change the method invocation name String firstValue = (String) ((J.Literal) firstArgument).getValue(); - if (Objects.nonNull(firstValue) && !mayBeRegExp(firstValue)) { + if (firstValue != null && !mayBeRegExp(firstValue)) { String unEscapedLiteral = unEscapeCharacters(firstValue); invocation = invocation .withName(invocation.getName().withSimpleName("replace")) diff --git a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java index 68bb55236..1dde17457 100644 --- a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java @@ -42,6 +42,9 @@ public class A { String s = null; if(s.equals("test")) {} if(s.equalsIgnoreCase("test")) {} + System.out.println(s.compareTo("test")); + System.out.println(s.compareToIgnoreCase("test")); + System.out.println(s.contentEquals("test")); } } """, @@ -51,6 +54,9 @@ public class A { String s = null; if("test".equals(s)) {} if("test".equalsIgnoreCase(s)) {} + System.out.println("test".compareTo(s)); + System.out.println("test".compareToIgnoreCase(s)); + System.out.println("test".contentEquals(s)); } } """ From d24228b286a572cc67f9582210e68ccb264612fb Mon Sep 17 00:00:00 2001 From: Niels de Bruin Date: Tue, 22 Oct 2024 12:09:20 +0200 Subject: [PATCH 138/183] Fix `CatchCauseOnlyRethrows` onlyRethrows check for multi catch statements (#355) * Fix CatchCauseOnlyRethrows rethrows check for multi catch statements * Specifically check for `JavaType.MultiCatch` --------- Co-authored-by: Niels de Bruin Co-authored-by: Tim te Beek --- .../CatchClauseOnlyRethrows.java | 19 ++++-- .../CatchClauseOnlyRethrowsTest.java | 65 +++++++++++++++++++ 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index d6a577d9b..53b978fce 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -20,7 +20,10 @@ import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.TypeUtils; import java.time.Duration; import java.util.Set; @@ -37,7 +40,7 @@ public String getDisplayName() { @Override public String getDescription() { return "A `catch` clause that only rethrows the caught exception is unnecessary. " + - "Letting the exception bubble up as normal achieves the same result with less code."; + "Letting the exception bubble up as normal achieves the same result with less code."; } @Override @@ -105,16 +108,18 @@ private boolean hasWiderExceptionType(J.Try.Catch aCatch, J.Try.Catch next) { private boolean onlyRethrows(J.Try.Catch aCatch) { if (aCatch.getBody().getStatements().size() != 1 || - !(aCatch.getBody().getStatements().get(0) instanceof J.Throw)) { + !(aCatch.getBody().getStatements().get(0) instanceof J.Throw)) { return false; } Expression exception = ((J.Throw) aCatch.getBody().getStatements().get(0)).getException(); - JavaType.FullyQualified catchType = TypeUtils.asFullyQualified(aCatch.getParameter().getType()); - if (catchType == null || !catchType.equals(exception.getType())) { - return false; + JavaType catchParameterType = aCatch.getParameter().getType(); + if (!(catchParameterType instanceof JavaType.MultiCatch)) { + JavaType.FullyQualified catchType = TypeUtils.asFullyQualified(catchParameterType); + if (catchType == null || !catchType.equals(exception.getType())) { + return false; + } } - if (exception instanceof J.Identifier) { return ((J.Identifier) exception).getSimpleName().equals(aCatch.getParameter().getTree().getVariables().get(0).getSimpleName()); } diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index 5a483f971..c6c93ebf1 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -141,6 +141,71 @@ void foo() throws IOException { ); } + @Test + void tryCanBeRemovedWithMultiCatch() { + rewriteRun( + //language=java + java( + """ + import java.io.FileReader; + import java.io.IOException; + import java.io.FileNotFoundException; + + class A { + void foo() throws IOException { + try { + new FileReader("").read(); + } catch (FileNotFoundException e) { + throw e; + } catch(IOException | ArrayIndexOutOfBoundsException e) { + throw e; + } catch(Exception e) { + throw e; + } + } + } + """, + """ + import java.io.FileReader; + import java.io.IOException; + import java.io.FileNotFoundException; + + class A { + void foo() throws IOException { + new FileReader("").read(); + } + } + """ + ) + ); + } + + @Test + void multiCatchPreservedOnDifferentThrow() { + rewriteRun( + //language=java + java( + """ + import java.io.FileReader; + import java.io.IOException; + import java.io.FileNotFoundException; + + class A { + void foo() throws IOException { + try { + new FileReader("").read(); + } catch (FileNotFoundException e) { + throw e; + } catch(IOException | ArrayIndexOutOfBoundsException e) { + throw new IOException("another message", e); + } + } + } + """ + ) + ); + } + @Test void tryShouldBePreservedBecauseFinally() { rewriteRun( From 4dc9f45f27d44f7cfd84d73ac61e53187262afbb Mon Sep 17 00:00:00 2001 From: lkerford Date: Tue, 22 Oct 2024 08:51:00 -0700 Subject: [PATCH 139/183] Setting the maximum generated variable length to 40 Removing the configuration option to modify the maximum variable length and changing it from 100 to 40. 40 is already a very long variable name, and we don't think that people will be changing this max length --- .../ReplaceDuplicateStringLiterals.java | 8 ++----- .../ReplaceDuplicateStringLiteralsTest.java | 22 +++++++++---------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 404739882..251dea221 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -44,12 +44,6 @@ public class ReplaceDuplicateStringLiterals extends Recipe { @Nullable Boolean includeTestSources; - @Option(displayName = "Maximum length of the generate variable names", - description = "By default this is set to 100 characters", - required = false) - @Nullable - Integer maxVariableLength = 100; - @Override public String getDisplayName() { return "Replace duplicate `String` literals"; @@ -74,6 +68,8 @@ public Duration getEstimatedEffortPerOccurrence() { return Duration.ofMinutes(2); } + int maxVariableLength = 40; + @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesType<>("java.lang.String", false), new JavaVisitor() { diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index 849e7f377..cec186f5f 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -164,10 +164,10 @@ class A { """ package org.foo; class A { - private static final String THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END = "ThisIsAnUnreasonablyLongVariableNameItGoesOnAndOnForAVeryLongTimeItMightNeverEndWhoIsToKnowHowLongItWillKeepGoingAndGoing"; - final String val1 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END; - final String val2 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END; - final String val3 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE_NAME_IT_GOES_ON_AND_ON_FOR_AVERY_LONG_TIME_IT_MIGHT_NEVER_END; + private static final String THIS_IS_AN_UNREASONABLY_LONG_VARIABLE = "ThisIsAnUnreasonablyLongVariableNameItGoesOnAndOnForAVeryLongTimeItMightNeverEndWhoIsToKnowHowLongItWillKeepGoingAndGoing"; + final String val1 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE; + final String val2 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE; + final String val3 = THIS_IS_AN_UNREASONABLY_LONG_VARIABLE; } """ ) @@ -278,17 +278,17 @@ void transformStringValue() { java( """ class A { - final String val1 = "An example,, of a :: String with `` special __ characters."; - final String val2 = "An example,, of a :: String with `` special __ characters."; - final String val3 = "An example,, of a :: String with `` special __ characters."; + final String val1 = "Example,, :: String with `` special __ characters."; + final String val2 = "Example,, :: String with `` special __ characters."; + final String val3 = "Example,, :: String with `` special __ characters."; } """, """ class A { - private static final String AN_EXAMPLE_OF_A_STRING_WITH_SPECIAL_CHARACTERS = "An example,, of a :: String with `` special __ characters."; - final String val1 = AN_EXAMPLE_OF_A_STRING_WITH_SPECIAL_CHARACTERS; - final String val2 = AN_EXAMPLE_OF_A_STRING_WITH_SPECIAL_CHARACTERS; - final String val3 = AN_EXAMPLE_OF_A_STRING_WITH_SPECIAL_CHARACTERS; + private static final String EXAMPLE_STRING_WITH_SPECIAL_CHARACTERS = "Example,, :: String with `` special __ characters."; + final String val1 = EXAMPLE_STRING_WITH_SPECIAL_CHARACTERS; + final String val2 = EXAMPLE_STRING_WITH_SPECIAL_CHARACTERS; + final String val3 = EXAMPLE_STRING_WITH_SPECIAL_CHARACTERS; } """ ) From 8468b209c9bc7d76b1a419930c2f12d5372a9e27 Mon Sep 17 00:00:00 2001 From: lkerford Date: Tue, 22 Oct 2024 15:00:12 -0700 Subject: [PATCH 140/183] Updating ReplaceDuplicateStringLiterals recipe to use VariableNameUtils to track variable names VariableNameUtils already provides a list of used names in the scope :). We need to handle some edges slightly different 1: When the constant already exists, in this case by default this existing constant name wouldn't be used because it's already being used. In this case, we need to allow the recipe to use this variable 2: We want to prevent namespace shadowing on existing constants (preventNamespaceShadowingOnExistingConstant is the associated test). When we solve edge 1, by allowing existing constants to be used, we break this edge case. As such we need to validate that we are assigning a name to the value which makes sense. --- .../ReplaceDuplicateStringLiterals.java | 26 +++++--- .../ReplaceDuplicateStringLiteralsTest.java | 62 +++++++++++++++++++ 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java index 251dea221..e4f5e371d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiterals.java @@ -28,6 +28,7 @@ import java.time.Duration; import java.util.*; +import java.util.stream.Collectors; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; @@ -97,8 +98,9 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct if (duplicateLiteralsMap.isEmpty()) { return classDecl; } - Set variableNames = duplicateLiteralInfo.getVariableNames(); Map fieldValueToFieldName = duplicateLiteralInfo.getFieldValueToFieldName(); + Set variableNames = VariableNameUtils.findNamesInScope(getCursor()).stream() + .filter(i -> !fieldValueToFieldName.containsValue(i)).collect(Collectors.toSet()); String classFqn = classDecl.getType().getFullyQualifiedName(); Map replacements = new HashMap<>(); for (Map.Entry> entry : duplicateLiteralsMap.entrySet()) { @@ -107,7 +109,13 @@ public J visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ct String classFieldName = fieldValueToFieldName.get(valueOfLiteral); String variableName; if (classFieldName != null) { - variableName = getNameWithoutShadow(classFieldName, variableNames); + String maybeVariableName = getNameWithoutShadow(classFieldName, variableNames); + if (duplicateLiteralInfo.existingFieldValueToFieldName.get(maybeVariableName) != null) { + variableNames.add(maybeVariableName); + maybeVariableName = getNameWithoutShadow(classFieldName, variableNames); + } + + variableName = maybeVariableName; if (StringUtils.isBlank(variableName)) { continue; } @@ -199,14 +207,14 @@ private static boolean isPrivateStaticFinalVariable(J.VariableDeclarations.Named @Value private static class DuplicateLiteralInfo { - Set variableNames; Map fieldValueToFieldName; + Map existingFieldValueToFieldName; @NonFinal Map> duplicateLiterals; public static DuplicateLiteralInfo find(J.ClassDeclaration inClass) { - DuplicateLiteralInfo result = new DuplicateLiteralInfo(new LinkedHashSet<>(), new LinkedHashMap<>(), new HashMap<>()); + DuplicateLiteralInfo result = new DuplicateLiteralInfo(new LinkedHashMap<>(), new LinkedHashMap<>(), new HashMap<>()); new JavaIsoVisitor() { @Override @@ -221,11 +229,11 @@ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations Cursor parentScope = getCursor().dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.MethodDeclaration); boolean privateStaticFinalVariable = isPrivateStaticFinalVariable(variable); // `private static final String`(s) are handled separately by `FindExistingPrivateStaticFinalFields`. - if (parentScope.getValue() instanceof J.MethodDeclaration || - parentScope.getValue() instanceof J.ClassDeclaration && - !(privateStaticFinalVariable && v.getInitializer() instanceof J.Literal && - ((J.Literal) v.getInitializer()).getValue() instanceof String)) { - result.variableNames.add(v.getSimpleName()); + if (v.getInitializer() instanceof J.Literal && + (parentScope.getValue() instanceof J.MethodDeclaration || parentScope.getValue() instanceof J.ClassDeclaration) && + !(privateStaticFinalVariable && ((J.Literal) v.getInitializer()).getValue() instanceof String)) { + String value = (((J.Literal) v.getInitializer()).getValue()).toString(); + result.existingFieldValueToFieldName.put(v.getSimpleName(), value); } if (parentScope.getValue() instanceof J.ClassDeclaration && privateStaticFinalVariable && v.getInitializer() instanceof J.Literal && diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index cec186f5f..f8e3e6958 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -148,6 +148,68 @@ class A { ); } + @Test + void fieldNameCollidesWithNewStringLiteral() { + rewriteRun( + //language=java + java( + """ + package org.foo; + class A { + final String val1 = "value"; + final String val2 = "value"; + final String val3 = "value"; + final int VALUE = 1; + } + """, + """ + package org.foo; + class A { + private static final String VALUE_1 = "value"; + final String val1 = VALUE_1; + final String val2 = VALUE_1; + final String val3 = VALUE_1; + final int VALUE = 1; + } + """ + ) + ); + } + + @Test + void staticImportCollidesWithNewStringLiteral() { + rewriteRun( + //language=java + java( + """ + package org.foo; + + import static java.lang.Long.MAX_VALUE; + + class A { + final String val1 = "max_value"; + final String val2 = "max_value"; + final String val3 = "max_value"; + final long value = MAX_VALUE; + } + """, + """ + package org.foo; + + import static java.lang.Long.MAX_VALUE; + + class A { + private static final String MAX_VALUE_1 = "max_value"; + final String val1 = MAX_VALUE_1; + final String val2 = MAX_VALUE_1; + final String val3 = MAX_VALUE_1; + final long value = MAX_VALUE; + } + """ + ) + ); + } + @Test void generatedNameIsVeryLong() { rewriteRun( From f4a2a381eae17b9f460aa149191dccbd2fdf4c26 Mon Sep 17 00:00:00 2001 From: lkerford Date: Tue, 22 Oct 2024 15:19:00 -0700 Subject: [PATCH 141/183] adding tests --- .../ReplaceDuplicateStringLiteralsTest.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java index f8e3e6958..8fc7e44a8 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceDuplicateStringLiteralsTest.java @@ -148,6 +148,44 @@ class A { ); } + @Test + void enumCollidesWithNewStringLiteral() { + rewriteRun( + //language=java + java( + """ + package org.foo; + enum TYPES { + VALUE, NUMBER, TEXT + } + + class A { + final String val1 = "types"; + final String val2 = "types"; + final String val3 = "types"; + TYPES type = TYPES.VALUE; + } + + """, + """ + package org.foo; + enum TYPES { + VALUE, NUMBER, TEXT + } + + class A { + private static final String TYPES_1 = "types"; + final String val1 = TYPES_1; + final String val2 = TYPES_1; + final String val3 = TYPES_1; + TYPES type = TYPES.VALUE; + } + + """ + ) + ); + } + @Test void fieldNameCollidesWithNewStringLiteral() { rewriteRun( From 75f9cc59d16c255992080681c3bd6b190a38cb2b Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 23 Oct 2024 12:09:19 +0200 Subject: [PATCH 142/183] Allow removal of unused local variables with side effects in initializer (#378) For https://github.com/openrewrite/rewrite-feature-flags/pull/35 --- .../RemoveUnusedLocalVariables.java | 10 ++++- .../RemoveUnusedLocalVariablesTest.java | 43 ++++++++++++++++--- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index 49296fb04..dc08fde4c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -43,6 +43,12 @@ public class RemoveUnusedLocalVariables extends Recipe { example = "[unused, notUsed, IGNORE_ME]") String @Nullable [] ignoreVariablesNamed; + @Option(displayName = "Remove unused local variables with side effects in initializer", + description = "Whether to remove unused local variables despite side effects in the initializer. Default false.", + required = false) + @Nullable + Boolean withSideEffects; + @Override public String getDisplayName() { return "Remove unused local variables"; @@ -177,7 +183,9 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocat if (SAFE_GETTER_METHODS.matches(methodInvocation)) { return methodInvocation; } - result.set(true); + if (withSideEffects == null || Boolean.FALSE.equals(withSideEffects)) { + result.set(true); + } return methodInvocation; } diff --git a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java index 54c3616ff..9aa093a2a 100644 --- a/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariablesTest.java @@ -46,7 +46,7 @@ class RemoveUnusedLocalVariablesTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { - spec.recipe(new RemoveUnusedLocalVariables(new String[0])); + spec.recipe(new RemoveUnusedLocalVariables(new String[0], null)); } @Test @@ -73,18 +73,21 @@ static int method(int x) { @SuppressWarnings("MethodMayBeStatic") void ignoreVariablesNamed() { rewriteRun( - spec -> spec.recipe(new RemoveUnusedLocalVariables(new String[]{"unused", "ignoreMe"})), + spec -> spec.recipe(new RemoveUnusedLocalVariables(new String[]{"unused"}, null)), //language=java java( """ class Test { void method(Object someData) { - int unused = writeDataToTheDB(someData); - int ignoreMe = writeDataToTheDB(someData); + int unused = 123; + int removed = 123; } - - int writeDataToTheDB(Object save) { - return 1; + } + """, + """ + class Test { + void method(Object someData) { + int unused = 123; } } """ @@ -1066,6 +1069,32 @@ static void method() { ); } + @Test + @Issue("https://github.com/openrewrite/rewrite-feature-flags/pull/35") + void removeDespiteSideEffects() { + rewriteRun( + spec -> spec.recipe(new RemoveUnusedLocalVariables(null, true)), + //language=java + java( + """ + class Test { + int sideEffect() { return 123; } + void method(Object someData) { + int unused = sideEffect(); + } + } + """, + """ + class Test { + int sideEffect() { return 123; } + void method(Object someData) { + } + } + """ + ) + ); + } + @Nested class Kotlin { From ed5d846f0a611f9aaceeecfea91c3f66e235ab3f Mon Sep 17 00:00:00 2001 From: lkerford Date: Wed, 23 Oct 2024 10:41:32 -0700 Subject: [PATCH 143/183] Updating MoveFieldAnnotationToType to handle moving annotation which are fully defined The previous behaviour `public void someFunction(@org.openrewrite.internal.lang.Nullable org.openrewrite.internal.MetricsHelper metrics)` --> `public void someFunction(org.openrewrite.internal.@org.openrewrite.internal.lang.Nullable MetricsHelper metrics)` New behaviour `public void someFunction(@org.openrewrite.internal.lang.Nullable org.openrewrite.internal.MetricsHelper metrics)` --> `public void someFunction(org.openrewrite.internal.@Nullable MetricsHelper metrics)` --- .../java/MoveFieldAnnotationToType.java | 19 +++++++++++++-- .../java/MoveFieldAnnotationToTypeTest.java | 24 +++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index 44b0e1b57..8535d82bd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -25,6 +25,8 @@ import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.*; +import java.util.ArrayList; +import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; @@ -150,10 +152,23 @@ private boolean isQualifiedClass(@Nullable TypeTree tree) { } private TypeTree annotateInnerClass(TypeTree qualifiedClassRef, J.Annotation annotation) { + J.Annotation usedAnnotation = annotation; + if (annotation.getAnnotationType() instanceof J.FieldAccess) { + usedAnnotation = usedAnnotation.withAnnotationType( + new J.Identifier( + UUID.randomUUID(), + annotation.getAnnotationType().getPrefix(), + annotation.getAnnotationType().getMarkers(), + new ArrayList<>(), + annotation.getSimpleName(), + annotation.getType(), + null + )); + } if (qualifiedClassRef instanceof J.FieldAccess) { J.FieldAccess q = (J.FieldAccess) qualifiedClassRef; q = q.withName(q.getName().withAnnotations( - ListUtils.concat(annotation.withPrefix(Space.EMPTY), q.getName().getAnnotations()))); + ListUtils.concat(usedAnnotation.withPrefix(Space.EMPTY), q.getName().getAnnotations()))); if (q.getName().getPrefix().getWhitespace().isEmpty()) { q = q.withName(q.getName().withPrefix(q.getName().getPrefix().withWhitespace(" "))); } @@ -161,7 +176,7 @@ private TypeTree annotateInnerClass(TypeTree qualifiedClassRef, J.Annotation ann } else if (qualifiedClassRef instanceof J.ParameterizedType && ((J.ParameterizedType) qualifiedClassRef).getClazz() instanceof TypeTree) { J.ParameterizedType pt = (J.ParameterizedType) qualifiedClassRef; - return pt.withClazz(annotateInnerClass((TypeTree) pt.getClazz(), annotation)); + return pt.withClazz(annotateInnerClass((TypeTree) pt.getClazz(), usedAnnotation)); } else if (qualifiedClassRef instanceof J.ArrayType) { J.ArrayType at = (J.ArrayType) qualifiedClassRef; at = at.withAnnotations(ListUtils.concat(annotation.withPrefix(Space.SINGLE_SPACE), at.getAnnotations())); diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index ea7e19c64..b7563cd81 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -196,4 +196,28 @@ interface Test { ) ); } + + @Test + void fullyDefinedAnnotationInMethodDeclaration() { + rewriteRun( + java( + """ + package org.openrewrite; + + public class Test { + public void someFunction(@org.openrewrite.internal.lang.Nullable org.openrewrite.internal.MetricsHelper metrics) { + } + } + """, + """ + package org.openrewrite; + + public class Test { + public void someFunction(org.openrewrite.internal.@Nullable MetricsHelper metrics) { + } + } + """ + ) + ); + } } From 983086d9309d0fb36666725cda7bb32b4e65b050 Mon Sep 17 00:00:00 2001 From: Leanne Kerford Date: Wed, 23 Oct 2024 10:50:34 -0700 Subject: [PATCH 144/183] Update src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../staticanalysis/java/MoveFieldAnnotationToType.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index 8535d82bd..62471bc18 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -156,7 +156,7 @@ private TypeTree annotateInnerClass(TypeTree qualifiedClassRef, J.Annotation ann if (annotation.getAnnotationType() instanceof J.FieldAccess) { usedAnnotation = usedAnnotation.withAnnotationType( new J.Identifier( - UUID.randomUUID(), + Tree.randomId(), annotation.getAnnotationType().getPrefix(), annotation.getAnnotationType().getMarkers(), new ArrayList<>(), From 3d42009f2ce57c65f5cbac0c96db59ad05c966b6 Mon Sep 17 00:00:00 2001 From: Leanne Kerford Date: Wed, 23 Oct 2024 10:50:39 -0700 Subject: [PATCH 145/183] Update src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../staticanalysis/java/MoveFieldAnnotationToType.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index 62471bc18..5d494c98c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -26,7 +26,6 @@ import org.openrewrite.java.tree.*; import java.util.ArrayList; -import java.util.UUID; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; From d46cb6878d6ec2db2017ebe49f775f2c9f6b2992 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 23 Oct 2024 19:31:53 +0000 Subject: [PATCH 146/183] refactor: Kill your Zombie code Use this link to re-run the recipe: https://app.moderne.io/builder/mybR2aHWC?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java b/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java index a70a063ca..42205b0cf 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java +++ b/src/main/java/org/openrewrite/staticanalysis/ModifierOrder.java @@ -92,7 +92,7 @@ public static List sortModifiers(List modifiers) { return ListUtils.map(modifiers, (i, mod) -> mod.getType() == sortedTypes.get(i) ? mod : mod.withType(sortedTypes.get(i))); } - + private static ToIntFunction createModifierTypeToPositionFunction() { final int DEFAULT_MOD_POSITION = 4; return type -> { From c320f2b48baf579ab82e540720421e06d3b9ae24 Mon Sep 17 00:00:00 2001 From: Leanne Kerford Date: Wed, 23 Oct 2024 10:50:39 -0700 Subject: [PATCH 147/183] adding missing import if required --- .../java/MoveFieldAnnotationToType.java | 22 +++++++++++-------- .../java/MoveFieldAnnotationToTypeTest.java | 2 ++ 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java index 5d494c98c..03159eeac 100644 --- a/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java +++ b/src/main/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToType.java @@ -153,16 +153,20 @@ private boolean isQualifiedClass(@Nullable TypeTree tree) { private TypeTree annotateInnerClass(TypeTree qualifiedClassRef, J.Annotation annotation) { J.Annotation usedAnnotation = annotation; if (annotation.getAnnotationType() instanceof J.FieldAccess) { + J.Identifier identifier = new J.Identifier( + Tree.randomId(), + annotation.getAnnotationType().getPrefix(), + annotation.getAnnotationType().getMarkers(), + new ArrayList<>(), + annotation.getSimpleName(), + annotation.getType(), + null + ); + if (identifier.getType() != null) { + maybeAddImport(((JavaType.Class) identifier.getType()).getFullyQualifiedName()); + } usedAnnotation = usedAnnotation.withAnnotationType( - new J.Identifier( - Tree.randomId(), - annotation.getAnnotationType().getPrefix(), - annotation.getAnnotationType().getMarkers(), - new ArrayList<>(), - annotation.getSimpleName(), - annotation.getType(), - null - )); + identifier); } if (qualifiedClassRef instanceof J.FieldAccess) { J.FieldAccess q = (J.FieldAccess) qualifiedClassRef; diff --git a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java index b7563cd81..a3cef8cfc 100644 --- a/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/java/MoveFieldAnnotationToTypeTest.java @@ -212,6 +212,8 @@ public void someFunction(@org.openrewrite.internal.lang.Nullable org.openrewrite """ package org.openrewrite; + import org.openrewrite.internal.lang.Nullable; + public class Test { public void someFunction(org.openrewrite.internal.@Nullable MetricsHelper metrics) { } From 81459cb7cf3caa196410867e093e598bc9d89fdc Mon Sep 17 00:00:00 2001 From: Niels de Bruin Date: Fri, 25 Oct 2024 15:21:19 +0200 Subject: [PATCH 148/183] Add `AnnotateNullableMethods` recipe (#364) * Add AnnotateNullableMethodsRecipe * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Tim te Beek * Update src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java Co-authored-by: Tim te Beek * Apply suggestions from code review Co-authored-by: Tim te Beek * Process PR feedback * Apply formatting suggestions * Slight polish * Further polish * Polish tests * Verify handling of J.NewClass with nested return * Update src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Only visit method body, to avoid arguments with a nested return * Reduce using the updated parent tree cursor * Fix check for static return annotations * Insert a qualified annotation that we then possibly shorten * Limit scope of import fix * Add extra null check --------- Co-authored-by: Niels de Bruin Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tim te Beek --- .../AnnotateNullableMethods.java | 162 +++++++++++ .../AnnotateNullableMethodsTest.java | 252 ++++++++++++++++++ 2 files changed, 414 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java new file mode 100644 index 000000000..94763084a --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java @@ -0,0 +1,162 @@ +/* + * 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.jspecify.annotations.Nullable; +import org.openrewrite.Cursor; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.*; +import org.openrewrite.java.service.AnnotationService; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AnnotateNullableMethods extends Recipe { + + private static final String NULLABLE_ANN_CLASS = "org.jspecify.annotations.Nullable"; + private static final AnnotationMatcher NULLABLE_ANNOTATION_MATCHER = new AnnotationMatcher("@" + NULLABLE_ANN_CLASS); + + @Override + public String getDisplayName() { + return "Annotate methods which may return `null` with `@Nullable`"; + } + + @Override + public String getDescription() { + return "Add the `@org.jspecify.annotation.Nullable` to non-private methods that may return `null`. " + + "This recipe scans for methods that do not already have a `@Nullable` annotation and checks their return " + + "statements for potential null values. It also identifies known methods from standard libraries that may " + + "return null, such as methods from `Map`, `Queue`, `Deque`, `NavigableSet`, and `Spliterator`. " + + "The return of streams, or lambdas are not taken into account."; + } + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor() { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) { + if (!methodDeclaration.hasModifier(J.Modifier.Type.Public) || + methodDeclaration.getMethodType() == null || + methodDeclaration.getMethodType().getReturnType() instanceof JavaType.Primitive || + service(AnnotationService.class).matches(getCursor(), NULLABLE_ANNOTATION_MATCHER) || + (methodDeclaration.getReturnTypeExpression() != null && + service(AnnotationService.class).matches(new Cursor(null, methodDeclaration.getReturnTypeExpression()), NULLABLE_ANNOTATION_MATCHER))) { + return methodDeclaration; + } + + J.MethodDeclaration md = super.visitMethodDeclaration(methodDeclaration, ctx); + updateCursor(md); + if (FindNullableReturnStatements.find(md.getBody(), getCursor().getParentTreeCursor())) { + J.MethodDeclaration annotatedMethod = JavaTemplate.builder("@" + NULLABLE_ANN_CLASS) + .javaParser(JavaParser.fromJavaVersion().dependsOn( + "package org.jspecify.annotations;public @interface Nullable {}")) + .build() + .apply(getCursor(), md.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); + doAfterVisit(ShortenFullyQualifiedTypeReferences.modifyOnly(annotatedMethod)); + return (J.MethodDeclaration) new NullableOnMethodReturnType().getVisitor().visitNonNull(annotatedMethod, ctx, getCursor().getParentTreeCursor()); + } + return md; + } + }; + } + + private static class FindNullableReturnStatements extends JavaIsoVisitor { + + private static final List KNOWN_NULLABLE_METHODS = Arrays.asList( + new MethodMatcher("java.util.Map computeIfAbsent(..)"), + new MethodMatcher("java.util.Map computeIfPresent(..)"), + new MethodMatcher("java.util.Map get(..)"), + new MethodMatcher("java.util.Map merge(..)"), + new MethodMatcher("java.util.Map put(..)"), + new MethodMatcher("java.util.Map putIfAbsent(..)"), + + new MethodMatcher("java.util.Queue poll(..)"), + new MethodMatcher("java.util.Queue peek(..)"), + + new MethodMatcher("java.util.Deque peekFirst(..)"), + new MethodMatcher("java.util.Deque pollFirst(..)"), + new MethodMatcher("java.util.Deque peekLast(..)"), + + new MethodMatcher("java.util.NavigableSet lower(..)"), + new MethodMatcher("java.util.NavigableSet floor(..)"), + new MethodMatcher("java.util.NavigableSet ceiling(..)"), + new MethodMatcher("java.util.NavigableSet higher(..)"), + new MethodMatcher("java.util.NavigableSet pollFirst(..)"), + new MethodMatcher("java.util.NavigableSet pollLast(..)"), + + new MethodMatcher("java.util.NavigableMap lowerEntry(..)"), + new MethodMatcher("java.util.NavigableMap floorEntry(..)"), + new MethodMatcher("java.util.NavigableMap ceilingEntry(..)"), + new MethodMatcher("java.util.NavigableMap higherEntry(..)"), + new MethodMatcher("java.util.NavigableMap lowerKey(..)"), + new MethodMatcher("java.util.NavigableMap floorKey(..)"), + new MethodMatcher("java.util.NavigableMap ceilingKey(..)"), + new MethodMatcher("java.util.NavigableMap higherKey(..)"), + new MethodMatcher("java.util.NavigableMap firstEntry(..)"), + new MethodMatcher("java.util.NavigableMap lastEntry(..)"), + new MethodMatcher("java.util.NavigableMap pollFirstEntry(..)"), + new MethodMatcher("java.util.NavigableMap pollLastEntry(..)"), + + new MethodMatcher("java.util.Spliterator trySplit(..)") + ); + + static boolean find(@Nullable J subtree, Cursor parentTreeCursor) { + return new FindNullableReturnStatements().reduce(subtree, new AtomicBoolean(), parentTreeCursor).get(); + } + + @Override + public J.Lambda visitLambda(J.Lambda lambda, AtomicBoolean atomicBoolean) { + // Do not evaluate return statements in lambdas + return lambda; + } + + @Override + public J.Return visitReturn(J.Return retrn, AtomicBoolean found) { + if (found.get()) { + return retrn; + } + J.Return r = super.visitReturn(retrn, found); + found.set(maybeIsNull(r.getExpression())); + return r; + } + + private boolean maybeIsNull(@Nullable Expression returnExpression) { + if (returnExpression instanceof J.Literal) { + return ((J.Literal) returnExpression).getValue() == null; + } + if (returnExpression instanceof J.MethodInvocation) { + return isKnowNullableMethod((J.MethodInvocation) returnExpression); + } + return false; + } + + private boolean isKnowNullableMethod(J.MethodInvocation methodInvocation) { + for (MethodMatcher m : KNOWN_NULLABLE_METHODS) { + if (m.matches(methodInvocation)) { + return true; + } + } + return false; + } + } +} diff --git a/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java b/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java new file mode 100644 index 000000000..59d3848fb --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java @@ -0,0 +1,252 @@ +/* + * 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.DocumentExample; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class AnnotateNullableMethodsTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec + .recipe(new AnnotateNullableMethods()) + .parser(JavaParser.fromJavaVersion().classpath("jspecify")); + } + + @DocumentExample + @Test + void methodReturnsNullLiteral() { + rewriteRun( + //language=java + java( + """ + public class Test { + + public String getString() { + return null; + } + + public String getStringWithMultipleReturn() { + if (System.currentTimeMillis() % 2 == 0) { + return "Not null"; + } + return null; + } + } + """, + """ + import org.jspecify.annotations.Nullable; + + public class Test { + + public @Nullable String getString() { + return null; + } + + public @Nullable String getStringWithMultipleReturn() { + if (System.currentTimeMillis() % 2 == 0) { + return "Not null"; + } + return null; + } + } + """ + ) + ); + } + + @Test + void methodReturnNullButIsAlreadyAnnotated() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.Nullable; + + public class Test { + public @Nullable String getString() { + return null; + } + + public @Nullable String getStringWithMultipleReturn() { + if (System.currentTimeMillis() % 2 == 0) { + return "Not null"; + } + return null; + } + } + """ + ) + ); + } + + @Test + void methodDoesNotReturnNull() { + rewriteRun( + //language=java + java( + """ + package org.example; + + public class Test { + public String getString() { + return "Hello"; + } + } + """ + ) + ); + } + + @Test + void methodReturnsDelegateKnowNullableMethod() { + rewriteRun( + //language=java + java( + """ + import java.util.Map; + + public class Test { + + public String getString(Map map) { + return map.get("key"); + } + } + """, + """ + import org.jspecify.annotations.Nullable; + + import java.util.Map; + + public class Test { + + public @Nullable String getString(Map map) { + return map.get("key"); + } + } + """ + ) + ); + } + + @Test + void methodWithLambdaShouldNotBeAnnotated() { + rewriteRun( + //language=java + java( + """ + import java.util.stream.Stream; + class A { + public Runnable getRunnable() { + return () -> null; + } + + public Integer someStream(){ + // Stream with lambda class. + return Stream.of(1, 2, 3) + .map(i -> {if (i == 2) return null; else return i;}) + .reduce((a, b) -> a + b) + .orElse(null); + } + } + """ + ) + ); + } + + @Test + void privateMethodsShouldNotBeAnnotated() { + rewriteRun( + //language=java + java( + """ + public class Test { + private String getString() { + return null; + } + } + """ + ) + ); + } + + @Test + void returnWithinNewClass() { + rewriteRun( + //language=java + java( + """ + import java.util.concurrent.Callable; + + public class Test { + + public Callable getString() { + return new Callable() { + @Override + public String call() throws Exception { + return null; + } + }; + } + + } + """, + """ + import org.jspecify.annotations.Nullable; + + import java.util.concurrent.Callable; + + public class Test { + + public Callable getString() { + return new Callable() { + + @Override + public @Nullable String call() throws Exception { + return null; + } + }; + } + + } + """ + ) + ); + } + + @Test + void returnStaticNestInnerClassAnnotation() { + rewriteRun( + //language=java + java( + """ + import org.jspecify.annotations.Nullable; + + public class Outer { + public static Outer.@Nullable Inner test() { return null; } + static class Inner {} + } + """ + ) + ); + } +} From d6e2331a6841146b393e51fca199ad120278fd96 Mon Sep 17 00:00:00 2001 From: Jonathan Leitschuh Date: Sun, 27 Oct 2024 11:48:22 -0400 Subject: [PATCH 149/183] Add `BufferedWriterCreation` recipe (#258) * Add Two Recipes - BufferedWriterCreation - SimplifyConstantTernaryExecution Signed-off-by: Jonathan Leitschuh * Apply auto formatting to owning LST element * Apply formatter and best practices * Drop unnecessary UsesType * Minor polish * Drop now unnecessary SimplifyConstantTernaryExecution Following https://github.com/openrewrite/rewrite/pull/4617 * Restore private `cleanupBooleanExpression` * Convert to regular Refaster recipe Following https://github.com/openrewrite/rewrite-templating/pull/114 --------- Signed-off-by: Jonathan Leitschuh Co-authored-by: Knut Wannheden Co-authored-by: Tim te Beek --- .../BufferedWriterCreation.java | 100 ++++++++++ .../SimplifyConstantIfBranchExecution.java | 35 ++-- .../BufferedWriterCreationTest.java | 183 ++++++++++++++++++ 3 files changed, 294 insertions(+), 24 deletions(-) create mode 100644 src/main/java/org/openrewrite/staticanalysis/BufferedWriterCreation.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/BufferedWriterCreationTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/BufferedWriterCreation.java b/src/main/java/org/openrewrite/staticanalysis/BufferedWriterCreation.java new file mode 100644 index 000000000..6d7552bf9 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/BufferedWriterCreation.java @@ -0,0 +1,100 @@ +/* + * 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 com.google.errorprone.refaster.annotation.AfterTemplate; +import com.google.errorprone.refaster.annotation.BeforeTemplate; +import org.openrewrite.java.template.Primitive; +import org.openrewrite.java.template.RecipeDescriptor; + +import java.io.BufferedWriter; +import java.io.IOException; + +@RecipeDescriptor( + name = "Modernize `BufferedWriter` creation & prevent file descriptor leaks", + description = "The code `new BufferedWriter(new FileWriter(f))` creates a `BufferedWriter` that does not close the underlying `FileWriter` when it is closed. " + + "This can lead to file descriptor leaks as per [CWE-755](https://cwe.mitre.org/data/definitions/755.html). " + + "Use `Files.newBufferedWriter` to create a `BufferedWriter` that closes the underlying file descriptor when it is closed." +) +public class BufferedWriterCreation { + + @RecipeDescriptor( + name = "Convert `new BufferedWriter(new FileWriter(File))` to `Files.newBufferedWriter(Path)`", + description = "Convert `new BufferedWriter(new FileWriter(f))` to `Files.newBufferedWriter(f.toPath())`." + ) + static class BufferedWriterFromNewFileWriterWithFileArgument { + @BeforeTemplate + BufferedWriter before(java.io.File f) throws IOException { + return new BufferedWriter(new java.io.FileWriter(f)); + } + + @AfterTemplate + BufferedWriter after(java.io.File f) throws IOException { + return java.nio.file.Files.newBufferedWriter(f.toPath()); + } + } + + @RecipeDescriptor( + name = "Convert `new BufferedWriter(new FileWriter(String))` to `Files.newBufferedWriter(Path)`", + description = "Convert `new BufferedWriter(new FileWriter(s))` to `Files.newBufferedWriter(new java.io.File(s).toPath())`." + ) + static class BufferedWriterFromNewFileWriterWithStringArgument { + @BeforeTemplate + BufferedWriter before(String s) throws IOException { + return new BufferedWriter(new java.io.FileWriter(s)); + } + + @AfterTemplate + BufferedWriter after(String s) throws IOException { + return java.nio.file.Files.newBufferedWriter(new java.io.File(s).toPath()); + } + } + + @RecipeDescriptor( + name = "Convert `new BufferedWriter(new FileWriter(File, boolean))` to `Files.newBufferedWriter(Path, StandardOpenOption)`", + description = "Convert `new BufferedWriter(new FileWriter(f, b))` to `Files.newBufferedWriter(f.toPath(), b ? StandardOpenOption.APPEND : StandardOpenOption.CREATE)`." + ) + static class BufferedWriterFromNewFileWriterWithFileAndBooleanArguments { + @BeforeTemplate + BufferedWriter before(java.io.File f, @Primitive Boolean b) throws IOException { + return new BufferedWriter(new java.io.FileWriter(f, b)); + } + + @AfterTemplate + BufferedWriter after(java.io.File f, @Primitive Boolean b) throws IOException { + return java.nio.file.Files.newBufferedWriter(f.toPath(), b ? + java.nio.file.StandardOpenOption.APPEND : java.nio.file.StandardOpenOption.CREATE); + } + } + + @RecipeDescriptor( + name = "Convert `new BufferedWriter(new FileWriter(String, boolean))` to `Files.newBufferedWriter(Path, StandardOpenOption)`", + description = "Convert `new BufferedWriter(new FileWriter(s, b))` to `Files.newBufferedWriter(new java.io.File(s).toPath(), b ? StandardOpenOption.APPEND : StandardOpenOption.CREATE)`." + ) + static class BufferedWriterFromNewFileWriterWithStringAndBooleanArguments { + @BeforeTemplate + BufferedWriter before(String s, @Primitive Boolean b) throws IOException { + return new BufferedWriter(new java.io.FileWriter(s, b)); + } + + @AfterTemplate + BufferedWriter after(String s, @Primitive Boolean b) throws IOException { + return java.nio.file.Files.newBufferedWriter(new java.io.File(s).toPath(), b ? + java.nio.file.StandardOpenOption.APPEND : java.nio.file.StandardOpenOption.CREATE); + } + } + +} diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java index 0c634cd66..d5295f4fb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java @@ -15,10 +15,9 @@ */ package org.openrewrite.staticanalysis; -import org.jspecify.annotations.Nullable; +import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; -import org.openrewrite.SourceFile; import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.cleanup.SimplifyBooleanExpressionVisitor; @@ -69,19 +68,16 @@ public J visitBlock(J.Block block, ExecutionContext ctx) { } @SuppressWarnings("unchecked") - private E cleanupBooleanExpression( - E expression, ExecutionContext ctx - ) { - E ex1 = - (E) new UnnecessaryParenthesesVisitor() - .visitNonNull(expression, ctx, getCursor().getParentOrThrow()); - ex1 = (E) new SimplifyBooleanExpressionVisitor() - .visitNonNull(ex1, ctx, getCursor().getParentTreeCursor()); - if (expression == ex1 || isLiteralFalse(ex1) || isLiteralTrue(ex1)) { + private static E cleanupBooleanExpression(E expression, Cursor c, ExecutionContext ctx) { + E ex1 = (E) new UnnecessaryParenthesesVisitor<>().visitNonNull(expression, ctx, c.getParentOrThrow()); + ex1 = (E) new SimplifyBooleanExpressionVisitor().visitNonNull(ex1, ctx, c.getParentTreeCursor()); + if (expression == ex1 || + J.Literal.isLiteralValue(ex1, Boolean.FALSE) || + J.Literal.isLiteralValue(ex1, Boolean.TRUE)) { return ex1; } // Run recursively until no further changes are needed - return cleanupBooleanExpression(ex1, ctx); + return cleanupBooleanExpression(ex1, c, ctx); } @Override @@ -89,14 +85,14 @@ public J visitIf(J.If if_, ExecutionContext ctx) { J.If if__ = (J.If) super.visitIf(if_, ctx); J.If ifBeforeCleanup = if__; - J.ControlParentheses cp = cleanupBooleanExpression(if__.getIfCondition(), ctx); + J.ControlParentheses cp = cleanupBooleanExpression(if__.getIfCondition(), getCursor(), ctx); if__ = if__.withIfCondition(cp); // The compile-time constant value of the if condition control parentheses. final Optional compileTimeConstantBoolean; - if (isLiteralTrue(cp.getTree())) { + if (J.Literal.isLiteralValue(cp.getTree(), Boolean.TRUE)) { compileTimeConstantBoolean = Optional.of(true); - } else if (isLiteralFalse(cp.getTree())) { + } else if (J.Literal.isLiteralValue(cp.getTree(), Boolean.FALSE)) { compileTimeConstantBoolean = Optional.of(false); } else { // The condition is not a literal, so we can't simplify it. @@ -147,14 +143,5 @@ public J visitIf(J.If if_, ExecutionContext ctx) { return J.Block.createEmptyBlock(); } } - - private static boolean isLiteralTrue(@Nullable Expression expression) { - return J.Literal.isLiteralValue(expression, Boolean.TRUE); - } - - private static boolean isLiteralFalse(@Nullable Expression expression) { - return J.Literal.isLiteralValue(expression, Boolean.FALSE); - } } - } diff --git a/src/test/java/org/openrewrite/staticanalysis/BufferedWriterCreationTest.java b/src/test/java/org/openrewrite/staticanalysis/BufferedWriterCreationTest.java new file mode 100644 index 000000000..b1f560f71 --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/BufferedWriterCreationTest.java @@ -0,0 +1,183 @@ +/* + * 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.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +@SuppressWarnings("EmptyTryBlock") +class BufferedWriterCreationTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new BufferedWriterCreationRecipes()); + } + + @Test + @DocumentExample + void bufferedReaderCreation() { + rewriteRun( + //language=java + java( + """ + import java.io.BufferedWriter; + import java.io.FileWriter; + import java.io.File; + import java.io.IOException; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(File f) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { + + } + } + } + """, + """ + import java.io.BufferedWriter; + import java.io.File; + import java.io.IOException; + import java.nio.file.Files; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(File f) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(f.toPath())) { + + } + } + } + """ + ) + ); + } + + @Test + void bufferedReaderCreationAppend() { + rewriteRun( + //language=java + java( + """ + import java.io.BufferedWriter; + import java.io.FileWriter; + import java.io.File; + import java.io.IOException; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(File f) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f, true))) { + + } + } + } + """, + """ + import java.io.BufferedWriter; + import java.io.File; + import java.io.IOException; + import java.nio.file.Files; + import java.nio.file.StandardOpenOption; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(File f) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(f.toPath(), StandardOpenOption.APPEND)) { + + } + } + } + """ + ) + ); + } + + @Test + void bufferedReaderStringCreation() { + rewriteRun( + //language=java + java( + """ + import java.io.BufferedWriter; + import java.io.FileWriter; + import java.io.File; + import java.io.IOException; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(String f) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f))) { + + } + } + } + """, + """ + import java.io.BufferedWriter; + import java.io.File; + import java.io.IOException; + import java.nio.file.Files; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(String f) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(new File(f).toPath())) { + + } + } + } + """ + ) + ); + } + + @Test + void bufferedReaderStringCreationAppend() { + rewriteRun( + //language=java + java( + """ + import java.io.BufferedWriter; + import java.io.FileWriter; + import java.io.File; + import java.io.IOException; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(String f) throws IOException { + try (BufferedWriter writer = new BufferedWriter(new FileWriter(f, false))) { + + } + } + } + """, + """ + import java.io.BufferedWriter; + import java.io.File; + import java.io.IOException; + import java.nio.file.Files; + import java.nio.file.StandardOpenOption; + + public class BufferedWriterCreationTest { + public void createBufferedWriter(String f) throws IOException { + try (BufferedWriter writer = Files.newBufferedWriter(new File(f).toPath(), StandardOpenOption.CREATE)) { + + } + } + } + """ + ) + ); + } +} From a2203854de107c7a01e6bd8beb5b6ad3f0a3434e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sun, 27 Oct 2024 17:11:36 +0100 Subject: [PATCH 150/183] SimplifyConstantIfBranchExecution should clean up ternaries - For https://github.com/openrewrite/rewrite-feature-flags/pull/36 --- .../SimplifyConstantIfBranchExecution.java | 6 ++++ ...SimplifyConstantIfBranchExecutionTest.java | 35 +++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java index d5295f4fb..6c921f251 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecution.java @@ -143,5 +143,11 @@ public J visitIf(J.If if_, ExecutionContext ctx) { return J.Block.createEmptyBlock(); } } + + @Override + public J visitTernary(J.Ternary ternary, ExecutionContext ctx) { + J.Ternary j = (J.Ternary) super.visitTernary(ternary, ctx); + return cleanupBooleanExpression(j, getCursor(), ctx); + } } } diff --git a/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java b/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java index 612da862e..b34022baa 100644 --- a/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/SimplifyConstantIfBranchExecutionTest.java @@ -1322,4 +1322,39 @@ void test() { ) ); } + + @Test + void constantTernarySimplfied() { + rewriteRun( + //language=java + java( + """ + class Test { + boolean trueCondition1 = true ? true : false; + boolean trueCondition2 = false ? false : true; + boolean trueCondition3 = !true ? false : true; + boolean trueCondition4 = !false ? true : false; + + boolean falseCondition1 = true ? false : true; + boolean falseCondition2 = false ? true : false; + boolean falseCondition3 = !false ? false : true; + boolean falseCondition4 = !true ? true : false; + } + """, + """ + class Test { + boolean trueCondition1 = true; + boolean trueCondition2 = true; + boolean trueCondition3 = true; + boolean trueCondition4 = true; + + boolean falseCondition1 = false; + boolean falseCondition2 = false; + boolean falseCondition3 = false; + boolean falseCondition4 = false; + } + """ + ) + ); + } } From be381dc0cb1cd28e479cb466f88f33e7e0425f68 Mon Sep 17 00:00:00 2001 From: Niels de Bruin Date: Mon, 28 Oct 2024 11:47:07 +0100 Subject: [PATCH 151/183] refactor: add @Nullable to methods who may return null (#383) Use this link to re-run the recipe: https://app.moderne.io/builder/ji8mLIdUI?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../staticanalysis/CombineSemanticallyEqualCatchBlocks.java | 4 ++-- .../staticanalysis/RemoveCallsToObjectFinalize.java | 4 +++- .../org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java | 4 +++- .../staticanalysis/RemoveEmptyJavaDocParameters.java | 3 ++- .../openrewrite/staticanalysis/RemoveSystemOutPrintln.java | 4 +++- .../openrewrite/staticanalysis/RemoveUnneededAssertion.java | 3 ++- .../staticanalysis/RemoveUnusedLocalVariables.java | 2 +- .../openrewrite/staticanalysis/RemoveUnusedPrivateFields.java | 4 ++-- .../staticanalysis/RemoveUnusedPrivateMethods.java | 3 ++- .../openrewrite/staticanalysis/SimplifyCompoundVisitor.java | 3 ++- 10 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java index 9976e1c21..ed9f3ed4d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java +++ b/src/main/java/org/openrewrite/staticanalysis/CombineSemanticallyEqualCatchBlocks.java @@ -149,7 +149,7 @@ static class RemoveCatches extends JavaVisitor { } @Override - public J visitMultiCatch(J.MultiCatch multiCatch, ExecutionContext ctx) { + public @Nullable J visitMultiCatch(J.MultiCatch multiCatch, ExecutionContext ctx) { Cursor parentCursor = getCursor().dropParentUntil(is -> is instanceof J.Try.Catch || is instanceof J.Try); if (removeCatches != null && parentCursor.getValue() instanceof J.Try.Catch) { if (removeCatches.contains((J.Try.Catch) parentCursor.getValue())) { @@ -160,7 +160,7 @@ public J visitMultiCatch(J.MultiCatch multiCatch, ExecutionContext ctx) { } @Override - public J visitCatch(J.Try.Catch _catch, ExecutionContext ctx) { + public @Nullable J visitCatch(J.Try.Catch _catch, ExecutionContext ctx) { if (removeCatches != null) { if (removeCatches.contains(_catch)) { return null; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java index 067fa55dc..6451b2ffc 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToObjectFinalize.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; @@ -55,8 +56,9 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesMethod<>(OBJECT_FINALIZE), new JavaIsoVisitor() { + @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation invocation = super.visitMethodInvocation(method, ctx); if (invocation.getMethodType() != null && "finalize".equals(invocation.getMethodType().getName()) && diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java index a53b105ed..de297e84c 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveCallsToSystemGc.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; @@ -56,8 +57,9 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { return Preconditions.check(Preconditions.or(new UsesMethod<>(SYSTEM_GC), new UsesMethod<>(RUNTIME_GC)), new JavaIsoVisitor() { + @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { J.MethodInvocation invocation = super.visitMethodInvocation(method, ctx); if (SYSTEM_GC.matches(invocation) || RUNTIME_GC.matches(invocation)) { doAfterVisit(new EmptyBlock().getVisitor()); diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java b/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java index 033d5060e..2d473f852 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveEmptyJavaDocParameters.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Incubating; import org.openrewrite.Recipe; @@ -74,7 +75,7 @@ public RemoveEmptyParamVisitor() { } @Override - public Javadoc visitDocComment(Javadoc.DocComment javadoc, ExecutionContext ctx) { + public @Nullable Javadoc visitDocComment(Javadoc.DocComment javadoc, ExecutionContext ctx) { List newBody = new ArrayList<>(javadoc.getBody().size()); boolean useNewBody = false; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintln.java b/src/main/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintln.java index 5a07e54ab..2d8126e8e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintln.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveSystemOutPrintln.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; @@ -40,8 +41,9 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesMethod<>(SYSTEM_OUT_PRINTLN), new JavaIsoVisitor() { + @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { if (SYSTEM_OUT_PRINTLN.matches(method)) { //noinspection DataFlowIssue return null; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnneededAssertion.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnneededAssertion.java index 1530a77cd..f5b7dd9ae 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnneededAssertion.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnneededAssertion.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; @@ -137,7 +138,7 @@ public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionCon } @Override - public J.Assert visitAssert(J.Assert anAssert, ExecutionContext ctx) { + public J.@Nullable Assert visitAssert(J.Assert anAssert, ExecutionContext ctx) { if (anAssert.getCondition() instanceof J.Literal) { if (J.Literal.isLiteralValue(anAssert.getCondition(), true)) { //noinspection ConstantConditions diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index dc08fde4c..54b0f1504 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -103,7 +103,7 @@ private Cursor getCursorToParentScope(Cursor cursor) { } @Override - public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { + public J.VariableDeclarations.@Nullable NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, ExecutionContext ctx) { // skip matching ignored variable names right away if (ignoreVariableNames != null && ignoreVariableNames.contains(variable.getSimpleName())) { return variable; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java index b3872fcea..842fcb5b8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateFields.java @@ -200,7 +200,7 @@ public RemoveUnusedField(J.VariableDeclarations.NamedVariable namedVariable) { } @Override - public J visitVariableDeclarations(J.VariableDeclarations multiVariable, AtomicBoolean declarationDeleted) { + public @Nullable J visitVariableDeclarations(J.VariableDeclarations multiVariable, AtomicBoolean declarationDeleted) { if (multiVariable.getVariables().size() == 1 && multiVariable.getVariables().contains(namedVariable)) { declarationDeleted.set(true); //noinspection ConstantConditions @@ -210,7 +210,7 @@ public J visitVariableDeclarations(J.VariableDeclarations multiVariable, AtomicB } @Override - public J visitVariable(J.VariableDeclarations.NamedVariable variable, AtomicBoolean declarationDeleted) { + public @Nullable J visitVariable(J.VariableDeclarations.NamedVariable variable, AtomicBoolean declarationDeleted) { if (variable == namedVariable) { //noinspection ConstantConditions return null; diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java index 6b422b900..6549d3111 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedPrivateMethods.java @@ -15,6 +15,7 @@ */ package org.openrewrite.staticanalysis; +import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; import org.openrewrite.Preconditions; import org.openrewrite.Recipe; @@ -81,7 +82,7 @@ private boolean unusedWarningsSuppressed(J classDeclaration) { } @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, + public J.@Nullable MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); JavaType.Method methodType = method.getMethodType(); diff --git a/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java b/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java index e386db1af..380b1541a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/SimplifyCompoundVisitor.java @@ -25,8 +25,9 @@ import org.openrewrite.java.tree.JLeftPadded; public class SimplifyCompoundVisitor extends JavaVisitor { + @Override - public J visitAssignmentOperation(J.AssignmentOperation assignOp, ExecutionContext ctx) { + public @Nullable J visitAssignmentOperation(J.AssignmentOperation assignOp, ExecutionContext ctx) { Expression cleanedUpAssignment = cleanupBooleanExpression(assignOp.getAssignment(), ctx); if (assignOp.getOperator() == J.AssignmentOperation.Type.BitAnd) { if (isLiteralTrue(cleanedUpAssignment)) { From 286d4eec6c1b5d51e357e49532eb398e3974b3d2 Mon Sep 17 00:00:00 2001 From: Yurii Date: Mon, 28 Oct 2024 13:19:29 +0100 Subject: [PATCH 152/183] Add recipe `ReplaceClassIsInstanceWithInstanceof` for SonarQube RSPEC-6202 (#381) * add: recipe for RSPEC-S1170, adding "static" to "public final" constants and fields * add: recipe and unit test for RSPEC-S6202 * update: add recipe to list * fix: correct the name of the recipe * update: for more cases in unit test * update: correct copyright messages * refactor: format code * Update src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/staticanalysis/AddStaticModifierToPublicFinalConstantsAndFields.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Update src/main/java/org/openrewrite/staticanalysis/AddStaticModifierToPublicFinalConstantsAndFields.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Test additional cases pointed out in review * Format tests * Slight polish to ReplaceClassIsInstanceWithInstanceof * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * update: Recipe for RSPEC-S1170 is removed because it needs further consideration, so we will do it in another PR * Minor polish --------- Co-authored-by: Sheng Yu Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek --- .../ReplaceClassIsInstanceWithInstanceof.java | 86 +++++++++++++ .../rewrite/common-static-analysis.yml | 1 + ...laceClassIsInstanceWithInstanceofTest.java | 116 ++++++++++++++++++ 3 files changed, 203 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java new file mode 100644 index 000000000..4ed0d4bca --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java @@ -0,0 +1,86 @@ +/* + * 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.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.J.FieldAccess; +import org.openrewrite.java.tree.J.Identifier; +import org.openrewrite.java.tree.J.MethodInvocation; +import org.openrewrite.java.tree.JavaType; + +import java.util.Collections; +import java.util.Set; + +public class ReplaceClassIsInstanceWithInstanceof extends Recipe { + + @Override + public String getDisplayName() { + return "Replace `A.class.isInstance(a)` with `a instanceof A`"; + } + + @Override + public String getDescription() { + return "There should be no `A.class.isInstance(a)`, it should be replaced by `a instanceof A`."; + } + + @Override + public Set getTags() { + return Collections.singleton("RSPEC-S6202"); + } + + @Override + public JavaVisitor getVisitor() { + // use JavaVisitor instead of JavaIsoVisitor because we changed the type of LST + return new JavaVisitor() { + + private final MethodMatcher matcher = new MethodMatcher("java.lang.Class isInstance(java.lang.Object)"); + + @Override + public J visitMethodInvocation(MethodInvocation method, ExecutionContext ctx) { + // make sure we find the right method and the left part is something like "SomeClass.class" + if (matcher.matches(method) && isObjectClass(method.getSelect())) { + // for code like "A.class.isInstance(a)", select is "String.class", name is "isInstance", argument is "a" + Identifier objectExpression = (Identifier) method.getArguments().get(0); + FieldAccess fieldAccessPart = (FieldAccess) method.getSelect(); + String className = ((JavaType.Class) fieldAccessPart.getTarget().getType()).getClassName(); + // upcast to type J, so J.MethodInvocation can be replaced by J.InstanceOf + J.InstanceOf instanceOf = JavaTemplate.apply("#{any(org.openrewrite.java.tree.Expression)} instanceof #{}", + getCursor(), method.getCoordinates().replace(), objectExpression, className); + return maybeAutoFormat(method, instanceOf, ctx); + } + return super.visitMethodInvocation(method, ctx); + } + + private boolean isObjectClass(@Nullable Expression expression) { + if (expression instanceof J.FieldAccess) { + J.FieldAccess fieldAccess = (J.FieldAccess) expression; + if (fieldAccess.getTarget() instanceof Identifier) { + Identifier identifier = (Identifier) fieldAccess.getTarget(); + return identifier.getType() instanceof JavaType.Class; + } + } + return false; + } + }; + } +} diff --git a/src/main/resources/META-INF/rewrite/common-static-analysis.yml b/src/main/resources/META-INF/rewrite/common-static-analysis.yml index 598e11499..bb6547e9e 100644 --- a/src/main/resources/META-INF/rewrite/common-static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/common-static-analysis.yml @@ -72,6 +72,7 @@ recipeList: - org.openrewrite.staticanalysis.RenameLocalVariablesToCamelCase - org.openrewrite.staticanalysis.RenameMethodsNamedHashcodeEqualOrToString - org.openrewrite.staticanalysis.RenamePrivateFieldsToCamelCase + - org.openrewrite.staticanalysis.ReplaceClassIsInstanceWithInstanceof - org.openrewrite.staticanalysis.ReplaceLambdaWithMethodReference - org.openrewrite.staticanalysis.ReplaceStringBuilderWithString - org.openrewrite.staticanalysis.SimplifyBooleanExpression diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java new file mode 100644 index 000000000..d96312d9c --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java @@ -0,0 +1,116 @@ +/* + * 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.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.java.Assertions.javaVersion; + +@SuppressWarnings({"RedundantClassCall", "ConstantValue", "UnusedAssignment"}) +class ReplaceClassIsInstanceWithInstanceofTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new ReplaceClassIsInstanceWithInstanceof()); + } + + @Test + @DocumentExample + void changeInstanceOf() { + rewriteRun( + //language=java + java( + """ + class A { + void foo() { + String s = ""; + boolean result = String.class.isInstance(s); + result = Integer.class.isInstance(s); + } + } + """, + """ + class A { + void foo() { + String s = ""; + boolean result = s instanceof String; + result = s instanceof Integer; + } + } + """ + ) + ); + } + + @Test + void doNotChangeWhenAlreadyInstanceOf() { + rewriteRun( + //language=java + java( + """ + class A { + boolean foo() { + String s = ""; + return s instanceof String; + } + } + """ + ) + ); + } + + @Test + void doNotChangeWhenVariable() { + rewriteRun( + //language=java + java( + """ + class A { + void foo(Class clazz) { + String s = ""; + boolean result = clazz.isInstance(s); + } + } + """ + ) + ); + } + + @Test + void doNotChangeInstanceOfWithVariable() { + rewriteRun( + //language=java + java( + """ + class A { + String foo(Object obj) { + if (obj instanceof String s) { + return s; + } + return null; + } + } + """, + spec -> spec.markers(javaVersion(17)) + ) + ); + } + +} From 62252250e117c5da194874c25ef2e332e381bffc Mon Sep 17 00:00:00 2001 From: Nate Danner Date: Fri, 1 Nov 2024 21:08:14 +0000 Subject: [PATCH 153/183] refactor: Standardize `.editorconfig` Use this link to re-run the recipe: https://app.moderne.io/builder/N3MJaFjMF?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .editorconfig | 9 +++++++++ src/test/java/.editorconfig | 5 ----- 2 files changed, 9 insertions(+), 5 deletions(-) create mode 100644 .editorconfig delete mode 100644 src/test/java/.editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..7a7ad4c70 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +insert_final_newline = true +trim_trailing_whitespace = true + +[src/test*/java/**.java] +indent_size = 4 +ij_continuation_indent_size = 2 diff --git a/src/test/java/.editorconfig b/src/test/java/.editorconfig deleted file mode 100644 index a4824935e..000000000 --- a/src/test/java/.editorconfig +++ /dev/null @@ -1,5 +0,0 @@ -root = true - -[*.java] -indent_size = 4 -ij_continuation_indent_size = 2 From 80cb6723914bb9304ce5b4902e86ae21580362c9 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 2 Nov 2024 10:59:00 +0100 Subject: [PATCH 154/183] Do not add nullable annotations for computeIfAbsent/Present Lead to excessive nullable annotations and warnings that in practice could not happen unless the computed value was null. --- .../staticanalysis/AnnotateNullableMethods.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java index 94763084a..30c749a77 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; public class AnnotateNullableMethods extends Recipe { @@ -83,13 +84,16 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDecl private static class FindNullableReturnStatements extends JavaIsoVisitor { private static final List KNOWN_NULLABLE_METHODS = Arrays.asList( - new MethodMatcher("java.util.Map computeIfAbsent(..)"), - new MethodMatcher("java.util.Map computeIfPresent(..)"), + // These mostly return a nullable current or previous value, which is more often null new MethodMatcher("java.util.Map get(..)"), new MethodMatcher("java.util.Map merge(..)"), new MethodMatcher("java.util.Map put(..)"), new MethodMatcher("java.util.Map putIfAbsent(..)"), + // These two return the current or computed value, which is less likely to be null in common usage + //new MethodMatcher("java.util.Map computeIfAbsent(..)"), + //new MethodMatcher("java.util.Map computeIfPresent(..)"), + new MethodMatcher("java.util.Queue poll(..)"), new MethodMatcher("java.util.Queue peek(..)"), From e152bcf9817c7b691e8fcf15010e53502313c347 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 2 Nov 2024 12:47:12 +0100 Subject: [PATCH 155/183] Create top level class VariableReferences For use in https://github.com/openrewrite/rewrite-testing-frameworks/pull/599 --- .../RemoveUnusedLocalVariables.java | 123 +--------------- .../staticanalysis/VariableReferences.java | 132 ++++++++++++++++++ 2 files changed, 134 insertions(+), 121 deletions(-) create mode 100644 src/main/java/org/openrewrite/staticanalysis/VariableReferences.java diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java index 54b0f1504..c915f0769 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveUnusedLocalVariables.java @@ -30,7 +30,6 @@ import java.time.Duration; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Predicate; @Value @EqualsAndHashCode(callSuper = false) @@ -132,9 +131,9 @@ private Cursor getCursorToParentScope(Cursor cursor) { return variable; } - List readReferences = References.findRhsReferences(parentScope.getValue(), variable.getName()); + List readReferences = VariableReferences.findRhsReferences(parentScope.getValue(), variable.getName()); if (readReferences.isEmpty()) { - List assignmentReferences = References.findLhsReferences(parentScope.getValue(), variable.getName()); + List assignmentReferences = VariableReferences.findLhsReferences(parentScope.getValue(), variable.getName()); for (Statement ref : assignmentReferences) { if (ref instanceof J.Assignment) { doAfterVisit(new PruneAssignmentExpression((J.Assignment) ref)); @@ -245,122 +244,4 @@ public J visitAssignment(J.Assignment a, ExecutionContext ctx) { } } - private static class References { - private static boolean isIncrementKind(Cursor tree) { - return tree.getValue() instanceof J.Unary && ((J.Unary) tree.getValue()).getOperator().isModifying(); - } - - private static @Nullable Cursor dropParentWhile(Predicate valuePredicate, Cursor cursor) { - while (cursor != null && valuePredicate.test(cursor.getValue())) { - cursor = cursor.getParent(); - } - return cursor; - } - - private static @Nullable Cursor dropParentUntil(Predicate valuePredicate, Cursor cursor) { - while (cursor != null && !valuePredicate.test(cursor.getValue())) { - cursor = cursor.getParent(); - } - return cursor; - } - - private static boolean isRhsValue(Cursor tree) { - if (!(tree.getValue() instanceof J.Identifier)) { - return false; - } - - Cursor parent = dropParentWhile(J.Parentheses.class::isInstance, tree.getParent()); - assert parent != null; - if (parent.getValue() instanceof J.Assignment) { - if (dropParentUntil(J.ControlParentheses.class::isInstance, parent) != null) { - return true; - } - J.Assignment assignment = parent.getValue(); - return assignment.getVariable() != tree.getValue(); - } - - if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable) { - J.VariableDeclarations.NamedVariable namedVariable = parent.getValue(); - return namedVariable.getName() != tree.getValue(); - } - - if (parent.getValue() instanceof J.AssignmentOperation) { - J.AssignmentOperation assignmentOperation = parent.getValue(); - if (assignmentOperation.getVariable() == tree.getValue()) { - Tree grandParent = parent.getParentTreeCursor().getValue(); - return (grandParent instanceof Expression || grandParent instanceof J.Return); - } - } - - return !(isIncrementKind(parent) && parent.getParentTreeCursor().getValue() instanceof J.Block); - } - - /** - * An identifier is considered a right-hand side ("rhs") read operation if it is not used as the left operand - * of an assignment, nor as the operand of a stand-alone increment. - * - * @param j The subtree to search. - * @param target A {@link J.Identifier} to check for usages. - * @return found {@link J} locations of "right-hand" read calls. - */ - private static List findRhsReferences(J j, J.Identifier target) { - final List refs = new ArrayList<>(); - new JavaIsoVisitor>() { - @Override - public J.Identifier visitIdentifier(J.Identifier identifier, List ctx) { - if (identifier.getSimpleName().equals(target.getSimpleName()) && isRhsValue(getCursor())) { - ctx.add(identifier); - } - return super.visitIdentifier(identifier, ctx); - } - }.visit(j, refs); - return refs; - } - - /** - * @param j The subtree to search. - * @param target A {@link J.Identifier} to check for usages. - * @return found {@link Statement} locations of "left-hand" assignment write calls. - */ - private static List findLhsReferences(J j, J.Identifier target) { - JavaIsoVisitor> visitor = new JavaIsoVisitor>() { - @Override - public J.Assignment visitAssignment(J.Assignment assignment, List ctx) { - if (assignment.getVariable() instanceof J.Identifier) { - J.Identifier i = (J.Identifier) assignment.getVariable(); - if (i.getSimpleName().equals(target.getSimpleName())) { - ctx.add(assignment); - } - } - return super.visitAssignment(assignment, ctx); - } - - @Override - public J.AssignmentOperation visitAssignmentOperation(J.AssignmentOperation assignOp, List ctx) { - if (assignOp.getVariable() instanceof J.Identifier) { - J.Identifier i = (J.Identifier) assignOp.getVariable(); - if (i.getSimpleName().equals(target.getSimpleName())) { - ctx.add(assignOp); - } - } - return super.visitAssignmentOperation(assignOp, ctx); - } - - @Override - public J.Unary visitUnary(J.Unary unary, List ctx) { - if (unary.getExpression() instanceof J.Identifier) { - J.Identifier i = (J.Identifier) unary.getExpression(); - if (i.getSimpleName().equals(target.getSimpleName())) { - ctx.add(unary); - } - } - return super.visitUnary(unary, ctx); - } - }; - - List refs = new ArrayList<>(); - visitor.visit(j, refs); - return refs; - } - } } diff --git a/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java b/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java new file mode 100644 index 000000000..557d80643 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java @@ -0,0 +1,132 @@ +package org.openrewrite.staticanalysis; + +import org.jspecify.annotations.Nullable; +import org.openrewrite.Cursor; +import org.openrewrite.Tree; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Statement; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + +public class VariableReferences { + private static boolean isIncrementKind(Cursor tree) { + return tree.getValue() instanceof J.Unary && ((J.Unary) tree.getValue()).getOperator().isModifying(); + } + + private static @Nullable Cursor dropParentWhile(Predicate valuePredicate, Cursor cursor) { + while (cursor != null && valuePredicate.test(cursor.getValue())) { + cursor = cursor.getParent(); + } + return cursor; + } + + private static @Nullable Cursor dropParentUntil(Predicate valuePredicate, Cursor cursor) { + while (cursor != null && !valuePredicate.test(cursor.getValue())) { + cursor = cursor.getParent(); + } + return cursor; + } + + private static boolean isRhsValue(Cursor tree) { + if (!(tree.getValue() instanceof J.Identifier)) { + return false; + } + + Cursor parent = dropParentWhile(J.Parentheses.class::isInstance, tree.getParent()); + assert parent != null; + if (parent.getValue() instanceof J.Assignment) { + if (dropParentUntil(J.ControlParentheses.class::isInstance, parent) != null) { + return true; + } + J.Assignment assignment = parent.getValue(); + return assignment.getVariable() != tree.getValue(); + } + + if (parent.getValue() instanceof J.VariableDeclarations.NamedVariable) { + J.VariableDeclarations.NamedVariable namedVariable = parent.getValue(); + return namedVariable.getName() != tree.getValue(); + } + + if (parent.getValue() instanceof J.AssignmentOperation) { + J.AssignmentOperation assignmentOperation = parent.getValue(); + if (assignmentOperation.getVariable() == tree.getValue()) { + Tree grandParent = parent.getParentTreeCursor().getValue(); + return (grandParent instanceof Expression || grandParent instanceof J.Return); + } + } + + return !(isIncrementKind(parent) && parent.getParentTreeCursor().getValue() instanceof J.Block); + } + + /** + * An identifier is considered a right-hand side ("rhs") read operation if it is not used as the left operand + * of an assignment, nor as the operand of a stand-alone increment. + * + * @param j The subtree to search. + * @param target A {@link J.Identifier} to check for usages. + * @return found {@link J} locations of "right-hand" read calls. + */ + public static List findRhsReferences(J j, J.Identifier target) { + final List refs = new ArrayList<>(); + new JavaIsoVisitor>() { + @Override + public J.Identifier visitIdentifier(J.Identifier identifier, List ctx) { + if (identifier.getSimpleName().equals(target.getSimpleName()) && isRhsValue(getCursor())) { + ctx.add(identifier); + } + return super.visitIdentifier(identifier, ctx); + } + }.visit(j, refs); + return refs; + } + + /** + * @param j The subtree to search. + * @param target A {@link J.Identifier} to check for usages. + * @return found {@link Statement} locations of "left-hand" assignment write calls. + */ + public static List findLhsReferences(J j, J.Identifier target) { + JavaIsoVisitor> visitor = new JavaIsoVisitor>() { + @Override + public J.Assignment visitAssignment(J.Assignment assignment, List ctx) { + if (assignment.getVariable() instanceof J.Identifier) { + J.Identifier i = (J.Identifier) assignment.getVariable(); + if (i.getSimpleName().equals(target.getSimpleName())) { + ctx.add(assignment); + } + } + return super.visitAssignment(assignment, ctx); + } + + @Override + public J.AssignmentOperation visitAssignmentOperation(J.AssignmentOperation assignOp, List ctx) { + if (assignOp.getVariable() instanceof J.Identifier) { + J.Identifier i = (J.Identifier) assignOp.getVariable(); + if (i.getSimpleName().equals(target.getSimpleName())) { + ctx.add(assignOp); + } + } + return super.visitAssignmentOperation(assignOp, ctx); + } + + @Override + public J.Unary visitUnary(J.Unary unary, List ctx) { + if (unary.getExpression() instanceof J.Identifier) { + J.Identifier i = (J.Identifier) unary.getExpression(); + if (i.getSimpleName().equals(target.getSimpleName())) { + ctx.add(unary); + } + } + return super.visitUnary(unary, ctx); + } + }; + + List refs = new ArrayList<>(); + visitor.visit(j, refs); + return refs; + } +} From 000bdd127edafc03240c6acdf37c6c5f721e0c50 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 2 Nov 2024 12:49:30 +0100 Subject: [PATCH 156/183] Add missing license header --- .../staticanalysis/VariableReferences.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java b/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java index 557d80643..8dfbfe565 100644 --- a/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java +++ b/src/main/java/org/openrewrite/staticanalysis/VariableReferences.java @@ -1,3 +1,18 @@ +/* + * 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.jspecify.annotations.Nullable; From f32b689d2218af60898005c6294d1268291157c9 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Mon, 4 Nov 2024 07:50:36 +0100 Subject: [PATCH 157/183] `ReplaceClassIsInstanceWithInstanceof`: Fix `ClassCastException` --- .../ReplaceClassIsInstanceWithInstanceof.java | 4 +- ...laceClassIsInstanceWithInstanceofTest.java | 48 +++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java index 4ed0d4bca..0f4454b8d 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java @@ -60,11 +60,11 @@ public J visitMethodInvocation(MethodInvocation method, ExecutionContext ctx) { // make sure we find the right method and the left part is something like "SomeClass.class" if (matcher.matches(method) && isObjectClass(method.getSelect())) { // for code like "A.class.isInstance(a)", select is "String.class", name is "isInstance", argument is "a" - Identifier objectExpression = (Identifier) method.getArguments().get(0); + Expression objectExpression = method.getArguments().get(0); FieldAccess fieldAccessPart = (FieldAccess) method.getSelect(); String className = ((JavaType.Class) fieldAccessPart.getTarget().getType()).getClassName(); // upcast to type J, so J.MethodInvocation can be replaced by J.InstanceOf - J.InstanceOf instanceOf = JavaTemplate.apply("#{any(org.openrewrite.java.tree.Expression)} instanceof #{}", + J.InstanceOf instanceOf = JavaTemplate.apply("#{any()} instanceof #{}", getCursor(), method.getCoordinates().replace(), objectExpression, className); return maybeAutoFormat(method, instanceOf, ctx); } diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java index d96312d9c..8119229f2 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java @@ -113,4 +113,52 @@ String foo(Object obj) { ); } + @Test + void methodInvocation() { + rewriteRun( + //language=java + java( + """ + class A { + boolean foo(Object obj) { + return String.class.isInstance(obj.hashCode()); + } + } + """, + """ + class A { + boolean foo(Object obj) { + return obj.hashCode() instanceof String; + } + } + """ + ) + ); + } + + @Test + void fieldAccess() { + rewriteRun( + //language=java + java( + """ + class A { + Object content; + boolean foo(Object obj) { + return String.class.isInstance(this.content); + } + } + """, + """ + class A { + Object content; + boolean foo(Object obj) { + return this.content instanceof String; + } + } + """ + ) + ); + } + } From f384181d9e4668ce17c2f6aad259a6fd6d6d9217 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Mon, 4 Nov 2024 08:07:42 +0100 Subject: [PATCH 158/183] `ReplaceClassIsInstanceWithInstanceof`: Fix more corner cases --- .../ReplaceClassIsInstanceWithInstanceof.java | 13 ++- ...laceClassIsInstanceWithInstanceofTest.java | 104 ++++++++++++++++++ 2 files changed, 111 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java index 0f4454b8d..716416abb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java @@ -21,12 +21,10 @@ import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.tree.Expression; -import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.*; import org.openrewrite.java.tree.J.FieldAccess; import org.openrewrite.java.tree.J.Identifier; import org.openrewrite.java.tree.J.MethodInvocation; -import org.openrewrite.java.tree.JavaType; import java.util.Collections; import java.util.Set; @@ -62,10 +60,13 @@ public J visitMethodInvocation(MethodInvocation method, ExecutionContext ctx) { // for code like "A.class.isInstance(a)", select is "String.class", name is "isInstance", argument is "a" Expression objectExpression = method.getArguments().get(0); FieldAccess fieldAccessPart = (FieldAccess) method.getSelect(); - String className = ((JavaType.Class) fieldAccessPart.getTarget().getType()).getClassName(); + String className = fieldAccessPart.getTarget().toString(); // upcast to type J, so J.MethodInvocation can be replaced by J.InstanceOf - J.InstanceOf instanceOf = JavaTemplate.apply("#{any()} instanceof #{}", - getCursor(), method.getCoordinates().replace(), objectExpression, className); + JavaCoordinates coordinates = method.getCoordinates().replace(); + J.InstanceOf instanceOf = JavaTemplate.builder("#{any()} instanceof #{}") + .build() + .apply(getCursor(), coordinates, new Object[]{objectExpression, className}); + instanceOf = instanceOf.withClazz(fieldAccessPart.getTarget().withPrefix(instanceOf.getClazz().getPrefix())); return maybeAutoFormat(method, instanceOf, ctx); } return super.visitMethodInvocation(method, ctx); diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java index 8119229f2..221caf5b4 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java @@ -161,4 +161,108 @@ boolean foo(Object obj) { ); } + @Test + void imported() { + rewriteRun( + //language=java + java( + """ + import java.util.Map; + + class A { + boolean foo(Object obj) { + return Map.class.isInstance(obj); + } + } + """, + """ + import java.util.Map; + + class A { + boolean foo(Object obj) { + return obj instanceof Map; + } + } + """ + ) + ); + } + + @Test + void importedNested() { + rewriteRun( + //language=java + java( + """ + import java.util.Map.Entry; + + class A { + boolean foo(Object obj) { + return Entry.class.isInstance(obj); + } + } + """, + """ + import java.util.Map.Entry; + + class A { + boolean foo(Object obj) { + return obj instanceof Entry; + } + } + """ + ) + ); + } + + @Test + void typeFromSourcePath() { + rewriteRun( + //language=java + java( + """ + package a; + + class A { + boolean foo(Object obj) { + return A.class.isInstance(obj); + } + } + """, + """ + package a; + + class A { + boolean foo(Object obj) { + return obj instanceof A; + } + } + """ + ) + ); + } + + @Test + void defaultPackage() { + rewriteRun( + //language=java + java( + """ + class A { + boolean foo(Object obj) { + return A.class.isInstance(obj); + } + } + """, + """ + class A { + boolean foo(Object obj) { + return obj instanceof A; + } + } + """ + ) + ); + } + } From 2ed0ae0602369941494eba18143fef800d26fa60 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 4 Nov 2024 09:55:09 +0000 Subject: [PATCH 159/183] refactor: OpenRewrite best practices Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.recipes.OpenRewriteBestPractices?organizationId=T3BlblJld3JpdGU%3D Co-authored-by: Moderne --- .../org/openrewrite/staticanalysis/AnnotateNullableMethods.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java index 30c749a77..aebf26726 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java @@ -29,7 +29,6 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; public class AnnotateNullableMethods extends Recipe { From 81341a8acebf19160e7add46561f73df08f8c5f8 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Tue, 5 Nov 2024 08:55:34 +0100 Subject: [PATCH 160/183] `ReplaceClassIsInstanceWithInstanceof`: Fix another corner case --- .../ReplaceClassIsInstanceWithInstanceof.java | 27 +++++++++++-------- ...laceClassIsInstanceWithInstanceofTest.java | 17 ++++++++++++ 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java index 716416abb..49f95d8ac 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceof.java @@ -17,20 +17,28 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.J.FieldAccess; import org.openrewrite.java.tree.J.Identifier; import org.openrewrite.java.tree.J.MethodInvocation; +import org.openrewrite.java.tree.JavaCoordinates; +import org.openrewrite.java.tree.JavaType; import java.util.Collections; import java.util.Set; public class ReplaceClassIsInstanceWithInstanceof extends Recipe { + private static final MethodMatcher ISINSTANCE_MATCHER = new MethodMatcher("java.lang.Class isInstance(..)"); + @Override public String getDisplayName() { return "Replace `A.class.isInstance(a)` with `a instanceof A`"; @@ -47,25 +55,22 @@ public Set getTags() { } @Override - public JavaVisitor getVisitor() { + public TreeVisitor getVisitor() { // use JavaVisitor instead of JavaIsoVisitor because we changed the type of LST - return new JavaVisitor() { - - private final MethodMatcher matcher = new MethodMatcher("java.lang.Class isInstance(java.lang.Object)"); + return Preconditions.check(new UsesMethod<>(ISINSTANCE_MATCHER), new JavaVisitor() { @Override public J visitMethodInvocation(MethodInvocation method, ExecutionContext ctx) { // make sure we find the right method and the left part is something like "SomeClass.class" - if (matcher.matches(method) && isObjectClass(method.getSelect())) { + if (ISINSTANCE_MATCHER.matches(method) && isObjectClass(method.getSelect())) { // for code like "A.class.isInstance(a)", select is "String.class", name is "isInstance", argument is "a" Expression objectExpression = method.getArguments().get(0); FieldAccess fieldAccessPart = (FieldAccess) method.getSelect(); - String className = fieldAccessPart.getTarget().toString(); // upcast to type J, so J.MethodInvocation can be replaced by J.InstanceOf JavaCoordinates coordinates = method.getCoordinates().replace(); - J.InstanceOf instanceOf = JavaTemplate.builder("#{any()} instanceof #{}") + J.InstanceOf instanceOf = JavaTemplate.builder("#{any()} instanceof Object") .build() - .apply(getCursor(), coordinates, new Object[]{objectExpression, className}); + .apply(getCursor(), coordinates, objectExpression); instanceOf = instanceOf.withClazz(fieldAccessPart.getTarget().withPrefix(instanceOf.getClazz().getPrefix())); return maybeAutoFormat(method, instanceOf, ctx); } @@ -75,13 +80,13 @@ public J visitMethodInvocation(MethodInvocation method, ExecutionContext ctx) { private boolean isObjectClass(@Nullable Expression expression) { if (expression instanceof J.FieldAccess) { J.FieldAccess fieldAccess = (J.FieldAccess) expression; - if (fieldAccess.getTarget() instanceof Identifier) { + if (fieldAccess.getName().getSimpleName().equals("class") && fieldAccess.getTarget() instanceof Identifier) { Identifier identifier = (Identifier) fieldAccess.getTarget(); return identifier.getType() instanceof JavaType.Class; } } return false; } - }; + }); } } diff --git a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java index 221caf5b4..292bc3ffc 100644 --- a/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/ReplaceClassIsInstanceWithInstanceofTest.java @@ -265,4 +265,21 @@ boolean foo(Object obj) { ); } + @Test + void typeVariable() { + rewriteRun( + //language=java + java( + """ + class A { + Class clazz; + boolean foo(Object obj) { + return this.clazz.isInstance(obj); + } + } + """ + ) + ); + } + } From 04408aa5787bde216ad08791a07ef8193d455f05 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Mon, 11 Nov 2024 16:20:30 +0000 Subject: [PATCH 161/183] refactor: Update Gradle wrapper Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sibmFtZSI6ImFkZElmTWlzc2luZyIsInZhbHVlIjoiRmFsc2UifV0= Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 6077047a1..1f50076b3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip -distributionSha256Sum=31c55713e40233a8303827ceb42ca48a47267a0ad4bab9177123121e71524c26 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionSha256Sum=57dafb5c2622c6cc08b993c85b7c06956a2f53536432a30ead46166dbca0f1e9 networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From f1ce05b697a8787d77bfb001476495450cc30959 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Sat, 16 Nov 2024 21:56:04 +0100 Subject: [PATCH 162/183] Do not annotate for `return null` nested in new class --- build.gradle.kts | 2 +- .../AnnotateNullableMethods.java | 6 +++ .../AnnotateNullableMethodsTest.java | 54 ++++++++++--------- 3 files changed, 35 insertions(+), 27 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c6bb64e08..3ce67a388 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,7 +29,7 @@ dependencies { testImplementation("org.jetbrains:annotations:24.+") testImplementation("org.openrewrite:rewrite-groovy") - testImplementation("org.junit-pioneer:junit-pioneer:2.0.1") + testImplementation("org.junit-pioneer:junit-pioneer:2.+") testImplementation("junit:junit:4.13.2") testImplementation("com.google.code.gson:gson:latest.release") diff --git a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java index aebf26726..019195363 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java +++ b/src/main/java/org/openrewrite/staticanalysis/AnnotateNullableMethods.java @@ -133,6 +133,12 @@ public J.Lambda visitLambda(J.Lambda lambda, AtomicBoolean atomicBoolean) { return lambda; } + @Override + public J.NewClass visitNewClass(J.NewClass newClass, AtomicBoolean atomicBoolean) { + // Do not evaluate return statements in new class expressions + return newClass; + } + @Override public J.Return visitReturn(J.Return retrn, AtomicBoolean found) { if (found.get()) { diff --git a/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java b/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java index 59d3848fb..41bc3248b 100644 --- a/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/AnnotateNullableMethodsTest.java @@ -40,11 +40,11 @@ void methodReturnsNullLiteral() { java( """ public class Test { - + public String getString() { return null; } - + public String getStringWithMultipleReturn() { if (System.currentTimeMillis() % 2 == 0) { return "Not null"; @@ -55,13 +55,13 @@ public String getStringWithMultipleReturn() { """, """ import org.jspecify.annotations.Nullable; - + public class Test { - + public @Nullable String getString() { return null; } - + public @Nullable String getStringWithMultipleReturn() { if (System.currentTimeMillis() % 2 == 0) { return "Not null"; @@ -81,12 +81,12 @@ void methodReturnNullButIsAlreadyAnnotated() { java( """ import org.jspecify.annotations.Nullable; - + public class Test { public @Nullable String getString() { return null; } - + public @Nullable String getStringWithMultipleReturn() { if (System.currentTimeMillis() % 2 == 0) { return "Not null"; @@ -106,7 +106,7 @@ void methodDoesNotReturnNull() { java( """ package org.example; - + public class Test { public String getString() { return "Hello"; @@ -124,9 +124,9 @@ void methodReturnsDelegateKnowNullableMethod() { java( """ import java.util.Map; - + public class Test { - + public String getString(Map map) { return map.get("key"); } @@ -134,11 +134,11 @@ public String getString(Map map) { """, """ import org.jspecify.annotations.Nullable; - + import java.util.Map; - + public class Test { - + public @Nullable String getString(Map map) { return map.get("key"); } @@ -159,7 +159,7 @@ class A { public Runnable getRunnable() { return () -> null; } - + public Integer someStream(){ // Stream with lambda class. return Stream.of(1, 2, 3) @@ -196,37 +196,39 @@ void returnWithinNewClass() { java( """ import java.util.concurrent.Callable; - + public class Test { - + public Callable getString() { - return new Callable() { + Callable callable = new Callable<>() { @Override public String call() throws Exception { return null; } }; + return callable; } - + } """, """ import org.jspecify.annotations.Nullable; - + import java.util.concurrent.Callable; - + public class Test { - + public Callable getString() { - return new Callable() { - + Callable callable = new Callable<>() { + @Override public @Nullable String call() throws Exception { return null; } }; + return callable; } - + } """ ) @@ -240,8 +242,8 @@ void returnStaticNestInnerClassAnnotation() { java( """ import org.jspecify.annotations.Nullable; - - public class Outer { + + public class Outer { public static Outer.@Nullable Inner test() { return null; } static class Inner {} } From dac73436b8e929018383e9e19777131b6dec6b68 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 18 Nov 2024 15:15:13 +0100 Subject: [PATCH 163/183] Document that RemoveMethodCallVisitor duplicates RemoveMethodInvocationsVisitor --- .../org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java index 52e9dd2ad..da23007b2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveMethodCallVisitor.java @@ -32,6 +32,7 @@ * Only removes {@link MethodCall} where the call's return value is unused. */ @AllArgsConstructor +// TODO Consider moving this to `org.openrewrite.java.RemoveMethodInvocationsVisitor` in rewrite-java public class RemoveMethodCallVisitor

    extends JavaIsoVisitor

    { /** * The {@link MethodCall} to match to be removed. From 9d5b12a4a5f03354368a93c5c661846e35168ad5 Mon Sep 17 00:00:00 2001 From: Shannon Pamperl Date: Wed, 20 Nov 2024 21:38:26 +0000 Subject: [PATCH 164/183] refactor: Update Gradle wrapper Use this link to re-run the recipe: https://app.moderne.io/recipes/org.openrewrite.gradle.UpdateGradleWrapper?organizationId=T3BlblJld3JpdGU%3D#defaults=W3sibmFtZSI6ImFkZElmTWlzc2luZyIsInZhbHVlIjoiRmFsc2UifV0= Co-authored-by: Moderne --- gradle/wrapper/gradle-wrapper.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1f50076b3..cf76c57a6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip -distributionSha256Sum=57dafb5c2622c6cc08b993c85b7c06956a2f53536432a30ead46166dbca0f1e9 +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionSha256Sum=f397b287023acdba1e9f6fc5ea72d22dd63669d59ed4a289a29b1a76eee151c6 networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 6199c0beaa60a5fc3805cb7ea4b45f3fb644112f Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 21 Nov 2024 14:51:15 +0100 Subject: [PATCH 165/183] Have `NoEqualityInForCondition` compare condition type and variable --- .../NoEqualityInForCondition.java | 58 ++++++++++++++----- .../NoEqualityInForConditionTest.java | 36 ++++++++++++ 2 files changed, 81 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java b/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java index edba8b70a..2927c899a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoEqualityInForCondition.java @@ -19,6 +19,7 @@ import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaVisitor; +import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; @@ -54,28 +55,59 @@ public TreeVisitor getVisitor() { public J visitForControl(J.ForLoop.Control control, ExecutionContext ctx) { if (control.getCondition() instanceof J.Binary) { J.Binary condition = (J.Binary) control.getCondition(); - if (condition.getRight() instanceof J.Literal && condition.getRight().getType() == JavaType.Primitive.Null) { - return super.visitForControl(control, ctx); - } - if (control.getUpdate().size() == 1 && control.getUpdate().get(0) instanceof J.Unary) { + if (isNumericalType(condition) && + control.getUpdate().size() == 1 && + control.getUpdate().get(0) instanceof J.Unary) { J.Unary update = (J.Unary) control.getUpdate().get(0); - - if (condition.getOperator() == J.Binary.Type.NotEqual) { - switch (update.getOperator()) { - case PreIncrement: - case PostIncrement: - return control.withCondition(condition.withOperator(J.Binary.Type.LessThan)); - case PreDecrement: - case PostDecrement: - return control.withCondition(condition.withOperator(J.Binary.Type.GreaterThan)); + if (updatedExpressionInConditional(update.getExpression(), condition)) { + if (condition.getOperator() == J.Binary.Type.NotEqual) { + switch (update.getOperator()) { + case PreIncrement: + case PostIncrement: + return control.withCondition(condition.withOperator(J.Binary.Type.LessThan)); + case PreDecrement: + case PostDecrement: + return control.withCondition(condition.withOperator(J.Binary.Type.GreaterThan)); + } } } + } } return super.visitForControl(control, ctx); } + + private boolean updatedExpressionInConditional(Expression updatedExpression, J.Binary condition) { + final String simpleName; + if (updatedExpression instanceof J.Identifier) { + simpleName = ((J.Identifier) updatedExpression).getSimpleName(); + } else if (updatedExpression instanceof J.FieldAccess) { + simpleName = ((J.FieldAccess) updatedExpression).getSimpleName(); + } else { + return false; + } + + if (condition.getLeft() instanceof J.Identifier) { + return simpleName.equals(((J.Identifier) condition.getLeft()).getSimpleName()); + } else if (condition.getLeft() instanceof J.FieldAccess) { + return simpleName.equals(((J.FieldAccess) condition.getLeft()).getSimpleName()); + } else if (condition.getRight() instanceof J.Identifier) { + return simpleName.equals(((J.Identifier) condition.getRight()).getSimpleName()); + } else if (condition.getRight() instanceof J.FieldAccess) { + return simpleName.equals(((J.FieldAccess) condition.getRight()).getSimpleName()); + } + return false; + } + + private boolean isNumericalType(J.Binary condition) { + JavaType type = condition.getRight().getType(); + return type == JavaType.Primitive.Short || + type == JavaType.Primitive.Byte || + type == JavaType.Primitive.Int || + type == JavaType.Primitive.Long; + } }; } } diff --git a/src/test/java/org/openrewrite/staticanalysis/NoEqualityInForConditionTest.java b/src/test/java/org/openrewrite/staticanalysis/NoEqualityInForConditionTest.java index 830a31744..c5c0fc801 100644 --- a/src/test/java/org/openrewrite/staticanalysis/NoEqualityInForConditionTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/NoEqualityInForConditionTest.java @@ -78,4 +78,40 @@ void test() { ) ); } + + @Test + void retainCharComparison() { + rewriteRun( + //language=java + java( + """ + class Test { + int[] arr; + void test() { + for (int i = 0; arr[i] != '\\n'; i++) { + } + } + } + """ + ) + ); + } + + @Test + void retainMismatchedComparison() { + rewriteRun( + //language=java + java( + """ + class Test { + void test() { + int y = 9; + for (int x = 0; y != 10; x++) { + } + } + } + """ + ) + ); + } } From 732da99f6d0483dfb6f01ecd666e5597ed700d00 Mon Sep 17 00:00:00 2001 From: Knut Wannheden Date: Thu, 21 Nov 2024 15:42:50 +0100 Subject: [PATCH 166/183] Fix failing `InstanceOfPatternMatchTest` --- .../InstanceOfPatternMatchTest.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java index fb23b21bc..4eaee8b51 100644 --- a/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/InstanceOfPatternMatchTest.java @@ -225,6 +225,19 @@ public static void applyRoutesType(Object routes) { } } } + """, + """ + import java.util.Collections; + import java.util.List; + public class A { + @SuppressWarnings("unchecked") + public static void applyRoutesType(Object routes) { + if (routes instanceof List list) { + List routesList = (List) routes; + String.join(",", list); + } + } + } """ ) ); @@ -484,7 +497,7 @@ void conflictingVariableOfNestedType() { java( """ import java.util.Map; - + public class A { void test(Object o) { Map.Entry entry = null; @@ -497,7 +510,7 @@ void test(Object o) { """, """ import java.util.Map; - + public class A { void test(Object o) { Map.Entry entry = null; @@ -1185,7 +1198,7 @@ void iterableParameter() { import java.util.HashMap; import java.util.List; import java.util.Map; - + public class ApplicationSecurityGroupsParameterHelper { static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; public Map transformGatewayParameters(Map parameters) { @@ -1202,7 +1215,7 @@ public Map transformGatewayParameters(Map parame import java.util.HashMap; import java.util.List; import java.util.Map; - + public class ApplicationSecurityGroupsParameterHelper { static final String APPLICATION_SECURITY_GROUPS = "application-security-groups"; public Map transformGatewayParameters(Map parameters) { From 8606477d5caae60f55f94f782f55337265f8001f Mon Sep 17 00:00:00 2001 From: Guillaume Gerbaud Date: Mon, 25 Nov 2024 16:08:20 +0100 Subject: [PATCH 167/183] Fix IndexOutOfBoundsException in EqualsAvoidsNull recipe (#394) fix #393 --- .../org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index 8ddc19a33..14864c924 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -59,6 +59,7 @@ public J visitMethodInvocation(J.MethodInvocation method, P p) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, p); if (m.getSelect() != null && !(m.getSelect() instanceof J.Literal) && + !m.getArguments().isEmpty() && m.getArguments().get(0) instanceof J.Literal && isStringComparisonMethod(m)) { return literalsFirstInComparisonsBinaryCheck(m, getCursor().getParentTreeCursor().getValue()); From b6df08d662ba6f974dfc2cc8850faec47d49235d Mon Sep 17 00:00:00 2001 From: Greg Oledzki <762437+mccartney@users.noreply.github.com> Date: Mon, 25 Nov 2024 17:02:24 +0100 Subject: [PATCH 168/183] Unnecessary `return` as last statement in `void` method (#388) * adding UnnecessaryReturnAsLastStatement * licenseFormat * remove public in test Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * ctx instead of executionContext Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Removing null check * Adding a check for Void return type and no return expression * Remove unnecessary else branch Co-authored-by: Tim te Beek * Refactor, using ListUtils.mapLast() * More test cases * Optimize imports * Slight polish to read from top down; add failing test * Handling return statements in Else too * More test cases * Uniquely name each of the three methods * Use a single recursive function * Prevent changes without any effect * Add `UnnecessaryReturnAsLastStatement` to `CommonStaticAnalysis` --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek Co-authored-by: Knut Wannheden --- .../UnnecessaryReturnAsLastStatement.java | 80 +++++ .../rewrite/common-static-analysis.yml | 1 + .../UnnecessaryReturnAsLastStatementTest.java | 320 ++++++++++++++++++ 3 files changed, 401 insertions(+) create mode 100644 src/main/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatement.java create mode 100644 src/test/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatementTest.java diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatement.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatement.java new file mode 100644 index 000000000..cf2e8151c --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatement.java @@ -0,0 +1,80 @@ +/* + * 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.jspecify.annotations.Nullable; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; +import org.openrewrite.java.tree.Statement; +import org.openrewrite.java.tree.TypeUtils; + +public class UnnecessaryReturnAsLastStatement extends Recipe { + @Override + public String getDisplayName() { + return "Unnecessary `return` as last statement in void method"; + } + + @Override + public String getDescription() { + return "Removes `return` from a `void` method if it's the last statement."; + } + + @Override + public TreeVisitor getVisitor() { + return new JavaIsoVisitor() { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + if (TypeUtils.asPrimitive(m.getType()) == JavaType.Primitive.Void && m.getBody() != null) { + return m.withBody(m.getBody().withStatements(ListUtils.mapLast(m.getBody().getStatements(), + this::maybeRemoveReturn))); + } + return m; + } + + private @Nullable Statement maybeRemoveReturn(Statement s) { + if (s instanceof J.Return && ((J.Return) s).getExpression() == null) { + return null; + } else if (s instanceof J.Block) { + J.Block block = (J.Block) s; + return block.withStatements(ListUtils.mapLast(block.getStatements(), this::maybeRemoveReturn)); + } else if (s instanceof J.If) { + J.If ifStatement = (J.If) s; + Statement trimmedThen = maybeRemoveReturn(ifStatement.getThenPart()); + if (trimmedThen != ifStatement.getThenPart() && trimmedThen != null) { + ifStatement = ifStatement.withThenPart(trimmedThen); + } + if (ifStatement.getElsePart() != null) { + Statement trimmedElse = maybeRemoveReturn(ifStatement.getElsePart().getBody()); + if (trimmedElse == null) { + return ifStatement.withElsePart(null); + } + if (trimmedElse != ifStatement.getElsePart().getBody()) { + return ifStatement.withElsePart(ifStatement.getElsePart().withBody(trimmedElse)); + } + } + return ifStatement; + } + return s; + } + }; + } +} diff --git a/src/main/resources/META-INF/rewrite/common-static-analysis.yml b/src/main/resources/META-INF/rewrite/common-static-analysis.yml index bb6547e9e..8bf41d6ee 100644 --- a/src/main/resources/META-INF/rewrite/common-static-analysis.yml +++ b/src/main/resources/META-INF/rewrite/common-static-analysis.yml @@ -84,6 +84,7 @@ recipeList: - org.openrewrite.staticanalysis.UnnecessaryExplicitTypeArguments - org.openrewrite.staticanalysis.UnnecessaryParentheses - org.openrewrite.staticanalysis.UnnecessaryPrimitiveAnnotations + - org.openrewrite.staticanalysis.UnnecessaryReturnAsLastStatement - org.openrewrite.staticanalysis.UpperCaseLiteralSuffixes # - org.openrewrite.staticanalysis.UnnecessaryThrows # - org.openrewrite.staticanalysis.UseCollectionInterfaces diff --git a/src/test/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatementTest.java b/src/test/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatementTest.java new file mode 100644 index 000000000..a482debfd --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/UnnecessaryReturnAsLastStatementTest.java @@ -0,0 +1,320 @@ +/* + * 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.DocumentExample; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class UnnecessaryReturnAsLastStatementTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec.recipe(new UnnecessaryReturnAsLastStatement()); + } + + @Test + @DocumentExample + void simpleReturn() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world() { + System.out.println("Hello world"); + return; + } + } + """, + """ + class Hello { + void world() { + System.out.println("Hello world"); + } + } + """ + ) + ); + } + + @Test + void ifBranches() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + return; + } else { + System.out.println("Zero or negative"); + return; + } + } + } + """, + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + } else { + System.out.println("Zero or negative"); + } + } + } + """ + ) + ); + } + + @Test + void ifWithoutElse() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + return; + } + } + } + """, + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + } + } + } + """ + ) + ); + } + + @Test + void ifElseIf() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + return; + } else if (i == 0) { + System.out.println("Zero"); + return; + } else { + System.out.println("Negative"); + return; + } + } + } + """, + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + } else if (i == 0) { + System.out.println("Zero"); + } else { + System.out.println("Negative"); + } + } + } + """ + ) + ); + } + + @Test + void ifIsNotTheLast() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + return; + } else { + System.out.println("Zero or negative"); + } + System.out.println("Some extra logic"); + } + } + """ + ) + ); + } + + @Test + void elseWithJustAReturnStatement() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + return; + } else return; + } + } + """, + """ + class Hello { + void world(int i) { + if (i > 0) { + System.out.println("Positive"); + } + } + } + """ + ) + ); + } + + @Test + void ifThenBeingJustAReturnStatement() { + //language=java + rewriteRun( + java( + """ + class Hello { + void world(int i) { + if (i == 436) return; else { + System.out.println("I don't like it"); + return; + } + } + } + """, + """ + class Hello { + void world(int i) { + if (i == 436) return; else { + System.out.println("I don't like it"); + } + } + } + """ + ) + ); + } + + @Test + void notChangingNonVoidMethods() { + //language=java + rewriteRun( + java( + """ + class Hello { + int world(int i) { + return i + 436; + } + } + """ + ) + ); + } + + @Test + void notChangingLambdas() { + //language=java + rewriteRun( + java( + """ + class Hello { + java.util.function.Consumer c = i -> { + return; + }; + } + """ + ) + ); + } + + @Test + void notChangingLoops() { + //language=java + rewriteRun( + java( + """ + class Main { + public static void main(String[] argv) { + while (true) { + return; + } + } + } + """ + ) + ); + } + + @Test + void newClass() { + //language=java + rewriteRun( + java( + """ + import java.util.concurrent.Callable; + class Hello { + Callable callable = new Callable<>() { + @Override + public String call() throws Exception { + otherMethod(); + return "success"; + } + private void otherMethod() { + return; + } + }; + } + """, + """ + import java.util.concurrent.Callable; + class Hello { + Callable callable = new Callable<>() { + @Override + public String call() throws Exception { + otherMethod(); + return "success"; + } + private void otherMethod() { + } + }; + } + """ + ) + ); + } +} From 0e0b4e90234f1601cf71ce2ae3b1d05b5c68040d Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Mon, 25 Nov 2024 23:47:05 +0100 Subject: [PATCH 169/183] Skip switch cases with a body for now Prevents an IndexOutOfBoundsException or incorrect change --- .../staticanalysis/DefaultComesLast.java | 19 +++++------- .../DefaultComesLastVisitor.java | 5 +++ .../staticanalysis/DefaultComesLastTest.java | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java index 27f9e3da8..4ab5bfbd2 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java +++ b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLast.java @@ -16,17 +16,19 @@ package org.openrewrite.staticanalysis; import org.jspecify.annotations.Nullable; -import org.openrewrite.*; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Recipe; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.DefaultComesLastStyle; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaSourceFile; -import java.time.Duration; -import java.util.Collections; import java.util.Set; +import static java.util.Collections.singleton; import static java.util.Objects.requireNonNull; public class DefaultComesLast extends Recipe { @@ -43,12 +45,7 @@ public String getDescription() { @Override public Set getTags() { - return Collections.singleton("RSPEC-S4524"); - } - - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); + return singleton("RSPEC-S4524"); } @Override @@ -58,14 +55,14 @@ public TreeVisitor getVisitor() { private static class DefaultComesLastFromCompilationUnitStyle extends JavaIsoVisitor { @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { + public @Nullable J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); DefaultComesLastStyle style = cu.getStyle(DefaultComesLastStyle.class); if (style == null) { style = Checkstyle.defaultComesLast(); } - return new DefaultComesLastVisitor<>(style).visit(cu, ctx); + return new DefaultComesLastVisitor<>(style).visitNonNull(cu, ctx); } return (J) tree; } diff --git a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java index 65d300b8a..4ee4fbb49 100644 --- a/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/DefaultComesLastVisitor.java @@ -50,6 +50,11 @@ public J.Switch visitSwitch(J.Switch switch_, P p) { for (int i = 0; i < cases.size(); i++) { J.Case aCase = cases.get(i); + + // skip cases with bodies for now + if (aCase.getBody() != null) { + return s; + } if (isDefaultCase(aCase)) { defaultCaseIndex = i; defaultCase = aCase; diff --git a/src/test/java/org/openrewrite/staticanalysis/DefaultComesLastTest.java b/src/test/java/org/openrewrite/staticanalysis/DefaultComesLastTest.java index ebd41c14c..f51ca4fda 100644 --- a/src/test/java/org/openrewrite/staticanalysis/DefaultComesLastTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/DefaultComesLastTest.java @@ -23,6 +23,7 @@ import org.openrewrite.style.NamedStyles; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; import static java.util.Collections.emptySet; import static java.util.Collections.singletonList; @@ -264,4 +265,34 @@ boolean foo() { ) ); } + + @Test + void defensivelySkipSwitchExpressionsForNow() { + //language=java + rewriteRun( + java( + """ + enum Product { + A, B, C + } + """, + SourceSpec::skip + ), + java( + """ + class Foo { + int bar(Product product) { + int var = 0; + switch (product) { + default -> var = 1; + case B -> { var = 2; } + case C -> { var = 3; } + } + return var; + } + } + """ + ) + ); + } } From 5b42a63e10569e1320875cb8a40fb00950f1aef9 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Tue, 26 Nov 2024 14:53:38 +0100 Subject: [PATCH 170/183] Add Csharp specific check for empty throw statements --- .../staticanalysis/CatchClauseOnlyRethrows.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java index 53b978fce..d31a84a23 100755 --- a/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java +++ b/src/main/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrows.java @@ -17,7 +17,9 @@ import org.openrewrite.ExecutionContext; import org.openrewrite.Recipe; +import org.openrewrite.SourceFile; import org.openrewrite.TreeVisitor; +import org.openrewrite.csharp.tree.Cs; import org.openrewrite.internal.ListUtils; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.Expression; @@ -113,6 +115,13 @@ private boolean onlyRethrows(J.Try.Catch aCatch) { } Expression exception = ((J.Throw) aCatch.getBody().getStatements().get(0)).getException(); + + // In C# an implicit rethrow is possible + if (getCursor().firstEnclosing(SourceFile.class) instanceof Cs && + exception instanceof J.Empty) { + return true; + } + JavaType catchParameterType = aCatch.getParameter().getType(); if (!(catchParameterType instanceof JavaType.MultiCatch)) { JavaType.FullyQualified catchType = TypeUtils.asFullyQualified(catchParameterType); From 4add0df9d27142a7a89f0216b3e35781f3baa86d Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Tue, 26 Nov 2024 17:05:01 +0100 Subject: [PATCH 171/183] Add questionable unit test --- .../CatchClauseOnlyRethrowsTest.java | 96 ++++++++++++++----- 1 file changed, 73 insertions(+), 23 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index c6c93ebf1..ee07611e5 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -17,9 +17,20 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; +import org.openrewrite.FileAttributes; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Tree; +import org.openrewrite.csharp.tree.Cs; +import org.openrewrite.java.tree.*; +import org.openrewrite.marker.Markers; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.java.Assertions.java; @SuppressWarnings("ALL") @@ -38,7 +49,7 @@ void rethrownButWithDifferentMessage() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -63,7 +74,7 @@ void catchShouldBePreservedBecauseLessSpecificCatchFollows() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -90,7 +101,7 @@ void catchShouldBePreservedBecauseLessSpecificCatchFollowsWithMultiCast() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -116,7 +127,7 @@ void tryCanBeRemoved() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -130,7 +141,7 @@ void foo() throws IOException { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { new FileReader("").read(); @@ -150,7 +161,7 @@ void tryCanBeRemovedWithMultiCatch() { import java.io.FileReader; import java.io.IOException; import java.io.FileNotFoundException; - + class A { void foo() throws IOException { try { @@ -166,16 +177,16 @@ void foo() throws IOException { } """, """ - import java.io.FileReader; - import java.io.IOException; - import java.io.FileNotFoundException; - - class A { - void foo() throws IOException { - new FileReader("").read(); - } - } - """ + import java.io.FileReader; + import java.io.IOException; + import java.io.FileNotFoundException; + + class A { + void foo() throws IOException { + new FileReader("").read(); + } + } + """ ) ); } @@ -189,7 +200,7 @@ void multiCatchPreservedOnDifferentThrow() { import java.io.FileReader; import java.io.IOException; import java.io.FileNotFoundException; - + class A { void foo() throws IOException { try { @@ -214,7 +225,7 @@ void tryShouldBePreservedBecauseFinally() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -230,7 +241,7 @@ void foo() throws IOException { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -253,7 +264,7 @@ void tryShouldBePreservedBecauseResources() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try(FileReader fr = new FileReader("")) { @@ -267,7 +278,7 @@ void foo() throws IOException { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try(FileReader fr = new FileReader("")) { @@ -288,7 +299,7 @@ void wrappingAndRethrowingIsUnchanged() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() { try { @@ -311,7 +322,7 @@ void loggingAndRethrowingIsUnchanged() { """ import java.io.FileReader; import java.io.IOException; - + class A { void foo() throws IOException { try { @@ -326,4 +337,43 @@ void foo() throws IOException { ) ); } + + @Test + void testCsharpImplicitThrow() { + Cs.CompilationUnit compilationUnit = new Cs.CompilationUnit(Tree.randomId(), Space.EMPTY, + Markers.EMPTY, Path.of("test.cs"), + new FileAttributes(null, null, null, true, true, true, 0l), + null, false, null, null, null, null, + List.of(JRightPadded.build( + new J.ClassDeclaration(Tree.randomId(), Space.EMPTY, Markers.EMPTY, + Collections.emptyList(), Collections.emptyList(), + new J.ClassDeclaration.Kind(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), J.ClassDeclaration.Kind.Type.Class), + new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), "doSome", null, null), + null, null, null, null, null, + new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), + List.of(JRightPadded.build(new J.Try(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, + new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), + List.of(JRightPadded.build(new J.Throw(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.NewClass( + Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, Space.EMPTY, TypeTree.build("java.lang.IllegalAccessException"), JContainer.empty(), null, null)))), + Space.EMPTY), + List.of(new J.Try.Catch(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.ControlParentheses(Tree.randomId(), Space.EMPTY, Markers.EMPTY, + JRightPadded.build(new J.VariableDeclarations(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), Collections.emptyList(), TypeTree.build("java.lang.IllegalAccessException"), null, List.of(), List.of(JRightPadded.build( + new J.VariableDeclarations.NamedVariable(Tree.randomId(), Space.EMPTY, Markers.EMPTY, + new J.Identifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, Collections.emptyList(), "e", null, null), + List.of(), null, null)))))), + new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), + List.of(JRightPadded.build(new J.Throw(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)))), + Space.EMPTY))), + null))), + Space.EMPTY), + null))) + , Space.EMPTY); + + assertThat(compilationUnit.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); + assertThat(((J.ClassDeclaration)compilationUnit.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.Try.class); + + Cs.CompilationUnit output = (Cs.CompilationUnit) new CatchClauseOnlyRethrows().getVisitor().visit(compilationUnit, new InMemoryExecutionContext()); + assertThat(output.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); + assertThat(((J.ClassDeclaration)output.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.Throw.class); + } } From 5c675c636146d2f930665103cb0106374c57afed Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Tue, 26 Nov 2024 17:29:14 +0100 Subject: [PATCH 172/183] Fix review comment --- .../openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index ee07611e5..dec07e77c 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -339,7 +339,7 @@ void foo() throws IOException { } @Test - void testCsharpImplicitThrow() { + void verifyCsharpImplicitThrow() { Cs.CompilationUnit compilationUnit = new Cs.CompilationUnit(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Path.of("test.cs"), new FileAttributes(null, null, null, true, true, true, 0l), From 70cc111e43c8b5a5731c1241aea844f80f17d188 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Wed, 27 Nov 2024 15:16:29 +0100 Subject: [PATCH 173/183] Improve test --- .../CatchClauseOnlyRethrowsTest.java | 92 ++++++++++--------- .../staticanalysis/JavaToCsharp.java | 32 +++++++ 2 files changed, 81 insertions(+), 43 deletions(-) create mode 100644 src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index dec07e77c..285effad2 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -16,20 +16,14 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; -import org.openrewrite.DocumentExample; -import org.openrewrite.FileAttributes; -import org.openrewrite.InMemoryExecutionContext; -import org.openrewrite.Tree; +import org.openrewrite.*; import org.openrewrite.csharp.tree.Cs; +import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.*; import org.openrewrite.marker.Markers; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import java.nio.file.Path; -import java.util.Collections; -import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.java.Assertions.java; @@ -340,40 +334,52 @@ void foo() throws IOException { @Test void verifyCsharpImplicitThrow() { - Cs.CompilationUnit compilationUnit = new Cs.CompilationUnit(Tree.randomId(), Space.EMPTY, - Markers.EMPTY, Path.of("test.cs"), - new FileAttributes(null, null, null, true, true, true, 0l), - null, false, null, null, null, null, - List.of(JRightPadded.build( - new J.ClassDeclaration(Tree.randomId(), Space.EMPTY, Markers.EMPTY, - Collections.emptyList(), Collections.emptyList(), - new J.ClassDeclaration.Kind(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), J.ClassDeclaration.Kind.Type.Class), - new J.Identifier(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), "doSome", null, null), - null, null, null, null, null, - new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), - List.of(JRightPadded.build(new J.Try(Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, - new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), - List.of(JRightPadded.build(new J.Throw(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.NewClass( - Tree.randomId(), Space.EMPTY, Markers.EMPTY, null, Space.EMPTY, TypeTree.build("java.lang.IllegalAccessException"), JContainer.empty(), null, null)))), - Space.EMPTY), - List.of(new J.Try.Catch(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.ControlParentheses(Tree.randomId(), Space.EMPTY, Markers.EMPTY, - JRightPadded.build(new J.VariableDeclarations(Tree.randomId(), Space.EMPTY, Markers.EMPTY, Collections.emptyList(), Collections.emptyList(), TypeTree.build("java.lang.IllegalAccessException"), null, List.of(), List.of(JRightPadded.build( - new J.VariableDeclarations.NamedVariable(Tree.randomId(), Space.EMPTY, Markers.EMPTY, - new J.Identifier(Tree.randomId(), Space.SINGLE_SPACE, Markers.EMPTY, Collections.emptyList(), "e", null, null), - List.of(), null, null)))))), - new J.Block(Tree.randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(false), - List.of(JRightPadded.build(new J.Throw(Tree.randomId(), Space.EMPTY, Markers.EMPTY, new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)))), - Space.EMPTY))), - null))), - Space.EMPTY), - null))) - , Space.EMPTY); - - assertThat(compilationUnit.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); - assertThat(((J.ClassDeclaration)compilationUnit.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.Try.class); - - Cs.CompilationUnit output = (Cs.CompilationUnit) new CatchClauseOnlyRethrows().getVisitor().visit(compilationUnit, new InMemoryExecutionContext()); - assertThat(output.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); - assertThat(((J.ClassDeclaration)output.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.Throw.class); + rewriteRun( + spec -> spec.recipe(Recipe.noop()), + //language=java + java( + """ + class A { + void foo() throws IllegalAccessException { + try { + throw new IllegalAccessException(); + } catch (Exception e) { + throw e; + } + } + }""" + , spec -> spec.beforeRecipe(compUnit -> { + Cs.CompilationUnit cSharpCompUnit = (Cs.CompilationUnit) new JavaVisitor() { + @Override + public J visitThrow(J.Throw thrown, ExecutionContext executionContext) { + if (thrown.getException() instanceof J.Identifier) { + return thrown.withException(new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)); + } + return thrown; + } + }.visit(JavaToCsharp.compilationUnit(compUnit), new InMemoryExecutionContext()); + + assertThat(cSharpCompUnit.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); + assertThat(((J.ClassDeclaration)cSharpCompUnit.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.MethodDeclaration.class); + J.MethodDeclaration md = (J.MethodDeclaration) ((J.ClassDeclaration)cSharpCompUnit.getMembers().get(0)).getBody().getStatements().get(0); + assertThat(md.getBody().getStatements().get(0)).isInstanceOf(J.Try.class); + + Cs.CompilationUnit output = (Cs.CompilationUnit) new CatchClauseOnlyRethrows().getVisitor().visit(cSharpCompUnit, new InMemoryExecutionContext()); + + assertThat(output.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); + assertThat(((J.ClassDeclaration)output.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.MethodDeclaration.class); + md = (J.MethodDeclaration) ((J.ClassDeclaration)output.getMembers().get(0)).getBody().getStatements().get(0); + assertThat(md.getBody().getStatements().get(0)).isInstanceOf(J.Throw.class); + } + ) + ) + ); + } + + public JavaVisitor getJavaToCsharpVisitor() { + return new JavaVisitor() { + + + }; } } diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java new file mode 100644 index 000000000..ba3fd564a --- /dev/null +++ b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java @@ -0,0 +1,32 @@ +/* + * 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.csharp.tree.Cs; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JRightPadded; +import org.openrewrite.java.tree.Statement; + +import java.util.List; +import java.util.stream.Collectors; + +public class JavaToCsharp { + + public static Cs.CompilationUnit compilationUnit(J.CompilationUnit cu) { + return new Cs.CompilationUnit(cu.getId(), cu.getPrefix(), cu.getMarkers(), cu.getSourcePath(), + cu.getFileAttributes(), cu.getCharset().name(), cu.isCharsetBomMarked(), cu.getChecksum(), List.of(), List.of(), List.of(), cu.getClasses().stream().map(Statement.class::cast).map(cd -> JRightPadded.build(cd)).collect(Collectors.toList()), cu.getEof()); + } +} From bb8d98ae7405d510875d427468ad857d6d595714 Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Wed, 27 Nov 2024 15:18:51 +0100 Subject: [PATCH 174/183] Add comment for clarity --- .../openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index 285effad2..c3cedf6b1 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -344,7 +344,7 @@ void foo() throws IllegalAccessException { try { throw new IllegalAccessException(); } catch (Exception e) { - throw e; + throw e; // C# can rethrow the caught exception implicitly and so the `e` Identifier is removed by the inline visitor below } } }""" From 8a0343b269eaa1449a2e2c3871d46849228d93bd Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Wed, 27 Nov 2024 16:59:34 +0100 Subject: [PATCH 175/183] Add testImplementation("org.openrewrite:rewrite-test") --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index 3ce67a388..ce0d2c7a4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,6 +29,7 @@ dependencies { testImplementation("org.jetbrains:annotations:24.+") testImplementation("org.openrewrite:rewrite-groovy") + testImplementation("org.openrewrite:rewrite-test") testImplementation("org.junit-pioneer:junit-pioneer:2.+") testImplementation("junit:junit:4.13.2") From 6a9a2ad9828cc7dd72a2eb6bf0e0b1fd19e24dcd Mon Sep 17 00:00:00 2001 From: Laurens Westerlaken Date: Thu, 28 Nov 2024 09:58:27 +0100 Subject: [PATCH 176/183] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../staticanalysis/CatchClauseOnlyRethrowsTest.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index c3cedf6b1..ee40b3999 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -19,7 +19,8 @@ import org.openrewrite.*; import org.openrewrite.csharp.tree.Cs; import org.openrewrite.java.JavaVisitor; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Space; import org.openrewrite.marker.Markers; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -351,7 +352,7 @@ void foo() throws IllegalAccessException { , spec -> spec.beforeRecipe(compUnit -> { Cs.CompilationUnit cSharpCompUnit = (Cs.CompilationUnit) new JavaVisitor() { @Override - public J visitThrow(J.Throw thrown, ExecutionContext executionContext) { + public J visitThrow(J.Throw thrown, ExecutionContext ctx) { if (thrown.getException() instanceof J.Identifier) { return thrown.withException(new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)); } @@ -377,7 +378,7 @@ public J visitThrow(J.Throw thrown, ExecutionContext executionContext) { } public JavaVisitor getJavaToCsharpVisitor() { - return new JavaVisitor() { + return new JavaVisitor<>() { }; From afe2c77c6b97ce289043b0f8d456e71f5e304793 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 28 Nov 2024 12:08:48 +0100 Subject: [PATCH 177/183] Apply suggestions from code review --- .../CatchClauseOnlyRethrowsTest.java | 55 +++++++++---------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index ee40b3999..2d945878c 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -345,42 +345,37 @@ void foo() throws IllegalAccessException { try { throw new IllegalAccessException(); } catch (Exception e) { - throw e; // C# can rethrow the caught exception implicitly and so the `e` Identifier is removed by the inline visitor below + throw e; // `e` is removed below } } - }""" - , spec -> spec.beforeRecipe(compUnit -> { + } + """, + spec -> spec.beforeRecipe(compUnit -> { + // C# can rethrow the caught exception implicitly and so the `e` Identifier is removed by the inline visitor below Cs.CompilationUnit cSharpCompUnit = (Cs.CompilationUnit) new JavaVisitor() { - @Override - public J visitThrow(J.Throw thrown, ExecutionContext ctx) { - if (thrown.getException() instanceof J.Identifier) { - return thrown.withException(new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)); - } - return thrown; - } - }.visit(JavaToCsharp.compilationUnit(compUnit), new InMemoryExecutionContext()); - - assertThat(cSharpCompUnit.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); - assertThat(((J.ClassDeclaration)cSharpCompUnit.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.MethodDeclaration.class); - J.MethodDeclaration md = (J.MethodDeclaration) ((J.ClassDeclaration)cSharpCompUnit.getMembers().get(0)).getBody().getStatements().get(0); - assertThat(md.getBody().getStatements().get(0)).isInstanceOf(J.Try.class); - - Cs.CompilationUnit output = (Cs.CompilationUnit) new CatchClauseOnlyRethrows().getVisitor().visit(cSharpCompUnit, new InMemoryExecutionContext()); - - assertThat(output.getMembers().get(0)).isInstanceOf(J.ClassDeclaration.class); - assertThat(((J.ClassDeclaration)output.getMembers().get(0)).getBody().getStatements().get(0)).isInstanceOf(J.MethodDeclaration.class); - md = (J.MethodDeclaration) ((J.ClassDeclaration)output.getMembers().get(0)).getBody().getStatements().get(0); - assertThat(md.getBody().getStatements().get(0)).isInstanceOf(J.Throw.class); - } + @Override + public J visitThrow(J.Throw thrown, ExecutionContext ctx) { + if (thrown.getException() instanceof J.Identifier) { + return thrown.withException(new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)); + } + return thrown; + } + }.visit(JavaToCsharp.compilationUnit(compUnit), new InMemoryExecutionContext()); + + Cs.CompilationUnit after = (Cs.CompilationUnit) new CatchClauseOnlyRethrows().getVisitor() + .visit(cSharpCompUnit, new InMemoryExecutionContext()); + + var classA = (J.ClassDeclaration) after.getMembers().get(0); + var methodFoo = (J.MethodDeclaration) classA.getBody().getStatements().get(0); + var firstStatement = methodFoo.getBody().getStatements().get(0); + assertThat(firstStatement) + .isInstanceOf(J.Throw.class) // The `try/catch` block removed + .extracting(s -> ((J.Throw) s).getException()) + .isInstanceOf(J.NewClass.class); + } ) ) ); } - public JavaVisitor getJavaToCsharpVisitor() { - return new JavaVisitor<>() { - - - }; - } } From 2feec02822f074a938b98ebd952c734c039963e7 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 28 Nov 2024 12:16:31 +0100 Subject: [PATCH 178/183] Use regular before/after text blocks --- .../CatchClauseOnlyRethrowsTest.java | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java index 2d945878c..f34d89657 100755 --- a/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/CatchClauseOnlyRethrowsTest.java @@ -16,7 +16,10 @@ package org.openrewrite.staticanalysis; import org.junit.jupiter.api.Test; -import org.openrewrite.*; +import org.openrewrite.DocumentExample; +import org.openrewrite.ExecutionContext; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Tree; import org.openrewrite.csharp.tree.Cs; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J; @@ -25,8 +28,8 @@ import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import static org.assertj.core.api.Assertions.assertThat; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.test.RewriteTest.toRecipe; @SuppressWarnings("ALL") class CatchClauseOnlyRethrowsTest implements RewriteTest { @@ -336,7 +339,24 @@ void foo() throws IOException { @Test void verifyCsharpImplicitThrow() { rewriteRun( - spec -> spec.recipe(Recipe.noop()), + spec -> spec.recipe(toRecipe(() -> new JavaVisitor<>() { + @Override + public J visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { + // C# can rethrow the caught exception implicitly and so the `e` Identifier is removed by the inline visitor below + Cs.CompilationUnit cscu = JavaToCsharp.compilationUnit(cu); + cscu = (Cs.CompilationUnit) new JavaVisitor() { + @Override + public J visitThrow(J.Throw thrown, ExecutionContext ctx) { + if (thrown.getException() instanceof J.Identifier) { + return thrown.withException(new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)); + } + return thrown; + } + }.visit(cscu, new InMemoryExecutionContext()); + // Exercise the regular recipe with the now modified CSharp compilation unit + return (J) new CatchClauseOnlyRethrows().getVisitor().visit(cscu, ctx); + } + })), //language=java java( """ @@ -350,32 +370,14 @@ void foo() throws IllegalAccessException { } } """, - spec -> spec.beforeRecipe(compUnit -> { - // C# can rethrow the caught exception implicitly and so the `e` Identifier is removed by the inline visitor below - Cs.CompilationUnit cSharpCompUnit = (Cs.CompilationUnit) new JavaVisitor() { - @Override - public J visitThrow(J.Throw thrown, ExecutionContext ctx) { - if (thrown.getException() instanceof J.Identifier) { - return thrown.withException(new J.Empty(Tree.randomId(), Space.EMPTY, Markers.EMPTY)); - } - return thrown; - } - }.visit(JavaToCsharp.compilationUnit(compUnit), new InMemoryExecutionContext()); - - Cs.CompilationUnit after = (Cs.CompilationUnit) new CatchClauseOnlyRethrows().getVisitor() - .visit(cSharpCompUnit, new InMemoryExecutionContext()); - - var classA = (J.ClassDeclaration) after.getMembers().get(0); - var methodFoo = (J.MethodDeclaration) classA.getBody().getStatements().get(0); - var firstStatement = methodFoo.getBody().getStatements().get(0); - assertThat(firstStatement) - .isInstanceOf(J.Throw.class) // The `try/catch` block removed - .extracting(s -> ((J.Throw) s).getException()) - .isInstanceOf(J.NewClass.class); + """ + class A { + void foo() throws IllegalAccessException { + throw new IllegalAccessException(); + } } - ) + """ ) ); } - } From 8c3d41a4ddb1b7dcc3364996f71a107fa310b8b8 Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 28 Nov 2024 12:18:36 +0100 Subject: [PATCH 179/183] Use newlines in JavaToCsharp.compilationUnit for cleaner future diffs --- .../staticanalysis/JavaToCsharp.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java index ba3fd564a..aa3d1949e 100644 --- a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java +++ b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java @@ -23,10 +23,27 @@ import java.util.List; import java.util.stream.Collectors; +import static java.util.stream.Collectors.toList; + public class JavaToCsharp { public static Cs.CompilationUnit compilationUnit(J.CompilationUnit cu) { - return new Cs.CompilationUnit(cu.getId(), cu.getPrefix(), cu.getMarkers(), cu.getSourcePath(), - cu.getFileAttributes(), cu.getCharset().name(), cu.isCharsetBomMarked(), cu.getChecksum(), List.of(), List.of(), List.of(), cu.getClasses().stream().map(Statement.class::cast).map(cd -> JRightPadded.build(cd)).collect(Collectors.toList()), cu.getEof()); + return new Cs.CompilationUnit( + cu.getId(), + cu.getPrefix(), + cu.getMarkers(), + cu.getSourcePath(), + cu.getFileAttributes(), + cu.getCharset().name(), + cu.isCharsetBomMarked(), + cu.getChecksum(), + List.of(), + List.of(), + List.of(), + cu.getClasses().stream() + .map(Statement.class::cast) + .map(JRightPadded::build) + .collect(toList()), + cu.getEof()); } } From 9f0bb94d913d99eb73129cf745814da59cfaa5be Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 28 Nov 2024 12:25:42 +0100 Subject: [PATCH 180/183] Remove unused import Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java index aa3d1949e..c0821e834 100644 --- a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java +++ b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java @@ -21,8 +21,6 @@ import org.openrewrite.java.tree.Statement; import java.util.List; -import java.util.stream.Collectors; - import static java.util.stream.Collectors.toList; public class JavaToCsharp { From f1a83f40fd1ff9359387f98bb7c4ea6edd9d2b1e Mon Sep 17 00:00:00 2001 From: Tim te Beek Date: Thu, 28 Nov 2024 12:32:39 +0100 Subject: [PATCH 181/183] Use Stream.toList() --- src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java index c0821e834..06b5b6221 100644 --- a/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java +++ b/src/test/java/org/openrewrite/staticanalysis/JavaToCsharp.java @@ -21,7 +21,6 @@ import org.openrewrite.java.tree.Statement; import java.util.List; -import static java.util.stream.Collectors.toList; public class JavaToCsharp { @@ -41,7 +40,7 @@ public static Cs.CompilationUnit compilationUnit(J.CompilationUnit cu) { cu.getClasses().stream() .map(Statement.class::cast) .map(JRightPadded::build) - .collect(toList()), + .toList(), cu.getEof()); } } From 0b00944b0310aa5045b586192e2de57e5d6e84b4 Mon Sep 17 00:00:00 2001 From: Jacob van Lingen Date: Thu, 28 Nov 2024 19:20:49 +0100 Subject: [PATCH 182/183] Improve recipes and excluded them from C# codebase if needed (#396) * Improve recipes and excluded them from csharp codebase if needed * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Improve recipes and excluded them from csharp codebase if needed * Update src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review * Apply suggestions from code review --------- Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../AtomicPrimitiveEqualsUsesGet.java | 20 +- ...InsensitiveComparisonsDoNotChangeCase.java | 55 +++--- .../staticanalysis/CovariantEquals.java | 156 +++++++-------- .../staticanalysis/EqualsAvoidsNull.java | 28 ++- .../ExternalizableHasNoArgsConstructor.java | 150 +++++++-------- .../FixStringFormatExpressions.java | 146 +++++++------- .../IndexOfChecksShouldUseAStartPosition.java | 15 +- .../IndexOfReplaceableByContains.java | 62 +++--- ...ndexOfShouldNotCompareGreaterThanZero.java | 40 ++-- .../NestedEnumsAreNotStatic.java | 23 ++- ...ewStringBuilderBufferWithCharArgument.java | 19 +- .../staticanalysis/NoFinalizer.java | 39 ++-- .../staticanalysis/NoValueOfOnStringType.java | 9 +- .../ObjectFinalizeCallsSuper.java | 28 +-- ...itiveWrapperClassConstructorToValueOf.java | 6 - .../RenameLocalVariablesToCamelCase.java | 10 +- .../RenamePrivateFieldsToCamelCase.java | 5 +- .../ReplaceStringBuilderWithString.java | 181 +++++++++--------- .../staticanalysis/StringLiteralEquality.java | 54 ++---- .../UseJavaStyleArrayDeclarations.java | 13 +- .../WriteOctalValuesAsDecimal.java | 12 +- .../csharp/CSharpFileChecker.java | 35 ++++ 22 files changed, 511 insertions(+), 595 deletions(-) create mode 100644 src/main/java/org/openrewrite/staticanalysis/csharp/CSharpFileChecker.java diff --git a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java index d4a42c3b0..77685dee4 100644 --- a/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java +++ b/src/main/java/org/openrewrite/staticanalysis/AtomicPrimitiveEqualsUsesGet.java @@ -30,16 +30,18 @@ import java.util.Arrays; import java.util.Collections; -import java.util.HashSet; +import java.util.List; import java.util.Set; public class AtomicPrimitiveEqualsUsesGet extends Recipe { - private static final Set ATOMIC_PRIMITIVE_TYPES = new HashSet<>(Arrays.asList( - "java.util.concurrent.atomic.AtomicBoolean", - "java.util.concurrent.atomic.AtomicInteger", - "java.util.concurrent.atomic.AtomicLong" - )); + public static final String ATOMIC_ATOMIC_BOOLEAN = "java.util.concurrent.atomic.AtomicBoolean"; + public static final String ATOMIC_ATOMIC_INTEGER = "java.util.concurrent.atomic.AtomicInteger"; + public static final String ATOMIC_ATOMIC_LONG = "java.util.concurrent.atomic.AtomicLong"; + + private static final List ATOMIC_PRIMITIVE_TYPES = Arrays.asList( + ATOMIC_ATOMIC_BOOLEAN, ATOMIC_ATOMIC_INTEGER, ATOMIC_ATOMIC_LONG + ); @Override public String getDisplayName() { @@ -59,9 +61,9 @@ public Set getTags() { @Override public TreeVisitor getVisitor() { return Preconditions.check(Preconditions.or( - new UsesType<>("java.util.concurrent.atomic.AtomicBoolean", false), - new UsesType<>("java.util.concurrent.atomic.AtomicInteger", false), - new UsesType<>("java.util.concurrent.atomic.AtomicLong", false) + new UsesType<>(ATOMIC_ATOMIC_BOOLEAN, false), + new UsesType<>(ATOMIC_ATOMIC_INTEGER, false), + new UsesType<>(ATOMIC_ATOMIC_LONG, false) ), new JavaVisitor() { private final MethodMatcher aiMethodMatcher = new MethodMatcher("java.lang.Object equals(java.lang.Object)"); diff --git a/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java b/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java index 672a4c8f5..8b2a16fd8 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/CaseInsensitiveComparisonsDoNotChangeCase.java @@ -31,6 +31,11 @@ import java.util.Set; public class CaseInsensitiveComparisonsDoNotChangeCase extends Recipe { + + private static final MethodMatcher COMPARE_IGNORE_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String equalsIgnoreCase(java.lang.String)"); + private static final MethodMatcher TO_LOWER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toLowerCase()"); + private static final MethodMatcher TO_UPPER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toUpperCase()"); + @Override public String getDisplayName() { return "CaseInsensitive comparisons do not alter case"; @@ -53,38 +58,32 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesMethod<>("java.lang.String equalsIgnoreCase(java.lang.String)"), new CaseInsensitiveComparisonVisitor<>()); - } - - private static class CaseInsensitiveComparisonVisitor extends JavaIsoVisitor { - private static final MethodMatcher COMPARE_IGNORE_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String equalsIgnoreCase(java.lang.String)"); - private static final MethodMatcher TO_LOWER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toLowerCase()"); - private static final MethodMatcher TO_UPPER_CASE_METHOD_MATCHER = new MethodMatcher("java.lang.String toUpperCase()"); - - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) { - J.MethodInvocation mi = super.visitMethodInvocation(method, executionContext); - if (COMPARE_IGNORE_CASE_METHOD_MATCHER.matches(mi)) { - mi = mi.withArguments(ListUtils.map(mi.getArguments(), arg -> { - if (arg instanceof J.MethodInvocation && isChangeCaseMethod(arg)) { - return ((J.MethodInvocation) arg).getSelect(); + return Preconditions.check(new UsesMethod<>(COMPARE_IGNORE_CASE_METHOD_MATCHER), new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); + if (COMPARE_IGNORE_CASE_METHOD_MATCHER.matches(mi)) { + mi = mi.withArguments(ListUtils.map(mi.getArguments(), arg -> { + if (arg instanceof J.MethodInvocation && isChangeCaseMethod(arg)) { + return ((J.MethodInvocation) arg).getSelect(); + } + return arg; + })); + if (isChangeCaseMethod(mi.getSelect())) { + J.MethodInvocation mChangeCase = (J.MethodInvocation) mi.getSelect(); + mi = mi.withSelect(mChangeCase.getSelect()); } - return arg; - })); - if (isChangeCaseMethod(mi.getSelect())) { - J.MethodInvocation mChangeCase = (J.MethodInvocation) mi.getSelect(); - mi = mi.withSelect(mChangeCase.getSelect()); } + return mi; } - return mi; - } - private boolean isChangeCaseMethod(@Nullable J j) { - if (j instanceof J.MethodInvocation) { - J.MethodInvocation mi = (J.MethodInvocation) j; - return TO_LOWER_CASE_METHOD_MATCHER.matches(mi) || TO_UPPER_CASE_METHOD_MATCHER.matches(mi); + private boolean isChangeCaseMethod(@Nullable J j) { + if (j instanceof J.MethodInvocation) { + J.MethodInvocation mi = (J.MethodInvocation) j; + return TO_LOWER_CASE_METHOD_MATCHER.matches(mi) || TO_UPPER_CASE_METHOD_MATCHER.matches(mi); + } + return false; } - return false; - } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java b/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java index 3c31d1f39..d69f309da 100644 --- a/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/CovariantEquals.java @@ -20,19 +20,23 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.DeclaresMethod; import org.openrewrite.java.service.AnnotationService; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaType; +import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; -import java.time.Duration; import java.util.Collections; import java.util.Comparator; import java.util.Set; -import java.util.stream.Stream; @Incubating(since = "7.0.0") public class CovariantEquals extends Recipe { + private static final MethodMatcher EQUALS_MATCHER = new MethodMatcher("* equals(..)"); + private static final MethodMatcher EQUALS_OBJECT_MATCHER = new MethodMatcher("* equals(java.lang.Object)"); + private static final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override"); + @Override public String getDisplayName() { return "Covariant equals"; @@ -49,105 +53,79 @@ public Set getTags() { return Collections.singleton("RSPEC-S2162"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { - MethodMatcher objectEquals = new MethodMatcher("* equals(java.lang.Object)"); - return new JavaIsoVisitor() { - + TreeVisitor conditions = Preconditions.and( + new DeclaresMethod<>(EQUALS_MATCHER), + Preconditions.not(new DeclaresMethod<>(EQUALS_OBJECT_MATCHER)), + Preconditions.not(new CSharpFileChecker<>()) + ); + return Preconditions.check(conditions, Repeat.repeatUntilStable(new JavaIsoVisitor() { @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); - Stream mds = cd.getBody().getStatements().stream() - .filter(J.MethodDeclaration.class::isInstance) - .map(J.MethodDeclaration.class::cast); - if (cd.getKind() != J.ClassDeclaration.Kind.Type.Interface && mds.noneMatch(m -> objectEquals.matches(m, classDecl))) { - cd = (J.ClassDeclaration) new ChangeCovariantEqualsMethodVisitor(cd).visit(cd, ctx, getCursor().getParentOrThrow()); - assert cd != null; - } - return cd; - } - - class ChangeCovariantEqualsMethodVisitor extends JavaIsoVisitor { - private final AnnotationMatcher OVERRIDE_ANNOTATION = new AnnotationMatcher("@java.lang.Override"); - - private final J.ClassDeclaration enclosingClass; - - public ChangeCovariantEqualsMethodVisitor(J.ClassDeclaration enclosingClass) { - this.enclosingClass = enclosingClass; + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { + J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); + J.ClassDeclaration enclosingClass = getCursor().dropParentUntil(p -> p instanceof J.ClassDeclaration).getValue(); + + /* + * Looking for "public boolean equals(EnclosingClassType)" as the method signature match. + * We'll replace it with "public boolean equals(Object)" + */ + JavaType.FullyQualified type = enclosingClass.getType(); + if (type == null || type instanceof JavaType.Unknown) { + return m; } - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) { - J.MethodDeclaration m = super.visitMethodDeclaration(method, ctx); - updateCursor(m); - - /* - * Looking for "public boolean equals(EnclosingClassType)" as the method signature match. - * We'll replace it with "public boolean equals(Object)" - */ - JavaType.FullyQualified type = enclosingClass.getType(); - if (type == null || type instanceof JavaType.Unknown) { - return m; - } - - String ecfqn = type.getFullyQualifiedName(); - if (m.hasModifier(J.Modifier.Type.Public) && - m.getReturnTypeExpression() != null && + String ecfqn = type.getFullyQualifiedName(); + if (m.hasModifier(J.Modifier.Type.Public) && m.getReturnTypeExpression() != null && JavaType.Primitive.Boolean.equals(m.getReturnTypeExpression().getType()) && new MethodMatcher(ecfqn + " equals(" + ecfqn + ")").matches(m, enclosingClass)) { - if (!service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)) { - m = JavaTemplate.builder("@Override").build() - .apply(updateCursor(m), - m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); - } - - /* - * Change parameter type to Object, and maybe change input parameter name representing the other object. - * This is because we prepend these type-checking replacement statements to the existing "equals(..)" body. - * Therefore we don't want to collide with any existing variable names. - */ - J.VariableDeclarations.NamedVariable oldParamName = ((J.VariableDeclarations) m.getParameters().get(0)).getVariables().get(0); - String paramName = "obj".equals(oldParamName.getSimpleName()) ? "other" : "obj"; - m = JavaTemplate.builder("Object #{}").build() + if (!service(AnnotationService.class).matches(getCursor(), OVERRIDE_ANNOTATION)) { + m = JavaTemplate.builder("@Override").build() .apply(updateCursor(m), - m.getCoordinates().replaceParameters(), - paramName); - - /* - * We'll prepend this type-check and type-cast to the beginning of the existing - * equals(..) method body statements, and let the existing equals(..) method definition continue - * with the logic doing what it was doing. - */ - String equalsBodyPrefixTemplate = "if (#{} == this) return true;\n" + - "if (#{} == null || getClass() != #{}.getClass()) return false;\n" + - "#{} #{} = (#{}) #{};\n"; - JavaTemplate equalsBodySnippet = JavaTemplate.builder(equalsBodyPrefixTemplate).contextSensitive().build(); - - assert m.getBody() != null; - Object[] params = new Object[]{ - paramName, - paramName, - paramName, - enclosingClass.getSimpleName(), - oldParamName.getSimpleName(), - enclosingClass.getSimpleName(), - paramName - }; - - m = equalsBodySnippet.apply(new Cursor(getCursor().getParent(), m), - m.getBody().getStatements().get(0).getCoordinates().before(), - params); + m.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName))); } - return m; + /* + * Change parameter type to Object, and maybe change input parameter name representing the other object. + * This is because we prepend these type-checking replacement statements to the existing "equals(..)" body. + * Therefore we don't want to collide with any existing variable names. + */ + J.VariableDeclarations.NamedVariable oldParamName = ((J.VariableDeclarations) m.getParameters().get(0)).getVariables().get(0); + String paramName = "obj".equals(oldParamName.getSimpleName()) ? "other" : "obj"; + m = JavaTemplate.builder("Object #{}").build() + .apply(updateCursor(m), + m.getCoordinates().replaceParameters(), + paramName); + + /* + * We'll prepend this type-check and type-cast to the beginning of the existing + * equals(..) method body statements, and let the existing equals(..) method definition continue + * with the logic doing what it was doing. + */ + String equalsBodyPrefixTemplate = "if (#{} == this) return true;\n" + + "if (#{} == null || getClass() != #{}.getClass()) return false;\n" + + "#{} #{} = (#{}) #{};\n"; + JavaTemplate equalsBodySnippet = JavaTemplate.builder(equalsBodyPrefixTemplate).contextSensitive().build(); + + assert m.getBody() != null; + Object[] params = new Object[]{ + paramName, + paramName, + paramName, + enclosingClass.getSimpleName(), + oldParamName.getSimpleName(), + enclosingClass.getSimpleName(), + paramName + }; + + m = equalsBodySnippet.apply(new Cursor(getCursor().getParent(), m), + m.getBody().getStatements().get(0).getCoordinates().before(), + params); } + + return m; } - }; + })); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java index e82365732..fede66cc0 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java @@ -53,22 +53,20 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return new EqualsAvoidsNullFromCompilationUnitStyle(); - } - - private static class EqualsAvoidsNullFromCompilationUnitStyle extends JavaIsoVisitor { - @Override - public J visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof JavaSourceFile) { - JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); - EqualsAvoidsNullStyle style = cu.getStyle(EqualsAvoidsNullStyle.class); - if (style == null) { - style = Checkstyle.equalsAvoidsNull(); + return new JavaIsoVisitor() { + @Override + public J visit(@Nullable Tree tree, ExecutionContext ctx) { + if (tree instanceof JavaSourceFile) { + JavaSourceFile cu = (JavaSourceFile) requireNonNull(tree); + EqualsAvoidsNullStyle style = cu.getStyle(EqualsAvoidsNullStyle.class); + if (style == null) { + style = Checkstyle.equalsAvoidsNull(); + } + return new EqualsAvoidsNullVisitor<>(style).visitNonNull(cu, ctx); } - return new EqualsAvoidsNullVisitor<>(style).visitNonNull(cu, ctx); + //noinspection DataFlowIssue + return (J) tree; } - //noinspection DataFlowIssue - return (J) tree; - } + }; } } diff --git a/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java b/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java index 1e84ca290..2e3f278d1 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java +++ b/src/main/java/org/openrewrite/staticanalysis/ExternalizableHasNoArgsConstructor.java @@ -34,6 +34,8 @@ public class ExternalizableHasNoArgsConstructor extends Recipe { + private static final JavaType EXTERNALIZABLE_TYPE = JavaType.buildType("java.io.Externalizable"); + @Override public String getDisplayName() { return "`Externalizable` classes have no-arguments constructor"; @@ -57,90 +59,86 @@ public Set getTags() { @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesType<>("java.io.Externalizable", false), - new ExternalizableHasNoArgsConstructorVisitor()); - } - - private static class ExternalizableHasNoArgsConstructorVisitor extends JavaIsoVisitor { - private static final JavaType externalizableType = JavaType.buildType("java.io.Externalizable"); - - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); - if (TypeUtils.isAssignableTo(externalizableType, cd.getType())) { - boolean hasFinalUninitializedFieldVar = false; - Integer firstMethodDeclarationIndex = null; - List statements = cd.getBody().getStatements(); - - for (int i = 0; i < statements.size(); i++) { - Statement statement = statements.get(i); - if (statement instanceof J.VariableDeclarations) { - J.VariableDeclarations varDecls = (J.VariableDeclarations) statement; - if (J.Modifier.hasModifier(varDecls.getModifiers(), J.Modifier.Type.Final) && - varDecls.getVariables().stream().anyMatch(v -> v.getInitializer() == null)) { - hasFinalUninitializedFieldVar = true; - break; + new JavaIsoVisitor() { + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); + if (TypeUtils.isAssignableTo(EXTERNALIZABLE_TYPE, cd.getType())) { + boolean hasFinalUninitializedFieldVar = false; + Integer firstMethodDeclarationIndex = null; + List statements = cd.getBody().getStatements(); + + for (int i = 0; i < statements.size(); i++) { + Statement statement = statements.get(i); + if (statement instanceof J.VariableDeclarations) { + J.VariableDeclarations varDecls = (J.VariableDeclarations) statement; + if (J.Modifier.hasModifier(varDecls.getModifiers(), J.Modifier.Type.Final) && + varDecls.getVariables().stream().anyMatch(v -> v.getInitializer() == null)) { + hasFinalUninitializedFieldVar = true; + break; + } + } + // The new no-args constructor should be the first methodDeclaration + if (statement instanceof J.MethodDeclaration && firstMethodDeclarationIndex == null) { + firstMethodDeclarationIndex = i; + } + } + if (!hasFinalUninitializedFieldVar && !hasNoArgsConstructor(cd) && parentClassHasNoArgsConstructor(cd)) { + cd = JavaTemplate.builder("public " + cd.getSimpleName() + "() {}") + .contextSensitive() + .build() + .apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement()); + if (firstMethodDeclarationIndex != null) { + statements.add(firstMethodDeclarationIndex, cd.getBody().getStatements().remove(cd.getBody().getStatements().size() - 1)); + cd = cd.withBody(cd.getBody().withStatements(statements)); + } + } } + return cd; } - // The new no-args constructor should be the first methodDeclaration - if (statement instanceof J.MethodDeclaration && firstMethodDeclarationIndex == null) { - firstMethodDeclarationIndex = i; - } - } - if (!hasFinalUninitializedFieldVar && !hasNoArgsConstructor(cd) && parentClassHasNoArgsConstructor(cd)) { - cd = JavaTemplate.builder("public " + cd.getSimpleName() + "() {}") - .contextSensitive() - .build() - .apply(updateCursor(cd), cd.getBody().getCoordinates().lastStatement()); - if (firstMethodDeclarationIndex != null) { - statements.add(firstMethodDeclarationIndex, cd.getBody().getStatements().remove(cd.getBody().getStatements().size() - 1)); - cd = cd.withBody(cd.getBody().withStatements(statements)); - } - } - } - return cd; - } - private boolean hasNoArgsConstructor(J.ClassDeclaration cd) { - boolean hasNoArgsConstructor = false; - boolean hasDefaultConstructor = true; - for (Statement statement : cd.getBody().getStatements()) { - if (statement instanceof J.MethodDeclaration) { - J.MethodDeclaration md = (J.MethodDeclaration) statement; - if (md.isConstructor()) { - if (md.getParameters().isEmpty() || md.getParameters().get(0) instanceof J.Empty) { - hasNoArgsConstructor = true; - } else { - hasDefaultConstructor = false; + private boolean hasNoArgsConstructor(J.ClassDeclaration cd) { + boolean hasNoArgsConstructor = false; + boolean hasDefaultConstructor = true; + for (Statement statement : cd.getBody().getStatements()) { + if (statement instanceof J.MethodDeclaration) { + J.MethodDeclaration md = (J.MethodDeclaration) statement; + if (md.isConstructor()) { + if (md.getParameters().isEmpty() || md.getParameters().get(0) instanceof J.Empty) { + hasNoArgsConstructor = true; + } else { + hasDefaultConstructor = false; + } + } + } } + return hasDefaultConstructor || hasNoArgsConstructor; } - } - } - return hasDefaultConstructor || hasNoArgsConstructor; - } - - private boolean parentClassHasNoArgsConstructor(J.ClassDeclaration cd) { - if (cd.getExtends() == null) { - return true; - } - JavaType.FullyQualified parentFq = TypeUtils.asFullyQualified(cd.getExtends().getType()); - if (parentFq == null) { - return false; - } + private boolean parentClassHasNoArgsConstructor(J.ClassDeclaration cd) { + if (cd.getExtends() == null) { + return true; + } - boolean hasNoArgsConstructor = false; - boolean hasDefaultConstructor = true; + JavaType.FullyQualified parentFq = TypeUtils.asFullyQualified(cd.getExtends().getType()); + if (parentFq == null) { + return false; + } - for (JavaType.Method method : parentFq.getMethods()) { - if ("".equals(method.getName())) { - if (method.getParameterNames().isEmpty()) { - hasNoArgsConstructor = true; - } else { - hasDefaultConstructor = false; + boolean hasNoArgsConstructor = false; + boolean hasDefaultConstructor = true; + + for (JavaType.Method method : parentFq.getMethods()) { + if ("".equals(method.getName())) { + if (method.getParameterNames().isEmpty()) { + hasNoArgsConstructor = true; + } else { + hasDefaultConstructor = false; + } + } + } + return hasDefaultConstructor || hasNoArgsConstructor; } - } - } - return hasDefaultConstructor || hasNoArgsConstructor; - } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java index db905298a..2e5a484ec 100644 --- a/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java +++ b/src/main/java/org/openrewrite/staticanalysis/FixStringFormatExpressions.java @@ -26,13 +26,19 @@ import org.openrewrite.java.tree.Expression; import org.openrewrite.java.tree.J; -import java.time.Duration; import java.util.Collections; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public class FixStringFormatExpressions extends Recipe { + + // %[argument_index$][flags][width][.precision][t]conversion + private static final Pattern FS_PATTERN = Pattern.compile("%(\\d+\\$)?([-#+ 0,(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])"); + + private static final MethodMatcher FORMAT_MATCHER = new MethodMatcher("java.lang.String format(..)"); + private static final MethodMatcher FORMATTED_MATCHER = new MethodMatcher("java.lang.String formatted(..)"); + @Override public String getDisplayName() { return "Fix `String#format` and `String#formatted` expressions"; @@ -48,90 +54,70 @@ public Set getTags() { return Collections.singleton("RSPEC-S3457"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - - @Override public TreeVisitor getVisitor() { - return Preconditions.check( - Preconditions.or( - new UsesMethod<>(new MethodMatcher("java.lang.String format(..)")), - new UsesMethod<>(new MethodMatcher("java.lang.String formatted(..)")) - ), - new FixPrintfExpressionsVisitor() - ); - } - - private static class FixPrintfExpressionsVisitor extends JavaIsoVisitor { - // %[argument_index$][flags][width][.precision][t]conversion - private final String formatSpecifier = "%(\\d+\\$)?([-#+ 0,(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])"; - private final Pattern fsPattern = Pattern.compile(formatSpecifier); - - MethodMatcher sFormatMatcher = new MethodMatcher("java.lang.String format(..)"); - MethodMatcher sFormattedMatcher = new MethodMatcher("java.lang.String formatted(..)"); - - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); - if (sFormatMatcher.matches(mi) || sFormattedMatcher.matches(mi)) { - boolean isStringFormattedExpression = false; - J.Literal fmtArg = null; - if (sFormatMatcher.matches(mi) && mi.getArguments().get(0) instanceof J.Literal) { - fmtArg = (J.Literal) mi.getArguments().get(0); - } else if (sFormattedMatcher.matches(mi) && mi.getSelect() instanceof J.Literal) { - fmtArg = (J.Literal) mi.getSelect(); - isStringFormattedExpression = true; - } - - if (fmtArg == null || fmtArg.getValue() == null || fmtArg.getValueSource() == null) { - return mi; - } - - // Replace any new line chars with %n - if (isStringFormattedExpression) { - mi = mi.withSelect(replaceNewLineChars(mi.getSelect())); - } else { - mi = mi.withArguments(ListUtils.mapFirst(mi.getArguments(), FixPrintfExpressionsVisitor::replaceNewLineChars)); - } - - // Trim any extra args - String val = (String) fmtArg.getValue(); - Matcher m = fsPattern.matcher(val); - int argIndex = isStringFormattedExpression ? 0 : 1; - while (m.find()) { - if (m.group(1) != null || m.group(2).contains("<")) { + return Preconditions.check(Preconditions.or(new UsesMethod<>(FORMAT_MATCHER), new UsesMethod<>(FORMATTED_MATCHER)), + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); + if (FORMAT_MATCHER.matches(mi) || FORMATTED_MATCHER.matches(mi)) { + boolean isStringFormattedExpression = false; + J.Literal fmtArg = null; + if (FORMAT_MATCHER.matches(mi) && mi.getArguments().get(0) instanceof J.Literal) { + fmtArg = (J.Literal) mi.getArguments().get(0); + } else if (FORMATTED_MATCHER.matches(mi) && mi.getSelect() instanceof J.Literal) { + fmtArg = (J.Literal) mi.getSelect(); + isStringFormattedExpression = true; + } + + if (fmtArg == null || fmtArg.getValue() == null || fmtArg.getValueSource() == null) { + return mi; + } + + // Replace any new line chars with %n + if (isStringFormattedExpression) { + mi = mi.withSelect(replaceNewLineChars(mi.getSelect())); + } else { + mi = mi.withArguments(ListUtils.mapFirst(mi.getArguments(), this::replaceNewLineChars)); + } + + // Trim any extra args + String val = (String) fmtArg.getValue(); + Matcher m = FS_PATTERN.matcher(val); + int argIndex = isStringFormattedExpression ? 0 : 1; + while (m.find()) { + if (m.group(1) != null || m.group(2).contains("<")) { + return mi; + } + argIndex++; + } + int finalArgIndex = argIndex; + mi = mi.withArguments(ListUtils.map(mi.getArguments(), (i, arg) -> { + if (i == 0 || i < finalArgIndex) { + return arg; + } + return null; + })); + return mi; + } return mi; } - argIndex++; - } - int finalArgIndex = argIndex; - mi = mi.withArguments(ListUtils.map(mi.getArguments(), (i, arg) -> { - if (i == 0 || i < finalArgIndex) { - return arg; - } - return null; - })); - return mi; - } - return mi; - } - private static Expression replaceNewLineChars(Expression arg0) { - if (arg0 instanceof J.Literal) { - J.Literal fmt = (J.Literal) arg0; - if (fmt.getValue() != null) { - fmt = fmt.withValue(fmt.getValue().toString().replaceAll("(? getTags() { return Collections.singleton("RSPEC-S2912"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { return Preconditions.check(new UsesMethod<>(STRING_INDEX_MATCHER), new JavaIsoVisitor() { - - private boolean isValueNotCompliant(J.Literal literal) { - return !(literal.getValue() instanceof Integer && ((Integer) (literal.getValue()) <= 0)); - } - @Override public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { J.Binary b = super.visitBinary(binary, ctx); @@ -85,6 +74,10 @@ public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { } return b; } + + private boolean isValueNotCompliant(J.Literal literal) { + return !(literal.getValue() instanceof Integer && ((Integer) (literal.getValue()) <= 0)); + } }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java b/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java index 868df23ec..b82524241 100644 --- a/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java +++ b/src/main/java/org/openrewrite/staticanalysis/IndexOfReplaceableByContains.java @@ -22,7 +22,6 @@ import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.tree.J; -import java.time.Duration; import java.util.Collections; import java.util.Set; @@ -46,48 +45,33 @@ public Set getTags() { return Collections.singleton("RSPEC-S2692"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { - return Preconditions.check( - Preconditions.or( - new UsesMethod<>(STRING_INDEX_MATCHER), - new UsesMethod<>(LIST_INDEX_MATCHER) - ), - new IndexOfReplaceableByContainsVisitor() - ); - } - - private static class IndexOfReplaceableByContainsVisitor extends JavaVisitor { - private final JavaTemplate stringContains = JavaTemplate.builder("#{any(java.lang.String)}.contains(#{any(java.lang.String)})").build(); - private final JavaTemplate listContains = JavaTemplate.builder("#{any(java.util.List)}.contains(#{any(java.lang.Object)})").build(); - - @Override - public J visitBinary(J.Binary binary, ExecutionContext ctx) { - J j = super.visitBinary(binary, ctx); - J.Binary asBinary = (J.Binary) j; - if (asBinary.getLeft() instanceof J.MethodInvocation) { - J.MethodInvocation mi = (J.MethodInvocation) asBinary.getLeft(); - if (STRING_INDEX_MATCHER.matches(mi) || mi.getSelect() != null && LIST_INDEX_MATCHER.matches(mi)) { - if (asBinary.getRight() instanceof J.Literal) { - String valueSource = ((J.Literal) asBinary.getRight()).getValueSource(); - boolean isGreaterThanNegativeOne = asBinary.getOperator() == J.Binary.Type.GreaterThan && "-1".equals(valueSource); - boolean isGreaterThanOrEqualToZero = asBinary.getOperator() == J.Binary.Type.GreaterThanOrEqual && "0".equals(valueSource); - if (isGreaterThanNegativeOne || isGreaterThanOrEqualToZero) { - Cursor cursor = new Cursor(updateCursor(asBinary), asBinary.getLeft()); - j = (STRING_INDEX_MATCHER.matches(mi) ? stringContains : listContains) - .apply(cursor, mi.getCoordinates().replace(), mi.getSelect(), mi.getArguments().get(0)) - .withPrefix(asBinary.getPrefix()); + return Preconditions.check(Preconditions.or(new UsesMethod<>(STRING_INDEX_MATCHER), new UsesMethod<>(LIST_INDEX_MATCHER)), + new JavaVisitor() { + @Override + public J visitBinary(J.Binary binary, ExecutionContext ctx) { + J j = super.visitBinary(binary, ctx); + J.Binary asBinary = (J.Binary) j; + if (asBinary.getLeft() instanceof J.MethodInvocation) { + J.MethodInvocation mi = (J.MethodInvocation) asBinary.getLeft(); + if (STRING_INDEX_MATCHER.matches(mi) || mi.getSelect() != null && LIST_INDEX_MATCHER.matches(mi)) { + if (asBinary.getRight() instanceof J.Literal) { + String valueSource = ((J.Literal) asBinary.getRight()).getValueSource(); + boolean isGreaterThanNegativeOne = asBinary.getOperator() == J.Binary.Type.GreaterThan && "-1".equals(valueSource); + boolean isGreaterThanOrEqualToZero = asBinary.getOperator() == J.Binary.Type.GreaterThanOrEqual && "0".equals(valueSource); + if (isGreaterThanNegativeOne || isGreaterThanOrEqualToZero) { + Cursor cursor = new Cursor(updateCursor(asBinary), asBinary.getLeft()); + j = JavaTemplate.builder("#{any()}.contains(#{any()})").build() + .apply(cursor, mi.getCoordinates().replace(), mi.getSelect(), mi.getArguments().get(0)) + .withPrefix(asBinary.getPrefix()); + } + } + } } + return j; } } - } - return j; - } + ); } - } diff --git a/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java b/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java index bed28337b..aa373c619 100644 --- a/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java +++ b/src/main/java/org/openrewrite/staticanalysis/IndexOfShouldNotCompareGreaterThanZero.java @@ -56,31 +56,23 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check( - Preconditions.or( - new UsesMethod<>(STRING_INDEX_MATCHER), - new UsesMethod<>(LIST_INDEX_MATCHER) - ), - new IndexOfShouldNotCompareGreaterThanZeroVisitor() - ); - } - - private static class IndexOfShouldNotCompareGreaterThanZeroVisitor extends JavaIsoVisitor { - @Override - public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { - J.Binary b = super.visitBinary(binary, ctx); - if (b.getOperator() == J.Binary.Type.GreaterThan) { - if (b.getLeft() instanceof J.MethodInvocation) { - J.MethodInvocation mi = (J.MethodInvocation) b.getLeft(); - if (STRING_INDEX_MATCHER.matches(mi) || LIST_INDEX_MATCHER.matches(mi)) { - if (b.getRight() instanceof J.Literal && "0".equals(((J.Literal) b.getRight()).getValueSource())) { - b = b.withRight(((J.Literal) b.getRight()).withValueSource("1")).withOperator(J.Binary.Type.GreaterThanOrEqual); + return Preconditions.check(Preconditions.or(new UsesMethod<>(STRING_INDEX_MATCHER), new UsesMethod<>(LIST_INDEX_MATCHER)), + new JavaIsoVisitor() { + @Override + public J.Binary visitBinary(J.Binary binary, ExecutionContext ctx) { + J.Binary b = super.visitBinary(binary, ctx); + if (b.getOperator() == J.Binary.Type.GreaterThan) { + if (b.getLeft() instanceof J.MethodInvocation) { + J.MethodInvocation mi = (J.MethodInvocation) b.getLeft(); + if (STRING_INDEX_MATCHER.matches(mi) || LIST_INDEX_MATCHER.matches(mi)) { + if (b.getRight() instanceof J.Literal && "0".equals(((J.Literal) b.getRight()).getValueSource())) { + b = b.withRight(((J.Literal) b.getRight()).withValueSource("1")).withOperator(J.Binary.Type.GreaterThanOrEqual); + } + } + } } + return b; } - } - } - return b; - } + }); } - } diff --git a/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java b/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java index 941cb8e5d..082cb236a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java +++ b/src/main/java/org/openrewrite/staticanalysis/NestedEnumsAreNotStatic.java @@ -23,6 +23,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.tree.J; import org.openrewrite.marker.SearchResult; +import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; import java.time.Duration; import java.util.Collections; @@ -51,16 +52,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new JavaIsoVisitor() { - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); - if (cd.getKind() == J.ClassDeclaration.Kind.Type.Enum && cd.getType() != null && cd.getType().getOwningClass() != null) { - cd = SearchResult.found(cd); - } - return cd; - } - }, new JavaIsoVisitor() { + return Preconditions.and(new HasNestedEnum(), Preconditions.not(new CSharpFileChecker<>()), new JavaIsoVisitor() { @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); @@ -80,4 +72,15 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex } }); } + + private static class HasNestedEnum extends JavaIsoVisitor { + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); + if (cd.getKind() == J.ClassDeclaration.Kind.Type.Enum && cd.getType() != null && cd.getType().getOwningClass() != null) { + cd = SearchResult.found(cd); + } + return cd; + } + } } diff --git a/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java b/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java index 296a36766..55a341b71 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java +++ b/src/main/java/org/openrewrite/staticanalysis/NewStringBuilderBufferWithCharArgument.java @@ -24,12 +24,14 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; -import java.time.Duration; import java.util.Collections; import java.util.Set; public class NewStringBuilderBufferWithCharArgument extends Recipe { + public static final String STRING_BUILDER = "java.lang.StringBuilder"; + public static final String STRING_BUFFER = "java.lang.StringBuffer"; + @Override public String getDisplayName() { return "Change `StringBuilder` and `StringBuffer` character constructor argument to `String`"; @@ -40,11 +42,6 @@ public String getDescription() { return "Instantiating a `StringBuilder` or a `StringBuffer` with a `Character` results in the `int` representation of the character being used for the initial size."; } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public Set getTags() { return Collections.singleton("RSPEC-S1317"); @@ -52,15 +49,13 @@ public Set getTags() { @Override public TreeVisitor getVisitor() { - TreeVisitor condition = Preconditions.or(new UsesType<>("java.lang.StringBuilder", true), new UsesType<>("java.lang.StringBuffer", true)); + TreeVisitor condition = Preconditions.or(new UsesType<>(STRING_BUILDER, true), new UsesType<>(STRING_BUFFER, true)); return Preconditions.check(condition, new JavaIsoVisitor() { - private final JavaTemplate toString = JavaTemplate.builder("String.valueOf(#{any()})").build(); @Override public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { J.NewClass nc = super.visitNewClass(newClass, ctx); - if ((TypeUtils.isOfClassType(nc.getType(), "java.lang.StringBuilder") || - TypeUtils.isOfClassType(nc.getType(), "java.lang.StringBuffer"))) { + if ((TypeUtils.isOfClassType(nc.getType(), STRING_BUILDER) || TypeUtils.isOfClassType(nc.getType(), STRING_BUFFER))) { nc.getArguments(); if (nc.getArguments().get(0).getType() == JavaType.Primitive.Char) { nc = nc.withArguments(ListUtils.mapFirst(nc.getArguments(), arg -> { @@ -72,8 +67,8 @@ public J.NewClass visitNewClass(J.NewClass newClass, ExecutionContext ctx) { } return l; } else { - Cursor argCursor = new Cursor(getCursor(), arg); - return toString.apply(argCursor, arg.getCoordinates().replace(), arg); + return JavaTemplate.builder("String.valueOf(#{any()})").build() + .apply(new Cursor(getCursor(), arg), arg.getCoordinates().replace(), arg); } })); } diff --git a/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java b/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java index f50acdd49..a265a1de5 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoFinalizer.java @@ -16,11 +16,13 @@ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; 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.search.DeclaresMethod; import org.openrewrite.java.tree.J; import java.time.Duration; @@ -28,6 +30,9 @@ import java.util.Set; public class NoFinalizer extends Recipe { + + private static final MethodMatcher FINALIZER = new MethodMatcher("java.lang.Object finalize()", true); + @Override public String getDisplayName() { return "Remove `finalize()` method"; @@ -50,27 +55,21 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return new NoFinalizerVisitor(); - } - - private static class NoFinalizerVisitor extends JavaIsoVisitor { - private static final MethodMatcher FINALIZER = new MethodMatcher("java.lang.Object finalize()", true); - - @Override - public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { - J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); - cd = cd.withBody(cd.getBody().withStatements(ListUtils.map(cd.getBody().getStatements(), stmt -> { - if (stmt instanceof J.MethodDeclaration) { - if (FINALIZER.matches((J.MethodDeclaration) stmt, classDecl)) { - return null; + return Preconditions.check(new DeclaresMethod<>(FINALIZER), new JavaIsoVisitor() { + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { + J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx); + cd = cd.withBody(cd.getBody().withStatements(ListUtils.map(cd.getBody().getStatements(), stmt -> { + if (stmt instanceof J.MethodDeclaration) { + if (FINALIZER.matches((J.MethodDeclaration) stmt, classDecl)) { + return null; + } } - } - return stmt; - }))); - - return cd; - } + return stmt; + }))); + return cd; + } + }); } - } diff --git a/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java b/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java index f50fbe145..d62fcaafb 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoValueOfOnStringType.java @@ -59,9 +59,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesMethod<>(new MethodMatcher("java.lang.String valueOf(..)")), new JavaVisitor() { - private final JavaTemplate t = JavaTemplate.builder("#{any(java.lang.String)}").build(); - + return Preconditions.check(new UsesMethod<>(VALUE_OF), new JavaVisitor() { @Override public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { if (VALUE_OF.matches(method.getSelect())) { @@ -71,9 +69,9 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) J.MethodInvocation mi = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); if (VALUE_OF.matches(mi) && mi.getArguments().size() == 1) { Expression argument = mi.getArguments().get(0); - if (TypeUtils.isString(argument.getType()) || removeValueOfFromBinaryExpression(argument)) { - return t.apply(updateCursor(mi), mi.getCoordinates().replace(), argument); + return JavaTemplate.builder("#{any(java.lang.String)}").build() + .apply(updateCursor(mi), mi.getCoordinates().replace(), argument); } } return mi; @@ -87,7 +85,6 @@ public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) * @return True if the method can be removed. */ private boolean removeValueOfFromBinaryExpression(Expression argument) { - if (TypeUtils.asPrimitive(argument.getType()) != null) { J parent = getCursor().getParent() != null ? getCursor().getParent().firstEnclosing(J.class) : null; if (parent instanceof J.Binary) { diff --git a/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java b/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java index e75a49225..e2cbaefc7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java +++ b/src/main/java/org/openrewrite/staticanalysis/ObjectFinalizeCallsSuper.java @@ -25,7 +25,6 @@ import org.openrewrite.java.search.DeclaresMethod; import org.openrewrite.java.tree.J; -import java.time.Duration; import java.util.Collections; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; @@ -48,11 +47,6 @@ public Set getTags() { return Collections.singleton("RSPEC-S1114"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { return Preconditions.check(new DeclaresMethod<>(FINALIZE_METHOD_MATCHER), new JavaIsoVisitor() { @@ -72,20 +66,18 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, Ex private boolean hasSuperFinalizeMethodInvocation(J.MethodDeclaration md) { AtomicBoolean hasSuperFinalize = new AtomicBoolean(Boolean.FALSE); - new FindSuperFinalizeVisitor().visit(md, hasSuperFinalize); + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean exists) { + J.MethodInvocation mi = super.visitMethodInvocation(method, exists); + if (FINALIZE_METHOD_MATCHER.matches(mi)) { + exists.set(Boolean.TRUE); + } + return mi; + } + }.visit(md, hasSuperFinalize); return hasSuperFinalize.get(); } }); } - - private static class FindSuperFinalizeVisitor extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean exists) { - J.MethodInvocation mi = super.visitMethodInvocation(method, exists); - if (FINALIZE_METHOD_MATCHER.matches(mi)) { - exists.set(Boolean.TRUE); - } - return mi; - } - } } diff --git a/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java b/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java index 5b69c1ed5..1372e9c75 100644 --- a/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java +++ b/src/main/java/org/openrewrite/staticanalysis/PrimitiveWrapperClassConstructorToValueOf.java @@ -27,7 +27,6 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.TypeUtils; -import java.time.Duration; import java.util.Collections; import java.util.Set; @@ -48,11 +47,6 @@ public Set getTags() { return Collections.singleton("RSPEC-S2129"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { TreeVisitor condition = Preconditions.or( diff --git a/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java index 0247ca1d2..5705a0ada 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenameLocalVariablesToCamelCase.java @@ -15,13 +15,11 @@ */ package org.openrewrite.staticanalysis; -import org.openrewrite.Cursor; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; +import org.openrewrite.*; import org.openrewrite.java.tree.J; import org.openrewrite.java.tree.JavaSourceFile; import org.openrewrite.java.tree.JavaType; +import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; import java.time.Duration; import java.util.Collections; @@ -72,7 +70,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return new RenameToCamelCase() { + return Preconditions.check(Preconditions.not(new CSharpFileChecker<>()), new RenameToCamelCase() { @Override protected boolean shouldRename(Set hasNameSet, J.VariableDeclarations.NamedVariable variable, String toName) { if (toName.isEmpty() || !Character.isAlphabetic(toName.charAt(0))) { @@ -191,6 +189,6 @@ private boolean isAvailableIdentifier(String identifier, J context, Set } return true; } - }; + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java index 3b421a594..f508cafd7 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java +++ b/src/main/java/org/openrewrite/staticanalysis/RenamePrivateFieldsToCamelCase.java @@ -24,6 +24,7 @@ import org.openrewrite.java.tree.JavaType; import org.openrewrite.java.tree.Space; import org.openrewrite.marker.Markers; +import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; import java.time.Duration; import java.util.*; @@ -72,7 +73,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return new RenameToCamelCase() { + return Preconditions.check(Preconditions.not(new CSharpFileChecker<>()), new RenameToCamelCase() { @Override protected boolean shouldRename(Set hasNameSet, J.VariableDeclarations.NamedVariable variable, String toName) { if (toName.isEmpty() || !Character.isAlphabetic(toName.charAt(0))) { @@ -158,6 +159,6 @@ public J.VariableDeclarations visitVariableDeclarations(J.VariableDeclarations m private Cursor getCursorToParentScope(Cursor cursor) { return cursor.dropParentUntil(is -> is instanceof J.ClassDeclaration || is instanceof J.Block || is instanceof SourceFile); } - }; + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java index 92b61a331..cd00bce7a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceStringBuilderWithString.java @@ -54,118 +54,111 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return Preconditions.check( - Preconditions.and( - new UsesMethod<>(STRING_BUILDER_APPEND), - new UsesMethod<>(STRING_BUILDER_TO_STRING)), - new StringBuilderToAppendVisitor() - ); - } + return Preconditions.check(Preconditions.and(new UsesMethod<>(STRING_BUILDER_APPEND), new UsesMethod<>(STRING_BUILDER_TO_STRING)), new JavaVisitor() { + @Override + public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); + + if (STRING_BUILDER_TO_STRING.matches(method)) { + List methodCallsChain = new ArrayList<>(); + List arguments = new ArrayList<>(); + boolean isFlattenable = flatMethodInvocationChain(method, methodCallsChain, arguments); + if (!isFlattenable || arguments.isEmpty()) { + return m; + } - private static class StringBuilderToAppendVisitor extends JavaVisitor { - @Override - public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, ctx); - - if (STRING_BUILDER_TO_STRING.matches(method)) { - List methodCallsChain = new ArrayList<>(); - List arguments = new ArrayList<>(); - boolean isFlattenable = flatMethodInvocationChain(method, methodCallsChain, arguments); - if (!isFlattenable || arguments.isEmpty()) { - return m; - } + Collections.reverse(arguments); + arguments = adjustExpressions(method, arguments); - Collections.reverse(arguments); - arguments = adjustExpressions(method, arguments); + Expression additive = ChainStringBuilderAppendCalls.additiveExpression(arguments).withPrefix(method.getPrefix()); + if (isAMethodSelect(method)) { + additive = new J.Parentheses<>(randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(additive)); + } - Expression additive = ChainStringBuilderAppendCalls.additiveExpression(arguments).withPrefix(method.getPrefix()); - if (isAMethodSelect(method)) { - additive = new J.Parentheses<>(randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(additive)); + return additive; } - - return additive; + return m; } - return m; - } - // Check if a method call is a "select" of another method call - private boolean isAMethodSelect(J.MethodInvocation method) { - Cursor parent = getCursor().getParent(2); // 2 means skip right padded cursor - if (parent == null || !(parent.getValue() instanceof J.MethodInvocation)) { - return false; + // Check if a method call is a "select" of another method call + private boolean isAMethodSelect(J.MethodInvocation method) { + Cursor parent = getCursor().getParent(2); // 2 means skip right padded cursor + if (parent == null || !(parent.getValue() instanceof J.MethodInvocation)) { + return false; + } + return ((J.MethodInvocation) parent.getValue()).getSelect() == method; } - return ((J.MethodInvocation) parent.getValue()).getSelect() == method; - } - private J.Literal toStringLiteral(J.Literal input) { - if (input.getType() == JavaType.Primitive.String) { - return input; + private J.Literal toStringLiteral(J.Literal input) { + if (input.getType() == JavaType.Primitive.String) { + return input; + } + + String value = input.getValueSource(); + return new J.Literal(randomId(), Space.EMPTY, Markers.EMPTY, value, + "\"" + value + "\"", null, JavaType.Primitive.String); } - String value = input.getValueSource(); - return new J.Literal(randomId(), Space.EMPTY, Markers.EMPTY, value, - "\"" + value + "\"", null, JavaType.Primitive.String); - } - - private List adjustExpressions(J.MethodInvocation method, List arguments) { - return ListUtils.map(arguments, (i, arg) -> { - if (i == 0) { - if (!TypeUtils.isString(arg.getType())) { - if (arg instanceof J.Literal) { - return toStringLiteral((J.Literal) arg); - } else { - return JavaTemplate.builder("String.valueOf(#{any()})").build() - .apply(getCursor(), method.getCoordinates().replace(), arg) - .withPrefix(arg.getPrefix()); + private List adjustExpressions(J.MethodInvocation method, List arguments) { + return ListUtils.map(arguments, (i, arg) -> { + if (i == 0) { + if (!TypeUtils.isString(arg.getType())) { + if (arg instanceof J.Literal) { + return toStringLiteral((J.Literal) arg); + } else { + return JavaTemplate.builder("String.valueOf(#{any()})").build() + .apply(getCursor(), method.getCoordinates().replace(), arg) + .withPrefix(arg.getPrefix()); + } } + } else if (!(arg instanceof J.Identifier || arg instanceof J.Literal || arg instanceof J.MethodInvocation)) { + return new J.Parentheses<>(randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(arg)); } - } else if (!(arg instanceof J.Identifier || arg instanceof J.Literal || arg instanceof J.MethodInvocation)) { - return new J.Parentheses<>(randomId(), Space.EMPTY, Markers.EMPTY, JRightPadded.build(arg)); - } - return arg; - }); - } - - /** - * Return true if the method calls chain is like "new StringBuilder().append("A")....append("B");" - * - * @param method a StringBuilder.toString() method call - * @param methodChain output methods chain - * @param arguments output expression list to be chained by '+'. - */ - private boolean flatMethodInvocationChain(J.MethodInvocation method, List methodChain, List arguments) { - Expression select = method.getSelect(); - while (select != null) { - methodChain.add(select); - if (!(select instanceof J.MethodInvocation)) { - break; - } + return arg; + }); + } - J.MethodInvocation selectMethod = (J.MethodInvocation) select; - select = selectMethod.getSelect(); + /** + * Return true if the method calls chain is like "new StringBuilder().append("A")....append("B");" + * + * @param method a StringBuilder.toString() method call + * @param methodChain output methods chain + * @param arguments output expression list to be chained by '+'. + */ + private boolean flatMethodInvocationChain(J.MethodInvocation method, List methodChain, List arguments) { + Expression select = method.getSelect(); + while (select != null) { + methodChain.add(select); + if (!(select instanceof J.MethodInvocation)) { + break; + } - if (!STRING_BUILDER_APPEND.matches(selectMethod)) { - return false; - } + J.MethodInvocation selectMethod = (J.MethodInvocation) select; + select = selectMethod.getSelect(); - List args = selectMethod.getArguments(); - if (args.size() != 1) { - return false; - } else { - arguments.add(args.get(0)); + if (!STRING_BUILDER_APPEND.matches(selectMethod)) { + return false; + } + + List args = selectMethod.getArguments(); + if (args.size() != 1) { + return false; + } else { + arguments.add(args.get(0)); + } } - } - if (select instanceof J.NewClass && - ((J.NewClass) select).getClazz() != null && - TypeUtils.isOfClassType(((J.NewClass) select).getClazz().getType(), "java.lang.StringBuilder")) { - J.NewClass nc = (J.NewClass) select; - if (nc.getArguments().size() == 1 && TypeUtils.isString(nc.getArguments().get(0).getType())) { - arguments.add(nc.getArguments().get(0)); + if (select instanceof J.NewClass && + ((J.NewClass) select).getClazz() != null && + TypeUtils.isOfClassType(((J.NewClass) select).getClazz().getType(), "java.lang.StringBuilder")) { + J.NewClass nc = (J.NewClass) select; + if (nc.getArguments().size() == 1 && TypeUtils.isString(nc.getArguments().get(0).getType())) { + arguments.add(nc.getArguments().get(0)); + } + return true; } - return true; + return false; } - return false; - } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java b/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java index a74847846..f9a427824 100644 --- a/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java +++ b/src/main/java/org/openrewrite/staticanalysis/StringLiteralEquality.java @@ -22,7 +22,6 @@ import org.openrewrite.marker.Markers; import org.openrewrite.staticanalysis.java.JavaFileChecker; -import java.time.Duration; import java.util.Collections; import java.util.Set; @@ -48,21 +47,27 @@ public Set getTags() { return Collections.singleton("RSPEC-S4973"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { - // Don't change for Kotlin because In Kotlin, `==` means structural equality, so it's redundant to call equals(). - // see https://rules.sonarsource.com/kotlin/RSPEC-S6519/ - TreeVisitor preconditions = Preconditions.and( - new JavaFileChecker<>(), - new UsesType<>("java.lang.String", false)); - return Preconditions.check(preconditions, new JavaVisitor() { - private final JavaType.FullyQualified TYPE_STRING = TypeUtils.asFullyQualified(JavaType.buildType("java.lang.String")); - private final JavaType TYPE_OBJECT = JavaType.buildType("java.lang.Object"); + // Don't change for other language than Java, because other languages uses different constructs. + // For example, in Kotlin `==` means structural equality, so it is redundant to call equals(). + return Preconditions.check(Preconditions.and(new JavaFileChecker<>(), new UsesType<>("java.lang.String", false)), new JavaVisitor() { + @Override + public J visitBinary(J.Binary binary, ExecutionContext ctx) { + if (isStringLiteral(binary.getLeft()) || isStringLiteral(binary.getRight())) { + J after = null; + if (binary.getOperator() == J.Binary.Type.Equal) { + after = asEqualsMethodInvocation(binary).withPrefix(binary.getPrefix()); + } else if (binary.getOperator() == J.Binary.Type.NotEqual) { + after = asNegatedUnary(asEqualsMethodInvocation(binary)).withPrefix(binary.getPrefix()); + } + if (after != null) { + doAfterVisit(new EqualsAvoidsNull().getVisitor()); + return after; + } + } + return super.visitBinary(binary, ctx); + } private boolean isStringLiteral(Expression expression) { return expression instanceof J.Literal && TypeUtils.isString(((J.Literal) expression).getType()); @@ -85,11 +90,11 @@ private J.MethodInvocation asEqualsMethodInvocation(J.Binary binary) { new JavaType.Method( null, Flag.Public.getBitMask(), - TYPE_STRING, + TypeUtils.asFullyQualified(JavaType.buildType("java.lang.String")), "equals", JavaType.Primitive.Boolean, singletonList("o"), - singletonList(TYPE_OBJECT), + singletonList(JavaType.buildType("java.lang.Object")), null, null, null ) ); @@ -110,23 +115,6 @@ private J.Unary asNegatedUnary(J.MethodInvocation mi) { JavaType.Primitive.Boolean ); } - - @Override - public J visitBinary(J.Binary binary, ExecutionContext ctx) { - if (isStringLiteral(binary.getLeft()) || isStringLiteral(binary.getRight())) { - J after = null; - if (binary.getOperator() == J.Binary.Type.Equal) { - after = asEqualsMethodInvocation(binary).withPrefix(binary.getPrefix()); - } else if (binary.getOperator() == J.Binary.Type.NotEqual) { - after = asNegatedUnary(asEqualsMethodInvocation(binary)).withPrefix(binary.getPrefix()); - } - if (after != null) { - doAfterVisit(new EqualsAvoidsNull().getVisitor()); - return after; - } - } - return super.visitBinary(binary, ctx); - } }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java b/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java index d4faa321c..b9e0caa80 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java +++ b/src/main/java/org/openrewrite/staticanalysis/UseJavaStyleArrayDeclarations.java @@ -15,8 +15,8 @@ */ package org.openrewrite.staticanalysis; -import org.jspecify.annotations.Nullable; import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; @@ -24,8 +24,8 @@ import org.openrewrite.java.tree.J.VariableDeclarations; import org.openrewrite.java.tree.JLeftPadded; import org.openrewrite.java.tree.Space; +import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; -import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.Set; @@ -47,14 +47,9 @@ public Set getTags() { return Collections.singleton("RSPEC-S1197"); } - @Override - public @Nullable Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { - return new JavaIsoVisitor() { + return Preconditions.check(Preconditions.not(new CSharpFileChecker<>()), new JavaIsoVisitor() { @Override public VariableDeclarations visitVariableDeclarations(VariableDeclarations multiVariable, ExecutionContext ctx) { VariableDeclarations varDecls = super.visitVariableDeclarations(multiVariable, ctx); @@ -74,6 +69,6 @@ public VariableDeclarations.NamedVariable visitVariable(VariableDeclarations.Nam } return nv; } - }; + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java b/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java index b9a633fb6..f92d0db19 100644 --- a/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java +++ b/src/main/java/org/openrewrite/staticanalysis/WriteOctalValuesAsDecimal.java @@ -16,12 +16,13 @@ package org.openrewrite.staticanalysis; import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; import org.openrewrite.Recipe; import org.openrewrite.TreeVisitor; import org.openrewrite.java.JavaVisitor; import org.openrewrite.java.tree.J; +import org.openrewrite.staticanalysis.csharp.CSharpFileChecker; -import java.time.Duration; import java.util.Collections; import java.util.Set; @@ -41,14 +42,9 @@ public Set getTags() { return Collections.singleton("RSPEC-S1314"); } - @Override - public Duration getEstimatedEffortPerOccurrence() { - return Duration.ofMinutes(5); - } - @Override public TreeVisitor getVisitor() { - return new JavaVisitor() { + return Preconditions.check(Preconditions.not(new CSharpFileChecker<>()), new JavaVisitor() { @Override public J visitLiteral(J.Literal literal, ExecutionContext ctx) { String src = literal.getValueSource(); @@ -67,6 +63,6 @@ public J visitLiteral(J.Literal literal, ExecutionContext ctx) { } return super.visitLiteral(literal, ctx); } - }; + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/csharp/CSharpFileChecker.java b/src/main/java/org/openrewrite/staticanalysis/csharp/CSharpFileChecker.java new file mode 100644 index 000000000..0e9ec85a5 --- /dev/null +++ b/src/main/java/org/openrewrite/staticanalysis/csharp/CSharpFileChecker.java @@ -0,0 +1,35 @@ +/* + * 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.csharp; + +import org.jspecify.annotations.Nullable; +import org.openrewrite.Tree; +import org.openrewrite.TreeVisitor; +import org.openrewrite.csharp.tree.Cs; +import org.openrewrite.marker.SearchResult; + +/** + * Add a search marker if vising a CSharp file + */ +public class CSharpFileChecker

    extends TreeVisitor { + @Override + public @Nullable Tree visit(@Nullable Tree tree, P p) { + if (tree instanceof Cs.CompilationUnit) { + return SearchResult.found(tree); + } + return tree; + } +} From 0d8d8739b8ba168bc8c364ca7a3308c1d7e378a4 Mon Sep 17 00:00:00 2001 From: punkratz312 Date: Fri, 29 Nov 2024 21:57:49 +0100 Subject: [PATCH 183/183] `EqualsAvoidsNull` should flip arguments for constants (#398) * replaceMethodArgs * replaceMethodArgs * replaceMethodArgs * replaceMethodArgs * replaceMethodArg * undo * undo * add String foo, String bar * multiple * add replaceMethodArg * Also place field accesses first * Check flags on fieldAccess.name.fieldType * Add test showing no change when not static & final * Also support static imports * Remove unused import --------- Co-authored-by: Vincent Potucek Co-authored-by: Tim te Beek --- .../staticanalysis/EqualsAvoidsNull.java | 10 +- .../EqualsAvoidsNullVisitor.java | 75 +++++----- .../staticanalysis/EqualsAvoidsNullTest.java | 131 +++++++++++++++++- 3 files changed, 179 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java index fede66cc0..bc7cc91ab 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNull.java @@ -18,6 +18,7 @@ import org.jspecify.annotations.Nullable; import org.openrewrite.*; import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.search.UsesMethod; import org.openrewrite.java.style.Checkstyle; import org.openrewrite.java.style.EqualsAvoidsNullStyle; import org.openrewrite.java.tree.J; @@ -53,7 +54,7 @@ public Duration getEstimatedEffortPerOccurrence() { @Override public TreeVisitor getVisitor() { - return new JavaIsoVisitor() { + JavaIsoVisitor replacementVisitor = new JavaIsoVisitor() { @Override public J visit(@Nullable Tree tree, ExecutionContext ctx) { if (tree instanceof JavaSourceFile) { @@ -68,5 +69,12 @@ public J visit(@Nullable Tree tree, ExecutionContext ctx) { return (J) tree; } }; + return Preconditions.check( + Preconditions.or( + new UsesMethod<>("java.lang.String equals*(..)"), + new UsesMethod<>("java.lang.String co*(..)") + ), + replacementVisitor + ); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java index 14864c924..0f4762d7e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsAvoidsNullVisitor.java @@ -57,16 +57,37 @@ public class EqualsAvoidsNullVisitor

    extends JavaVisitor

    { @Override public J visitMethodInvocation(J.MethodInvocation method, P p) { J.MethodInvocation m = (J.MethodInvocation) super.visitMethodInvocation(method, p); - if (m.getSelect() != null && - !(m.getSelect() instanceof J.Literal) && - !m.getArguments().isEmpty() && - m.getArguments().get(0) instanceof J.Literal && - isStringComparisonMethod(m)) { - return literalsFirstInComparisonsBinaryCheck(m, getCursor().getParentTreeCursor().getValue()); + if (m.getSelect() != null && !(m.getSelect() instanceof J.Literal) && + isStringComparisonMethod(m) && hasCompatibleArgument(m)) { + + maybeHandleParentBinary(m); + + Expression firstArgument = m.getArguments().get(0); + return firstArgument.getType() == JavaType.Primitive.Null ? + literalsFirstInComparisonsNull(m, firstArgument) : + literalsFirstInComparisons(m, firstArgument); } return m; } + private boolean hasCompatibleArgument(J.MethodInvocation m) { + if (m.getArguments().isEmpty()) { + return false; + } + Expression firstArgument = m.getArguments().get(0); + if (firstArgument instanceof J.Literal) { + return true; + } + if (firstArgument instanceof J.FieldAccess) { + firstArgument = ((J.FieldAccess) firstArgument).getName(); + } + if (firstArgument instanceof J.Identifier) { + JavaType.Variable fieldType = ((J.Identifier) firstArgument).getFieldType(); + return fieldType != null && fieldType.hasFlags(Flag.Static, Flag.Final); + } + return false; + } + private boolean isStringComparisonMethod(J.MethodInvocation methodInvocation) { return EQUALS.matches(methodInvocation) || !style.getIgnoreEqualsIgnoreCase() && @@ -76,17 +97,26 @@ private boolean isStringComparisonMethod(J.MethodInvocation methodInvocation) { CONTENT_EQUALS.matches(methodInvocation); } - private Expression literalsFirstInComparisonsBinaryCheck(J.MethodInvocation m, P parent) { + private void maybeHandleParentBinary(J.MethodInvocation m) { + P parent = getCursor().getParentTreeCursor().getValue(); if (parent instanceof J.Binary) { - handleBinaryExpression(m, (J.Binary) parent); + if (((J.Binary) parent).getOperator() == J.Binary.Type.And && ((J.Binary) parent).getLeft() instanceof J.Binary) { + J.Binary potentialNullCheck = (J.Binary) ((J.Binary) parent).getLeft(); + if (isNullLiteral(potentialNullCheck.getLeft()) && matchesSelect(potentialNullCheck.getRight(), requireNonNull(m.getSelect())) || + isNullLiteral(potentialNullCheck.getRight()) && matchesSelect(potentialNullCheck.getLeft(), requireNonNull(m.getSelect()))) { + doAfterVisit(new RemoveUnnecessaryNullCheck<>((J.Binary) parent)); + } + } } - return getExpression(m, m.getArguments().get(0)); } - private static Expression getExpression(J.MethodInvocation m, Expression firstArgument) { - return firstArgument.getType() == JavaType.Primitive.Null ? - literalsFirstInComparisonsNull(m, firstArgument) : - literalsFirstInComparisons(m, firstArgument); + private boolean isNullLiteral(Expression expression) { + return expression instanceof J.Literal && ((J.Literal) expression).getType() == JavaType.Primitive.Null; + } + + private boolean matchesSelect(Expression expression, Expression select) { + return expression.printTrimmed(getCursor()).replaceAll("\\s", "") + .equals(select.printTrimmed(getCursor()).replaceAll("\\s", "")); } private static J.Binary literalsFirstInComparisonsNull(J.MethodInvocation m, Expression firstArgument) { @@ -104,25 +134,6 @@ private static J.MethodInvocation literalsFirstInComparisons(J.MethodInvocation .withArguments(singletonList(m.getSelect().withPrefix(Space.EMPTY))); } - private void handleBinaryExpression(J.MethodInvocation m, J.Binary binary) { - if (binary.getOperator() == J.Binary.Type.And && binary.getLeft() instanceof J.Binary) { - J.Binary potentialNullCheck = (J.Binary) binary.getLeft(); - if (isNullLiteral(potentialNullCheck.getLeft()) && matchesSelect(potentialNullCheck.getRight(), requireNonNull(m.getSelect())) || - isNullLiteral(potentialNullCheck.getRight()) && matchesSelect(potentialNullCheck.getLeft(), requireNonNull(m.getSelect()))) { - doAfterVisit(new RemoveUnnecessaryNullCheck<>(binary)); - } - } - } - - private boolean isNullLiteral(Expression expression) { - return expression instanceof J.Literal && ((J.Literal) expression).getType() == JavaType.Primitive.Null; - } - - private boolean matchesSelect(Expression expression, Expression select) { - return expression.printTrimmed(getCursor()).replaceAll("\\s", "") - .equals(select.printTrimmed(getCursor()).replaceAll("\\s", "")); - } - private static class RemoveUnnecessaryNullCheck

    extends JavaVisitor

    { private final J.Binary scope; diff --git a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java index 1dde17457..60e0fb456 100644 --- a/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java +++ b/src/test/java/org/openrewrite/staticanalysis/EqualsAvoidsNullTest.java @@ -15,8 +15,10 @@ */ package org.openrewrite.staticanalysis; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; +import org.openrewrite.Issue; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -94,8 +96,8 @@ public class A { @Test void nullLiteral() { rewriteRun( - //language=java - java(""" + //language=java + java(""" public class A { void foo(String s) { if(s.equals(null)) { @@ -103,8 +105,7 @@ void foo(String s) { } } """, - """ - + """ public class A { void foo(String s) { if(s == null) { @@ -114,4 +115,126 @@ void foo(String s) { """) ); } + + @Nested + class ReplaceConstantMethodArg { + + @Issue("https://github.com/openrewrite/rewrite-static-analysis/pull/398") + @Test + void one() { + rewriteRun( + // language=java + java( + """ + public class Constants { + public static final String FOO = "FOO"; + } + class A { + private boolean isFoo(String foo) { + return foo.contentEquals(Constants.FOO); + } + } + """, + """ + public class Constants { + public static final String FOO = "FOO"; + } + class A { + private boolean isFoo(String foo) { + return Constants.FOO.contentEquals(foo); + } + } + """ + ) + ); + } + + @Test + void staticImport() { + rewriteRun( + // language=java + java( + """ + package c; + public class Constants { + public static final String FOO = "FOO"; + } + """ + ), + // language=java + java( + """ + import static c.Constants.FOO; + class A { + private boolean isFoo(String foo) { + return foo.contentEquals(FOO); + } + } + """, + """ + import static c.Constants.FOO; + class A { + private boolean isFoo(String foo) { + return FOO.contentEquals(foo); + } + } + """ + ) + ); + } + + @Test + void multiple() { + rewriteRun( + //language=java + java( + """ + public class Constants { + public static final String FOO = "FOO"; + } + class A { + private boolean isFoo(String foo, String bar) { + return foo.contentEquals(Constants.FOO) + || bar.compareToIgnoreCase(Constants.FOO); + } + } + """, + """ + public class Constants { + public static final String FOO = "FOO"; + } + class A { + private boolean isFoo(String foo, String bar) { + return Constants.FOO.contentEquals(foo) + || Constants.FOO.compareToIgnoreCase(bar); + } + } + """ + ) + ); + } + + @Test + void nonStaticNonFinalNoChange() { + rewriteRun( + // language=java + java( + """ + public class Constants { + public final String FOO = "FOO"; + public static String BAR = "BAR"; + } + class A { + private boolean isFoo(String foo) { + return foo.contentEquals(new Constants().FOO); + } + private boolean isBar(String bar) { + return bar.contentEquals(Constants.BAR); + } + } + """ + ) + ); + } + } }