Skip to content

Commit 5f1148d

Browse files
SONARJAVA-5598 S3063: Fix FPs on usage in expressions, getChars, and fields (#5200)
1 parent ea7eb0c commit 5f1148d

3 files changed

Lines changed: 34 additions & 12 deletions

File tree

java-checks-test-sources/default/src/main/java/checks/unused/UnusedStringBuilderCheckSample.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,28 @@ public String usedButInitializedSeparately() {
2828
return sb.toString();
2929
}
3030

31+
public String usedInExpression() {
32+
StringBuilder inExpr = new StringBuilder();
33+
inExpr.append("two ");
34+
inExpr.append("three ");
35+
return "one" + inExpr + "four";
36+
}
37+
38+
public void usedInException() {
39+
StringBuilder exceptionMessage = new StringBuilder();
40+
exceptionMessage.append("message");
41+
throw new IllegalStateException("message:" + exceptionMessage);
42+
}
43+
44+
public char[] usedGetChars() {
45+
StringBuilder buf = new StringBuilder();
46+
buf.append("Hello ");
47+
buf.append("World!");
48+
char[] chars = new char[5];
49+
buf.getChars(6, 11, chars, 0);
50+
return chars;
51+
}
52+
3153
public void unused() {
3254
StringBuilder sb = new StringBuilder(); // Noncompliant {{Consume or remove this unused StringBuilder}}
3355
// ^^
@@ -156,8 +178,8 @@ void appendHello() {
156178
}
157179

158180
static class UnusedFieldPrivate {
159-
private StringBuilder stringBuilder = new StringBuilder(); // Noncompliant
160-
// ^^^^^^^^^^^^^
181+
// FN: Ideally we should report this one, but there are some FPs when analyzing fields, so we skip them.
182+
private StringBuilder stringBuilder = new StringBuilder();
161183

162184
void appendHello() {
163185
stringBuilder.append("Hello");
@@ -174,7 +196,7 @@ void appendHello() {
174196
}
175197

176198
static class UsedFieldPrivate {
177-
private StringBuilder stringBuilder = new StringBuilder(); // Compliant
199+
private StringBuilder stringBuilder = new StringBuilder();
178200

179201
void appendHello() {
180202
stringBuilder.append("Hello");
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package checks.unused;
2+
3+
public class UnusedStringBuilderCheckSample2 {
4+
}

java-checks/src/main/java/org/sonar/java/checks/unused/UnusedStringBuilderCheck.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.sonar.plugins.java.api.semantic.Symbol;
2727
import org.sonar.plugins.java.api.semantic.Type;
2828
import org.sonar.plugins.java.api.tree.AssignmentExpressionTree;
29+
import org.sonar.plugins.java.api.tree.BinaryExpressionTree;
2930
import org.sonar.plugins.java.api.tree.ExpressionTree;
3031
import org.sonar.plugins.java.api.tree.MemberSelectExpressionTree;
3132
import org.sonar.plugins.java.api.tree.NewClassTree;
@@ -38,7 +39,7 @@
3839
*/
3940
@Rule(key = "S3063")
4041
public class UnusedStringBuilderCheck extends IssuableSubscriptionVisitor {
41-
private static final Set<String> TERMINAL_METHODS = Set.of("toString", "substring");
42+
private static final Set<String> TERMINAL_METHODS = Set.of("getChars", "substring", "toString");
4243

4344
@Override
4445
public List<Tree.Kind> nodesToVisit() {
@@ -51,10 +52,9 @@ public void visitNode(Tree tree) {
5152
Symbol symbol = variableTree.symbol();
5253
String typeName = getStringBuilderOrStringBuffer(symbol.type());
5354

54-
// Exclude non-local variables with non-private visibility,
55-
// because they can be changed in a way that is hard to track.
55+
// Exclude non-local variables, because they can be changed in a way that is hard to track.
5656
if (typeName != null && isInitializedByConstructor(variableTree.initializer()) &&
57-
isLocalOrPrivate(symbol) &&
57+
symbol.isLocalVariable() &&
5858
symbol.usages().stream().noneMatch(UnusedStringBuilderCheck::isUsedInAssignment) &&
5959
symbol.usages().stream().noneMatch(UnusedStringBuilderCheck::isConsumed)) {
6060
reportIssue(variableTree.simpleName(), "Consume or remove this unused %s".formatted(typeName));
@@ -83,10 +83,6 @@ private static boolean isInitializedByConstructor(@Nullable ExpressionTree initi
8383
return initializer instanceof NewClassTree;
8484
}
8585

86-
private static boolean isLocalOrPrivate(Symbol symbol) {
87-
return symbol.isLocalVariable() || symbol.isPrivate();
88-
}
89-
9086
/**
9187
* True if the argument is the LHS of an assignment.
9288
*
@@ -114,7 +110,7 @@ private static boolean isConsumed(Tree tree) {
114110
.filter(UnusedStringBuilderCheck::isConsumed)
115111
.isPresent();
116112
}
117-
} else if (parent instanceof ReturnStatementTree || parent instanceof ArgumentListTreeImpl) {
113+
} else if (parent instanceof ReturnStatementTree || parent instanceof ArgumentListTreeImpl || parent instanceof BinaryExpressionTree) {
118114
return true;
119115
}
120116
return false;

0 commit comments

Comments
 (0)