diff --git a/spotbugs-tests/src/test/java/edu/umd/cs/findbugs/detect/Issue79Test.java b/spotbugs-tests/src/test/java/edu/umd/cs/findbugs/detect/Issue79Test.java new file mode 100644 index 00000000000..439bf060a28 --- /dev/null +++ b/spotbugs-tests/src/test/java/edu/umd/cs/findbugs/detect/Issue79Test.java @@ -0,0 +1,28 @@ +package edu.umd.cs.findbugs.detect; + +import edu.umd.cs.findbugs.AbstractIntegrationTest; +import edu.umd.cs.findbugs.BugInstance; +import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcher; +import edu.umd.cs.findbugs.test.matcher.BugInstanceMatcherBuilder; +import org.junit.Test; + +import static edu.umd.cs.findbugs.test.CountMatcher.containsExactly; +import static org.junit.Assert.assertThat; + +/** + * SpotBugs should remove the ResultSet obligation from all set + * when one occurrence of that type of obligation is Statement + * from all states. + * + * @see GitHub issue + * @since 4.1.3 + */ +public class Issue79Test extends AbstractIntegrationTest { + + @Test + public void test(){ + performAnalysis("ghIssues/Issue79.class"); + BugInstanceMatcher bugMatcher = new BugInstanceMatcherBuilder().build(); + assertThat(getBugCollection(), containsExactly(0, bugMatcher)); + } +} diff --git a/spotbugs/src/main/java/edu/umd/cs/findbugs/ba/obl/ObligationAnalysis.java b/spotbugs/src/main/java/edu/umd/cs/findbugs/ba/obl/ObligationAnalysis.java index 18573ee1b5d..e1c0370479e 100644 --- a/spotbugs/src/main/java/edu/umd/cs/findbugs/ba/obl/ObligationAnalysis.java +++ b/spotbugs/src/main/java/edu/umd/cs/findbugs/ba/obl/ObligationAnalysis.java @@ -215,6 +215,17 @@ public void edgeTransfer(Edge edge, StateSet fact) throws DataflowAnalysisExcept + edge.getSource().getLastInstruction()); } fact.deleteObligation(comparedObligation, edge.getTarget().getLabel()); + + // closing a Statement closes the ResultSet + Obligation statement = database.getFactory().getObligationByName("java.sql.Statement"); + if (comparedObligation.equals(statement)){ + Obligation resultSet = database.getFactory().getObligationByName("java.sql.ResultSet"); + fact.deleteObligation(resultSet, edge.getTarget().getLabel()); + if (DEBUG_NULL_CHECK) { + System.out.println("Deleting " + resultSet.toString() + " on edge from comparison " + + edge.getSource().getLastInstruction()); + } + } } } } diff --git a/spotbugsTestCases/src/java/ghIssues/Issue79.java b/spotbugsTestCases/src/java/ghIssues/Issue79.java new file mode 100644 index 00000000000..b542355456c --- /dev/null +++ b/spotbugsTestCases/src/java/ghIssues/Issue79.java @@ -0,0 +1,30 @@ +package ghIssues; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public class Issue79 { + private static final String QUERY = ""; + + public void f(Connection cnx) throws SQLException { + PreparedStatement st = null; + ResultSet rs = null; + try { + st = cnx.prepareStatement(QUERY); + rs = st.executeQuery(); + while (rs.next()) { + System.out.println(rs.getString("ID")); + } + } finally { + /* + * The statement closes the result set, and there is no scenario where st may be null + * but not the resultset, however an unsatisfied obligation is reported on the resultset + */ + if (st != null) { + st.close(); + } + } + } +}