Bug 541759 - [resource] don't suggest using t-w-r for a foreach element
variable
Change-Id: I18f766ec36fa8e2c4bc25fafaa0e01bc3d40e388
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 765e10d..0d2d7a5 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
@@ -5437,4 +5437,63 @@
"----------\n",
compilerOptions);
}
+public void testBug541705() {
+ if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // uses diamond
+ Runner runner = new Runner();
+ runner.customOptions = getCompilerOptions();
+ runner.customOptions.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+ runner.testFiles = new String[] {
+ "Test.java",
+ "import java.util.*;\n" +
+ "import java.util.zip.*;\n" +
+ "import java.io.*;\n" +
+ "public class Test {\n" +
+ " private static HashMap<String, ZipFile> fgZipFileCache = new HashMap<>(5);\n" +
+ " public static void closeArchives() {\n" +
+ " synchronized (fgZipFileCache) {\n" +
+ " for (ZipFile file : fgZipFileCache.values()) {\n" +
+ " synchronized (file) {\n" +
+ " try {\n" +
+ " file.close();\n" +
+ " } catch (IOException e) {\n" +
+ " System.out.println(e);\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " fgZipFileCache.clear();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ };
+ runner.runConformTest();
+}
+public void testBug541705b() {
+ if (this.complianceLevel < ClassFileConstants.JDK9) return; // variable used in t-w-r
+ Runner runner = new Runner();
+ runner.customOptions = getCompilerOptions();
+ runner.customOptions.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR);
+ runner.testFiles = new String[] {
+ "Test.java",
+ "import java.util.*;\n" +
+ "import java.util.zip.*;\n" +
+ "import java.io.*;\n" +
+ "public class Test {\n" +
+ " private static HashMap<String, ZipFile> fgZipFileCache = new HashMap<>(5);\n" +
+ " public static void closeArchives() {\n" +
+ " synchronized (fgZipFileCache) {\n" +
+ " for (ZipFile file : fgZipFileCache.values()) {\n" +
+ " synchronized (file) {\n" +
+ " try (file) {\n" +
+ " } catch (IOException e) {\n" +
+ " System.out.println(e);\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " fgZipFileCache.clear();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ };
+ runner.runConformTest();
+}
}
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 d093225..8498b31 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
@@ -79,7 +79,9 @@
private static final int REPORTED_POTENTIAL_LEAK = 32;
// a location independent definitive problem has been reported against this resource:
private static final int REPORTED_DEFINITIVE_LEAK = 64;
-
+ // a local declarations that acts as the element variable of a foreach loop (should never suggest to use t-w-r):
+ private static final int FOREACH_ELEMENT_VAR = 128;
+
public static boolean TEST_372319 = false; // see https://bugs.eclipse.org/372319
/**
@@ -449,9 +451,9 @@
if (rhsTrackVar.originalBinding != null)
local.closeTracker = rhsTrackVar; // a.: let fresh LHS share it
if (rhsTrackVar.currentAssignment == location) {
- // pre-set tracker from lhs - passed from outside?
+ // pre-set tracker from lhs - passed from outside (or foreach)?
// now it's a fresh resource
- rhsTrackVar.globalClosingState &= ~(SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE);
+ rhsTrackVar.globalClosingState &= ~(SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE|FOREACH_ELEMENT_VAR);
}
} else {
if (rhs instanceof AllocationExpression || rhs instanceof ConditionalExpression) {
@@ -484,7 +486,7 @@
}
}
// 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|FOREACH_ELEMENT_VAR)) == 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);
@@ -494,7 +496,7 @@
if (rhsTrackVar != null) {
local.closeTracker = rhsTrackVar;
// a fresh resource, mark as not-closed:
- if ((rhsTrackVar.globalClosingState & (SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE)) == 0)
+ if ((rhsTrackVar.globalClosingState & (SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE|FOREACH_ELEMENT_VAR)) == 0)
flowInfo.markAsDefinitelyNull(rhsTrackVar.binding);
// TODO(stephan): this might be useful, but I could not find a test case for it:
// if (flowContext.initsOnFinally != null)
@@ -753,6 +755,12 @@
return flowInfo;
}
+ public static void markForeachElementVar(LocalDeclaration local) {
+ if (local.binding != null && local.binding.closeTracker != null) {
+ local.binding.closeTracker.globalClosingState |= FOREACH_ELEMENT_VAR;
+ }
+ }
+
/**
* Iterator for a set of FakedTrackingVariable, which dispenses the elements
* according to the priorities defined by enum {@link Stage}.
@@ -987,7 +995,7 @@
}
public void reportExplicitClosing(ProblemReporter problemReporter) {
- if ((this.globalClosingState & (OWNED_BY_OUTSIDE|REPORTED_EXPLICIT_CLOSE)) == 0) { // can't use t-w-r for OWNED_BY_OUTSIDE
+ if ((this.globalClosingState & (OWNED_BY_OUTSIDE|REPORTED_EXPLICIT_CLOSE|FOREACH_ELEMENT_VAR)) == 0) { // can't use t-w-r for OWNED_BY_OUTSIDE
this.globalClosingState |= REPORTED_EXPLICIT_CLOSE;
problemReporter.explicitlyClosedAutoCloseable(this);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
index 26e30f7..fe438b0 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
@@ -99,7 +99,7 @@
int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;
// process the element variable and collection
- flowInfo = this.elementVariable.analyseCode(this.scope, flowContext, flowInfo);
+ flowInfo = this.elementVariable.analyseCode(this.scope, flowContext, flowInfo);
FlowInfo condInfo = this.collection.analyseCode(this.scope, flowContext, flowInfo.copy());
this.collection.checkNPE(currentScope, flowContext, condInfo.copy(), 1);
LocalVariableBinding elementVarBinding = this.elementVariable.binding;
@@ -132,6 +132,7 @@
if (this.action.complainIfUnreachable(actionInfo, this.scope, initialComplaintLevel, true) < Statement.COMPLAINED_UNREACHABLE) {
actionInfo = this.action.analyseCode(this.scope, loopingContext, actionInfo).unconditionalCopy();
if (this.action instanceof Block) {
+ FakedTrackingVariable.markForeachElementVar(this.elementVariable);
// action.analyseCode() missed the following check due to identical scopes of ForeachStatement and Block:
this.scope.checkUnclosedCloseables(actionInfo, loopingContext, null, null);
}