Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3f4e109
new recipe that changes .equals() to .contentEquals() when comparing …
AlekSimpson Jun 16, 2023
b4877a3
polished somethings up and added license headers
AlekSimpson Jun 16, 2023
ae41eb5
Update src/test/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
90338a6
Update src/main/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
b17a46b
Update src/main/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
05753ca
Update src/main/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
74ed6f4
Update src/main/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
24c02a3
Update src/main/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
ac3ccee
Update src/test/java/org/openrewrite/staticanalysis/EqualsToContentEq…
AlekSimpson Jun 16, 2023
15089c1
added missing import
AlekSimpson Jun 16, 2023
86e3d79
updated license header year
AlekSimpson Jun 16, 2023
9423d0e
added test to check that the recipe runs correctly on selects that ar…
AlekSimpson Jun 17, 2023
a69675b
added first test, need to test recipe
AlekSimpson Jun 18, 2023
dbc8624
the recipe for the toString() replacement is mostly finished, except …
AlekSimpson Jun 19, 2023
83890a9
Merge branch 'main' into alek/RemoveToStringCallsFromArrayInstances
AlekSimpson Jun 20, 2023
a574b69
fixed edge case, all tests pass now
AlekSimpson Jun 20, 2023
3c4ed65
added preconidition check
AlekSimpson Jun 20, 2023
bd586b7
added support for more methods that implicitly call toString() and al…
AlekSimpson Jun 22, 2023
18e5c8c
added RSPEC tags and updated import statements
AlekSimpson Jun 22, 2023
40167dd
The cursor message must be added before the `super` call
knutwannheden Jun 23, 2023
5ca3b34
fixed cursor messaging, updated tests for more edge case methods, rec…
AlekSimpson Jun 23, 2023
ffadf48
more changes from PR feedback
AlekSimpson Jun 26, 2023
f488a15
added support for static methods
AlekSimpson Jun 26, 2023
522afbe
Update src/main/java/org/openrewrite/staticanalysis/RemoveToStringCal…
AlekSimpson Jun 27, 2023
81f193a
Update src/test/java/org/openrewrite/staticanalysis/RemoveToStringCal…
timtebeek Jun 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.java.JavaIsoVisitor;
import org.openrewrite.java.JavaTemplate;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.search.UsesMethod;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.java.tree.JavaType;

import java.util.List;

public class RemoveToStringCallsFromArrayInstances extends Recipe {
private static final MethodMatcher TO_STRING_MATCHER = new MethodMatcher("java.lang.Object toString(..)");
private static final MethodMatcher PRINTLN_MATCHER = new MethodMatcher("java.io.PrintStream println(..)");
private static final MethodMatcher STR_FORMAT_MATCHER = new MethodMatcher("java.lang.String format(..)");

@Override
public String getDisplayName() {
return "toString should not be called on array instances";
}

@Override
public String getDescription() {
return "toString should not be called on array instances.";
}

public TreeVisitor<?, ExecutionContext> getVisitor() {
return Preconditions.check(Preconditions.or(
new UsesMethod<>(TO_STRING_MATCHER),
new UsesMethod<>(PRINTLN_MATCHER),
new UsesMethod<>(STR_FORMAT_MATCHER)
), new RemoveToStringFromArraysVisitor());
}

private static class RemoveToStringFromArraysVisitor extends JavaIsoVisitor<ExecutionContext> {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) {
J.MethodInvocation m = super.visitMethodInvocation(mi, ctx);

if (TO_STRING_MATCHER.matches(m)) {
String builder_string = "Arrays.toString(#{anyArray(java.lang.String)})";
Expression select = m.getSelect();
assert select != null;

return buildReplacement(builder_string, m, select);
}else if (PRINTLN_MATCHER.matches(m)) {
Expression select = m.getArguments().get(0);
String builder_string = "System.out.println(Arrays.toString(#{anyArray(java.lang.String)}))";

return buildReplacement(builder_string, m, select);
}else if (STR_FORMAT_MATCHER.matches(m)) {
List<Expression> arguments = m.getArguments();
StringBuilder builder_string = new StringBuilder("String.format(#{any(java.lang.String)}");
boolean arrayExists = false;

for (int i = 0; i<arguments.size(); i++) {
Expression argument = arguments.get(i);
if (i == 0) { continue; }
if (argument.getType() instanceof JavaType.Array) {
arrayExists = true;
String arg = ", Arrays.toString(#{anyArray(java.lang.Object)})";
builder_string.append(arg);
continue;
}
builder_string.append(", #{any(java.lang.String)}");
}
builder_string.append(")");

if (!arrayExists) { return m; }

J.MethodInvocation newInvocation = JavaTemplate.builder(builder_string.toString())
.imports("java.util.Arrays")
.build()
.apply(getCursor(), m.getCoordinates().replace(), arguments.toArray());
maybeAddImport("java.util.Arrays");

return newInvocation;
}

return m;
}

public J.MethodInvocation buildReplacement(String builder_string, J.MethodInvocation m, Expression select) {
if (!(select.getType() instanceof JavaType.Array)) {
return m;
}

J.MethodInvocation retVal = JavaTemplate.builder(builder_string)
.imports("java.util.Arrays")
.build()
.apply(getCursor(), m.getCoordinates().replace(), select);
maybeAddImport("java.util.Arrays");
return retVal;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import static org.openrewrite.java.Assertions.java;

class EqualsToContentEqualsTest implements RewriteTest {

@Override
public void defaults(RecipeSpec spec) {
spec
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,269 @@
/*
* Copyright 2023 the original author or authors.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.Issue;
import org.openrewrite.java.JavaParser;
import org.openrewrite.test.RecipeSpec;
import org.openrewrite.test.RewriteTest;

import static org.openrewrite.java.Assertions.java;

@SuppressWarnings({"ImplicitArrayToString", "UnnecessaryLocalVariable", "RedundantStringFormatCall", "MalformedFormatString", "PrimitiveArrayArgumentToVarargsMethod"})
public class RemoveToStringCallsFromArrayInstancesTest implements RewriteTest {
@Override
public void defaults(RecipeSpec spec) {
spec
.parser(JavaParser.fromJavaVersion())
.recipe(new RemoveToStringCallsFromArrayInstances());
}

@Test
@DocumentExample
@Issue("https://github.com/openrewrite/rewrite-static-analysis/issues/44")
void fixNonCompliantToString() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
String argStr = args.toString();
}
}
""",
"""
import java.util.Arrays;

class SomeClass {
public static void main(String[] args) {
String argStr = Arrays.toString(args);
}
}
"""
)
);
}

@Test
void doesNotRunOnNonArrayInstances() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
int number = 5;
System.out.println(number.toString());
}
}
"""
)
);
}

@Test
void runsOnNonStringArrays() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
String arrStr = getNumArr().toString();
}

public int[] getNumArr() {
int[] nums = {1, 2, 3, 4};
return nums;
}
}
""",
"""
import java.util.Arrays;

class SomeClass {
public static void main(String[] args) {
String arrStr = Arrays.toString(getNumArr());
}

public int[] getNumArr() {
int[] nums = {1, 2, 3, 4};
return nums;
}
}
"""
)
);
}

@Test
void selectIsAMethod() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
String arrStr = getArr().toString();
}

public String[] getArr() {
String[] arr = {"test", "array"};
return arr;
}
}
""",
"""
import java.util.Arrays;

class SomeClass {
public static void main(String[] args) {
String arrStr = Arrays.toString(getArr());
}

public String[] getArr() {
String[] arr = {"test", "array"};
return arr;
}
}
"""
)
);
}

@Test
void printlnEdgeCase() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
int[] s = new int[]{1,2,3};
System.out.println(s);
}
}
""",
"""
import java.util.Arrays;

class SomeClass {
public static void main(String[] args) {
int[] s = new int[]{1,2,3};
System.out.println(Arrays.toString(s));
}
}
"""
)
);
}

@Test
void stringFormatEdgeCase() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
int[] s = new int[]{1, 2, 3};
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)));
}
}
"""
)
);
}

@Test
void stringFormatMultipleArraysPassedIn() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
int[] s1 = new int[]{1, 2, 3};
int[] s2 = new int[]{4, 5, 6};

System.out.println(String.format("s1=%s, s2=%s", s1, s2));
}
}
""",
"""
import java.util.Arrays;

class SomeClass {
public static void main(String[] args) {
int[] s1 = new int[]{1, 2, 3};
int[] s2 = new int[]{4, 5, 6};

System.out.println(String.format("s1=%s, s2=%s", Arrays.toString(s1), Arrays.toString(s2)));
}
}
"""
)
);
}

@Test
void stringFormatMultipleValuesWithArraysPassedIn() {
//language=java
rewriteRun(
java(
"""
class SomeClass {
public static void main(String[] args) {
int[] s1 = new int[]{1, 2, 3};
int[] s2 = new int[]{4, 5, 6};
String name = "First array:";
String secondName = "Second array:";

System.out.println(String.format("%s %s, %s %s", name, s1, secondName, s2));
}
}
""",
"""
import java.util.Arrays;

class SomeClass {
public static void main(String[] args) {
int[] s1 = new int[]{1, 2, 3};
int[] s2 = new int[]{4, 5, 6};
String name = "First array:";
String secondName = "Second array:";

System.out.println(String.format("%s %s, %s %s", name, Arrays.toString(s1), secondName, Arrays.toString(s2)));
}
}
"""
)
);
}
}