Skip to content

Commit 97beabc

Browse files
Calvin-Lmsridhar
andauthored
Fix a resource leak false positive due to a cast (#6821)
Co-authored-by: Manu Sridharan <[email protected]>
1 parent 350a2f1 commit 97beabc

File tree

3 files changed

+37
-43
lines changed

3 files changed

+37
-43
lines changed

checker/src/main/java/org/checkerframework/checker/resourceleak/MustCallConsistencyAnalyzer.java

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import org.checkerframework.dataflow.cfg.block.Block.BlockType;
5555
import org.checkerframework.dataflow.cfg.block.ConditionalBlock;
5656
import org.checkerframework.dataflow.cfg.block.ExceptionBlock;
57-
import org.checkerframework.dataflow.cfg.block.SingleSuccessorBlock;
5857
import org.checkerframework.dataflow.cfg.node.AssignmentNode;
5958
import org.checkerframework.dataflow.cfg.node.ClassNameNode;
6059
import org.checkerframework.dataflow.cfg.node.FieldAccessNode;
@@ -66,7 +65,6 @@
6665
import org.checkerframework.dataflow.cfg.node.ReturnNode;
6766
import org.checkerframework.dataflow.cfg.node.SuperNode;
6867
import org.checkerframework.dataflow.cfg.node.ThisNode;
69-
import org.checkerframework.dataflow.cfg.node.TypeCastNode;
7068
import org.checkerframework.dataflow.expression.FieldAccess;
7169
import org.checkerframework.dataflow.expression.JavaExpression;
7270
import org.checkerframework.dataflow.expression.LocalVariable;
@@ -986,36 +984,6 @@ private boolean returnTypeIsMustCallAliasWithUntrackable(MethodInvocationNode no
986984
return !mustCallAliasArguments.isEmpty();
987985
}
988986

989-
/**
990-
* Checks if {@code node} is either directly enclosed by a {@link TypeCastNode}, by looking at the
991-
* successor block in the CFG. In this case the enclosing operator is a "no-op" that evaluates to
992-
* the same value as {@code node}. This method is only used within {@link
993-
* #propagateObligationsToSuccessorBlocks(ControlFlowGraph, Set, Block, Set, Deque)} to ensure
994-
* Obligations are propagated to cast nodes properly. It relies on the assumption that a {@link
995-
* TypeCastNode} will only appear in a CFG as the first node in a block.
996-
*
997-
* @param node the CFG node
998-
* @return {@code true} if {@code node} is in a {@link SingleSuccessorBlock} {@code b}, the first
999-
* {@link Node} in {@code b}'s successor block is a {@link TypeCastNode}, and {@code node} is
1000-
* an operand of the successor node; {@code false} otherwise
1001-
*/
1002-
private boolean inCast(Node node) {
1003-
if (!(node.getBlock() instanceof SingleSuccessorBlock)) {
1004-
return false;
1005-
}
1006-
Block successorBlock = ((SingleSuccessorBlock) node.getBlock()).getSuccessor();
1007-
if (successorBlock != null) {
1008-
List<Node> succNodes = successorBlock.getNodes();
1009-
if (succNodes.size() > 0) {
1010-
Node succNode = succNodes.get(0);
1011-
if (succNode instanceof TypeCastNode) {
1012-
return ((TypeCastNode) succNode).getOperand().equals(node);
1013-
}
1014-
}
1015-
}
1016-
return false;
1017-
}
1018-
1019987
/**
1020988
* Transfer ownership of any locals passed as arguments to {@code @Owning} parameters at a method
1021989
* or constructor call by removing the Obligations corresponding to those locals.
@@ -2073,17 +2041,6 @@ private void propagateObligationsToSuccessorBlock(
20732041
}
20742042
}
20752043

2076-
// Always propagate the Obligation to the successor if current block represents
2077-
// code nested in a cast. Without this logic, the analysis may report a false
2078-
// positive when the Obligation represents a temporary variable for a nested
2079-
// expression, as the temporary may not appear in the successor store and hence
2080-
// seems to be going out of scope. The temporary will be handled with special
2081-
// logic; casts are unwrapped at various points in the analysis.
2082-
if (currentBlockNodes.size() == 1 && inCast(currentBlockNodes.get(0))) {
2083-
successorObligations.add(obligation);
2084-
continue;
2085-
}
2086-
20872044
// At this point, a consistency check will definitely occur, unless the
20882045
// obligation was derived from a MustCallAlias parameter. If it was, an error is
20892046
// immediately issued, because such a parameter should not go out of scope
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import java.io.Closeable;
2+
3+
public abstract class CastBeforeFinallyBlock {
4+
5+
protected abstract Closeable alloc() throws Exception;
6+
7+
protected abstract Object getInt() throws Exception;
8+
9+
public void f() throws Exception {
10+
// Previous versions of the resource leak checker reported a false positive
11+
// for this code ("close may not have been invoked on y"). However, this
12+
// code is correct.
13+
14+
Integer x = null;
15+
try {
16+
try (Closeable y = alloc()) {
17+
System.out.println(y);
18+
}
19+
x = (Integer) getInt();
20+
} finally {
21+
System.out.println(x);
22+
}
23+
}
24+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// simple test that the Resource Leak Checker can track data flow through a downcast
2+
import java.io.Closeable;
3+
import java.io.InputStream;
4+
5+
public abstract class SafeCast {
6+
7+
protected abstract Closeable alloc() throws Exception;
8+
9+
public void f() throws Exception {
10+
InputStream s = (InputStream) alloc();
11+
s.close();
12+
}
13+
}

0 commit comments

Comments
 (0)