Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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,172 @@
package checks;

import java.io.IOException;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.NClob;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLXML;

class TryWithResourcesCheck_java_26 {

void processFromProcessBuilderIsCloseableAsOfJava26() throws IOException {
Process p = null;
try { // Noncompliant {{Change this "try" to a try-with-resources.}}
p = new ProcessBuilder("ls").start();
p.waitFor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (p != null) {
p.close();
}
}
}

void processFromRuntimeExec() throws IOException {
Process p = null;
try { // Noncompliant
p = Runtime.getRuntime().exec(new String[] {"ls"});
p.waitFor();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
p.close();
}
}

void blobFromConnectionFactory(Connection connection) throws SQLException {
Blob blob = null;
try { // Noncompliant
blob = connection.createBlob();
blob.setBytes(1L, new byte[] {0});
} finally {
if (blob != null) {
blob.close();
}
}
}

void clobFromResultSetGetter(ResultSet rs) throws SQLException {
Clob clob = null;
try { // Noncompliant
clob = rs.getClob("payload");
clob.length();
} finally {
if (clob != null) {
clob.close();
}
}
}

void arrayFromResultSetGetter(ResultSet rs) throws SQLException {
Array array = null;
try { // Noncompliant
array = rs.getArray(1);
array.getBaseType();
} finally {
if (array != null) {
array.close();
}
}
}

void sqlxmlFromConnectionFactory(Connection connection) throws SQLException {
SQLXML xml = null;
try { // Noncompliant
xml = connection.createSQLXML();
xml.setString("<root/>");
} finally {
if (xml != null) {
xml.close();
}
}
}

void blobFromCallableStatement(CallableStatement cs) throws SQLException {
Blob blob = null;
try { // Noncompliant
blob = cs.getBlob(1);
blob.length();
} finally {
if (blob != null) {
blob.close();
}
}
}

void clobFromConnectionFactory(Connection connection) throws SQLException {
Clob clob = null;
try { // Noncompliant
clob = connection.createClob();
clob.length();
} finally {
if (clob != null) {
clob.close();
}
}
}

void nclobFromConnectionFactory(Connection connection) throws SQLException {
NClob nclob = null;
try { // Noncompliant
nclob = connection.createNClob();
nclob.length();
} finally {
if (nclob != null) {
nclob.close();
}
}
}

void arrayFromConnectionFactory(Connection connection) throws SQLException {
Array array = null;
try { // Noncompliant
array = connection.createArrayOf("VARCHAR", new String[] {"a", "b"});
array.getBaseType();
} finally {
if (array != null) {
array.close();
}
}
}

void nclobFromResultSetGetter(ResultSet rs) throws SQLException {
NClob nclob = null;
try { // Noncompliant
nclob = rs.getNClob("payload");
nclob.length();
} finally {
if (nclob != null) {
nclob.close();
}
}
}

void sqlxmlFromResultSetGetter(ResultSet rs) throws SQLException {
SQLXML xml = null;
try { // Noncompliant
xml = rs.getSQLXML("payload");
xml.getString();
} finally {
if (xml != null) {
xml.close();
}
}
}

void compliantTryWithResources(Connection connection) throws IOException, SQLException {
try (Process p = new ProcessBuilder("ls").start();
Blob blob = connection.createBlob()) {
p.waitFor();
blob.setBytes(1L, new byte[] {0});
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
@Rule(key = "S2093")
public class TryWithResourcesCheck extends IssuableSubscriptionVisitor implements JavaVersionAwareVisitor {

private static final MethodMatchers AUTOCLOSEABLE_BUILDER_MATCHER = MethodMatchers.or(
private static final MethodMatchers AUTOCLOSEABLE_JAVA21_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()
);
Expand All @@ -47,6 +47,21 @@ public class TryWithResourcesCheck extends IssuableSubscriptionVisitor implement
.names("of")
.addParametersMatcher("java.lang.CharSequence").build();

private static final MethodMatchers AUTOCLOSEABLE_JAVA26_MATCHER = MethodMatchers.or(
MethodMatchers.create()
.ofTypes("java.lang.ProcessBuilder").names("start").addWithoutParametersMatcher().build(),
MethodMatchers.create()
.ofTypes("java.lang.Runtime").names("exec").withAnyParameters().build(),
MethodMatchers.create()
.ofSubTypes("java.sql.Connection")
.names("createBlob", "createClob", "createNClob", "createSQLXML", "createArrayOf")
.withAnyParameters().build(),
MethodMatchers.create()
.ofSubTypes("java.sql.ResultSet", "java.sql.CallableStatement")
.names("getBlob", "getClob", "getNClob", "getSQLXML", "getArray")
.withAnyParameters().build()
Comment thread
gitar-bot[bot] marked this conversation as resolved.
);
Comment thread
gitar-bot[bot] marked this conversation as resolved.

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

Expand Down Expand Up @@ -82,9 +97,15 @@ 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))||
(tree instanceof MethodInvocationTree mit2 && AUTOCLOSEABLE_FACTORY_MATCHER.matches(mit2));
if (tree instanceof NewClassTree newClass) {
return newClass.symbolType().isSubtypeOf("java.lang.AutoCloseable");
} else if (tree instanceof MethodInvocationTree mit) {
return AUTOCLOSEABLE_FACTORY_MATCHER.matches(mit) ||
(context.getJavaVersion().isJava21Compatible() && AUTOCLOSEABLE_JAVA21_MATCHER.matches(mit)) ||
(context.getJavaVersion().isJava26Compatible() && AUTOCLOSEABLE_JAVA26_MATCHER.matches(mit));
} else {
return false;
}
}

private static boolean isFollowedByTryWithFinally(Tree tree) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,17 @@ void test_java_21() {
.withJavaVersion(21)
.verifyIssues();
}

@Test
void test_java_26() {
CheckVerifier.newVerifier()
.onFile(mainCodeSourcesPath("checks/TryWithResourcesCheck_java_26.java"))
.withCheck(new TryWithResourcesCheck())
.verifyNoIssues();
CheckVerifier.newVerifier()
.onFile(mainCodeSourcesPath("checks/TryWithResourcesCheck_java_26.java"))
.withCheck(new TryWithResourcesCheck())
.withJavaVersion(26)
.verifyIssues();
}
}
Loading