diff --git a/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java b/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java index 9c29963c8..4f493b123 100644 --- a/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java +++ b/src/main/java/org/openrewrite/staticanalysis/EqualsToContentEquals.java @@ -30,6 +30,9 @@ import static java.util.Collections.singletonList; public class EqualsToContentEquals extends Recipe { + private static final MethodMatcher EQUALS_MATCHER = new MethodMatcher("String equals(Object)"); + private static final MethodMatcher TOSTRING_MATCHER = new MethodMatcher("java.lang.* toString()"); + private static final TreeVisitor PRECONDITION = Preconditions.or( new UsesType<>("java.lang.CharSequence", false), new UsesType<>("java.lang.StringBuffer", false), @@ -44,31 +47,26 @@ public class EqualsToContentEquals extends Recipe { @Override public TreeVisitor getVisitor() { - return Preconditions.check(PRECONDITION, new EqualsToContentEqualsVisitor()); - } - - private static class EqualsToContentEqualsVisitor extends JavaIsoVisitor { - private static final MethodMatcher EQUALS_MATCHER = new MethodMatcher("String equals(Object)"); - private static final MethodMatcher TOSTRING_MATCHER = new MethodMatcher("java.lang.* toString()"); - - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) { - J.MethodInvocation m = super.visitMethodInvocation(mi, ctx); - if (!EQUALS_MATCHER.matches(m)) { - return m; - } - Expression equalsArgument = m.getArguments().get(0); - if (!TOSTRING_MATCHER.matches(equalsArgument)) { - return m; - } - J.MethodInvocation inv = (J.MethodInvocation) equalsArgument; - Expression toStringSelect = inv.getSelect(); - if (toStringSelect == null || !TypeUtils.isAssignableTo("java.lang.CharSequence", toStringSelect.getType())) { - return m; + return Preconditions.check(PRECONDITION, new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) { + J.MethodInvocation m = super.visitMethodInvocation(mi, ctx); + if (!EQUALS_MATCHER.matches(m)) { + return m; + } + Expression equalsArgument = m.getArguments().get(0); + if (!TOSTRING_MATCHER.matches(equalsArgument)) { + return m; + } + J.MethodInvocation inv = (J.MethodInvocation) equalsArgument; + Expression toStringSelect = inv.getSelect(); + if (toStringSelect == null || !TypeUtils.isAssignableTo("java.lang.CharSequence", toStringSelect.getType())) { + return m; + } + // Strip out the toString() on the argument and replace with contentEquals + return m.withArguments(singletonList(toStringSelect)) + .withName(m.getName().withSimpleName("contentEquals")); } - // Strip out the toString() on the argument and replace with contentEquals - return m.withArguments(singletonList(toStringSelect)) - .withName(m.getName().withSimpleName("contentEquals")); - } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java index 168871b79..8751e448a 100644 --- a/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java +++ b/src/main/java/org/openrewrite/staticanalysis/NoPrimitiveWrappersForToStringOrCompareTo.java @@ -36,6 +36,9 @@ import static java.util.Collections.singleton; public class NoPrimitiveWrappersForToStringOrCompareTo extends Recipe { + private static final MethodMatcher VALUE_OF_NUMBER_MATCHER = new MethodMatcher("java.lang.Number valueOf(*)", true); + private static final MethodMatcher VALUE_OF_BOOLEAN_MATCHER = new MethodMatcher("java.lang.Boolean valueOf(*)", true); + private static final MethodMatcher NUMBER_TO_STRING_MATCHER = new MethodMatcher("java.lang.Number toString()", true); private static final MethodMatcher BOOLEAN_TO_STRING_MATCHER = new MethodMatcher("java.lang.Boolean toString()", true); @@ -63,63 +66,57 @@ public TreeVisitor getVisitor() { new UsesMethod<>(BOOLEAN_COMPARE_TO_MATCHER), new UsesMethod<>(BOOLEAN_TO_STRING_MATCHER) ), - new NoPrimitiveWrapperVisitor() - ); - } - - private static class NoPrimitiveWrapperVisitor extends JavaIsoVisitor { - - private static final MethodMatcher VALUE_OF_NUMBER_MATCHER = new MethodMatcher("java.lang.Number valueOf(*)", true); - private static final MethodMatcher VALUE_OF_BOOLEAN_MATCHER = new MethodMatcher("java.lang.Boolean valueOf(*)", true); - - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { - J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); - JavaType.Class clazz = mi.getMethodType() != null ? TypeUtils.asClass(mi.getMethodType().getDeclaringType()) : null; - if (clazz != null && "java.lang".equals(clazz.getPackageName())) { - if (NUMBER_TO_STRING_MATCHER.matches(mi) || BOOLEAN_TO_STRING_MATCHER.matches(mi)) { - Expression arg = null; - if (mi.getSelect() instanceof J.NewClass) { - arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments()); - } else if (mi.getSelect() instanceof J.MethodInvocation) { - J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect(); - if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) { - arg = getSingleArg(selectMethod.getArguments()); + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); + JavaType.Class clazz = mi.getMethodType() != null ? TypeUtils.asClass(mi.getMethodType().getDeclaringType()) : null; + if (clazz != null && "java.lang".equals(clazz.getPackageName())) { + if (NUMBER_TO_STRING_MATCHER.matches(mi) || BOOLEAN_TO_STRING_MATCHER.matches(mi)) { + Expression arg = null; + if (mi.getSelect() instanceof J.NewClass) { + arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments()); + } else if (mi.getSelect() instanceof J.MethodInvocation) { + J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect(); + if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) { + arg = getSingleArg(selectMethod.getArguments()); + } } - } - if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) { - JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType(); - 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)); - } - } else if (NUMBER_COMPARE_TO_MATCHER.matches(mi) || BOOLEAN_COMPARE_TO_MATCHER.matches(mi)) { - Expression arg = null; - if (mi.getSelect() instanceof J.NewClass) { - arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments()); - } else if (mi.getSelect() instanceof J.MethodInvocation) { - J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect(); - if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) { - arg = getSingleArg(selectMethod.getArguments()); + if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) { + JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType(); + 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)); + } + } else if (NUMBER_COMPARE_TO_MATCHER.matches(mi) || BOOLEAN_COMPARE_TO_MATCHER.matches(mi)) { + Expression arg = null; + if (mi.getSelect() instanceof J.NewClass) { + arg = getSingleArg(((J.NewClass) mi.getSelect()).getArguments()); + } else if (mi.getSelect() instanceof J.MethodInvocation) { + J.MethodInvocation selectMethod = (J.MethodInvocation) mi.getSelect(); + if (VALUE_OF_NUMBER_MATCHER.matches(selectMethod) || VALUE_OF_BOOLEAN_MATCHER.matches(selectMethod)) { + arg = getSingleArg(selectMethod.getArguments()); + } } - } - if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) { - JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType(); - 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); + if (arg != null && !TypeUtils.isString(arg.getType()) && mi.getSelect() != null) { + JavaType.FullyQualified fq = mi.getMethodType().getDeclaringType(); + 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); + } } } + return mi; } - return mi; - } - private @Nullable Expression getSingleArg(@Nullable List args) { - if (args != null && args.size() == 1 && !(args.get(0) instanceof J.Empty)) { - return args.get(0); + private @Nullable Expression getSingleArg(@Nullable List args) { + if (args != null && args.size() == 1 && !(args.get(0) instanceof J.Empty)) { + return args.get(0); + } + return null; } - return null; } + ); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java b/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java index d1e485bb7..15d929abd 100644 --- a/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java +++ b/src/main/java/org/openrewrite/staticanalysis/RemoveHashCodeCallsFromArrayInstances.java @@ -49,26 +49,24 @@ public class RemoveHashCodeCallsFromArrayInstances extends Recipe { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesMethod<>(HASHCODE_MATCHER), new RemoveHashCodeCallsFromArrayInstancesVisitor()); - } - - private static class RemoveHashCodeCallsFromArrayInstancesVisitor extends JavaIsoVisitor { - @Override - public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) { - J.MethodInvocation mi = super.visitMethodInvocation(methodInvocation, ctx); + return Preconditions.check(new UsesMethod<>(HASHCODE_MATCHER), new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(methodInvocation, ctx); - if (HASHCODE_MATCHER.matches(mi)) { - Expression select = mi.getSelect(); - if (select != null && select.getType() instanceof JavaType.Array) { - maybeAddImport("java.util.Arrays"); - return JavaTemplate.builder("Arrays.hashCode(#{anyArray(java.lang.Object)})") - .imports("java.util.Arrays") - .build() - .apply(getCursor(), mi.getCoordinates().replace(), select); + if (HASHCODE_MATCHER.matches(mi)) { + Expression select = mi.getSelect(); + if (select != null && select.getType() instanceof JavaType.Array) { + maybeAddImport("java.util.Arrays"); + return JavaTemplate.builder("Arrays.hashCode(#{anyArray(java.lang.Object)})") + .imports("java.util.Arrays") + .build() + .apply(getCursor(), mi.getCoordinates().replace(), select); + } } - } - return mi; - } + return mi; + } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/ReplaceTextBlockWithString.java b/src/main/java/org/openrewrite/staticanalysis/ReplaceTextBlockWithString.java index 9af9550fe..c0f069425 100644 --- a/src/main/java/org/openrewrite/staticanalysis/ReplaceTextBlockWithString.java +++ b/src/main/java/org/openrewrite/staticanalysis/ReplaceTextBlockWithString.java @@ -50,58 +50,56 @@ public class ReplaceTextBlockWithString extends Recipe { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesJavaVersion<>(13), new ReplaceTextBlockWithStringVisitor()); - } - - private static class ReplaceTextBlockWithStringVisitor extends JavaVisitor { + return Preconditions.check(new UsesJavaVersion<>(13), new JavaVisitor() { - @Override - public @Nullable J visitLiteral(J.Literal literal, ExecutionContext ctx) { - if (literal.getType() == Primitive.String && - literal.getValue() != null && - literal.getValueSource() != null && - literal.getValueSource().startsWith("\"\"\"")) { - // Split the literal into lines, including trailing empty lines - String[] lines = ((String) literal.getValue()).split("\n", -1); - // Add trailing "\n" to each line but the last one - // If there is only one line and it's empty, then add "\n" to it as well - boolean lastLineIsEmpty = lines[lines.length - 1].isEmpty(); - int n = lastLineIsEmpty && lines.length == 1 ? 1 : lines.length - 1; - for (int i = 0; i < n; i++) { - lines[i] += "\\n"; - } - // Take all lines except the last one if it's empty - // If there is only one line and it's empty, take it as well - int linesNumber = !lastLineIsEmpty || lines.length == 1 ? lines.length : lines.length - 1; - Expression[] literals = new Expression[linesNumber]; - // Add a prefix (possibly containing a comment) of the original literal - literals[0] = toLiteral(lines[0]).withPrefix(literal.getPrefix()); - // Add newlines before rest string literals - for (int i = 1; i < linesNumber; i++) { - literals[i] = toLiteral(lines[i]).withPrefix(Space.build("\n", emptyList())); + @Override + public @Nullable J visitLiteral(J.Literal literal, ExecutionContext ctx) { + if (literal.getType() == Primitive.String && + literal.getValue() != null && + literal.getValueSource() != null && + literal.getValueSource().startsWith("\"\"\"")) { + // Split the literal into lines, including trailing empty lines + String[] lines = ((String) literal.getValue()).split("\n", -1); + // Add trailing "\n" to each line but the last one + // If there is only one line and it's empty, then add "\n" to it as well + boolean lastLineIsEmpty = lines[lines.length - 1].isEmpty(); + int n = lastLineIsEmpty && lines.length == 1 ? 1 : lines.length - 1; + for (int i = 0; i < n; i++) { + lines[i] += "\\n"; + } + // Take all lines except the last one if it's empty + // If there is only one line and it's empty, take it as well + int linesNumber = !lastLineIsEmpty || lines.length == 1 ? lines.length : lines.length - 1; + Expression[] literals = new Expression[linesNumber]; + // Add a prefix (possibly containing a comment) of the original literal + literals[0] = toLiteral(lines[0]).withPrefix(literal.getPrefix()); + // Add newlines before rest string literals + for (int i = 1; i < linesNumber; i++) { + literals[i] = toLiteral(lines[i]).withPrefix(Space.build("\n", emptyList())); + } + // Format the resulting expression + Expression j = ChainStringBuilderAppendCalls.additiveExpression(literals); + //noinspection DataFlowIssue + return j == null ? null : autoFormat(j, ctx); } - // Format the resulting expression - Expression j = ChainStringBuilderAppendCalls.additiveExpression(literals); - //noinspection DataFlowIssue - return j == null ? null : autoFormat(j, ctx); + return literal; } - return literal; - } - private J.Literal toLiteral(String str) { - return new J.Literal( - Tree.randomId(), - Space.EMPTY, - Markers.EMPTY, - str, - quote(str), - emptyList(), - Primitive.String); - } + private J.Literal toLiteral(String str) { + return new J.Literal( + Tree.randomId(), + Space.EMPTY, + Markers.EMPTY, + str, + quote(str), + emptyList(), + Primitive.String); + } - private String quote(String str) { - return "\"" + str.replace("\"", "\\\"") + "\""; - } + private String quote(String str) { + return "\"" + str.replace("\"", "\\\"") + "\""; + } + }); } } diff --git a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java index 8db5f32e2..be791089e 100644 --- a/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java +++ b/src/main/java/org/openrewrite/staticanalysis/UnnecessaryCloseInTryWithResources.java @@ -34,6 +34,8 @@ import static java.util.Collections.singleton; public class UnnecessaryCloseInTryWithResources extends Recipe { + private static final MethodMatcher AUTO_CLOSEABLE_METHOD_MATCHER = new MethodMatcher("java.lang.AutoCloseable close()", true); + @Getter final String displayName = "Unnecessary close in try-with-resources"; @@ -57,45 +59,41 @@ public TreeVisitor getVisitor() { new KotlinFileChecker<>(), new GroovyFileChecker<>() ), - new UnnecessaryAutoCloseableVisitor() - ); - } - - private static class UnnecessaryAutoCloseableVisitor extends JavaIsoVisitor { - private static final MethodMatcher AUTO_CLOSEABLE_METHOD_MATCHER = new MethodMatcher("java.lang.AutoCloseable close()", true); - - @Override - public J.Try visitTry(J.Try aTry, ExecutionContext ctx) { - J.Try tr = super.visitTry(aTry, ctx); - if (tr.getResources() != null) { - String[] resourceNames = new String[tr.getResources().size()]; - for (int i = 0; i < tr.getResources().size(); i++) { - J.Try.Resource tryResource = tr.getResources().get(i); - if (tryResource.getVariableDeclarations() instanceof J.VariableDeclarations) { - J.VariableDeclarations varDecls = (J.VariableDeclarations) tryResource.getVariableDeclarations(); - resourceNames[i] = varDecls.getVariables().get(0).getSimpleName(); - } else if (tryResource.getVariableDeclarations() instanceof J.Identifier) { - J.Identifier identifier = (J.Identifier) tryResource.getVariableDeclarations(); - resourceNames[i] = identifier.getSimpleName(); + new JavaIsoVisitor() { + @Override + public J.Try visitTry(J.Try aTry, ExecutionContext ctx) { + J.Try tr = super.visitTry(aTry, ctx); + if (tr.getResources() != null) { + String[] resourceNames = new String[tr.getResources().size()]; + for (int i = 0; i < tr.getResources().size(); i++) { + J.Try.Resource tryResource = tr.getResources().get(i); + if (tryResource.getVariableDeclarations() instanceof J.VariableDeclarations) { + J.VariableDeclarations varDecls = (J.VariableDeclarations) tryResource.getVariableDeclarations(); + resourceNames[i] = varDecls.getVariables().get(0).getSimpleName(); + } else if (tryResource.getVariableDeclarations() instanceof J.Identifier) { + J.Identifier identifier = (J.Identifier) tryResource.getVariableDeclarations(); + resourceNames[i] = identifier.getSimpleName(); + } } - } - tr = tr.withBody(tr.getBody().withStatements(ListUtils.map(tr.getBody().getStatements(), statement -> { - if (statement instanceof J.MethodInvocation) { - J.MethodInvocation mi = (J.MethodInvocation) statement; - if (AUTO_CLOSEABLE_METHOD_MATCHER.matches(mi) && mi.getSelect() instanceof J.Identifier) { - String selectName = ((J.Identifier) mi.getSelect()).getSimpleName(); - for (String resourceName : resourceNames) { - if (resourceName.equals(selectName)) { - return null; + tr = tr.withBody(tr.getBody().withStatements(ListUtils.map(tr.getBody().getStatements(), statement -> { + if (statement instanceof J.MethodInvocation) { + J.MethodInvocation mi = (J.MethodInvocation) statement; + if (AUTO_CLOSEABLE_METHOD_MATCHER.matches(mi) && mi.getSelect() instanceof J.Identifier) { + String selectName = ((J.Identifier) mi.getSelect()).getSimpleName(); + for (String resourceName : resourceNames) { + if (resourceName.equals(selectName)) { + return null; + } } } } - } - return statement; - }))); + return statement; + }))); + } + return tr; } - return tr; } + ); } }