Bug 411098 - [compiler][resource] Invalid Resource Leak Warning using
ternary operator inside try-with-resource
- handle nested resource allocation inside conditional expression
- code simplification
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
index cbecb16..73d5d84 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java
@@ -4481,6 +4481,7 @@
options);
}
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
public void testBug411098_test1() {
if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
Map options = getCompilerOptions();
@@ -4500,6 +4501,8 @@
options
);
}
+
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
public void testBug411098_test2() {
if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
Map options = getCompilerOptions();
@@ -4529,6 +4532,8 @@
options
);
}
+
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
public void testBug411098_test3() {
if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
Map options = getCompilerOptions();
@@ -4559,6 +4564,8 @@
options
);
}
+
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
public void testBug411098_test4() {
if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
Map options = getCompilerOptions();
@@ -4583,6 +4590,7 @@
);
}
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
public void testBug411098_test5() {
if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
Map options = getCompilerOptions();
@@ -4603,6 +4611,7 @@
);
}
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
public void testBug411098_test6() {
if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
Map options = getCompilerOptions();
@@ -4622,4 +4631,26 @@
options
);
}
+
+// https://bugs.eclipse.org/411098 - [compiler][resource] Invalid Resource Leak Warning using ternary operator inside try-with-resource
+// challenge nested resource allocations
+public void testBug411098_test7() {
+ if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used
+ Map options = getCompilerOptions();
+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+ runConformTest(
+ new String[] {
+ "A.java",
+ "import java.io.*;\n" +
+ "class A {\n" +
+ " void testA(boolean b) throws Exception {\n" +
+ " BufferedReader in = b ? new BufferedReader(new FileReader(\"a\")) : new BufferedReader(new FileReader(\"b\"));\n" +
+ " in.close();\n" +
+ " }\n" +
+ "}"
+ },
+ options
+ );
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java
index 295c2b5..49cfc04 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java
@@ -224,25 +224,13 @@
}
if (closeTracker != null) {
closeTracker.currentAssignment = location;
- if (rhs instanceof ConditionalExpression) {
- ConditionalExpression conditional = (ConditionalExpression)rhs;
- preConnectTrackerAcrossAssignment(location, local, flowInfo, conditional, closeTracker);
- }
- else if (rhs instanceof AllocationExpression) {
- AllocationExpression allocation = (AllocationExpression)rhs;
- preConnectTrackerAcrossAssignment(location, local, flowInfo, allocation, closeTracker);
- }
+ preConnectTrackerAcrossAssignment(location, local, flowInfo, closeTracker, rhs);
}
}
}
- private static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, FlowInfo flowInfo, ConditionalExpression conditional,
- FakedTrackingVariable closeTracker) {
- preConnectTrackerAcrossAssignment(location, local, flowInfo, closeTracker, conditional.valueIfFalse);
- preConnectTrackerAcrossAssignment(location, local, flowInfo, closeTracker, conditional.valueIfTrue);
- }
-
- private static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, FlowInfo flowInfo, FakedTrackingVariable closeTracker, Expression expression) {
+ private static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, FlowInfo flowInfo,
+ FakedTrackingVariable closeTracker, Expression expression) {
if (expression instanceof AllocationExpression) {
preConnectTrackerAcrossAssignment(location, local, flowInfo, (AllocationExpression) expression, closeTracker);
} else if (expression instanceof ConditionalExpression) {
@@ -250,8 +238,14 @@
}
}
- private static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local,
- FlowInfo flowInfo, AllocationExpression allocationExpression, FakedTrackingVariable closeTracker) {
+ private static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, FlowInfo flowInfo,
+ ConditionalExpression conditional, FakedTrackingVariable closeTracker) {
+ preConnectTrackerAcrossAssignment(location, local, flowInfo, closeTracker, conditional.valueIfFalse);
+ preConnectTrackerAcrossAssignment(location, local, flowInfo, closeTracker, conditional.valueIfTrue);
+ }
+
+ private static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, FlowInfo flowInfo,
+ AllocationExpression allocationExpression, FakedTrackingVariable closeTracker) {
allocationExpression.closeTracker = closeTracker;
if (allocationExpression.arguments != null && allocationExpression.arguments.length > 0) {
// also push into nested allocations, see https://bugs.eclipse.org/368709
@@ -295,6 +289,9 @@
newStatus = finallyStatus;
}
}
+ if (allocation.closeTracker.innerTracker != null) {
+ innerTracker = pickMoreUnsafe(allocation.closeTracker.innerTracker, innerTracker, scope, flowInfo);
+ }
allocation.closeTracker.innerTracker = innerTracker;
innerTracker.outerTracker = allocation.closeTracker;
flowInfo.markNullStatus(allocation.closeTracker.binding, newStatus);
@@ -332,6 +329,23 @@
}
}
+ private static FakedTrackingVariable pickMoreUnsafe(FakedTrackingVariable tracker1, FakedTrackingVariable tracker2, BlockScope scope, FlowInfo info) {
+ // whichever of the two trackers has stronger indication to be leaking will be returned,
+ // the other one will be removed from the scope (considered to be merged into the former).
+ int status1 = info.nullStatus(tracker1.binding);
+ int status2 = info.nullStatus(tracker2.binding);
+ if (status1 == FlowInfo.NULL || status2 == FlowInfo.NON_NULL) return pick(tracker1, tracker2, scope);
+ if (status1 == FlowInfo.NON_NULL || status2 == FlowInfo.NULL) return pick(tracker2, tracker1, scope);
+ if ((status1 & FlowInfo.POTENTIALLY_NULL) != 0) return pick(tracker1, tracker2, scope);
+ if ((status2 & FlowInfo.POTENTIALLY_NULL) != 0) return pick(tracker2, tracker1, scope);
+ return pick(tracker1, tracker2, scope);
+ }
+
+ private static FakedTrackingVariable pick(FakedTrackingVariable tracker1, FakedTrackingVariable tracker2, BlockScope scope) {
+ scope.removeTrackingVar(tracker2);
+ return tracker1;
+ }
+
private static void handleRegularResource(BlockScope scope, FlowInfo flowInfo, AllocationExpression allocation) {
FakedTrackingVariable presetTracker = allocation.closeTracker;
if (presetTracker != null && presetTracker.originalBinding != null) {