Skip to content

Commit

Permalink
SONARJAVA-4784 Add HttpClient to S2093 for Java21 (#4686)
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardo-pilastri-sonarsource authored Mar 5, 2024
1 parent a39e170 commit 6a7d5c5
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package checks;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

class TryWithResourcesCheck_java_21 {

void httpClientIsAutoCloseableAsOfJava21(HttpRequest request) throws IOException, InterruptedException {
HttpClient client = null;
var o = new Object();
try { // Noncompliant {{Change this "try" to a try-with-resources.}}
client = HttpClient.newBuilder().build();
client.send(request, HttpResponse.BodyHandlers.ofString());
} finally {
if (client != null) {
client.close();
}
}

try { // Noncompliant
client = HttpClient.newHttpClient();
client.send(request, HttpResponse.BodyHandlers.ofString());
} finally {
client.close();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,32 @@
*/
package org.sonar.java.checks;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import org.sonar.check.Rule;
import org.sonar.plugins.java.api.JavaVersionAwareVisitor;
import org.sonar.java.cfg.CFG;
import org.sonar.plugins.java.api.IssuableSubscriptionVisitor;
import org.sonar.plugins.java.api.JavaFileScannerContext;
import org.sonar.plugins.java.api.JavaVersion;
import org.sonar.plugins.java.api.JavaVersionAwareVisitor;
import org.sonar.plugins.java.api.semantic.MethodMatchers;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;
import org.sonar.plugins.java.api.tree.NewClassTree;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.TryStatementTree;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;

@Rule(key = "S2093")
public class TryWithResourcesCheck extends IssuableSubscriptionVisitor implements JavaVersionAwareVisitor {

private static final MethodMatchers AUTOCLOSEABLE_BUILDER_MATCHER = MethodMatchers.or(
MethodMatchers.create().ofTypes("java.net.http.HttpClient$Builder").names("build").addWithoutParametersMatcher().build(),
MethodMatchers.create().ofTypes("java.net.http.HttpClient").names("newHttpClient").addWithoutParametersMatcher().build()
);

private final Deque<TryStatementTree> withinTry = new LinkedList<>();
private final Deque<List<Tree>> toReport = new LinkedList<>();

Expand All @@ -50,7 +56,7 @@ public void leaveFile(JavaFileScannerContext context) {

@Override
public List<Tree.Kind> nodesToVisit() {
return Arrays.asList(Tree.Kind.TRY_STATEMENT, Tree.Kind.NEW_CLASS);
return Arrays.asList(Tree.Kind.TRY_STATEMENT, Tree.Kind.NEW_CLASS, Tree.Kind.METHOD_INVOCATION);
}

@Override
Expand All @@ -60,7 +66,7 @@ public void visitNode(Tree tree) {
if (withinTry.size() != toReport.size()) {
toReport.push(new ArrayList<>());
}
} else if (((NewClassTree) tree).symbolType().isSubtypeOf("java.lang.AutoCloseable")) {
} else if (isNewAutocloseableOrBuilder(tree, context)) {
if (withinStandardTryWithFinally()) {
toReport.peek().add(tree);
} else if (isFollowedByTryWithFinally(tree)) {
Expand All @@ -73,6 +79,11 @@ public void visitNode(Tree tree) {
}
}

private static boolean isNewAutocloseableOrBuilder(Tree tree, JavaFileScannerContext context) {
return (tree instanceof NewClassTree newClass && newClass.symbolType().isSubtypeOf("java.lang.AutoCloseable")) ||
(context.getJavaVersion().isJava21Compatible() && tree instanceof MethodInvocationTree mit && AUTOCLOSEABLE_BUILDER_MATCHER.matches(mit));
}

private static boolean isFollowedByTryWithFinally(Tree tree) {
Tree blockParent = tree.parent();
while (blockParent != null && !blockParent.is(Tree.Kind.BLOCK)) {
Expand Down Expand Up @@ -129,7 +140,8 @@ public void leaveNode(Tree tree) {
for (Tree autoCloseable : secondaryTrees) {
secondary.add(new JavaFileScannerContext.Location("AutoCloseable resource", autoCloseable));
}
reportIssue(tryStatementTree.tryKeyword(), "Change this \"try\" to a try-with-resources." + context.getJavaVersion().java7CompatibilityMessage(), secondary, null);
reportIssue(tryStatementTree.tryKeyword(),
"Change this \"try\" to a try-with-resources." + context.getJavaVersion().java7CompatibilityMessage(), secondary, null);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,17 @@ void test_no_java_version() {
.withCheck(new TryWithResourcesCheck())
.verifyIssues();
}

@Test
void test_java_21() {
CheckVerifier.newVerifier()
.onFile(mainCodeSourcesPath("checks/TryWithResourcesCheck_java_21.java"))
.withCheck(new TryWithResourcesCheck())
.verifyNoIssues();
CheckVerifier.newVerifier()
.onFile(mainCodeSourcesPath("checks/TryWithResourcesCheck_java_21.java"))
.withCheck(new TryWithResourcesCheck())
.withJavaVersion(21)
.verifyIssues();
}
}

0 comments on commit 6a7d5c5

Please sign in to comment.