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 210478 Details for
Bug 368546
[compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Tests & fix
Bug_368546.patch (text/plain), 68.22 KB, created by
Stephan Herrmann
on 2012-02-02 18:13:41 EST
(
hide
)
Description:
Tests & fix
Filename:
MIME Type:
Creator:
Stephan Herrmann
Created:
2012-02-02 18:13:41 EST
Size:
68.22 KB
patch
obsolete
>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 c587eaf..f324263 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[] { "testBug368709"}; >+// TESTS_NAMES = new String[] { "test074"}; > // TESTS_NUMBERS = new int[] { 50 }; > // TESTS_RANGE = new int[] { 11, -1 }; > } >@@ -1489,7 +1489,7 @@ public void test056y() { > " final FileReader reader23 = new FileReader(\"file\");\n" + > " provider = new ResourceProvider() {\n" + > " public FileReader provide() {\n" + >- " return reader23;\n" + >+ " return reader23;\n" + // responsibility now lies at the caller of this method > " }\n" + > " };\n" + > " }\n" + >@@ -1500,11 +1500,6 @@ public void test056y() { > " final FileReader reader31 = new FileReader(\"file\");\n" + > " ^^^^^^^^\n" + > "Potential resource leak: 'reader31' may not be closed\n" + >- "----------\n" + >- "2. WARNING in X.java (at line 17)\n" + >- " final FileReader reader23 = new FileReader(\"file\");\n" + >- " ^^^^^^^^\n" + >- "Potential resource leak: 'reader23' may not be closed\n" + > "----------\n", > null, > true, >@@ -3143,12 +3138,7 @@ public void testBug368709a() { > "}\n" > }, > "----------\n" + >- "1. ERROR in X.java (at line 15)\n" + >- " return wc.open(getObjectId(), type).openStream();\n" + >- " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + >- "Resource leak: \'in\' is not closed at this location\n" + >- "----------\n" + >- "2. ERROR in X.java (at line 18)\n" + >+ "1. ERROR in X.java (at line 18)\n" + > " return new ObjectStream.Filter(type, size, in);\n" + > " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + > "Potential resource leak: \'in\' may not be closed at this location\n" + >@@ -3189,4 +3179,509 @@ public void testBug368709b() { > true, > options); > } >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 3 >+public void test064() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ this.runConformTest(new String[] { >+ "Test064.java", >+ "import java.io.*;\n" + >+ "public class Test064 {\n" + >+ " void foo(File outfile) {\n" + >+ " OutputStream out= System.out;\n" + >+ " if (outfile != null) {\n" + >+ " try {\n" + >+ " out = new FileOutputStream(outfile);\n" + >+ " } catch (java.io.IOException e) {\n" + >+ " throw new RuntimeException(e);\n" + >+ " }\n" + >+ " }\n" + >+ " setOutput(out);\n" + >+ " }\n" + >+ " private void setOutput(OutputStream out) { }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 10 >+// disabled, because basic null-analysis machinery doesn't support this pattern >+// see also Bug 370424 - [compiler][null] throw-catch analysis for null flow could be more precise >+public void _test065() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test065.java", >+ "import java.io.*;\n" + >+ "class MyException extends Exception{}\n" + >+ "public class Test065 {\n" + >+ " void foo(String fileName) throws IOException, MyException {\n" + >+ " FileReader fileRead = new FileReader(fileName);\n" + >+ " BufferedReader bufRead = new BufferedReader(fileRead);\n" + >+ " LineNumberReader lineReader = new LineNumberReader(bufRead);\n" + >+ " try {\n" + >+ " while (lineReader.readLine() != null) {\n" + >+ " bufRead.close();\n" + >+ " callSome(); // only this can throw MyException\n" + >+ " }\n" + >+ " } catch (MyException e) {\n" + >+ " throw e; // Pot. leak reported here\n" + >+ " }\n" + >+ " bufRead.close(); \n" + >+ " }\n" + >+ " private void callSome() throws MyException\n" + >+ " {\n" + >+ " \n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 11 >+public void test066() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runNegativeTest(new String[] { >+ "Test066.java", >+ "import java.io.*;\n" + >+ "class MyException extends Exception{}\n" + >+ "public class Test066 {\n" + >+ " void countFileLines(String fileName) throws IOException {\n" + >+ " FileReader fileRead = new FileReader(fileName);\n" + >+ " BufferedReader bufRead = new BufferedReader(fileRead);\n" + >+ " LineNumberReader lineReader = new LineNumberReader(bufRead);\n" + >+ " while (lineReader.readLine() != null) {\n" + >+ " if (lineReader.markSupported())\n" + >+ " throw new IOException();\n" + >+ " bufRead.close();\n" + >+ " }\n" + >+ " bufRead.close();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in Test066.java (at line 10)\n" + >+ " throw new IOException();\n" + >+ " ^^^^^^^^^^^^^^^^^^^^^^^^\n" + >+ "Resource leak: \'lineReader\' is not closed at this location\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 11 - variant with closing top-level resource >+public void test066b() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runNegativeTest(new String[] { >+ "Test066.java", >+ "import java.io.*;\n" + >+ "class MyException extends Exception{}\n" + >+ "public class Test066 {\n" + >+ " void countFileLines(String fileName) throws IOException {\n" + >+ " FileReader fileRead = new FileReader(fileName);\n" + >+ " BufferedReader bufRead = new BufferedReader(fileRead);\n" + >+ " LineNumberReader lineReader = new LineNumberReader(bufRead);\n" + >+ " while (lineReader.readLine() != null) {\n" + >+ " if (lineReader.markSupported())\n" + >+ " throw new IOException();\n" + >+ " lineReader.close();\n" + >+ " }\n" + >+ " lineReader.close();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in Test066.java (at line 10)\n" + >+ " throw new IOException();\n" + >+ " ^^^^^^^^^^^^^^^^^^^^^^^^\n" + >+ "Potential resource leak: \'lineReader\' may not be closed at this location\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 12 >+// disabled because null info after try-catch is too weak, >+// see also Bug 370424 - [compiler][null] throw-catch analysis for null flow could be more precise >+public void _test067() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test067.java", >+ "import java.io.*;\n" + >+ "public class Test067 {\n" + >+ " public void comment12() throws IOException {\n" + >+ " LineNumberReader o = null;\n" + >+ " try {\n" + >+ " o = new LineNumberReader(null); \n" + >+ " } catch (NumberFormatException e) { \n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 12 >+// disabled because null info after try-catch is too weak, >+// see also Bug 370424 - [compiler][null] throw-catch analysis for null flow could be more precise >+public void _test067b() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test067.java", >+ "import java.io.*;\n" + >+ "public class Test067 {\n" + >+ " public void comment12b() throws IOException {\n" + >+ " LineNumberReader o = new LineNumberReader(null);\n" + >+ " try {\n" + >+ " o.close();\n" + >+ " } catch (NumberFormatException e) {\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 13 >+public void test068() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test068.java", >+ "import java.io.*;\n" + >+ "public class Test068 {\n" + >+ " class ProcessingStep extends OutputStream {\n" + >+ " public void write(int b) throws IOException {}\n" + >+ " public OutputStream getDestination() { return null; }\n" + >+ " }\n" + >+ " class ArtifactOutputStream extends OutputStream {\n" + >+ " public void write(int b) throws IOException {}\n" + >+ " }" + >+ " ArtifactOutputStream comment13(OutputStream stream) {\n" + >+ " OutputStream current = stream;\n" + >+ " while (current instanceof ProcessingStep)\n" + >+ " current = ((ProcessingStep) current).getDestination(); //warning\n" + >+ " if (current instanceof ArtifactOutputStream)\n" + >+ " return (ArtifactOutputStream) current;\n" + >+ " return null;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// example from comment 16 >+public void test069() { >+ if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // generics used >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test069.java", >+ "import java.io.*;\n" + >+ "import java.util.Collection;\n" + >+ "public class Test069 {\n" + >+ " class Profile {}\n" + >+ " class CoreException extends Exception {}\n" + >+ " void writeProfilesToStream(Collection<Profile> p, OutputStream s, String enc) {}\n" + >+ " CoreException createException(IOException ioex, String message) { return new CoreException(); }\n" + >+ " public void comment16(Collection<Profile> profiles, File file, String encoding) throws CoreException {\n" + >+ " final OutputStream stream;\n" + >+ " try {\n" + >+ " stream= new FileOutputStream(file);\n" + >+ " try {\n" + >+ " writeProfilesToStream(profiles, stream, encoding);\n" + >+ " } finally {\n" + >+ " try { stream.close(); } catch (IOException e) { /* ignore */ }\n" + >+ " }\n" + >+ " } catch (IOException e) {\n" + >+ " throw createException(e, \"message\"); // should not shout here\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// referenced in array initializer >+public void test070() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runNegativeTest(new String[] { >+ "Test070.java", >+ "import java.io.*;\n" + >+ "public class Test070 {\n" + >+ " void storeInArray(String fileName) throws IOException {\n" + >+ " FileReader fileRead = new FileReader(fileName);\n" + >+ " closeThemAll(new FileReader[] { fileRead });\n" + >+ " }\n" + >+ " void closeThemAll(FileReader[] readers) { }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in Test070.java (at line 4)\n" + >+ " FileReader fileRead = new FileReader(fileName);\n" + >+ " ^^^^^^^^\n" + >+ "Potential resource leak: \'fileRead\' may not be closed\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// referenced in array initializer >+public void test071() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runNegativeTest(new String[] { >+ "Test071.java", >+ "import java.io.*;\n" + >+ "public class Test071 {\n" + >+ " class ReaderHolder {\n" + >+ " FileReader reader;\n" + >+ " }\n" + >+ " private FileReader getReader() {\n" + >+ " return null;\n" + >+ " }\n" + >+ " void invokeCompiler(ReaderHolder readerHolder, boolean flag) throws FileNotFoundException {\n" + >+ " FileReader reader = readerHolder.reader;\n" + >+ " if (reader == null)\n" + >+ " reader = getReader();\n" + >+ " try {\n" + >+ " return;\n" + >+ " } finally {\n" + >+ " try {\n" + >+ " if (flag)\n" + >+ " reader.close();\n" + >+ " } catch (IOException e) {\n" + >+ " // nop\n" + >+ " }\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in Test071.java (at line 14)\n" + >+ " return;\n" + >+ " ^^^^^^^\n" + >+ "Potential resource leak: \'reader\' may not be closed at this location\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// referenced in array initializer >+// disabled because it would require correlation analysis between the tracking variable and its original >+// need to pass to downstream: either (nonnull & open) or (null) >+public void _test071b() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runNegativeTest(new String[] { >+ "Test071b.java", >+ "import java.io.*;\n" + >+ "public class Test071b {\n" + >+ " private FileReader getReader() {\n" + >+ " return null;\n" + >+ " }\n" + >+ " void invokeCompiler(boolean flag) throws FileNotFoundException {\n" + >+ " FileReader reader = null;\n" + >+ " if (flag)\n" + >+ " reader = new FileReader(\"file\");\n" + >+ " if (reader == null)\n" + >+ " reader = getReader();\n" + >+ " try {\n" + >+ " return;\n" + >+ " } finally {\n" + >+ " try {\n" + >+ " if (flag)\n" + >+ " reader.close();\n" + >+ " } catch (IOException e) {\n" + >+ " // nop\n" + >+ " }\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in Test071.java (at line 14)\n" + >+ " return;\n" + >+ " ^^^^^^^\n" + >+ "Potential resource leak: \'reader\' may not be closed at this location\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// throw inside loop inside try - while closed in finally >+public void test072() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test072.java", >+ "import java.io.*;\n" + >+ "public class Test072 {\n" + >+ " void readState(File file) {\n" + >+ " DataInputStream in = null;\n" + >+ " try {\n" + >+ " in= new DataInputStream(new BufferedInputStream(new FileInputStream(file)));\n" + >+ " int sizeOfFlags = in.readInt();\n" + >+ " for (int i = 0; i < sizeOfFlags; ++i) {\n" + >+ " String childPath = in.readUTF();\n" + >+ " if (childPath.length() == 0)\n" + >+ " throw new IOException();\n" + >+ " }\n" + >+ " }\n" + >+ " catch (IOException ioe) { /* nop */ }\n" + >+ " finally {\n" + >+ " if (in != null) {\n" + >+ " try {in.close();} catch (IOException ioe) {}\n" + >+ " }\n" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// unspecific parameter is casted into a resource, yet need to mark as OWNED_BY_OUTSIDE >+public void test073() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runConformTest(new String[] { >+ "Test073.java", >+ "import java.io.*;\n" + >+ "public class Test073 {\n" + >+ " String getEncoding(Object reader) {\n" + >+ " if (reader instanceof FileReader) {\n" + >+ " final FileReader fr = (FileReader) reader;\n" + >+ " return fr.getEncoding();\n" + >+ " }\n" + >+ " return null;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "", >+ null, >+ true, >+ null, >+ options, >+ null); >+} >+ >+// Bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK >+// status after nested try-finally >+public void test074() { >+ Map options = getCompilerOptions(); >+ options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); >+ options.put(CompilerOptions.OPTION_ReportMissingSerialVersion, CompilerOptions.IGNORE); >+ this.runNegativeTest(new String[] { >+ "Test074.java", >+ "import java.io.*;\n" + >+ "public class Test074 {\n" + >+ " void foo() throws FileNotFoundException {\n" + >+ " FileOutputStream out = null;\n" + >+ " try {\n" + >+ " out = new FileOutputStream(\"outfile\");\n" + >+ " } finally {\n" + >+ " try {\n" + >+ " out.flush();\n" + >+ " out.close();\n" + >+ " } catch (IOException e) {\n" + >+ " e.printStackTrace();\n" + >+ " }\n" + >+ " out = null;\n" + // unclosed if exception occurred on flush() >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in Test074.java (at line 14)\n" + >+ " out = null;\n" + >+ " ^^^^^^^^^^\n" + >+ "Potential resource leak: \'out\' may not be closed at this location\n" + >+ "----------\n", >+ null, >+ true, >+ options); >+} > } >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 fd64fd9..f97f464 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 >@@ -64,9 +64,6 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl > analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); > } > >- if (FakedTrackingVariable.isAnyCloseable(this.resolvedType)) >- FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this); >- > // record some dependency information for exception types > ReferenceBinding[] thrownExceptions; > if (((thrownExceptions = this.binding.thrownExceptions).length) != 0) { >@@ -81,6 +78,11 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl > flowInfo.unconditionalCopy(), > currentScope); > } >+ >+ // after having analysed exceptions above start tracking newly allocated resource: >+ if (FakedTrackingVariable.isAnyCloseable(this.resolvedType)) >+ FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this); >+ > if (this.binding.declaringClass.isMemberType() && !this.binding.declaringClass.isStatic()) { > // allocating a non-static member type without an enclosing instance of parent type > // https://bugs.eclipse.org/bugs/show_bug.cgi?id=335845 >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java >index afe4153..bab5bd3 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayInitializer.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2009 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 >@@ -34,6 +34,10 @@ public class ArrayInitializer extends Expression { > if (this.expressions != null) { > for (int i = 0, max = this.expressions.length; i < max; i++) { > flowInfo = this.expressions[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); >+ >+ if (FakedTrackingVariable.isAnyCloseable(this.expressions[i].resolvedType)) { >+ flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.expressions[i], flowInfo, false); >+ } > } > } > return flowInfo; >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java >index c61629e..aec6b38 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java >@@ -39,7 +39,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl > } > > if (this.explicitDeclarations > 0) // if block has its own scope analyze tracking vars now: >- this.scope.checkUnclosedCloseables(flowInfo, null, null); >+ this.scope.checkUnclosedCloseables(flowInfo, flowContext, null, null); > return flowInfo; > } > /** >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java >index 9b4cf8a..c90dc95 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java >@@ -177,7 +177,7 @@ public void analyseCode(ClassScope classScope, InitializationFlowContext initial > constructorContext.complainIfUnusedExceptionHandlers(this); > // check unused parameters > this.scope.checkUnusedParameters(this.binding); >- this.scope.checkUnclosedCloseables(flowInfo, null/*don't report against a specific location*/, null); >+ this.scope.checkUnclosedCloseables(flowInfo, null, null/*don't report against a specific location*/, null); > } catch (AbortMethod e) { > this.ignoreFurtherInvestigation = true; > } >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 6343221..806d603 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 >@@ -109,6 +109,7 @@ public class FakedTrackingVariable extends LocalDeclaration { > scope.getJavaLangObject(), // dummy, just needs to be a reference type > 0, > false); >+ this.binding.closeTracker = this; > this.binding.declaringScope = scope; > this.binding.setConstant(Constant.NotAConstant); > this.binding.useFlag = LocalVariableBinding.USED; >@@ -341,9 +342,14 @@ public class FakedTrackingVariable extends LocalDeclaration { > FakedTrackingVariable rhsTrackVar = getCloseTrackingVariable(rhs); > if (rhsTrackVar != null) { // 1. if RHS has a tracking variable... > if (local.closeTracker == null) { >- // null shouldn't occur but let's play safe >+ // null shouldn't occur but let's play safe: > if (rhsTrackVar.originalBinding != null) >- local.closeTracker = rhsTrackVar; // a.: let fresh LHS share it >+ local.closeTracker = rhsTrackVar; // a.: let fresh LHS share it >+ if (rhsTrackVar.currentAssignment == location) { >+ // pre-set tracker from lhs - passed from outside? >+ // now it's a fresh resource >+ rhsTrackVar.globalClosingState &= ~(SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE); >+ } > } else { > if (rhsTrackVar == disconnectedTracker && rhs instanceof AllocationExpression) > return; // b.: self wrapper: res = new Wrap(res); -> done! >@@ -420,8 +426,11 @@ public class FakedTrackingVariable extends LocalDeclaration { > FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); > tracker.globalClosingState |= SHARED_WITH_OUTSIDE; > flowInfo.markPotentiallyNullBit(tracker.binding); // shed some doubt >- return tracker; >- } else if ((expression.bits & RestrictiveFlagMASK) == Binding.FIELD) >+ return tracker; >+ } else if ( >+ (expression.bits & RestrictiveFlagMASK) == Binding.FIELD >+ ||((expression instanceof QualifiedNameReference) >+ && ((QualifiedNameReference) expression).isFieldAccess())) > { > // responsibility for this resource probably lies at a higher level > FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); >@@ -440,7 +449,12 @@ public class FakedTrackingVariable extends LocalDeclaration { > if (local.closeTracker != null) > // (c): inner has already been analyzed: -> re-use track var > return local.closeTracker; >- return new FakedTrackingVariable(local, location); >+ FakedTrackingVariable newTracker = new FakedTrackingVariable(local, location); >+ LocalVariableBinding rhsLocal = expression.localVariableBinding(); >+ if (rhsLocal != null && rhsLocal.isParameter()) { >+ newTracker.globalClosingState |= OWNED_BY_OUTSIDE; >+ } >+ return newTracker; > } > > public static void cleanUpAfterAssignment(BlockScope currentScope, int lhsBits, Expression expression) { >@@ -631,6 +645,35 @@ public class FakedTrackingVariable extends LocalDeclaration { > return trackingVar; > } > >+ /** >+ * Answer true if we know for sure that no resource is bound to this variable >+ * at the point of 'flowInfo'. >+ */ >+ public boolean hasDefinitelyNoResource(FlowInfo flowInfo) { >+ if (this.originalBinding == null) return false; // shouldn't happen but keep quiet. >+ if (flowInfo.isDefinitelyNull(this.originalBinding)) { >+ return true; >+ } >+ if (!(flowInfo.isDefinitelyAssigned(this.originalBinding) >+ || flowInfo.isPotentiallyAssigned(this.originalBinding))) { >+ return true; >+ } >+ return false; >+ } >+ >+ public boolean isClosedInFinallyOfEnclosing(BlockScope scope) { >+ BlockScope currentScope = scope; >+ while (true) { >+ if (currentScope.finallyInfo != null >+ && currentScope.finallyInfo.isDefinitelyNonNull(this.binding)) { >+ return true; // closed in enclosing finally >+ } >+ if (!(currentScope.parent instanceof BlockScope)) { >+ return false; >+ } >+ currentScope = (BlockScope) currentScope.parent; >+ } >+ } > /** > * If current is the same as 'returnedResource' or a wrapper thereof, > * mark as reported and return true, otherwise false. >@@ -648,6 +691,9 @@ public class FakedTrackingVariable extends LocalDeclaration { > } > > public void recordErrorLocation(ASTNode location, int nullStatus) { >+ if ((this.globalClosingState & OWNED_BY_OUTSIDE) != 0) { >+ return; >+ } > if (this.recordedLocations == null) > this.recordedLocations = new HashMap(); > this.recordedLocations.put(location, new Integer(nullStatus)); >@@ -684,12 +730,15 @@ public class FakedTrackingVariable extends LocalDeclaration { > } > > public int reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) { >+ if ((this.globalClosingState & OWNED_BY_OUTSIDE) != 0) { >+ return 0; // TODO: should we still propagate some flags?? >+ } > // which degree of problem? > boolean isPotentialProblem = false; > if (nullStatus == FlowInfo.NULL) { > if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0) > isPotentialProblem = true; >- } else if (nullStatus == FlowInfo.POTENTIALLY_NULL) { >+ } else if ((nullStatus & (FlowInfo.POTENTIALLY_NULL|FlowInfo.POTENTIALLY_NON_NULL)) != 0) { > isPotentialProblem = true; > } > // report: >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java >index f430f68..de57ece 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.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 >@@ -139,7 +139,7 @@ public class MethodDeclaration extends AbstractMethodDeclaration { > } > > } >- this.scope.checkUnclosedCloseables(flowInfo, null/*don't report against a specific location*/, null); >+ this.scope.checkUnclosedCloseables(flowInfo, null, null/*don't report against a specific location*/, null); > } catch (AbortMethod e) { > this.ignoreFurtherInvestigation = true; > } >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 6739bbc..2408c2d 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 >@@ -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 >@@ -109,6 +109,12 @@ public class QualifiedAllocationExpression extends AllocationExpression { > flowInfo.unconditionalCopy(), > currentScope); > } >+ >+ // after having analysed exceptions above start tracking newly allocated resource: >+ if (FakedTrackingVariable.isAnyCloseable(this.resolvedType)) { >+ FakedTrackingVariable.analyseCloseableAllocation(currentScope, flowInfo, this); >+ } >+ > manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo); > manageSyntheticAccessIfNecessary(currentScope, flowInfo); > return flowInfo; >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >index 95bb17b..ef68d3b 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >@@ -785,6 +785,13 @@ public TypeBinding getOtherFieldBindings(BlockScope scope) { > : type; > } > >+public boolean isFieldAccess() { >+ if (this.otherBindings != null) { >+ return true; >+ } >+ return (this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD; >+} >+ > public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) { > //If inlinable field, forget the access emulation, the code gen will directly target it > if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) { >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 f8955db..4e4c67c 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 >@@ -129,7 +129,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl > } > } > } >- currentScope.checkUnclosedCloseables(flowInfo, this, currentScope); >+ currentScope.checkUnclosedCloseables(flowInfo, flowContext, this, currentScope); > return FlowInfo.DEAD_END; > } > void checkAgainstNullAnnotation(BlockScope scope, FlowContext flowContext, int nullStatus) { >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java >index 66c17f5..44497e5 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.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 >@@ -126,14 +126,14 @@ public int complainIfUnreachable(FlowInfo flowInfo, BlockScope scope, int previo > if (previousComplaintLevel < COMPLAINED_UNREACHABLE) { > scope.problemReporter().unreachableCode(this); > if (endOfBlock) >- scope.checkUnclosedCloseables(flowInfo, null, null); >+ scope.checkUnclosedCloseables(flowInfo, null, null, null); > } > return COMPLAINED_UNREACHABLE; > } else { > if (previousComplaintLevel < COMPLAINED_FAKE_REACHABLE) { > scope.problemReporter().fakeReachable(this); > if (endOfBlock) >- scope.checkUnclosedCloseables(flowInfo, null, null); >+ scope.checkUnclosedCloseables(flowInfo, null, null, null); > } > return COMPLAINED_FAKE_REACHABLE; > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java >index 7c06835..66a1e62 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThrowStatement.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 >@@ -36,7 +36,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl > this.exception.checkNPE(currentScope, flowContext, flowInfo); > // need to check that exception thrown is actually caught somewhere > flowContext.checkExceptionHandlers(this.exceptionType, this, flowInfo, currentScope); >- currentScope.checkUnclosedCloseables(flowInfo, this, currentScope); >+ currentScope.checkUnclosedCloseables(flowInfo, flowContext, this, currentScope); > return FlowInfo.DEAD_END; > } > >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >index ea64a1a..5204b55 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >@@ -34,8 +34,10 @@ public class FinallyFlowContext extends FlowContext { > VariableBinding[] finalVariables; > int assignCount; > >+ // the following three arrays are in sync regarding their indices: > VariableBinding[] nullVariables; >- Expression[] nullReferences; >+ ASTNode[] nullReferences; // Expressions for null checking, Statements for resource analysis >+ // cast to Expression is safe if corresponding nullCheckType != EXIT_RESOURCE > int[] nullCheckTypes; > int nullCount; > // see also the related field FlowContext#expectedTypes >@@ -89,7 +91,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative > for (int i = 0; i < this.nullCount; i++) { > if (this.nullCheckTypes[i] == ASSIGN_TO_NONNULL) >- this.parent.recordNullityMismatch(scope, this.nullReferences[i], >+ this.parent.recordNullityMismatch(scope, (Expression)this.nullReferences[i], > flowInfo.nullStatus(this.nullVariables[i]), this.expectedTypes[i]); > else > this.parent.recordUsingNullReference(scope, this.nullVariables[i], >@@ -98,7 +100,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > } > else { // no enclosing loop, be as precise as possible right now > for (int i = 0; i < this.nullCount; i++) { >- Expression expression = this.nullReferences[i]; >+ ASTNode location = this.nullReferences[i]; > // final local variable > VariableBinding var = this.nullVariables[i]; > switch (this.nullCheckTypes[i]) { >@@ -107,11 +109,11 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > if (flowInfo.isDefinitelyNonNull(var)) { > if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNonNull(var, expression); >+ scope.problemReporter().variableRedundantCheckOnNonNull(var, location); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNonNullComparedToNull(var, expression); >+ scope.problemReporter().variableNonNullComparedToNull(var, location); > } > } > continue; >@@ -121,6 +123,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ Expression expression = (Expression) location; > if (flowInfo.isDefinitelyNull(var)) { > switch(this.nullCheckTypes[i] & CONTEXT_MASK) { > case FlowContext.IN_COMPARISON_NULL: >@@ -169,18 +172,18 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > break; > case MAY_NULL: > if (flowInfo.isDefinitelyNull(var)) { >- scope.problemReporter().variableNullReference(var, expression); >+ scope.problemReporter().variableNullReference(var, location); > continue; > } > if (flowInfo.isPotentiallyNull(var)) { >- scope.problemReporter().variablePotentialNullReference(var, expression); >+ scope.problemReporter().variablePotentialNullReference(var, location); > } > break; > case ASSIGN_TO_NONNULL: > int nullStatus = flowInfo.nullStatus(var); > if (nullStatus != FlowInfo.NON_NULL) { > char[][] annotationName = scope.environment().getNonNullAnnotationName(); >- scope.problemReporter().nullityMismatch(expression, this.expectedTypes[i], nullStatus, annotationName); >+ scope.problemReporter().nullityMismatch((Expression) location, this.expectedTypes[i], nullStatus, annotationName); > } > break; > default: >@@ -229,7 +232,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > } > > public void recordUsingNullReference(Scope scope, VariableBinding var, >- Expression reference, int checkType, FlowInfo flowInfo) { >+ ASTNode location, int checkType, FlowInfo flowInfo) { > if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 && !flowInfo.isDefinitelyUnknown(var)) { > if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative > switch (checkType) { >@@ -239,6 +242,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ Expression reference = (Expression) location; > if (flowInfo.cannotBeNull(var)) { > if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >@@ -312,7 +316,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > return; > } > if (flowInfo.canOnlyBeNull(var)) { >- scope.problemReporter().variableNullReference(var, reference); >+ scope.problemReporter().variableNullReference(var, location); > return; > } > break; >@@ -327,14 +331,14 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > if (flowInfo.isDefinitelyNonNull(var)) { > if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNonNull(var, reference); >+ scope.problemReporter().variableRedundantCheckOnNonNull(var, location); > } > if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(var)) { > flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNonNullComparedToNull(var, reference); >+ scope.problemReporter().variableNonNullComparedToNull(var, location); > } > if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(var)) { > flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); >@@ -347,6 +351,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ Expression reference = (Expression) location; > if (flowInfo.isDefinitelyNull(var)) { > switch(checkType & CONTEXT_MASK) { > case FlowContext.IN_COMPARISON_NULL: >@@ -399,11 +404,11 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > break; > case MAY_NULL : > if (flowInfo.isDefinitelyNull(var)) { >- scope.problemReporter().variableNullReference(var, reference); >+ scope.problemReporter().variableNullReference(var, location); > return; > } > if (flowInfo.isPotentiallyNull(var)) { >- scope.problemReporter().variablePotentialNullReference(var, reference); >+ scope.problemReporter().variablePotentialNullReference(var, location); > return; > } > if (flowInfo.isDefinitelyNonNull(var)) { >@@ -419,7 +424,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > if(((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) || checkType == MAY_NULL > || (checkType & CONTEXT_MASK) == FlowContext.IN_ASSIGNMENT > || (checkType & CONTEXT_MASK) == FlowContext.IN_INSTANCEOF) { >- recordNullReference(var, reference, checkType); >+ recordNullReference(var, location, checkType); > } > // prepare to re-check with try/catch flow info > } >@@ -436,7 +441,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { > } > > protected void recordNullReference(VariableBinding var, >- Expression expression, int status) { >+ ASTNode expression, int status) { > if (this.nullCount == 0) { > this.nullVariables = new VariableBinding[5]; > this.nullReferences = new Expression[5]; >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java >index 33fd6c5..506ae8a 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.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 >@@ -19,6 +19,7 @@ import org.eclipse.jdt.core.compiler.CharOperation; > import org.eclipse.jdt.internal.compiler.ast.ASTNode; > import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; > import org.eclipse.jdt.internal.compiler.ast.Expression; >+import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable; > import org.eclipse.jdt.internal.compiler.ast.LabeledStatement; > import org.eclipse.jdt.internal.compiler.ast.Reference; > import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; >@@ -74,6 +75,8 @@ public static final int CAN_ONLY_NON_NULL = 0x0002; > public static final int MAY_NULL = 0x0003; > //check binding a value to a @NonNull variable > public final static int ASSIGN_TO_NONNULL = 0x0080; >+//check against unclosed resource at early exit: >+public static final int EXIT_RESOURCE = 0x0800; > // check against null, with potential values -- NPE guard > public static final int CHECK_MASK = 0x00FF; > public static final int IN_COMPARISON_NULL = 0x0100; >@@ -556,6 +559,10 @@ public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) > // default implementation: do nothing > } > >+public boolean recordExitAgainstResource(BlockScope scope, FlowInfo flowInfo, FakedTrackingVariable trackingVar, ASTNode reference) { >+ return false; // not handled >+} >+ > protected void recordExpectedType(TypeBinding expectedType, int nullCount) { > if (nullCount == 0) { > this.expectedTypes = new TypeBinding[5]; >@@ -580,7 +587,9 @@ protected boolean recordFinalAssignment(VariableBinding variable, Reference fina > * Record a null reference for use by deferred checks. Only looping or > * finally contexts really record that information. > * @param local the local variable involved in the check >- * @param expression the expression within which local lays >+ * @param location the location triggering the analysis, for normal null dereference >+ * this is an expression resolving to 'local', for resource leaks it is an >+ * early exit statement. > * @param status the status against which the check must be performed; one of > * {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL > * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL}, >@@ -589,7 +598,7 @@ protected boolean recordFinalAssignment(VariableBinding variable, Reference fina > * {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF}) > */ > protected void recordNullReference(VariableBinding local, >- Expression expression, int status) { >+ ASTNode location, int status) { > // default implementation: do nothing > } > >@@ -620,7 +629,9 @@ public void recordSettingFinal(VariableBinding variable, Reference finalReferenc > * context). > * @param scope the scope into which the check is performed > * @param local the local variable involved in the check >- * @param reference the expression within which local lies >+ * @param location the location triggering the analysis, for normal null dereference >+ * this is an expression resolving to 'local', for resource leaks it is an >+ * early exit statement. > * @param checkType the status against which the check must be performed; one > * of {@link #CAN_ONLY_NULL CAN_ONLY_NULL}, {@link #CAN_ONLY_NULL_NON_NULL > * CAN_ONLY_NULL_NON_NULL}, {@link #MAY_NULL MAY_NULL}, potentially >@@ -632,7 +643,7 @@ public void recordSettingFinal(VariableBinding variable, Reference finalReferenc > * code that follows the current point) > */ > public void recordUsingNullReference(Scope scope, VariableBinding local, >- Expression reference, int checkType, FlowInfo flowInfo) { >+ ASTNode location, int checkType, FlowInfo flowInfo) { > if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || > flowInfo.isDefinitelyUnknown(local)) { > return; >@@ -643,14 +654,14 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > if (flowInfo.isDefinitelyNonNull(local)) { > if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNonNull(local, reference); >+ scope.problemReporter().variableRedundantCheckOnNonNull(local, location); > } > if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { > flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNonNullComparedToNull(local, reference); >+ scope.problemReporter().variableNonNullComparedToNull(local, location); > } > if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { > flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); >@@ -666,6 +677,7 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ Expression reference = (Expression)location; > if (flowInfo.isDefinitelyNull(local)) { > switch(checkType & CONTEXT_MASK) { > case FlowContext.IN_COMPARISON_NULL: >@@ -720,11 +732,11 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > break; > case MAY_NULL : > if (flowInfo.isDefinitelyNull(local)) { >- scope.problemReporter().variableNullReference(local, reference); >+ scope.problemReporter().variableNullReference(local, location); > return; > } > if (flowInfo.isPotentiallyNull(local)) { >- scope.problemReporter().variablePotentialNullReference(local, reference); >+ scope.problemReporter().variablePotentialNullReference(local, location); > return; > } > break; >@@ -732,7 +744,7 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > // never happens > } > if (this.parent != null) { >- this.parent.recordUsingNullReference(scope, local, reference, checkType, >+ this.parent.recordUsingNullReference(scope, local, location, checkType, > flowInfo); > } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >index cbf63e5..9d85bb9 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >@@ -18,6 +18,7 @@ import java.util.ArrayList; > > import org.eclipse.jdt.internal.compiler.ast.ASTNode; > import org.eclipse.jdt.internal.compiler.ast.Expression; >+import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable; > import org.eclipse.jdt.internal.compiler.ast.Reference; > import org.eclipse.jdt.internal.compiler.codegen.BranchLabel; > import org.eclipse.jdt.internal.compiler.lookup.BlockScope; >@@ -48,8 +49,10 @@ public class LoopingFlowContext extends SwitchFlowContext { > VariableBinding finalVariables[]; > int assignCount = 0; > >+ // the following three arrays are in sync regarding their indices: > VariableBinding[] nullVariables; >- Expression[] nullReferences; >+ ASTNode[] nullReferences; // Expressions for null checking, Statements for resource analysis >+ // cast to Expression is safe if corresponding nullCheckType != EXIT_RESOURCE > int[] nullCheckTypes; > int nullCount; > // see also the related field FlowContext#expectedTypes >@@ -146,7 +149,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > // check only immutable null checks on innermost looping context > for (int i = 0; i < this.nullCount; i++) { > VariableBinding local = this.nullVariables[i]; >- Expression expression = this.nullReferences[i]; >+ ASTNode location = this.nullReferences[i]; > // final local variable > switch (this.nullCheckTypes[i]) { > case CAN_ONLY_NON_NULL | IN_COMPARISON_NULL: >@@ -155,11 +158,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > this.nullReferences[i] = null; > if (this.nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); >+ scope.problemReporter().variableRedundantCheckOnNonNull(local, location); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNonNullComparedToNull(local, expression); >+ scope.problemReporter().variableNonNullComparedToNull(local, location); > } > } > continue; >@@ -171,11 +174,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > this.nullReferences[i] = null; > if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); >+ scope.problemReporter().variableRedundantCheckOnNonNull(local, location); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNonNullComparedToNull(local, expression); >+ scope.problemReporter().variableNonNullComparedToNull(local, location); > } > } > continue; >@@ -184,11 +187,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > this.nullReferences[i] = null; > if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNull(local, expression); >+ scope.problemReporter().variableRedundantCheckOnNull(local, location); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNullComparedToNonNull(local, expression); >+ scope.problemReporter().variableNullComparedToNonNull(local, location); > } > } > continue; >@@ -198,6 +201,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ Expression expression = (Expression)location; > if (flowInfo.isDefinitelyNull(local)) { > this.nullReferences[i] = null; > switch(this.nullCheckTypes[i] & CONTEXT_MASK) { >@@ -248,24 +252,41 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > case MAY_NULL: > if (flowInfo.isDefinitelyNull(local)) { > this.nullReferences[i] = null; >- scope.problemReporter().variableNullReference(local, expression); >+ scope.problemReporter().variableNullReference(local, location); > continue; > } > break; > case ASSIGN_TO_NONNULL: >- this.parent.recordNullityMismatch(scope, expression, flowInfo.nullStatus(local), this.expectedTypes[i]); >+ this.parent.recordNullityMismatch(scope, (Expression)location, flowInfo.nullStatus(local), this.expectedTypes[i]); >+ break; >+ case EXIT_RESOURCE: >+ if (local instanceof LocalVariableBinding) { >+ FakedTrackingVariable trackingVar = ((LocalVariableBinding) local).closeTracker; >+ if (trackingVar != null) { >+ if (trackingVar.hasDefinitelyNoResource(flowInfo)) { >+ continue; // no resource - no warning. >+ } >+ if (trackingVar.isClosedInFinallyOfEnclosing(scope)) { >+ continue; >+ } >+ if (this.parent.recordExitAgainstResource(scope, flowInfo, trackingVar, location)) { >+ this.nullReferences[i] = null; >+ continue; >+ } >+ } >+ } > break; > default: > // never happens > } >- this.parent.recordUsingNullReference(scope, local, expression, >+ this.parent.recordUsingNullReference(scope, local, location, > this.nullCheckTypes[i], flowInfo); > } > } > else { > // check inconsistent null checks on outermost looping context > for (int i = 0; i < this.nullCount; i++) { >- Expression expression = this.nullReferences[i]; >+ ASTNode location = this.nullReferences[i]; > // final local variable > VariableBinding local = this.nullVariables[i]; > switch (this.nullCheckTypes[i]) { >@@ -275,11 +296,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > this.nullReferences[i] = null; > if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableRedundantCheckOnNonNull(local, expression); >+ scope.problemReporter().variableRedundantCheckOnNonNull(local, location); > } > } else { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >- scope.problemReporter().variableNonNullComparedToNull(local, expression); >+ scope.problemReporter().variableNonNullComparedToNull(local, location); > } > } > continue; >@@ -289,6 +310,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ Expression expression = (Expression) location; > if (flowInfo.isDefinitelyNull(local)) { > this.nullReferences[i] = null; > switch(this.nullCheckTypes[i] & CONTEXT_MASK) { >@@ -339,12 +361,12 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > case MAY_NULL: > if (flowInfo.isDefinitelyNull(local)) { > this.nullReferences[i] = null; >- scope.problemReporter().variableNullReference(local, expression); >+ scope.problemReporter().variableNullReference(local, location); > continue; > } > if (flowInfo.isPotentiallyNull(local)) { > this.nullReferences[i] = null; >- scope.problemReporter().variablePotentialNullReference(local, expression); >+ scope.problemReporter().variablePotentialNullReference(local, location); > continue; > } > break; >@@ -352,7 +374,25 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn > int nullStatus = flowInfo.nullStatus(local); > if (nullStatus != FlowInfo.NON_NULL) { > char[][] annotationName = scope.environment().getNonNullAnnotationName(); >- scope.problemReporter().nullityMismatch(expression, this.expectedTypes[i], nullStatus, annotationName); >+ scope.problemReporter().nullityMismatch((Expression) location, this.expectedTypes[i], nullStatus, annotationName); >+ } >+ break; >+ case EXIT_RESOURCE: >+ nullStatus = flowInfo.nullStatus(local); >+ if (nullStatus != FlowInfo.NON_NULL && local instanceof LocalVariableBinding) { >+ FakedTrackingVariable closeTracker = ((LocalVariableBinding)local).closeTracker; >+ if (closeTracker != null) { >+ if (closeTracker.hasDefinitelyNoResource(flowInfo)) { >+ continue; // no resource - no warning. >+ } >+ if (closeTracker.isClosedInFinallyOfEnclosing(scope)) { >+ continue; >+ } >+ closeTracker.recordErrorLocation(this.nullReferences[i], nullStatus); >+ closeTracker.reportRecordedErrors(scope, nullStatus); >+ this.nullReferences[i] = null; >+ continue; >+ } > } > break; > default: >@@ -477,17 +517,17 @@ public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) > } > > protected void recordNullReference(VariableBinding local, >- Expression expression, int status) { >+ ASTNode expression, int status) { > if (this.nullCount == 0) { > this.nullVariables = new VariableBinding[5]; >- this.nullReferences = new Expression[5]; >+ this.nullReferences = new ASTNode[5]; > this.nullCheckTypes = new int[5]; > } > else if (this.nullCount == this.nullVariables.length) { > System.arraycopy(this.nullVariables, 0, > this.nullVariables = new VariableBinding[this.nullCount * 2], 0, this.nullCount); > System.arraycopy(this.nullReferences, 0, >- this.nullReferences = new Expression[this.nullCount * 2], 0, this.nullCount); >+ this.nullReferences = new ASTNode[this.nullCount * 2], 0, this.nullCount); > System.arraycopy(this.nullCheckTypes, 0, > this.nullCheckTypes = new int[this.nullCount * 2], 0, this.nullCount); > } >@@ -496,8 +536,26 @@ protected void recordNullReference(VariableBinding local, > this.nullCheckTypes[this.nullCount++] = status; > } > >+/** Record the fact that we see an early exit (in 'reference') while 'trackingVar' is in scope and may be unclosed. */ >+public boolean recordExitAgainstResource(BlockScope scope, FlowInfo flowInfo, FakedTrackingVariable trackingVar, ASTNode reference) { >+ LocalVariableBinding local = trackingVar.binding; >+ if (flowInfo.isDefinitelyNonNull(local)) { >+ return false; >+ } >+ if (flowInfo.isDefinitelyNull(local)) { >+ scope.problemReporter().unclosedCloseable(trackingVar, reference); >+ return true; // handled >+ } >+ if (flowInfo.isPotentiallyNull(local)) { >+ scope.problemReporter().potentiallyUnclosedCloseable(trackingVar, reference); >+ return true; // handled >+ } >+ recordNullReference(trackingVar.binding, reference, EXIT_RESOURCE); >+ return true; // handled >+} >+ > public void recordUsingNullReference(Scope scope, VariableBinding local, >- Expression reference, int checkType, FlowInfo flowInfo) { >+ ASTNode location, int checkType, FlowInfo flowInfo) { > if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || > flowInfo.isDefinitelyUnknown(local)) { > return; >@@ -505,6 +563,7 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > switch (checkType) { > case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL: > case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL: >+ Expression reference = (Expression)location; > if (flowInfo.isDefinitelyNonNull(local)) { > if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { > if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { >@@ -564,6 +623,7 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL: > case CAN_ONLY_NULL | IN_ASSIGNMENT: > case CAN_ONLY_NULL | IN_INSTANCEOF: >+ reference = (Expression)location; > if (flowInfo.isPotentiallyNonNull(local) > || flowInfo.isPotentiallyUnknown(local) > || flowInfo.isProtectedNonNull(local)) { >@@ -633,14 +693,14 @@ public void recordUsingNullReference(Scope scope, VariableBinding local, > return; > } > if (flowInfo.isDefinitelyNull(local)) { >- scope.problemReporter().variableNullReference(local, reference); >+ scope.problemReporter().variableNullReference(local, location); > return; > } > if (flowInfo.isPotentiallyNull(local)) { >- scope.problemReporter().variablePotentialNullReference(local, reference); >+ scope.problemReporter().variablePotentialNullReference(local, location); > return; > } >- recordNullReference(local, reference, checkType); >+ recordNullReference(local, location, checkType); > return; > default: > // never happens >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 33c3b6f..6fa655e 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 >@@ -23,6 +23,7 @@ import org.eclipse.jdt.core.compiler.CharOperation; > import org.eclipse.jdt.internal.compiler.ast.*; > import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; > import org.eclipse.jdt.internal.compiler.codegen.CodeStream; >+import org.eclipse.jdt.internal.compiler.flow.FlowContext; > import org.eclipse.jdt.internal.compiler.flow.FlowInfo; > import org.eclipse.jdt.internal.compiler.impl.Constant; > import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; >@@ -1001,11 +1002,11 @@ public void pruneWrapperTrackingVar(FakedTrackingVariable trackingVariable) { > * At the end of a block check the closing-status of all tracked closeables that are declared in this block. > * Also invoked when entering unreachable code. > */ >-public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockScope locationScope) { >+public void checkUnclosedCloseables(FlowInfo flowInfo, FlowContext flowContext, ASTNode location, BlockScope locationScope) { > if (this.trackingVariables == null) { > // at a method return we also consider enclosing scopes > if (location != null && this.parent instanceof BlockScope) >- ((BlockScope) this.parent).checkUnclosedCloseables(flowInfo, location, locationScope); >+ ((BlockScope) this.parent).checkUnclosedCloseables(flowInfo, flowContext, location, locationScope); > return; > } > if (location != null && flowInfo.reachMode() != 0) return; >@@ -1022,9 +1023,14 @@ public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockSc > continue; > } > >- 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 >- >+ if (location != null && trackingVar.hasDefinitelyNoResource(flowInfo)) { >+ continue; // reporting against a specific location, there is no resource at this flow, don't complain >+ } >+ >+ if (location != null && flowContext != null && flowContext.recordExitAgainstResource(this, flowInfo, trackingVar, location)) { >+ continue; // handled by the flow context >+ } >+ > // compute the most specific null status for this resource, > int status = trackingVar.findMostSpecificStatus(flowInfo, this, locationScope); >
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 368546
:
209453
|
209555
| 210478 |
210479