Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 209107 Details for
Bug 358903
Filter practically unimportant resource leak warnings
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Tests & fix v0.6
Bug_358903_v0.6.patch (text/plain), 58.70 KB, created by
Stephan Herrmann
on 2012-01-05 17:02:07 EST
(
hide
)
Description:
Tests & fix v0.6
Filename:
MIME Type:
Creator:
Stephan Herrmann
Created:
2012-01-05 17:02:07 EST
Size:
58.70 KB
patch
obsolete
>diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryWithResourcesStatementTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryWithResourcesStatementTest.java >index f21a57f..ea5f44b 100644 >--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryWithResourcesStatementTest.java >+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TryWithResourcesStatementTest.java >@@ -14,8 +14,13 @@ > *******************************************************************************/ > package org.eclipse.jdt.core.tests.compiler.regression; > >+import java.io.IOException; >+import java.net.URL; > import java.util.Map; > >+import org.eclipse.core.runtime.FileLocator; >+import org.eclipse.core.runtime.Path; >+import org.eclipse.core.runtime.Platform; > import org.eclipse.jdt.core.JavaCore; > import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; > >@@ -23,7 +28,7 @@ > public class TryWithResourcesStatementTest extends AbstractRegressionTest { > > static { >-// TESTS_NAMES = new String[] { "test056throw"}; >+// TESTS_NAMES = new String[] { "test061"}; > // TESTS_NUMBERS = new int[] { 50 }; > // TESTS_RANGE = new int[] { 11, -1 }; > } >@@ -4444,7 +4449,7 @@ > options); > } > // Bug 349326 - [1.7] new warning for missing try-with-resources >-// closed in dead code >+// properly closed, dead code in between > public void test056r() { > Map options = getCompilerOptions(); > options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); >@@ -5354,7 +5359,424 @@ > "X::~X\n" + > "true"); > } >- >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// Bug 360908 - Avoid resource leak warning when the underlying/chained resource is closed explicitly >+// a resource wrapper is not closed but the underlying resource is >+public void test061a() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runConformTest( >+ new String[] { >+ "X.java", >+ "import java.io.File;\n" + >+ "import java.io.BufferedInputStream;\n" + >+ "import java.io.FileInputStream;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void foo() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " FileInputStream fileStream = new FileInputStream(file);\n" + >+ " BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " System.out.println(bis.available());\n" + >+ " fileStream.close();\n" + >+ " }\n" + >+ " void inline() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " FileInputStream fileStream;\n" + >+ " BufferedInputStream bis = new BufferedInputStream(fileStream = new FileInputStream(file));\n" + >+ " System.out.println(bis.available());\n" + >+ " fileStream.close();\n" + >+ " }\n" + >+ " public static void main(String[] args) throws IOException {\n" + >+ " try {\n" + >+ " new X().foo();\n" + >+ " } catch (IOException ex) {" + >+ " System.out.println(\"Got IO Exception\");\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "Got IO Exception", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// a closeable without OS resource is not closed >+public void test061b() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runConformTest( >+ new String[] { >+ "X.java", >+ "import java.io.StringReader;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void foo() throws IOException {\n" + >+ " StringReader string = new StringReader(\"content\");\n" + >+ " System.out.println(string.read());\n" + >+ " }\n" + >+ " public static void main(String[] args) throws IOException {\n" + >+ " new X().foo();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "99", // character 'c' >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// a resource wrapper is not closed but the underlying closeable is resource-free >+public void test061c() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runConformTest( >+ new String[] { >+ "X.java", >+ "import java.io.BufferedReader;\n" + >+ "import java.io.StringReader;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void foo() throws IOException {\n" + >+ " StringReader input = new StringReader(\"content\");\n" + >+ " BufferedReader br = new BufferedReader(input);\n" + >+ " BufferedReader doubleWrap = new BufferedReader(br);\n" + >+ " System.out.println(br.read());\n" + >+ " }\n" + >+ " void inline() throws IOException {\n" + >+ " BufferedReader br = new BufferedReader(new StringReader(\"content\"));\n" + >+ " System.out.println(br.read());\n" + >+ " }\n" + >+ " public static void main(String[] args) throws IOException {\n" + >+ " new X().foo();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "99", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// a resource wrapper is not closed neither is the underlying resource >+public void test061d() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); >+ this.runNegativeTest( >+ new String[] { >+ "X.java", >+ "import java.io.File;\n" + >+ "import java.io.BufferedInputStream;\n" + >+ "import java.io.FileInputStream;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void foo() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " FileInputStream fileStream = new FileInputStream(file);\n" + >+ " BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " System.out.println(bis.available());\n" + >+ " }\n" + >+ " void inline() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file));\n" + >+ " System.out.println(bis2.available());\n" + >+ " }\n" + >+ " public static void main(String[] args) throws IOException {\n" + >+ " try {\n" + >+ " new X().foo();\n" + >+ " } catch (IOException ex) {" + >+ " System.out.println(\"Got IO Exception\");\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 10)\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " ^^^^^^^^^^\n" + >+ "Resource leak: \'doubleWrap\' is never closed\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 15)\n" + >+ " BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream(file));\n" + >+ " ^^^^\n" + >+ "Resource leak: \'bis2\' is never closed\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// Bug 361073 - Avoid resource leak warning when the top level resource is closed explicitly >+// a resource wrapper is closed closing also the underlying resource >+public void test061e() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runConformTest( >+ new String[] { >+ "X.java", >+ "import java.io.File;\n" + >+ "import java.io.BufferedInputStream;\n" + >+ "import java.io.FileInputStream;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " FileInputStream fis;" + >+ " void foo() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " FileInputStream fileStream = new FileInputStream(file);\n" + >+ " BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " System.out.println(bis.available());\n" + >+ " bis.close();\n" + >+ " }\n" + >+ " void inline() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " BufferedInputStream bis2 = new BufferedInputStream(fis = new FileInputStream(file));\n" + // field assignment >+ " System.out.println(bis2.available());\n" + >+ " bis2.close();\n" + >+ " FileInputStream fileStream = null;\n" + >+ " BufferedInputStream bis3 = new BufferedInputStream(fileStream = new FileInputStream(file));\n" + >+ " System.out.println(bis3.available());\n" + >+ " bis3.close();\n" + >+ " }\n" + >+ " public static void main(String[] args) throws IOException {\n" + >+ " try {\n" + >+ " new X().foo();\n" + >+ " } catch (IOException ex) {" + >+ " System.out.println(\"Got IO Exception\");\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "Got IO Exception", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// Bug 361073 - Avoid resource leak warning when the top level resource is closed explicitly >+// a resource wrapper is closed closing also the underlying resource - original test case >+public void test061f() throws IOException { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ URL url = FileLocator.toFileURL(FileLocator.find(Platform.getBundle("org.eclipse.jdt.core.tests.compiler"), new Path("META-INF/MANIFEST.MF"), null)); >+ this.runConformTest( >+ new String[] { >+ "X.java", >+ "import java.io.InputStream;\n" + >+ "import java.io.InputStreamReader;\n" + >+ "import java.io.BufferedReader;\n" + >+ "import java.io.IOException;\n" + >+ "import java.net.URL;\n" + >+ "public class X {\n" + >+ " boolean loadURL(final URL url) throws IOException {\n" + >+ " InputStream stream = null;\n" + >+ " BufferedReader reader = null;\n" + >+ " try {\n" + >+ " stream = url.openStream();\n" + >+ " reader = new BufferedReader(new InputStreamReader(stream));\n" + >+ " System.out.println(reader.readLine());\n" + >+ " } finally {\n" + >+ " try {\n" + >+ " if (reader != null)\n" + >+ " reader.close();\n" + >+ " } catch (IOException x) {\n" + >+ " }\n" + >+ " }\n" + >+ " return false; // 'stream' may not be closed at this location\n" + >+ " }\n" + >+ " public static void main(String[] args) throws IOException {\n" + >+ " try {\n" + >+ " new X().loadURL(new URL(\""+url.toString()+"\"));\n" + >+ " } catch (IOException ex) {\n" + >+ " System.out.println(\"Got IO Exception\"+ex);\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "Manifest-Version: 1.0", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// Bug 360908 - Avoid resource leak warning when the underlying/chained resource is closed explicitly >+// Different points in a resource chain are closed >+public void test061g() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runNegativeTest( >+ new String[] { >+ "X.java", >+ "import java.io.File;\n" + >+ "import java.io.BufferedInputStream;\n" + >+ "import java.io.FileInputStream;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void closeMiddle() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " FileInputStream fileStream = new FileInputStream(file);\n" + >+ " BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " System.out.println(bis.available());\n" + >+ " bis.close();\n" + >+ " }\n" + >+ " void closeOuter() throws IOException {\n" + >+ " File file2 = new File(\"somefile\");\n" + >+ " FileInputStream fileStream2 = new FileInputStream(file2);\n" + >+ " BufferedInputStream bis2 = new BufferedInputStream(fileStream2);\n" + >+ " BufferedInputStream doubleWrap2 = new BufferedInputStream(bis2);\n" + >+ " System.out.println(bis2.available());\n" + >+ " doubleWrap2.close();\n" + >+ " }\n" + >+ " void neverClosed() throws IOException {\n" + >+ " File file3 = new File(\"somefile\");\n" + >+ " FileInputStream fileStream3 = new FileInputStream(file3);\n" + >+ " BufferedInputStream bis3 = new BufferedInputStream(fileStream3);\n" + >+ " BufferedInputStream doubleWrap3 = new BufferedInputStream(bis3);\n" + >+ " System.out.println(doubleWrap3.available());\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 26)\n" + >+ " BufferedInputStream doubleWrap3 = new BufferedInputStream(bis3);\n" + >+ " ^^^^^^^^^^^\n" + >+ "Resource leak: \'doubleWrap3\' is never closed\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// Bug 360908 - Avoid resource leak warning when the underlying/chained resource is closed explicitly >+// Different points in a resource chain are potentially closed >+public void test061h() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runNegativeTest( >+ new String[] { >+ "X.java", >+ "import java.io.File;\n" + >+ "import java.io.BufferedInputStream;\n" + >+ "import java.io.FileInputStream;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void closeMiddle(boolean b) throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " FileInputStream fileStream = new FileInputStream(file);\n" + >+ " BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " System.out.println(bis.available());\n" + >+ " if (b)\n" + >+ " bis.close();\n" + >+ " }\n" + >+ " void closeOuter(boolean b) throws IOException {\n" + >+ " File file2 = new File(\"somefile\");\n" + >+ " FileInputStream fileStream2 = new FileInputStream(file2);\n" + >+ " BufferedInputStream bis2 = new BufferedInputStream(fileStream2);\n" + >+ " BufferedInputStream doubleWrap2 = new BufferedInputStream(bis2);\n" + >+ " System.out.println(bis2.available());\n" + >+ " if (b)\n" + >+ " doubleWrap2.close();\n" + >+ " }\n" + >+ " void potAndDef(boolean b) throws IOException {\n" + >+ " File file3 = new File(\"somefile\");\n" + >+ " FileInputStream fileStream3 = new FileInputStream(file3);\n" + >+ " BufferedInputStream bis3 = new BufferedInputStream(fileStream3);\n" + >+ " BufferedInputStream doubleWrap3 = new BufferedInputStream(bis3);\n" + >+ " System.out.println(doubleWrap3.available());\n" + >+ " if (b) bis3.close();\n" + >+ " fileStream3.close();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 10)\n" + >+ " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + >+ " ^^^^^^^^^^\n" + >+ "Potential resource leak: \'doubleWrap\' may not be closed\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 19)\n" + >+ " BufferedInputStream doubleWrap2 = new BufferedInputStream(bis2);\n" + >+ " ^^^^^^^^^^^\n" + >+ "Potential resource leak: \'doubleWrap2\' may not be closed\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+// Bug 358903 - Filter practically unimportant resource leak warnings >+// local var is re-used for two levels of wrappers >+public void test061i() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runNegativeTest( >+ new String[] { >+ "X.java", >+ "import java.io.File;\n" + >+ "import java.io.InputStream;\n" + >+ "import java.io.BufferedInputStream;\n" + >+ "import java.io.FileInputStream;\n" + >+ "import java.io.IOException;\n" + >+ "public class X {\n" + >+ " void closeMiddle() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " InputStream stream = new FileInputStream(file);\n" + >+ " stream = new BufferedInputStream(stream);\n" + >+ " InputStream middle;\n" + >+ " stream = new BufferedInputStream(middle = stream);\n" + >+ " System.out.println(stream.available());\n" + >+ " middle.close();\n" + >+ " }\n" + >+ " void closeOuter() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " InputStream stream = new FileInputStream(file);\n" + >+ " stream = new BufferedInputStream(stream);\n" + >+ " stream = new BufferedInputStream(stream);\n" + >+ " System.out.println(stream.available());\n" + >+ " stream.close();\n" + >+ " }\n" + >+ " void neverClosed() throws IOException {\n" + >+ " File file = new File(\"somefile\");\n" + >+ " InputStream stream = new FileInputStream(file);\n" + >+ " stream = new BufferedInputStream(stream);\n" + >+ " stream = new BufferedInputStream(stream);\n" + >+ " System.out.println(stream.available());\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 26)\n" + >+ " InputStream stream = new FileInputStream(file);\n" + >+ " ^^^^^^\n" + >+ "Resource leak: \'stream\' is never closed\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} > public static Class testClass() { > return TryWithResourcesStatementTest.class; > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java >index e811ea9..0a64324 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -45,7 +45,7 @@ > if (this.arguments != null) { > for (int i = 0, count = this.arguments.length; i < count; i++) { > // if argument is an AutoCloseable insert info that it *may* be closed (by the target method, i.e.) >- flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo); >+ flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo, this.resolvedType); > flowInfo = > this.arguments[i] > .analyseCode(currentScope, flowContext, flowInfo) >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 970fc24..f0dcb9e 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 >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -46,19 +46,22 @@ > if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) { > this.expression.checkNPE(currentScope, flowContext, flowInfo); > } >+ >+ FlowInfo preInitInfo = null; >+ boolean shouldAnalyseResource = local != null >+ && flowInfo.reachMode() == FlowInfo.REACHABLE >+ && (FakedTrackingVariable.isAutoCloseable(this.expression.resolvedType) >+ || this.expression.resolvedType.id == TypeIds.T_null); >+ if (shouldAnalyseResource) >+ preInitInfo = flowInfo.unconditionalCopy(); >+ > flowInfo = ((Reference) this.lhs) > .analyseAssignment(currentScope, flowContext, flowInfo, this, false) > .unconditionalInits(); >- if (local != null) { >- LocalVariableBinding previousTrackerBinding = null; >- if (local.closeTracker != null) { >- // Assigning to a variable already holding an AutoCloseable, has it been closed before? >- previousTrackerBinding = local.closeTracker.binding; >- if (!flowInfo.isDefinitelyNull(local)) // only if previous value may be non-null >- local.closeTracker.recordErrorLocation(this, flowInfo.nullStatus(previousTrackerBinding)); >- } >- FakedTrackingVariable.handleResourceAssignment(flowInfo, this, this.expression, local, previousTrackerBinding); >- } >+ >+ if (shouldAnalyseResource) >+ FakedTrackingVariable.handleResourceAssignment(preInitInfo, flowInfo, this, this.expression, local); >+ > int nullStatus = this.expression.nullStatus(flowInfo); > if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { > if (nullStatus == FlowInfo.NULL) { >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 ee13047..6dcf2dc 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 >@@ -48,9 +48,11 @@ > private static final int CLOSED_IN_NESTED_METHOD = 4; > // a location independent issue has been reported already against this resource: > private static final int REPORTED = 8; >+ // a resource is wrapped in another resource: >+ private static final int WRAPPED = 16; > > /** >- * Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD} and {@link #REPORTED}. >+ * Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD}, {@link #REPORTED} and {@link #WRAPPED}. > */ > private int globalClosingState = 0; > >@@ -58,10 +60,19 @@ > > public LocalVariableBinding originalBinding; // the real local being tracked > >- HashMap recordedLocations; // initially null, ASTNode -> Integer >+ HashMap recordedLocations; // initially null, ASTNode -> Integer > >+ public FakedTrackingVariable innerTracker; // chained tracking variable of a chained (wrapped) resource > >- public FakedTrackingVariable(LocalVariableBinding original, Statement location) { >+ // marker instance to signal this pattern: resource = new Wrapper(resource); >+ final private static FakedTrackingVariable SELF_WRAP = new FakedTrackingVariable(); >+ >+ private FakedTrackingVariable() { >+ super(null, 0, 0); >+ /* empty ctor for marker instance */ >+ } >+ >+ public FakedTrackingVariable(LocalVariableBinding original, ASTNode location) { > super(original.name, location.sourceStart, location.sourceEnd); > this.type = new SingleTypeReference( > TypeConstants.OBJECT, >@@ -129,36 +140,245 @@ > > /** > * Check if the rhs of an assignment or local declaration is an (Auto)Closeable. >- * If so create or re-use a tracking variable, and wire and initialize everything. >+ * If so create or re-use a tracking variable, and wire and initialize everything. >+ * @param upstreamInfo info without analysis of the rhs, use this to determine the status of a resource being disconnected >+ * @param flowInfo info with analysis of the rhs, use this for recording resource status because this will be passed downstream >+ * @param location where to report warnigs/errors against >+ * @param rhs the right hand side of the assignment, this expression is to be analyzed. >+ * The caller has already checked that the rhs is either of a closeable type or null. >+ * @param local the local variable into which the rhs is being assigned > */ >- public static void handleResourceAssignment(FlowInfo flowInfo, Statement location, Expression rhs, LocalVariableBinding local, >- LocalVariableBinding previousTrackerBinding) >+ public static void handleResourceAssignment(FlowInfo upstreamInfo, FlowInfo flowInfo, ASTNode location, Expression rhs, LocalVariableBinding local) > { >- if (isAutoCloseable(rhs.resolvedType)) { >+ FakedTrackingVariable previousTracker = null; >+ FakedTrackingVariable disconnectedTracker = null; >+ if (local.closeTracker != null) { >+ // assigning to a variable already holding an AutoCloseable, has it been closed before? >+ previousTracker = local.closeTracker; >+ int status = upstreamInfo.nullStatus(local); >+ if (status != FlowInfo.NULL && status != FlowInfo.UNKNOWN) // only if previous value may be relevant >+ disconnectedTracker = previousTracker; >+ } >+ >+ if (rhs.resolvedType.id != TypeIds.T_null) { > // new value is AutoCloseable, start tracking, possibly re-using existing tracker var: >- > FakedTrackingVariable rhsTrackVar = getCloseTrackingVariable(rhs); > if (rhsTrackVar != null) { // 1. share tracking variable with RHS? > local.closeTracker = rhsTrackVar; > // keep null-status unchanged across this assignment >- } else if (previousTrackerBinding != null) { // 2. re-use tracking variable from the LHS? >+ } else if (previousTracker != null) { // 2. re-use tracking variable from the LHS? > // re-assigning from a fresh, mark as not-closed again: >- flowInfo.markAsDefinitelyNull(previousTrackerBinding); >+ flowInfo.markAsDefinitelyNull(previousTracker.binding); >+ local.closeTracker = analyseCloseableExpression(flowInfo, local, location, rhs, previousTracker); >+ if (local.closeTracker == SELF_WRAP) { >+ local.closeTracker = previousTracker; >+ return; // avoid calling recordErrorLocation below >+ } > } else { // 3. no re-use, create a fresh tracking variable: >- local.closeTracker = new FakedTrackingVariable(local, location); >- // a fresh resource, mark as not-closed: >- flowInfo.markAsDefinitelyNull(local.closeTracker.binding); >+ rhsTrackVar = analyseCloseableExpression(flowInfo, local, location, rhs, null); >+ if (rhsTrackVar != null) { >+ local.closeTracker = rhsTrackVar; >+ // a fresh resource, mark as not-closed: >+ flowInfo.markAsDefinitelyNull(local.closeTracker.binding); > // TODO(stephan): this might be useful, but I could not find a test case for it: >-// if (flowContext.initsOnFinally != null) >-// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); >+// if (flowContext.initsOnFinally != null) >+// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); >+ } > } > } >+ >+ if (disconnectedTracker != null) >+ disconnectedTracker.recordErrorLocation(location, upstreamInfo.nullStatus(disconnectedTracker.binding)); >+ } >+ /** >+ * analyze structure of a closeable expression, matching (chained) resources against our white lists. >+ * See Bug 358903 - Filter practically unimportant resource leak warnings >+ * @param flowInfo used when recursing back into {@link #handleResourceAssignment} >+ * @param local local variable to which the closeable is being assigned >+ * @param location where to flag errors/warnings against >+ * @param expression expression to be analyzed >+ * @param previousTracker when analyzing a re-assignment we may already have a tracking variable for local, >+ * which we should then re-use >+ * @return a tracking variable associated with local or null if no need to track >+ */ >+ private static FakedTrackingVariable analyseCloseableExpression(FlowInfo flowInfo, >+ LocalVariableBinding local, ASTNode location, Expression expression, FakedTrackingVariable previousTracker) >+ { >+ if (expression.resolvedType instanceof ReferenceBinding) { >+ ReferenceBinding resourceType = (ReferenceBinding) expression.resolvedType; >+ >+ if (resourceType.hasTypeBit(TypeIds.BitResourceFreeCloseable)) { >+ // (a) resource-free closeable: -> null >+ return null; >+ } >+ >+ if (resourceType.hasTypeBit(TypeIds.BitWrapperCloseable)) { >+ // (b) wrapper >+ Expression innerExpression = expression; >+ if (innerExpression instanceof Assignment) >+ innerExpression = ((Assignment)innerExpression).expression; >+ if (innerExpression instanceof AllocationExpression) { >+ Expression[] args = ((AllocationExpression) innerExpression).arguments; >+ if (args != null && args.length == 1) { >+ // (b.1) wrapper allocation with argument >+ return analyseCloseableAllocationArgument(flowInfo, local, location, args[0], previousTracker); >+ } >+ } >+ LocalVariableBinding innerLocal = innerExpression.localVariableBinding(); >+ if (innerLocal != null && innerLocal.closeTracker != null) { >+ FakedTrackingVariable outerTracker = previousTracker != null ? previousTracker : new FakedTrackingVariable(local, location); >+ outerTracker.innerTracker = innerLocal.closeTracker; >+ innerLocal.closeTracker.globalClosingState |= WRAPPED; >+ return outerTracker; >+ } >+ // (b.2) wrapper with irrelevant inner: -> null >+ return null; >+ } >+ } >+ if (local.closeTracker != null) >+ // (c): inner has already been analysed: -> re-use track var >+ return local.closeTracker; >+ // (d): normal resource: -> normal tracking var >+ if (previousTracker != null) >+ return previousTracker; // (d.1): re-use existing tracking var >+ return new FakedTrackingVariable(local, location); >+ } >+ >+ // an outer allocation expression has an argument, recursively analyze whether the arg is closeable >+ // return (1) a possible nested tracker for the outer expression or (2) null signaling no relevant resource contained >+ static FakedTrackingVariable analyseCloseableAllocationArgument(FlowInfo flowInfo, LocalVariableBinding outerLocal, ASTNode outerLocation, >+ Expression arg, FakedTrackingVariable previousTracker) >+ { >+ if (arg instanceof Assignment) { >+ Assignment assign = (Assignment)arg; >+ LocalVariableBinding innerLocal = assign.localVariableBinding(); >+ if (innerLocal != null) { >+ // nested assignment has already been processed >+ if (innerLocal.closeTracker != null && innerLocal.closeTracker.originalBinding == outerLocal) >+ return SELF_WRAP; // signal special case to our caller: resource = new Wrapper(resource); >+ return innerLocal.closeTracker; // FIXME do we need a nested tracker here? see test061a/e >+ } else { >+ arg = assign.expression; // unwrap assignment and fall through >+ } >+ } >+ if (arg instanceof SingleNameReference) { >+ SingleNameReference ref = (SingleNameReference) arg; >+ if (ref.binding instanceof LocalVariableBinding) { >+ // allocation arg is a reference to an existing closeable? >+ return getTrackingVarForNested(flowInfo, outerLocal, outerLocation, (LocalVariableBinding)ref.binding, ref, ref, previousTracker); >+ } >+ } else if (arg instanceof AllocationExpression && arg.resolvedType instanceof ReferenceBinding) { >+ // nested allocation >+ ReferenceBinding innerType = (ReferenceBinding)arg.resolvedType; >+ if (innerType.hasTypeBit(TypeIds.BitResourceFreeCloseable)) { >+ return null; // leaf of wrapper-chain is irrelevant >+ } else if (innerType.hasTypeBit(TypeIds.BitWrapperCloseable)) { >+ // nested wrapper -> nested tracking variables may skip this level as it is not bound to a local variable >+ Expression[] args = ((AllocationExpression) arg).arguments; >+ if (args != null && args.length > 0) >+ return analyseCloseableAllocationArgument(flowInfo, outerLocal, arg, args[0], previousTracker); >+ return null; // wrapper with no arg? shouldn't occur actually >+ } else { >+ // (c) wrapper alloc with direct nested alloc of regular: -> normal track var (no local represents inner) >+ return previousTracker != null ? previousTracker : new FakedTrackingVariable(outerLocal, outerLocation); >+ } >+ } >+ return null; >+ } >+ >+ // an outer allocation expression has an argument, create/link outer and inner tracking variable and return the outer >+ // return null if inner is not tracked >+ private static FakedTrackingVariable getTrackingVarForNested(FlowInfo flowInfo, LocalVariableBinding outerLocal, ASTNode outerLocation, >+ LocalVariableBinding innerLocal, ASTNode innerLocation, Expression innerExpression, FakedTrackingVariable previousTracker) >+ { >+ if (outerLocal == innerLocal) >+ return SELF_WRAP; >+ FakedTrackingVariable innerTracker = analyseCloseableExpression(flowInfo, innerLocal, innerLocation, innerExpression, null); >+ if (innerTracker == SELF_WRAP) >+ return SELF_WRAP; >+ if (innerTracker != null) { >+ FakedTrackingVariable outerTracker = previousTracker != null ? previousTracker : new FakedTrackingVariable(outerLocal, outerLocation); >+ outerTracker.innerTracker = innerTracker; >+ innerTracker.globalClosingState |= WRAPPED; >+ return outerTracker; >+ } >+ return null; > } > > /** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ > public static boolean isAutoCloseable(TypeBinding typeBinding) { > return typeBinding instanceof ReferenceBinding > && ((ReferenceBinding)typeBinding).hasTypeBit(TypeIds.BitAutoCloseable|TypeIds.BitCloseable); >+ } >+ >+ public int findMostSpecificStatus(FlowInfo flowInfo, BlockScope currentScope, BlockScope locationScope) { >+ int status = FlowInfo.UNKNOWN; >+ FakedTrackingVariable currentTracker = this; >+ // loop as to consider wrappers (per white list) encapsulating an inner resource. >+ while (currentTracker != null) { >+ LocalVariableBinding currentVar = currentTracker.binding; >+ int currentStatus = getNullStatusAggressively(currentVar, flowInfo); >+ if (locationScope != null) // only check at method exit points >+ currentStatus = mergeCloseStatus(locationScope, currentStatus, currentVar, currentScope); >+ if (currentStatus == FlowInfo.NON_NULL) { >+ status = currentStatus; >+ break; // closed -> stop searching >+ } else if (status == FlowInfo.NULL || status == FlowInfo.UNKNOWN) { >+ status = currentStatus; // improved although not yet safe -> keep searching for better >+ } >+ currentTracker = currentTracker.innerTracker; >+ } >+ return status; >+ } >+ >+ /** >+ * Get the null status looking even into unreachable flows >+ * @param local >+ * @param flowInfo >+ * @return one of the constants FlowInfo.{NULL,POTENTIALLY_NULL,POTENTIALLY_NON_NULL,NON_NULL}. >+ */ >+ private int getNullStatusAggressively(LocalVariableBinding local, FlowInfo flowInfo) { >+ int reachMode = flowInfo.reachMode(); >+ int status = 0; >+ try { >+ // unreachable flowInfo is too shy in reporting null-issues, temporarily forget reachability: >+ if (reachMode != FlowInfo.REACHABLE) >+ flowInfo.tagBits &= ~FlowInfo.UNREACHABLE; >+ status = flowInfo.nullStatus(local); >+ } finally { >+ // reset >+ flowInfo.tagBits |= reachMode; >+ } >+ // at this point some combinations are not useful so flatten to a single bit: >+ if ((status & FlowInfo.NULL) != 0) { >+ if ((status & (FlowInfo.NON_NULL | FlowInfo.POTENTIALLY_NON_NULL)) != 0) >+ return FlowInfo.POTENTIALLY_NULL; // null + doubt = pot null >+ return FlowInfo.NULL; >+ } else if ((status & FlowInfo.NON_NULL) != 0) { >+ if ((status & FlowInfo.POTENTIALLY_NULL) != 0) >+ return FlowInfo.POTENTIALLY_NULL; // non-null + doubt = pot null >+ return FlowInfo.NON_NULL; >+ } else if ((status & FlowInfo.POTENTIALLY_NULL) != 0) >+ return FlowInfo.POTENTIALLY_NULL; >+ return status; >+ } >+ >+ public int mergeCloseStatus(BlockScope currentScope, int status, LocalVariableBinding local, BlockScope outerScope) { >+ // get the most suitable null status representing whether resource 'binding' has been closed >+ // start at 'currentScope' and potentially travel out until 'outerScope' >+ // at each scope consult any recorded 'finallyInfo'. >+ if (status != FlowInfo.NON_NULL) { >+ if (currentScope.finallyInfo != null) { >+ int finallyStatus = currentScope.finallyInfo.nullStatus(local); >+ if (finallyStatus == FlowInfo.NON_NULL) >+ return finallyStatus; >+ if (finallyStatus != FlowInfo.NULL) // neither is NON_NULL, but not both are NULL => call it POTENTIALLY_NULL >+ status = FlowInfo.POTENTIALLY_NULL; >+ } >+ if (currentScope != outerScope && currentScope.parent instanceof BlockScope) >+ return mergeCloseStatus(((BlockScope) currentScope.parent), status, local, outerScope); >+ } >+ return status; > } > > /** Mark that this resource is closed locally. */ >@@ -180,7 +400,11 @@ > * (as argument to a method/ctor call or as a return value from the current method), > * and thus should be considered as potentially closed. > */ >- public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo) { >+ public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo, TypeBinding allocatedType) { >+ if ((allocatedType instanceof ReferenceBinding) >+ && ((ReferenceBinding) allocatedType).hasTypeBit(TypeIds.BitWrapperCloseable)) >+ return flowInfo; // wrapped closeables are analyzed separately: >+ > FakedTrackingVariable trackVar = getCloseTrackingVariable(expression); > if (trackVar != null) { > trackVar.globalClosingState |= PASSED_TO_OUTSIDE; >@@ -201,9 +425,14 @@ > } > > public boolean reportRecordedErrors(Scope scope) { >- if (this.globalClosingState == 0) { >- reportError(scope.problemReporter(), null, FlowInfo.NULL); >- return true; >+ FakedTrackingVariable current = this; >+ while (current.globalClosingState == 0) { >+ current = current.innerTracker; >+ if (current == null) { >+ // no relevant state found -> report: >+ reportError(scope.problemReporter(), null, FlowInfo.NULL); >+ return true; >+ } > } > boolean hasReported = false; > if (this.recordedLocations != null) { >@@ -218,6 +447,8 @@ > } > > public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) { >+ if ((this.globalClosingState & WRAPPED) != 0) >+ return; > if (nullStatus == FlowInfo.NULL) { > if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0) > problemReporter.potentiallyUnclosedCloseable(this, location); >@@ -225,7 +456,7 @@ > problemReporter.unclosedCloseable(this, location); > } else if (nullStatus == FlowInfo.POTENTIALLY_NULL) { > problemReporter.potentiallyUnclosedCloseable(this, location); >- } >+ } > } > > public void reportExplicitClosing(ProblemReporter problemReporter) { >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 dac029c..0088944 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 >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -74,11 +74,21 @@ > this.initialization.checkNPE(currentScope, flowContext, flowInfo); > } > >+ FlowInfo preInitInfo = null; >+ boolean shouldAnalyseResource = this.binding != null >+ && flowInfo.reachMode() == FlowInfo.REACHABLE >+ && FakedTrackingVariable.isAutoCloseable(this.initialization.resolvedType); >+ if (shouldAnalyseResource) >+ preInitInfo = flowInfo.unconditionalCopy(); >+ > flowInfo = > this.initialization > .analyseCode(currentScope, flowContext, flowInfo) > .unconditionalInits(); >- FakedTrackingVariable.handleResourceAssignment(flowInfo, this, this.initialization, this.binding, null); >+ >+ if (shouldAnalyseResource) >+ FakedTrackingVariable.handleResourceAssignment(preInitInfo, flowInfo, this, this.initialization, this.binding); >+ > int nullStatus = this.initialization.nullStatus(flowInfo); > if (!flowInfo.isDefinitelyAssigned(this.binding)){// for local variable debug attributes > this.bits |= FirstAssignmentToLocal; >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 46d4540..eae974b 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 >@@ -97,7 +97,7 @@ > this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); > } > // if argument is an AutoCloseable insert info that it *may* be closed (by the target method, i.e.) >- flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo); >+ flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo, null); > flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); > } > analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java >index 3a2a0ed..7169032 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java >@@ -81,7 +81,7 @@ > if (this.arguments != null) { > for (int i = 0, count = this.arguments.length; i < count; i++) { > // if argument is an AutoCloseable insert info that it *may* be closed (by the target method, i.e.) >- flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo); >+ flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo, null); > flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo); > if ((this.arguments[i].implicitConversion & TypeIds.UNBOXING) != 0) { > this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java >index 9294726..e65acae 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -1275,7 +1275,9 @@ > this.environment.mayTolerateMissingType = wasToleratingMissingTypeProcessingAnnotations; > } > } >- this.typeBits |= this.superclass.typeBits; >+ this.typeBits |= (this.superclass.typeBits & TypeIds.InheritableBits); >+ if ((this.typeBits & (TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) // avoid the side-effects of hasTypeBit()! >+ this.typeBits |= applyCloseableWhitelists(); > return this.superclass; > } > // NOTE: superInterfaces of binary types are resolved when needed >@@ -1298,7 +1300,7 @@ > this.environment.mayTolerateMissingType = wasToleratingMissingTypeProcessingAnnotations; > } > } >- this.typeBits |= this.superInterfaces[i].typeBits; >+ this.typeBits |= (this.superInterfaces[i].typeBits & TypeIds.InheritableBits); > } > this.tagBits &= ~TagBits.HasUnresolvedSuperinterfaces; > return this.superInterfaces; >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 c93c9d9..afaabfd 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 >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -1003,10 +1003,10 @@ > FakedTrackingVariable trackingVar = (FakedTrackingVariable) this.trackingVariables.get(i); > if (location != null && trackingVar.originalBinding != null && flowInfo.isDefinitelyNull(trackingVar.originalBinding)) > continue; // reporting against a specific location, resource is null at this flow, don't complain >- int status = getNullStatusAggressively(trackingVar.binding, flowInfo); >- // try to improve info if a close() inside finally was observed: >- if (locationScope != null) // only check at method exit points >- status = locationScope.mergeCloseStatus(status, trackingVar.binding, this); >+ >+ // compute the most specific null status for this resource, >+ int status = trackingVar.findMostSpecificStatus(flowInfo, this, locationScope); >+ > if (status == FlowInfo.NULL) { > // definitely unclosed: highest priority > reportResourceLeak(trackingVar, location, status); >@@ -1033,24 +1033,6 @@ > this.locals[i].closeTracker = null; > this.trackingVariables = null; > } >-} >- >-private int mergeCloseStatus(int status, LocalVariableBinding binding, BlockScope outerScope) { >- // get the most suitable null status representing whether resource 'binding' has been closed >- // start at this scope and potentially travel out until 'outerScope' >- // at each scope consult any recorded 'finallyInfo'. >- if (status != FlowInfo.NON_NULL) { >- if (this.finallyInfo != null) { >- int finallyStatus = this.finallyInfo.nullStatus(binding); >- if (finallyStatus == FlowInfo.NON_NULL) >- return finallyStatus; >- if (finallyStatus != FlowInfo.NULL) // neither is NON_NULL, but not both are NULL => call it POTENTIALLY_NULL >- status = FlowInfo.POTENTIALLY_NULL; >- } >- if (this != outerScope && this.parent instanceof BlockScope) >- return ((BlockScope) this.parent).mergeCloseStatus(status, binding, outerScope); >- } >- return status; > } > > private void reportResourceLeak(FakedTrackingVariable trackingVar, ASTNode location, int nullStatus) { >@@ -1097,37 +1079,5 @@ > } > if (this.parent instanceof BlockScope) > ((BlockScope) this.parent).correlateTrackingVarsIfElse(thenFlowInfo, elseFlowInfo); >-} >- >-/** >- * Get the null status looking even into unreachable flows >- * @param local >- * @param flowInfo >- * @return one of the constants FlowInfo.{NULL,POTENTIALLY_NULL,POTENTIALLY_NON_NULL,NON_NULL}. >- */ >-private int getNullStatusAggressively(LocalVariableBinding local, FlowInfo flowInfo) { >- int reachMode = flowInfo.reachMode(); >- int status = 0; >- try { >- // unreachable flowInfo is too shy in reporting null-issues, temporarily forget reachability: >- if (reachMode != FlowInfo.REACHABLE) >- flowInfo.tagBits &= ~FlowInfo.UNREACHABLE; >- status = flowInfo.nullStatus(local); >- } finally { >- // reset >- flowInfo.tagBits |= reachMode; >- } >- // at this point some combinations are not useful so flatten to a single bit: >- if ((status & FlowInfo.NULL) != 0) { >- if ((status & (FlowInfo.NON_NULL | FlowInfo.POTENTIALLY_NON_NULL)) != 0) >- return FlowInfo.POTENTIALLY_NULL; // null + doubt = pot null >- return FlowInfo.NULL; >- } else if ((status & FlowInfo.NON_NULL) != 0) { >- if ((status & FlowInfo.POTENTIALLY_NULL) != 0) >- return FlowInfo.POTENTIALLY_NULL; // non-null + doubt = pot null >- return FlowInfo.NON_NULL; >- } else if ((status & FlowInfo.POTENTIALLY_NULL) != 0) >- return FlowInfo.POTENTIALLY_NULL; >- return status; > } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java >index 3eb3cca..1743015 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -909,7 +909,10 @@ > } else { > // only want to reach here when no errors are reported > sourceType.superclass = superclass; >- sourceType.typeBits |= superclass.typeBits; >+ sourceType.typeBits |= (superclass.typeBits & TypeIds.InheritableBits); >+ // further analysis against white lists for the unlikely case we are compiling java.io.*: >+ if (((sourceType.typeBits & TypeIds.BitAutoCloseable|TypeIds.BitCloseable)) != 0) >+ sourceType.typeBits |= sourceType.applyCloseableWhitelists(); > return true; > } > } >@@ -1025,7 +1028,7 @@ > noProblems &= superInterfaceRef.resolvedType.isValidBinding(); > } > // only want to reach here when no errors are reported >- sourceType.typeBits |= superInterface.typeBits; >+ sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits); > interfaceBindings[count++] = superInterface; > } > // hold onto all correctly resolved superinterfaces >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java >index 75de854..057fb4f 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java >@@ -1469,4 +1469,31 @@ > public FieldBinding[] unResolvedFields() { > return Binding.NO_FIELDS; > } >+ >+/* >+ * If a type - known to be a Closeable - is mentioned in one of our white lists >+ * answer the typeBit for the white list (BitWrapperCloseable or BitResourceFreeCloseable). >+ */ >+protected int applyCloseableWhitelists() { >+ switch (this.compoundName.length) { >+ case 3: >+ if (CharOperation.equals(TypeConstants.JAVA, this.compoundName[0])) { >+ if (CharOperation.equals(TypeConstants.IO, this.compoundName[1])) { >+ char[] simpleName = this.compoundName[2]; >+ int l = TypeConstants.JAVA_IO_WRAPPER_CLOSEABLES.length; >+ for (int i = 0; i < l; i++) { >+ if (CharOperation.equals(simpleName, TypeConstants.JAVA_IO_WRAPPER_CLOSEABLES[i])) >+ return TypeIds.BitWrapperCloseable; >+ } >+ l = TypeConstants.JAVA_IO_RESOURCE_FREE_CLOSEABLES.length; >+ for (int i = 0; i < l; i++) { >+ if (CharOperation.equals(simpleName, TypeConstants.JAVA_IO_RESOURCE_FREE_CLOSEABLES[i])) >+ return TypeIds.BitResourceFreeCloseable; >+ } >+ } >+ } >+ break; >+ } >+ return 0; >+} > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java >index 0bbef47..07913fc 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -154,6 +154,24 @@ > }; > char[][] JAVA_LANG_AUTOCLOSEABLE = {JAVA, LANG, "AutoCloseable".toCharArray()}; //$NON-NLS-1$ > char[] CLOSE = "close".toCharArray(); //$NON-NLS-1$ >+ // white lists of closeables: >+ char[][] JAVA_IO_WRAPPER_CLOSEABLES = new char[][] { >+ "BufferedInputStream".toCharArray(), //$NON-NLS-1$ >+ "BufferedOutputStream".toCharArray(), //$NON-NLS-1$ >+ "BufferedReader".toCharArray(), //$NON-NLS-1$ >+ "BufferedWriter".toCharArray(), //$NON-NLS-1$ >+ "InputStreamReader".toCharArray(), //$NON-NLS-1$ >+ "PrintWriter".toCharArray(), //$NON-NLS-1$ >+ "LineNumberReader".toCharArray(), //$NON-NLS-1$ >+ }; >+ char[][] JAVA_IO_RESOURCE_FREE_CLOSEABLES = new char[][] { >+ "StringReader".toCharArray(), //$NON-NLS-1$ >+ "StringWriter".toCharArray(), //$NON-NLS-1$ >+ "ByteArrayInputStream".toCharArray(), //$NON-NLS-1$ >+ "ByteArrayOutputStream".toCharArray(), //$NON-NLS-1$ >+ "CharArrayReader".toCharArray(), //$NON-NLS-1$ >+ "CharArrayWriter".toCharArray(), //$NON-NLS-1$ >+ }; > > // Constraints for generic type argument inference > int CONSTRAINT_EQUAL = 0; // Actual = Formal >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java >index 7fff434..d6ec945 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -207,4 +207,19 @@ > * @see ReferenceBinding#hasTypeBit(int) > */ > final int BitCloseable = 2; >+ /** >+ * Bit for members of a white list: >+ * Subtypes of Closeable that wrap another resource without directly holding any OS resources. >+ */ >+ final int BitWrapperCloseable = 4; >+ /** >+ * Bit for members of a white list: >+ * Subtypes of Closeable that do not hold an OS resource that needs to be released. >+ */ >+ final int BitResourceFreeCloseable = 8; >+ >+ /** >+ * Set of type bits that should be inherited by any sub types. >+ */ >+ final int InheritableBits = BitAutoCloseable | BitCloseable; > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java >index 995f30e..bd698f3 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -316,11 +316,11 @@ > // initialize from bounds > this.typeBits = 0; > if (this.superclass != null && this.superclass.hasTypeBit(~TypeIds.BitUninitialized)) >- this.typeBits |= this.superclass.typeBits; >+ this.typeBits |= (this.superclass.typeBits & TypeIds.InheritableBits); > if (this.superInterfaces != null) > for (int i = 0, l = this.superInterfaces.length; i < l; i++) > if (this.superInterfaces[i].hasTypeBit(~TypeIds.BitUninitialized)) >- this.typeBits |= this.superInterfaces[i].typeBits; >+ this.typeBits |= (this.superInterfaces[i].typeBits & TypeIds.InheritableBits); > } > return (this.typeBits & bit) != 0; > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java >index 01f369e..b887196 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2005, 2011 IBM Corporation and others. >+ * Copyright (c) 2005, 2012 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -429,11 +429,11 @@ > // initialize from upper bounds > this.typeBits = 0; > if (this.superclass != null && this.superclass.hasTypeBit(~TypeIds.BitUninitialized)) >- this.typeBits |= this.superclass.typeBits; >+ this.typeBits |= (this.superclass.typeBits & TypeIds.InheritableBits); > if (this.superInterfaces != null) > for (int i = 0, l = this.superInterfaces.length; i < l; i++) > if (this.superInterfaces[i].hasTypeBit(~TypeIds.BitUninitialized)) >- this.typeBits |= this.superInterfaces[i].typeBits; >+ this.typeBits |= (this.superInterfaces[i].typeBits & TypeIds.InheritableBits); > } > return (this.typeBits & bit) != 0; > }
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 358903
:
208899
|
209082
|
209107
|
209169
|
209179
|
209232
|
209234
|
209276
|
209373
|
209374
|
209384
|
209409
|
209457
|
209460
|
209464
|
209472
|
209482