diff options
| author | Stephan Herrmann | 2012-12-04 13:20:44 +0000 |
|---|---|---|
| committer | Jayaprakash Arthanareeswaran | 2012-12-04 13:20:44 +0000 |
| commit | 909d290bd029fb6f505219cc6f64089f759548bb (patch) | |
| tree | 2954d480ea796198aa4b09fe7ab5d83ee5d79f28 | |
| parent | e14a67b89e92e257eae6e75fc0c7b3046b4c9f63 (diff) | |
| download | eclipse.jdt.core-909d290bd029fb6f505219cc6f64089f759548bb.tar.gz eclipse.jdt.core-909d290bd029fb6f505219cc6f64089f759548bb.tar.xz eclipse.jdt.core-909d290bd029fb6f505219cc6f64089f759548bb.zip | |
Bug 394768 - [compiler][resource] Incorrect resource leak warning when
creating stream in conditional
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 d8aced88e0..e679c99096 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 4c32b015e4..9acab18d4f 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 ca8439311b..687b880696 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 dc2397d6c4..19866d55c0 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 ba0cdd20b9..cefefa9808 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 c1ac8198bf..53ed355e8b 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 9939437185..288fb8f4dc 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 d2e1cb25a9..cb3fa8d0e9 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 7322456c27..4afcd8d44a 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 ee6637529f..40bdb8e4e1 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) |
