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) {