summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorStephan Herrmann2012-12-04 08:20:44 (EST)
committer Jayaprakash Arthanareeswaran2012-12-04 08:20:44 (EST)
commit909d290bd029fb6f505219cc6f64089f759548bb (patch)
tree2954d480ea796198aa4b09fe7ab5d83ee5d79f28
parente14a67b89e92e257eae6e75fc0c7b3046b4c9f63 (diff)
downloadeclipse.jdt.core-909d290bd029fb6f505219cc6f64089f759548bb.zip
eclipse.jdt.core-909d290bd029fb6f505219cc6f64089f759548bb.tar.gz
eclipse.jdt.core-909d290bd029fb6f505219cc6f64089f759548bb.tar.bz2
Bug 394768 - [compiler][resource] Incorrect resource leak warning when
creating stream in conditional
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java90
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FakedTrackingVariable.java39
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java59
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java26
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java36
10 files changed, 240 insertions, 28 deletions
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 d8aced8..e679c99 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
@@ -26,7 +26,7 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
public class ResourceLeakTests extends AbstractRegressionTest {
static {
-// TESTS_NAMES = new String[] { "testBug386534" };
+// TESTS_NAMES = new String[] { "testBug394768" };
// TESTS_NUMBERS = new int[] { 50 };
// TESTS_RANGE = new int[] { 11, -1 };
}
@@ -3923,4 +3923,92 @@ public void testBug386534() {
options,
null);
}
+
+//https://bugs.eclipse.org/386534 - [compiler][resource] "Potential resource leak" false positive warning
+public void testBug394768() {
+ Map options = getCompilerOptions();
+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+ runConformTest(
+ new String[] {
+ "Bug394768.java",
+ "import java.io.File;\n" +
+ "import java.io.FileInputStream;\n" +
+ "import java.io.InputStream;\n" +
+ "\n" +
+ "public class Bug394768 {\n" +
+ " public void readFile(String path) throws Exception {\n" +
+ " InputStream stream = null;\n" +
+ " File file = new File(path);\n" +
+ "\n" +
+ " if (file.exists())\n" +
+ " stream = new FileInputStream(path);\n" +
+ " else\n" +
+ " stream = getClass().getClassLoader().getResourceAsStream(path);\n" +
+ "\n" +
+ " if (stream == null)\n" +
+ " return;\n" +
+ "\n" +
+ " try {\n" +
+ " // Use the opened stream here\n" +
+ " stream.read();\n" +
+ " } finally {\n" +
+ " stream.close();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ },
+ "",
+ null,
+ true,
+ null,
+ options,
+ null);
+}
+
+// https://bugs.eclipse.org/386534 - [compiler][resource] "Potential resource leak" false positive warning
+// variation: 2nd branch closes and nulls the newly acquired resource
+public void testBug394768_1() {
+ Map options = getCompilerOptions();
+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR);
+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR);
+ runConformTest(
+ new String[] {
+ "Bug394768.java",
+ "import java.io.File;\n" +
+ "import java.io.FileInputStream;\n" +
+ "import java.io.InputStream;\n" +
+ "\n" +
+ "public class Bug394768 {\n" +
+ " public void readFile(String path) throws Exception {\n" +
+ " InputStream stream = null;\n" +
+ " File file = new File(path);\n" +
+ "\n" +
+ " if (file.exists()) {\n" +
+ " stream = new FileInputStream(path);\n" +
+ " } else {\n" +
+ " stream = getClass().getClassLoader().getResourceAsStream(path);" +
+ " stream.close();\n" +
+ " stream = null;\n" +
+ " }\n" +
+ "\n" +
+ " if (stream == null)\n" +
+ " return;\n" +
+ "\n" +
+ " try {\n" +
+ " // Use the opened stream here\n" +
+ " stream.read();\n" +
+ " } finally {\n" +
+ " stream.close();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ },
+ "",
+ null,
+ true,
+ null,
+ options,
+ null);
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
index 4c32b01..9acab18 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
@@ -19,6 +19,7 @@
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -61,7 +62,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
if (shouldAnalyseResource) {
preInitInfo = flowInfo.unconditionalCopy();
// analysis of resource leaks needs additional context while analyzing the RHS:
- FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, local, this.expression);
+ FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, local, this.expression, flowInfo);
}
flowInfo = ((Reference) this.lhs)
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 ca84393..687b880 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
@@ -84,7 +84,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
// if tracking var was allocated from a finally context, record here the flow context of the corresponding try block
private FlowContext tryContext;
- public FakedTrackingVariable(LocalVariableBinding original, ASTNode location, FlowContext flowContext) {
+ public FakedTrackingVariable(LocalVariableBinding original, ASTNode location, FlowInfo flowInfo, FlowContext flowContext, int nullStatus) {
super(original.name, location.sourceStart, location.sourceEnd);
this.type = new SingleTypeReference(
TypeConstants.OBJECT,
@@ -101,10 +101,12 @@ public class FakedTrackingVariable extends LocalDeclaration {
flowContext = flowContext.parent;
}
resolve(original.declaringScope);
+ if (nullStatus != 0)
+ flowInfo.markNullStatus(this.binding, nullStatus); // mark that this flow has seen the resource
}
/* Create an unassigned tracking variable while analyzing an allocation expression: */
- private FakedTrackingVariable(BlockScope scope, ASTNode location) {
+ private FakedTrackingVariable(BlockScope scope, ASTNode location, FlowInfo flowInfo, int nullStatus) {
super("<unassigned Closeable value>".toCharArray(), location.sourceStart, location.sourceEnd); //$NON-NLS-1$
this.type = new SingleTypeReference(
TypeConstants.OBJECT,
@@ -112,6 +114,8 @@ public class FakedTrackingVariable extends LocalDeclaration {
this.methodScope = scope.methodScope();
this.originalBinding = null;
resolve(scope);
+ if (nullStatus != 0)
+ flowInfo.markNullStatus(this.binding, nullStatus); // mark that this flow has seen the resource
}
public void generateCode(BlockScope currentScope, CodeStream codeStream)
@@ -142,7 +146,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
* @param expression
* @return a new {@link FakedTrackingVariable} or null.
*/
- public static FakedTrackingVariable getCloseTrackingVariable(Expression expression, FlowContext flowContext) {
+ public static FakedTrackingVariable getCloseTrackingVariable(Expression expression, FlowInfo flowInfo, FlowContext flowContext) {
while (true) {
if (expression instanceof CastExpression)
expression = ((CastExpression) expression).expression;
@@ -162,7 +166,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
// tracking var doesn't yet exist. This happens in finally block
// which is analyzed before the corresponding try block
Statement location = local.declaration;
- local.closeTracker = new FakedTrackingVariable(local, location, flowContext);
+ local.closeTracker = new FakedTrackingVariable(local, location, flowInfo, flowContext, FlowInfo.UNKNOWN);
if (local.isParameter()) {
local.closeTracker.globalClosingState |= OWNED_BY_OUTSIDE;
// status of this tracker is now UNKNOWN
@@ -188,13 +192,13 @@ public class FakedTrackingVariable extends LocalDeclaration {
* @param rhs the rhs of the assignment resp. the initialization of the local variable declaration.
* <strong>Precondition:</strong> client has already checked that the resolved type of this expression is either a closeable type or NULL.
*/
- public static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, Expression rhs) {
+ public static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, Expression rhs, FlowInfo flowInfo) {
FakedTrackingVariable closeTracker = null;
if (rhs instanceof AllocationExpression) {
closeTracker = local.closeTracker;
if (closeTracker == null) {
if (rhs.resolvedType != TypeBinding.NULL) { // not NULL means valid closeable as per method precondition
- closeTracker = new FakedTrackingVariable(local, location, null);
+ closeTracker = new FakedTrackingVariable(local, location, flowInfo, null, FlowInfo.UNKNOWN);
if (local.isParameter()) {
closeTracker.globalClosingState |= OWNED_BY_OUTSIDE;
}
@@ -206,7 +210,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
allocation.closeTracker = closeTracker;
if (allocation.arguments != null && allocation.arguments.length > 0) {
// also push into nested allocations, see https://bugs.eclipse.org/368709
- preConnectTrackerAcrossAssignment(location, local, allocation.arguments[0]);
+ preConnectTrackerAcrossAssignment(location, local, allocation.arguments[0], flowInfo);
}
}
}
@@ -239,7 +243,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
} while (currentInner != null);
int newStatus = FlowInfo.NULL;
if (allocation.closeTracker == null) {
- allocation.closeTracker = new FakedTrackingVariable(scope, allocation); // no local available, closeable is unassigned
+ allocation.closeTracker = new FakedTrackingVariable(scope, allocation, flowInfo, FlowInfo.UNKNOWN); // no local available, closeable is unassigned
} else {
if (scope.finallyInfo != null) {
// inject results from analysing a finally block onto the newly connected wrapper
@@ -297,7 +301,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
&& !(presetTracker.currentAssignment instanceof LocalDeclaration)) // forgetting old val in local decl is syntactically impossible
allocation.closeTracker.recordErrorLocation(presetTracker.currentAssignment, closeStatus);
} else {
- allocation.closeTracker = new FakedTrackingVariable(scope, allocation); // no local available, closeable is unassigned
+ allocation.closeTracker = new FakedTrackingVariable(scope, allocation, flowInfo, FlowInfo.UNKNOWN); // no local available, closeable is unassigned
}
flowInfo.markAsDefinitelyNull(allocation.closeTracker.binding);
}
@@ -356,7 +360,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
rhsAnalyis:
if (rhs.resolvedType != TypeBinding.NULL) {
// new value is AutoCloseable, start tracking, possibly re-using existing tracker var:
- FakedTrackingVariable rhsTrackVar = getCloseTrackingVariable(rhs, flowContext);
+ FakedTrackingVariable rhsTrackVar = getCloseTrackingVariable(rhs, flowInfo, flowContext);
if (rhsTrackVar != null) { // 1. if RHS has a tracking variable...
if (local.closeTracker == null) {
// null shouldn't occur but let's play safe:
@@ -375,8 +379,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
&& ((rhsTrackVar.globalClosingState & OWNED_BY_OUTSIDE) != 0)) {
// c.: assigning a fresh resource (pre-connected alloc)
// to a local previously holding an alien resource -> start over
- local.closeTracker = new FakedTrackingVariable(local, location, flowContext);
- flowInfo.markAsDefinitelyNull(local.closeTracker.binding);
+ local.closeTracker = new FakedTrackingVariable(local, location, flowInfo, flowContext, FlowInfo.NULL);
// still check disconnectedTracker below
break rhsAnalyis;
}
@@ -399,7 +402,8 @@ public class FakedTrackingVariable extends LocalDeclaration {
}
}
// re-assigning from a fresh value, mark as not-closed again:
- if ((previousTracker.globalClosingState & (SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE)) == 0)
+ if ((previousTracker.globalClosingState & (SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE)) == 0
+ && flowInfo.hasNullInfoFor(previousTracker.binding)) // avoid spilling info into a branch that doesn't see the corresponding resource
flowInfo.markAsDefinitelyNull(previousTracker.binding);
local.closeTracker = analyseCloseableExpression(flowInfo, flowContext, local, location, rhs, previousTracker);
}
@@ -465,9 +469,8 @@ public class FakedTrackingVariable extends LocalDeclaration {
|| expression instanceof ArrayReference)
{
// we *might* be responsible for the resource obtained
- FakedTrackingVariable tracker = new FakedTrackingVariable(local, location, flowContext);
+ FakedTrackingVariable tracker = new FakedTrackingVariable(local, location, flowInfo, flowContext, FlowInfo.POTENTIALLY_NULL); // shed some doubt
tracker.globalClosingState |= SHARED_WITH_OUTSIDE;
- flowInfo.markPotentiallyNullBit(tracker.binding); // shed some doubt
return tracker;
} else if (
(expression.bits & RestrictiveFlagMASK) == Binding.FIELD
@@ -475,7 +478,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
&& ((QualifiedNameReference) expression).isFieldAccess()))
{
// responsibility for this resource probably lies at a higher level
- FakedTrackingVariable tracker = new FakedTrackingVariable(local, location, flowContext);
+ FakedTrackingVariable tracker = new FakedTrackingVariable(local, location, flowInfo, flowContext, FlowInfo.UNKNOWN);
tracker.globalClosingState |= OWNED_BY_OUTSIDE;
// leave state as UNKNOWN, the bit OWNED_BY_OUTSIDE will prevent spurious warnings
return tracker;
@@ -491,7 +494,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
if (local.closeTracker != null)
// (c): inner has already been analyzed: -> re-use track var
return local.closeTracker;
- FakedTrackingVariable newTracker = new FakedTrackingVariable(local, location, flowContext);
+ FakedTrackingVariable newTracker = new FakedTrackingVariable(local, location, flowInfo, flowContext, FlowInfo.UNKNOWN);
LocalVariableBinding rhsLocal = expression.localVariableBinding();
if (rhsLocal != null && rhsLocal.isParameter()) {
newTracker.globalClosingState |= OWNED_BY_OUTSIDE;
@@ -634,7 +637,7 @@ public class FakedTrackingVariable extends LocalDeclaration {
*/
public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo, FlowContext flowContext, boolean owned) {
- FakedTrackingVariable trackVar = getCloseTrackingVariable(expression, flowContext);
+ FakedTrackingVariable trackVar = getCloseTrackingVariable(expression, flowInfo, flowContext);
if (trackVar != null) {
// insert info that the tracked resource *may* be closed (by the target method, i.e.)
FlowInfo infoResourceIsClosed = owned ? flowInfo : flowInfo.copy();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
index dc2397d..19866d5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java
@@ -21,6 +21,7 @@
* bug 370639 - [compiler][resource] restore the default for resource leak warnings
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -105,7 +106,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
if (shouldAnalyseResource) {
preInitInfo = flowInfo.unconditionalCopy();
// analysis of resource leaks needs additional context while analyzing the RHS:
- FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, this.binding, this.initialization);
+ FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, this.binding, this.initialization, flowInfo);
}
flowInfo =
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index ba0cdd2..cefefa9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -24,6 +24,7 @@
* bug 379834 - Wrong "method can be static" in presence of qualified super and different staticness of nested super class.
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -83,7 +84,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
boolean analyseResources = currentScope.compilerOptions().analyseResourceLeaks;
if (analyseResources && CharOperation.equals(TypeConstants.CLOSE, this.selector))
{
- FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.receiver, flowContext);
+ FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.receiver, flowInfo, flowContext);
if (trackingVariable != null) { // null happens if receiver is not a local variable or not an AutoCloseable
if (trackingVariable.methodScope == currentScope.methodScope()) {
trackingVariable.markClose(flowInfo, flowContext);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
index c1ac819..53ed355 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
@@ -20,6 +20,7 @@
* bug 365859 - [compiler][null] distinguish warnings based on flow analysis vs. null annotations
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -56,7 +57,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
if (flowInfo.reachMode() == FlowInfo.REACHABLE)
checkAgainstNullAnnotation(currentScope, flowContext, this.expression.nullStatus(flowInfo));
if (currentScope.compilerOptions().analyseResourceLeaks) {
- FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.expression, flowContext);
+ FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.expression, flowInfo, flowContext);
if (trackingVariable != null) {
if (methodScope != trackingVariable.methodScope)
trackingVariable.markClosedInNestedMethod();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
index 9939437..288fb8f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
@@ -10,6 +10,7 @@
* Stephan Herrmann - Contributions for
* bug 332637 - Dead Code detection removing code that isn't dead
* bug 391517 - java.lang.VerifyError on code that runs correctly in Eclipse 3.7 and eclipse 3.6
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -103,6 +104,11 @@ public boolean isDefinitelyUnknown(LocalVariableBinding local) {
&& this.initsWhenFalse.isDefinitelyUnknown(local);
}
+public boolean hasNullInfoFor(LocalVariableBinding local) {
+ return this.initsWhenTrue.hasNullInfoFor(local)
+ || this.initsWhenFalse.hasNullInfoFor(local);
+}
+
public boolean isPotentiallyAssigned(FieldBinding field) {
return this.initsWhenTrue.isPotentiallyAssigned(field)
|| this.initsWhenFalse.isPotentiallyAssigned(field);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
index d2e1cb2..cb3fa8d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
@@ -8,8 +8,9 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
- * bug 292478 - Report potentially null across variable assignment
- * bug 332637 - Dead Code detection removing code that isn't dead
+ * bug 292478 - Report potentially null across variable assignment
+ * bug 332637 - Dead Code detection removing code that isn't dead
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -194,6 +195,12 @@ public abstract boolean isDefinitelyNull(LocalVariableBinding local);
*/
public abstract boolean isDefinitelyUnknown(LocalVariableBinding local);
+/**
+ * Check if any null info has been recorded for a given local variable.
+ * Here even recording of 'UNKNOWN' is considered as null info.
+ */
+public abstract boolean hasNullInfoFor(LocalVariableBinding local);
+
/**
* Check status of potential assignment for a field.
*/
@@ -361,6 +368,54 @@ public int nullStatus(LocalVariableBinding local) {
}
/**
+ * Merge two single bits (NULL, NON_NULL, POTENTIALLY*..) into one.
+ * This method implements a simpler logic than the 4-bit encoding used in FlowInfo instances.
+ */
+public static int mergeNullStatus(int nullStatus1, int nullStatus2) {
+ boolean canBeNull = false;
+ boolean canBeNonNull = false;
+ switch (nullStatus1) {
+ case POTENTIALLY_NULL:
+ canBeNonNull = true;
+ //$FALL-THROUGH$
+ case NULL:
+ canBeNull = true;
+ break;
+ case POTENTIALLY_NON_NULL:
+ canBeNull = true;
+ //$FALL-THROUGH$
+ case NON_NULL:
+ canBeNonNull = true;
+ break;
+ }
+ switch (nullStatus2) {
+ case POTENTIALLY_NULL:
+ canBeNonNull = true;
+ //$FALL-THROUGH$
+ case NULL:
+ canBeNull = true;
+ break;
+ case POTENTIALLY_NON_NULL:
+ canBeNull = true;
+ //$FALL-THROUGH$
+ case NON_NULL:
+ canBeNonNull = true;
+ break;
+ }
+ if (canBeNull) {
+ if (canBeNonNull)
+ return POTENTIALLY_NULL;
+ else
+ return NULL;
+ } else {
+ if (canBeNonNull)
+ return NON_NULL;
+ else
+ return UNKNOWN;
+ }
+}
+
+/**
* Merge branches using optimized boolean conditions
*/
public static UnconditionalFlowInfo mergedOptimizedBranches(
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java
index 7322456..4afcd8d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java
@@ -16,6 +16,7 @@
* bug 349326 - [1.7] new warning for missing try-with-resources
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 386181 - [compiler][null] wrong transition in UnconditionalFlowInfo.mergedWith()
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
@@ -829,6 +830,31 @@ final public boolean isDefinitelyUnknown(LocalVariableBinding local) {
& (1L << (position % BitCacheSize))) != 0;
}
+final public boolean hasNullInfoFor(LocalVariableBinding local) {
+ // do not want to complain in unreachable code
+ if ((this.tagBits & UNREACHABLE) != 0 ||
+ (this.tagBits & NULL_FLAG_MASK) == 0) {
+ return false;
+ }
+ int position = local.id + this.maxFieldCount;
+ if (position < BitCacheSize) { // use bits
+ return ((this.nullBit1 | this.nullBit2
+ | this.nullBit3 | this.nullBit4) & (1L << position)) != 0;
+ }
+ // use extra vector
+ if (this.extra == null) {
+ return false; // if vector not yet allocated, then not initialized
+ }
+ int vectorIndex;
+ if ((vectorIndex = (position / BitCacheSize) - 1) >=
+ this.extra[2].length) {
+ return false; // if not enough room in vector, then not initialized
+ }
+ return ((this.extra[2][vectorIndex] | this.extra[3][vectorIndex]
+ | this.extra[4][vectorIndex] | this.extra[5][vectorIndex])
+ & (1L << (position % BitCacheSize))) != 0;
+}
+
/**
* Check status of potential assignment at a given position.
*/
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java
index ee66375..40bdb8e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java
@@ -15,6 +15,7 @@
* bug 370639 - [compiler][resource] restore the default for resource leak warnings
* bug 388996 - [compiler][resource] Incorrect 'potential resource leak'
* bug 379784 - [compiler] "Method can be static" is not getting reported
+ * bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1054,7 +1055,7 @@ public void checkUnclosedCloseables(FlowInfo flowInfo, FlowContext flowContext,
if (location != null && flowInfo.reachMode() != 0) return;
FakedTrackingVariable returnVar = (location instanceof ReturnStatement) ?
- FakedTrackingVariable.getCloseTrackingVariable(((ReturnStatement)location).expression, flowContext) : null;
+ FakedTrackingVariable.getCloseTrackingVariable(((ReturnStatement)location).expression, flowInfo, flowContext) : null;
Set varSet = new HashSet(this.trackingVariables);
FakedTrackingVariable trackingVar;
@@ -1104,7 +1105,7 @@ public void checkUnclosedCloseables(FlowInfo flowInfo, FlowContext flowContext,
} else {
int size = this.trackingVariables.size();
for (int i=0; i<size; i++) {
- FakedTrackingVariable tracker = (FakedTrackingVariable) this.trackingVariables.get(0);
+ FakedTrackingVariable tracker = (FakedTrackingVariable) this.trackingVariables.get(i);
tracker.resetReportingBits();
}
}
@@ -1138,7 +1139,8 @@ private void reportResourceLeak(FakedTrackingVariable trackingVar, ASTNode locat
*/
public void correlateTrackingVarsIfElse(FlowInfo thenFlowInfo, FlowInfo elseFlowInfo) {
if (this.trackingVariables != null) {
- for (int i=0; i<this.trackingVariables.size(); i++) {
+ int trackVarCount = this.trackingVariables.size();
+ for (int i=0; i<trackVarCount; i++) {
FakedTrackingVariable trackingVar = (FakedTrackingVariable) this.trackingVariables.get(i);
if (trackingVar.originalBinding == null)
continue;
@@ -1152,6 +1154,34 @@ public void correlateTrackingVarsIfElse(FlowInfo thenFlowInfo, FlowInfo elseFlow
{
thenFlowInfo.markAsDefinitelyNonNull(trackingVar.binding); // -> always closed
}
+ else {
+ if (thenFlowInfo == FlowInfo.DEAD_END || elseFlowInfo == FlowInfo.DEAD_END)
+ continue; // short cut
+
+ for (int j=i+1; j<trackVarCount; j++) {
+ FakedTrackingVariable var2 = ((FakedTrackingVariable) this.trackingVariables.get(j));
+ if (trackingVar.originalBinding == var2.originalBinding) {
+ // two tracking variables for the same original, merge info from both branches now:
+ boolean var1SeenInThen = thenFlowInfo.hasNullInfoFor(trackingVar.binding);
+ boolean var1SeenInElse = elseFlowInfo.hasNullInfoFor(trackingVar.binding);
+ boolean var2SeenInThen = thenFlowInfo.hasNullInfoFor(var2.binding);
+ boolean var2SeenInElse = elseFlowInfo.hasNullInfoFor(var2.binding);
+ int newStatus;
+ if (!var1SeenInThen && var1SeenInElse && var2SeenInThen && !var2SeenInElse) {
+ newStatus = FlowInfo.mergeNullStatus(thenFlowInfo.nullStatus(var2.binding), elseFlowInfo.nullStatus(trackingVar.binding));
+ } else if (var1SeenInThen && !var1SeenInElse && !var2SeenInThen && var2SeenInElse) {
+ newStatus = FlowInfo.mergeNullStatus(thenFlowInfo.nullStatus(trackingVar.binding), elseFlowInfo.nullStatus(var2.binding));
+ } else {
+ continue;
+ }
+ thenFlowInfo.markNullStatus(trackingVar.binding, newStatus);
+ elseFlowInfo.markNullStatus(trackingVar.binding, newStatus);
+ trackingVar.originalBinding.closeTracker = trackingVar; // avoid further use of var2
+ thenFlowInfo.markNullStatus(var2.binding, FlowInfo.NON_NULL);
+ elseFlowInfo.markNullStatus(var2.binding, FlowInfo.NON_NULL);
+ }
+ }
+ }
}
}
if (this.parent instanceof BlockScope)