diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java index 0c9324c..e24388f 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java @@ -6020,6 +6020,7 @@ public void test0561_try_catch_unchecked_exception() { public void test0562_try_catch_unchecked_exception() { Map options = getCompilerOptions(); options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.WARNING); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); this.runNegativeTest( true, new String[] { @@ -6042,7 +6043,7 @@ public void test0562_try_catch_unchecked_exception() { "1. WARNING in X.java (at line 6)\n" + " o = new LineNumberReader(new FileReader(\"dummy\"));\n" + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'o' is never closed\n" + + "Potential resource leak: \'o\' may not be closed\n" + "----------\n" + "2. ERROR in X.java (at line 8)\n" + " o.toString();\n" + 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 new file mode 100644 index 0000000..51d5e74 --- /dev/null +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ResourceLeakTests.java @@ -0,0 +1,2973 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 GK Software AG 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Stephan Herrmann - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.core.tests.compiler.regression; + +import java.io.IOException; +import java.net.URL; +import java.util.Map; + +import junit.framework.Test; + +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.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; + +public class ResourceLeakTests extends AbstractRegressionTest { + +static { +// TESTS_NAMES = new String[] { "test061a"}; +// TESTS_NUMBERS = new int[] { 50 }; +// TESTS_RANGE = new int[] { 11, -1 }; +} +public ResourceLeakTests(String name) { + super(name); +} +public static Test suite() { + return buildAllCompliancesTestSuite(ResourceLeakTests.class); +} + +void runTestsExpectingErrorsOnlyIn17(String[] testFiles, String errorsIn17, Map options) { + if (this.complianceLevel >= ClassFileConstants.JDK1_7) + runNegativeTest(testFiles, errorsIn17, null, true, options); + else + runConformTest(testFiles, "", null, true, null, options, null); +} + +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses an AutoCloseable without ever closing it. +public void test056() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + +// not invoking any methods on FileReader, try to avoid necessary call to superclass() in the compiler +// " char[] in = new char[50];\n" + +// " fileReader.read(in);\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileReader fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^\n" + + "Resource leak: 'fileReader' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses an AutoCloseable and closes it but not protected by t-w-r nor regular try-finally +public void test056a() { + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); + runTestsExpectingErrorsOnlyIn17( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " fileReader.close();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileReader fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^\n" + + "Resource 'fileReader' should be managed by try-with-resource\n" + + "----------\n", + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses an AutoCloseable and closes it properly in a finally block +public void test056b() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " try {\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " } finally {\n" + + " fileReader.close();\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " try {\n" + + " new X().foo();\n" + + " } catch (IOException ioex) {\n" + + " System.out.println(\"caught\");\n" + + " }\n" + + " }\n" + + "}\n" + }, + "caught", /*output*/ + null/*classLibs*/, + true/*shouldFlush*/, + null/*vmargs*/, + options, + null/*requestor*/); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses an AutoCloseable properly within try-with-resources. +public void test056c() { + if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); + this.runConformTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " try (FileReader fileReader = new FileReader(file)) {\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " try {\n" + + " new X().foo();\n" + + " } catch (IOException ioex) {\n" + + " System.out.println(\"caught\");\n" + + " }\n" + + " }\n" + + "}\n" + }, + "caught", /*output*/ + null/*classLibs*/, + true/*shouldFlush*/, + null/*vmargs*/, + options, + null/*requestor*/); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses two AutoCloseables (testing independent analysis) +//- one closeable may be unclosed at a conditional return +//- the other is only conditionally closed +public void test056d() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean flag1, boolean flag2) throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " char[] in = new char[50];\n" + + " FileReader fileReader1 = new FileReader(file);\n" + + " fileReader1.read(in);\n" + + " FileReader fileReader2 = new FileReader(file);\n" + + " fileReader2.read(in);\n" + + " if (flag1) {\n" + + " fileReader2.close();\n" + + " return;\n" + + " } else if (flag2) {\n" + + " fileReader2.close();\n" + + " }\n" + + " fileReader1.close();\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo(false, true);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. WARNING in X.java (at line 10)\n" + + " FileReader fileReader2 = new FileReader(file);\n" + + " ^^^^^^^^^^^\n" + + "Potential resource leak: 'fileReader2' may not be closed\n" + + "----------\n" + + "2. ERROR in X.java (at line 14)\n" + + " return;\n" + + " ^^^^^^^\n" + + "Resource leak: 'fileReader1' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses two AutoCloseables (testing independent analysis) +//- one closeable may be unclosed at a conditional return +//- the other is only conditionally closed +public void test056d_suppress() { + if (this.complianceLevel < ClassFileConstants.JDK1_5) return; // annotations used + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); + options.put(CompilerOptions.OPTION_SuppressOptionalErrors, CompilerOptions.ENABLED); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean flag1, boolean flag2) throws IOException {\n" + + " @SuppressWarnings(\"resource\") File file = new File(\"somefile\"); // unnecessary suppress\n" + + " char[] in = new char[50];\n" + + " FileReader fileReader1 = new FileReader(file);\n" + + " fileReader1.read(in);\n" + + " @SuppressWarnings(\"resource\") FileReader fileReader2 = new FileReader(file); // useful suppress\n" + + " fileReader2.read(in);\n" + + " if (flag1) {\n" + + " fileReader2.close();\n" + + " return; // not suppressed\n" + + " } else if (flag2) {\n" + + " fileReader2.close();\n" + + " }\n" + + " fileReader1.close();\n" + + " }\n" + + " @SuppressWarnings(\"resource\") // useful suppress\n" + + " void bar() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo(false, true);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. WARNING in X.java (at line 6)\n" + + " @SuppressWarnings(\"resource\") File file = new File(\"somefile\"); // unnecessary suppress\n" + + " ^^^^^^^^^^\n" + + "Unnecessary @SuppressWarnings(\"resource\")\n" + + "----------\n" + + "2. ERROR in X.java (at line 14)\n" + + " return; // not suppressed\n" + + " ^^^^^^^\n" + + "Resource leak: 'fileReader1' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// one method returns an AutoCleasble, a second method uses this object without ever closing it. +public void test056e() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " FileReader getReader(String filename) throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " return fileReader;\n" + // don't complain here, pass responsibility to caller + " }\n" + + " void foo() throws IOException {\n" + + " FileReader reader = getReader(\"somefile\");\n" + + " char[] in = new char[50];\n" + + " reader.read(in);\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 11)\n" + + " FileReader reader = getReader(\"somefile\");\n" + + " ^^^^^^\n" + + "Potential resource leak: \'reader\' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method explicitly closes its AutoCloseable rather than using t-w-r +public void test056f() { + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); + options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); + runTestsExpectingErrorsOnlyIn17( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = null;\n" + + " try {\n" + + " fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " } finally {\n" + + " fileReader.close();\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileReader fileReader = null;\n" + + " ^^^^^^^^^^\n" + + "Resource 'fileReader' should be managed by try-with-resource\n" + + "----------\n", + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// an AutoCloseable local is re-assigned +public void test056g() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " fileReader = new FileReader(file);\n" + + " fileReader.read(in);\n" + + " fileReader.close();\n" + + " fileReader = null;\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'fileReader' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// an AutoCloseable local is re-assigned after null-assigned +public void test056g2() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " fileReader = null;\n" + + " fileReader = new FileReader(file);\n" + // don't complain again, fileReader is null, so nothing can leak here + " fileReader.read(in);\n" + + " fileReader.close();\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " fileReader = null;\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'fileReader' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// two AutoCloseables at different nesting levels (anonymous local type) +public void test056h() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " final File file = new File(\"somefile\");\n" + + " final FileReader fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " new Runnable() {\n public void run() {\n" + + " try {\n" + + " fileReader.close();\n" + + " FileReader localReader = new FileReader(file);\n" + + " } catch (IOException ex) { /* nop */ }\n" + + " }}.run();\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. WARNING in X.java (at line 7)\n" + + " final FileReader fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^\n" + + "Potential resource leak: 'fileReader' may not be closed\n" + + "----------\n" + + "2. ERROR in X.java (at line 14)\n" + + " FileReader localReader = new FileReader(file);\n" + + " ^^^^^^^^^^^\n" + + "Resource leak: 'localReader' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// three AutoCloseables in different blocks of the same method +public void test056i() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean f1, boolean f2) throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " if (f1) {\n" + + " FileReader fileReader = new FileReader(file); // err: not closed\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " while (true) {\n" + + " FileReader loopReader = new FileReader(file); // don't warn, properly closed\n" + + " loopReader.close();" + + " break;\n" + + " }\n" + + " } else {\n" + + " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + + " if (f2)\n" + + " fileReader.close();\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo(true, true);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " FileReader fileReader = new FileReader(file); // err: not closed\n" + + " ^^^^^^^^^^\n" + + "Resource leak: 'fileReader' is never closed\n" + + "----------\n" + + "2. WARNING in X.java (at line 16)\n" + + " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + + " ^^^^^^^^^^\n" + + "Potential resource leak: 'fileReader' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// three AutoCloseables in different blocks of the same method - problems ignored +public void test056i_ignore() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.IGNORE); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.IGNORE); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runConformTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean f1, boolean f2) throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " if (f1) {\n" + + " FileReader fileReader = new FileReader(file); // err: not closed\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " while (true) {\n" + + " FileReader loopReader = new FileReader(file); // don't warn, properly closed\n" + + " loopReader.close();" + + " break;\n" + + " }\n" + + " } else {\n" + + " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + + " if (f2)\n" + + " fileReader.close();\n" + + " }\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// three AutoCloseables in different blocks of the same method +public void test056i2() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean f1, boolean f2) throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " if (f1) {\n" + + " FileReader fileReader = new FileReader(file); // properly closed\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " while (true) {\n" + + " fileReader.close();\n" + + " FileReader loopReader = new FileReader(file); // don't warn, properly closed\n" + + " loopReader.close();\n" + + " break;\n" + + " }\n" + + " } else {\n" + + " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + + " if (f2)\n" + + " fileReader.close();\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo(true, true);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 18)\n" + + " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + + " ^^^^^^^^^^\n" + + "Potential resource leak: 'fileReader' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses an AutoCloseable without closing it locally but passing as arg to another method +public void test056j() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " read(fileReader);\n" + + " }\n" + + " void read(FileReader reader) { }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileReader fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^\n" + + "Potential resource leak: 'fileReader' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// a method uses an AutoCloseable without closing it locally but passing as arg to another method +public void test056jconditional() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean b) throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " synchronized (b ? this : new X()) {\n" + + " new ReadDelegator(fileReader);\n" + + " }\n" + + " }\n" + + " class ReadDelegator { ReadDelegator(FileReader reader) { } }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo(true);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileReader fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^\n" + + "Potential resource leak: 'fileReader' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// many locals, some are AutoCloseable. +// Unfortunately analysis cannot respect how exception exits may affect ra3 and rb3, +// doing so would create false positives. +public void test056k() { + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); + options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); + String expectedProblems = this.complianceLevel < ClassFileConstants.JDK1_7 ? + "----------\n" + + "1. ERROR in X.java (at line 15)\n" + + " ra2 = new FileReader(file);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: \'ra2\' is never closed\n" + + "----------\n" + + "2. ERROR in X.java (at line 28)\n" + + " rb2 = new FileReader(file);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: \'rb2\' is never closed\n" + + "----------\n" + : + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " FileReader ra1 = null, ra2 = null;\n" + + " ^^^\n" + + "Resource 'ra1' should be managed by try-with-resource\n" + + "----------\n" + + "2. ERROR in X.java (at line 15)\n" + + " ra2 = new FileReader(file);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'ra2' is never closed\n" + + "----------\n" + + "3. ERROR in X.java (at line 16)\n" + + " FileReader ra3 = new FileReader(file);\n" + + " ^^^\n" + + "Resource 'ra3' should be managed by try-with-resource\n" + + "----------\n" + + "4. ERROR in X.java (at line 25)\n" + + " FileReader rb1 = null, rb2 = null;\n" + + " ^^^\n" + + "Resource 'rb1' should be managed by try-with-resource\n" + + "----------\n" + + "5. ERROR in X.java (at line 28)\n" + + " rb2 = new FileReader(file);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'rb2' is never closed\n" + + "----------\n" + + "6. ERROR in X.java (at line 29)\n" + + " FileReader rb3 = new FileReader(file);\n" + + " ^^^\n" + + "Resource 'rb3' should be managed by try-with-resource\n" + + "----------\n"; + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " int i01, i02, i03, i04, i05, i06, i07, i08, i09,\n" + + " i11, i12, i13, i14, i15, i16, i17, i18, i19,\n" + + " i21, i22, i23, i24, i25, i26, i27, i28, i29,\n" + + " i31, i32, i33, i34, i35, i36, i37, i38, i39,\n" + + " i41, i42, i43, i44, i45, i46, i47, i48, i49;\n" + + " File file = new File(\"somefile\");\n" + + " FileReader ra1 = null, ra2 = null;\n" + + " try {\n" + + " ra1 = new FileReader(file);\n" + + " ra2 = new FileReader(file);\n" + + " FileReader ra3 = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " ra1.read(in);\n" + + " ra2.read(in);\n" + + " ra3.close();\n" + + " } finally {\n" + + " ra1.close();\n" + + " }\n" + + " int i51, i52, i53, i54, i55, i56, i57, i58, i59, i60;\n" + // beyond this point locals are analyzed using extraBits + " FileReader rb1 = null, rb2 = null;\n" + + " try {\n" + + " rb1 = new FileReader(file);\n" + + " rb2 = new FileReader(file);\n" + + " FileReader rb3 = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " rb1.read(in);\n" + + " rb2.read(in);\n" + + " rb3.close();\n" + + " } finally {\n" + + " rb1.close();\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + expectedProblems, + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// various non-problems +public void test056l() { + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); + String expectedProblems = this.complianceLevel >= ClassFileConstants.JDK1_7 ? + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " FileReader fileReader = getReader();\n" + + " ^^^^^^^^^^\n" + + "Resource 'fileReader' should be managed by try-with-resource\n" + + "----------\n" + + "2. ERROR in X.java (at line 11)\n" + + " FileReader r3 = getReader();\n" + + " ^^\n" + + "Resource 'r3' should be managed by try-with-resource\n" + + "----------\n" + + "3. ERROR in X.java (at line 24)\n" + + " FileReader r2 = new FileReader(new File(\"inexist\")); // only potential problem: ctor X below might close r2\n" + + " ^^\n" + + "Potential resource leak: 'r2' may not be closed\n" + + "----------\n" + : + "----------\n" + + "1. ERROR in X.java (at line 24)\n" + + " FileReader r2 = new FileReader(new File(\"inexist\")); // only potential problem: ctor X below might close r2\n" + + " ^^\n" + + "Potential resource leak: 'r2' may not be closed\n" + + "----------\n"; + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " X(FileReader r0) {}\n" + // don't complain against argument + " FileReader getReader() { return null; }\n" + + " void foo(FileReader r1) throws IOException {\n" + + " FileReader fileReader = getReader();\n" + + " if (fileReader == null)\n" + + " return;\n" + // don't complain, resource is actually null + " FileReader r3 = getReader();\n" + + " if (r3 == null)\n" + + " r3 = new FileReader(new File(\"absent\"));\n" + // don't complain, previous resource is actually null + " try {\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " r1.read(in);\n" + + " } finally {\n" + + " fileReader.close();\n" + + " r3.close();\n" + // the effect of this close() call might be spoiled by exception in fileReader.close() above, but we ignore exception exits in the analysis + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " FileReader r2 = new FileReader(new File(\"inexist\")); // only potential problem: ctor X below might close r2\n" + + " new X(r2).foo(new FileReader(new File(\"notthere\")));\n" + + " }\n" + + "}\n" + }, + expectedProblems, + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// nested try with early exit +public void test056m() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() {\n" + + " File file = new File(\"somefile\");" + + " try {\n" + + " FileReader fileReader = new FileReader(file);\n" + + " try {\n" + + " char[] in = new char[50];\n" + + " if (fileReader.read(in)==0)\n" + + " return;\n" + + " } finally {\n" + + " fileReader.close();\n" + + " }\n" + + " } catch (IOException e) {\n" + + " System.out.println(\"caught\");\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "caught", /*output*/ + null/*classLibs*/, + true/*shouldFlush*/, + null/*vmargs*/, + options, + null/*requestor*/); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// nested try should not interfere with earlier analysis. +public void test056n() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "import java.io.FileNotFoundException;\n" + + "public class X {\n" + + " void foo(File someFile, char[] buf) throws IOException {\n" + + " FileReader fr1 = new FileReader(someFile);\n" + + " try {\n" + + " fr1.read(buf);\n" + + " } finally {\n" + + " fr1.close();\n" + + " }\n" + + " try {\n" + + " FileReader fr3 = new FileReader(someFile);\n" + + " try {\n" + + " } finally {\n" + + " fr3.close();\n" + + " }\n" + + " } catch (IOException e) {\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " try {\n" + + " new X().foo(new File(\"missing\"), new char[100]);\n" + + " } catch (FileNotFoundException e) {\n" + + " System.out.println(\"caught\");\n" + + " }\n" + + " }\n" + + "}\n" + }, + "caught", /*output*/ + null/*classLibs*/, + true/*shouldFlush*/, + null/*vmargs*/, + options, + null/*requestor*/); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// if close is guarded by null check this should still be recognized as definitely closed +public void test056o() { + 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.FileReader;\n" + + "import java.io.IOException;\n" + + "import java.io.FileNotFoundException;\n" + + "public class X {\n" + + " void foo(File someFile, char[] buf) throws IOException {\n" + + " FileReader fr1 = null;\n" + + " try {\n" + + " fr1 = new FileReader(someFile);" + + " fr1.read(buf);\n" + + " } finally {\n" + + " if (fr1 != null)\n" + + " try {\n" + + " fr1.close();\n" + + " } catch (IOException e) { /*do nothing*/ }\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " try {\n" + + " new X().foo(new File(\"missing\"), new char[100]);\n" + + " } catch (FileNotFoundException e) {\n" + + " System.out.println(\"caught\");\n" + + " }\n" + + " }\n" + + "}\n" + }, + "caught", /*output*/ + null/*classLibs*/, + true/*shouldFlush*/, + null/*vmargs*/, + options, + null/*requestor*/); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// a method uses an AutoCloseable without ever closing it, type from a type variable +public void test056p() { + 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); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.Reader;\n" + + "import java.io.IOException;\n" + + "public abstract class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " T fileReader = newReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " }\n" + + " abstract T newReader(File file) throws IOException;\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X() {\n" + + " FileReader newReader(File f) throws IOException { return new FileReader(f); }\n" + + " }.foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " T fileReader = newReader(file);\n" + + " ^^^^^^^^^^\n" + + "Potential resource leak: \'fileReader\' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// closed in dead code +public void test056q() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fileReader = new FileReader(file);\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " if (2*2 == 4)\n" + + " return;\n" + + " fileReader.close();\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileReader fileReader = new FileReader(file);\n" + + " ^^^^^^^^^^\n" + + "Resource leak: 'fileReader' is never closed\n" + + "----------\n" + + "2. WARNING in X.java (at line 10)\n" + + " if (2*2 == 4)\n" + + " ^^^^^^^^\n" + + "Comparing identical expressions\n" + + "----------\n" + + "3. WARNING in X.java (at line 12)\n" + + " fileReader.close();\n" + + " ^^^^^^^^^^^^^^^^^^\n" + + "Dead code\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// properly closed, dead code in between +public void test056r() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " FileReader fr = new FileReader(file);\n" + + " Object b = null;\n" + + " fr.close();\n" + + " if (b != null) {\n" + + " fr = new FileReader(file);\n" + + " return;\n" + + " } else {\n" + + " System.out.print(42);\n" + + " }\n" + + " return; // Should not complain about fr\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " if (b != null) {\n" + + " fr = new FileReader(file);\n" + + " return;\n" + + " } else {\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Dead code\n" + + "----------\n" + + "2. WARNING in X.java (at line 13)\n" + + " } else {\n" + + " System.out.print(42);\n" + + " }\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// resource inside t-w-r is re-assigned, shouldn't even record an errorLocation +public void test056s() { + if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.File;\n" + + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " try (FileReader fileReader = new FileReader(file);) {\n" + + " char[] in = new char[50];\n" + + " fileReader.read(in);\n" + + " fileReader = new FileReader(file); // debug here\n" + + " fileReader.read(in);\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " fileReader = new FileReader(file); // debug here\n" + + " ^^^^^^^^^^\n" + + "The resource fileReader of a try-with-resources statement cannot be assigned\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// resource is closed, dead code follows +public void test056t() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo31() throws IOException {\n" + + " FileReader reader = new FileReader(\"file\"); //warning\n" + + " if (reader != null) {\n" + + " reader.close();\n" + + " } else {\n" + + " // nop\n" + + " }\n" + + " }\n" + + " public static void main(String[] args) throws IOException {\n" + + " new X().foo31();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " } else {\n" + + " // nop\n" + + " }\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Dead code\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// resource is reassigned within t-w-r with different resource +// was initially broken due to https://bugs.eclipse.org/358827 +public void test056u() { + if (this.complianceLevel < ClassFileConstants.JDK1_7) return; // t-w-r used + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo() throws Exception {\n" + + " FileReader reader1 = new FileReader(\"file1\");\n" + + " FileReader reader2 = new FileReader(\"file2\");\n" + + " reader2 = reader1;// this disconnects reader 2\n" + + " try (FileReader reader3 = new FileReader(\"file3\")) {\n" + + " int ch;\n" + + " while ((ch = reader2.read()) != -1) {\n" + + " System.out.println(ch);\n" + + " reader1.read();\n" + + " }\n" + + " reader2 = reader1; // warning 1 regarding original reader1\n" + // this warning was missing + " reader2 = reader1; // warning 2 regarding original reader1\n" + + " } finally {\n" + + " if (reader2 != null) {\n" + + " reader2.close();\n" + + " } else {\n" + + " System.out.println();\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " FileReader reader2 = new FileReader(\"file2\");\n" + + " ^^^^^^^\n" + + "Resource leak: 'reader2' is never closed\n" + + "----------\n" + + "2. ERROR in X.java (at line 13)\n" + + " reader2 = reader1; // warning 1 regarding original reader1\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'reader1' is not closed at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 14)\n" + + " reader2 = reader1; // warning 2 regarding original reader1\n" + + " ^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'reader1' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// scope-related pbs reported in https://bugs.eclipse.org/349326#c70 and https://bugs.eclipse.org/349326#c82 +public void test056v() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + String expectedProblems = this.complianceLevel >= ClassFileConstants.JDK1_7 ? + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " ^^^^^^\n" + + "Resource leak: 'reader' is never closed\n" + + "----------\n" + + "2. WARNING in X.java (at line 19)\n" + + " FileReader reader111 = new FileReader(\"file2\");\n" + + " ^^^^^^^^^\n" + + "Resource 'reader111' should be managed by try-with-resource\n" + + "----------\n" + + "3. ERROR in X.java (at line 42)\n" + + " return;\n" + + " ^^^^^^^\n" + + "Resource leak: 'reader2' is not closed at this location\n" + + "----------\n" + : + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " ^^^^^^\n" + + "Resource leak: 'reader' is never closed\n" + + "----------\n" + + "2. ERROR in X.java (at line 42)\n" + + " return;\n" + + " ^^^^^^^\n" + + "Resource leak: 'reader2' is not closed at this location\n" + + "----------\n"; + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " boolean foo1() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " try {\n" + + " int ch;\n" + + " while ((ch = reader.read()) != -1) {\n" + + " System.out.println(ch);\n" + + " reader.read();\n" + + " }\n" + + " if (ch > 10) {\n" + + " return true;\n" + + " }\n" + + " return false;\n" + // return while resource from enclosing scope remains unclosed + " } finally {\n" + + " }\n" + + " }\n" + + " void foo111() throws Exception {\n" + + " FileReader reader111 = new FileReader(\"file2\");\n" + + " try {\n" + + " int ch;\n" + + " while ((ch = reader111.read()) != -1) {\n" + + " System.out.println(ch);\n" + + " reader111.read();\n" + + " }\n" + + " return;\n" + // this shouldn't spoil the warning "should be managed with t-w-r" + " } finally {\n" + + " if (reader111 != null) {\n" + + " reader111.close();\n" + + " }\n" + + " }\n" + + " }\n" + + " void foo2() throws Exception {\n" + + " FileReader reader2 = new FileReader(\"file\");\n" + + " try {\n" + + " int ch;\n" + + " while ((ch = reader2.read()) != -1) {\n" + + " System.out.println(ch);\n" + + " reader2.read();\n" + + " }\n" + + " if (ch > 10) {\n" + + " return;\n" + // potential leak + " }\n" + + " } finally {\n" + + " }\n" + + " reader2.close();\n" + // due to this close we don't say "never closed" + " }\n" + + "}\n" + }, + expectedProblems, + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// end of method is dead end, but before we have both a close() and an early return +public void test056w() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " boolean foo1() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " try {\n" + + " int ch;\n" + + " while ((ch = reader.read()) != -1) {\n" + + " System.out.println(ch);\n" + + " reader.read();\n" + + " }\n" + + " if (ch > 10) {\n" + + " reader.close();\n" + + " return true;\n" + + " }\n" + + " return false;\n" + + " } finally {\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 15)\n" + + " return false;\n" + + " ^^^^^^^^^^^^^\n" + + "Resource leak: 'reader' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// different early exits, if no close seen report as definitely unclosed +public void test056x() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo31(boolean b) throws Exception {\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " if (b) {\n" + + " reader.close();\n" + + " } else {\n" + + " return; // warning\n" + + " }\n" + + " }\n" + + " void foo32(boolean b) throws Exception {\n" + + " FileReader reader = new FileReader(\"file\"); // warn here\n" + + " return;\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 8)\n" + + " return; // warning\n" + + " ^^^^^^^\n" + + "Resource leak: 'reader' is not closed at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 12)\n" + + " FileReader reader = new FileReader(\"file\"); // warn here\n" + + " ^^^^^^\n" + + "Resource leak: 'reader' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// nested method passes the resource to outside code +public void test056y() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo31(boolean b) throws Exception {\n" + + " final FileReader reader31 = new FileReader(\"file\");\n" + + " new Runnable() {\n" + + " public void run() {\n" + + " foo18(reader31);\n" + + " }\n" + + " }.run();\n" + + " }\n" + + " void foo18(FileReader r18) {\n" + + " // could theoretically close r18;\n" + + " }\n" + + " abstract class ResourceProvider {\n" + + " abstract FileReader provide();" + + " }\n" + + " ResourceProvider provider;" + + " void foo23() throws Exception {\n" + + " final FileReader reader23 = new FileReader(\"file\");\n" + + " provider = new ResourceProvider() {\n" + + " public FileReader provide() {\n" + + " return reader23;\n" + + " }\n" + + " };\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. WARNING in X.java (at line 4)\n" + + " 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, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// resource assigned to second local and is (potentially) closed on the latter +public void test056z() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo17() throws Exception {\n" + + " FileReader reader17 = new FileReader(\"file\");\n" + + " final FileReader readerCopy = reader17;\n" + + " readerCopy.close();\n" + + " }\n" + + " void foo17a() throws Exception {\n" + + " FileReader reader17a = new FileReader(\"file\");\n" + + " FileReader readerCopya;" + + " readerCopya = reader17a;\n" + + " bar(readerCopya);\n" + // potentially closes + " }\n" + + " void bar(FileReader r) {}\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " FileReader reader17a = new FileReader(\"file\");\n" + + " ^^^^^^^^^\n" + + "Potential resource leak: 'reader17a' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// multiple early exists from nested scopes (always closed) +public void test056zz() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + runTestsExpectingErrorsOnlyIn17( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo16() throws Exception {\n" + + " FileReader reader16 = new FileReader(\"file\");\n" + + " try {\n" + + " reader16.close();\n " + + " return;\n" + + " } catch (RuntimeException re) {\n" + + " return;\n" + + " } catch (Error e) {\n" + + " return;\n" + + " } finally {\n" + + " reader16.close();\n " + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " FileReader reader16 = new FileReader(\"file\");\n" + + " ^^^^^^^^\n" + + "Resource 'reader16' should be managed by try-with-resource\n" + + "----------\n", + options); +} +// Bug 349326 - [1.7] new warning for missing try-with-resources +// multiple early exists from nested scopes (never closed) +public void test056zzz() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo16() throws Exception {\n" + + " FileReader reader16 = new FileReader(\"file\");\n" + + " try {\n" + + " return;\n" + + " } catch (RuntimeException re) {\n" + + " return;\n" + + " } catch (Error e) {\n" + + " return;\n" + + " } finally {\n" + + " System.out.println();\n " + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " FileReader reader16 = new FileReader(\"file\");\n" + + " ^^^^^^^^\n" + + "Resource leak: 'reader16' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points +// explicit throw is a true method exit here +public void test056throw1() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo2(boolean a, boolean b, boolean c) throws Exception {\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " if(a)\n" + + " throw new Exception(); //warning 1\n" + + " else if (b)\n" + + " reader.close();\n" + + " else if(c)\n" + + " throw new Exception(); //warning 2\n" + + " reader.close();\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " throw new Exception(); //warning 1\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'reader' is not closed at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 10)\n" + + " throw new Exception(); //warning 2\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: 'reader' is not closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points +// close() within finally provides protection for throw +public void test056throw2() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + runTestsExpectingErrorsOnlyIn17( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo1() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " try {\n" + + " reader.read();\n" + + " return;\n" + + " } catch (Exception e) {\n" + + " throw new Exception();\n" + + " } finally {\n" + + " reader.close();\n" + + " }\n" + + " }\n" + + "\n" + + " void foo2() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " try {\n" + + " reader.read();\n" + + " throw new Exception(); // should not warn here\n" + + " } catch (Exception e) {\n" + + " throw new Exception();\n" + + " } finally {\n" + + " reader.close();\n" + + " }\n" + + " }\n" + + "\n" + + " void foo3() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " try {\n" + + " reader.read();\n" + + " throw new Exception();\n" + + " } finally {\n" + + " reader.close();\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " ^^^^^^\n" + + "Resource 'reader' should be managed by try-with-resource\n" + + "----------\n" + + "2. ERROR in X.java (at line 16)\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " ^^^^^^\n" + + "Resource 'reader' should be managed by try-with-resource\n" + + "----------\n" + + "3. ERROR in X.java (at line 28)\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " ^^^^^^\n" + + "Resource 'reader' should be managed by try-with-resource\n" + + "----------\n", + options); +} +// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points +// close() nested within finally provides protection for throw +public void test056throw3() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + runTestsExpectingErrorsOnlyIn17( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo2x() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " try {\n" + + " reader.read();\n" + + " throw new Exception(); // should not warn here\n" + + " } catch (Exception e) {\n" + + " throw new Exception();\n" + + " } finally {\n" + + " if (reader != null)\n" + + " try {\n" + + " reader.close();\n" + + " } catch (java.io.IOException io) {}\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 4)\n" + + " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + + " ^^^^^^\n" + + "Resource 'reader' should be managed by try-with-resource\n" + + "----------\n", + options); +} +// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points +// additional boolean should shed doubt on whether we reach the close() call +public void test056throw4() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo2x(boolean b) throws Exception {\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " try {\n" + + " reader.read();\n" + + " throw new Exception(); // should warn here\n" + + " } catch (Exception e) {\n" + + " throw new Exception(); // should warn here\n" + + " } finally {\n" + + " if (reader != null && b)\n" + // this condition is too strong to protect reader + " try {\n" + + " reader.close();\n" + + " } catch (java.io.IOException io) {}\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " throw new Exception(); // should warn here\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: 'reader' may not be closed at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 9)\n" + + " throw new Exception(); // should warn here\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: 'reader' may not be closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points +// similar to test056throw3() but indirectly calling close(), so doubts remain. +public void test056throw5() { + Map options = getCompilerOptions(); + options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); + options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); + this.runNegativeTest( + new String[] { + "X.java", + "import java.io.FileReader;\n" + + "public class X {\n" + + " void foo2x() throws Exception {\n" + + " FileReader reader = new FileReader(\"file\");\n" + + " try {\n" + + " reader.read();\n" + + " throw new Exception(); // should warn 'may not' here\n" + + " } catch (Exception e) {\n" + + " throw new Exception(); // should warn 'may not' here\n" + + " } finally {\n" + + " doClose(reader);\n" + + " }\n" + + " }\n" + + " void doClose(FileReader r) { try { r.close(); } catch (java.io.IOException ex) {}}\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " throw new Exception(); // should warn \'may not\' here\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: 'reader' may not be closed at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 9)\n" + + " throw new Exception(); // should warn \'may not\' here\n" + + " ^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: 'reader' may not be closed at this location\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 +// 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 361073 - Avoid resource leak warning when the top level resource is closed explicitly +// a resource wrapper is closed closing also the underlying resource - from a real-world example +public void test061f2() throws IOException { + 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.OutputStream;\n" + + "import java.io.FileOutputStream;\n" + + "import java.io.BufferedOutputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void zork() throws IOException {\n" + + " try {\n" + + " OutputStream os = null;\n" + + " try {\n" + + " os = new BufferedOutputStream(new FileOutputStream(\"somefile\"));\n" + + " String externalForm = \"externalPath\";\n" + + " } finally {\n" + + " if (os != null)\n" + + " os.close();\n" + + " }\n" + + " } catch (IOException e) {\n" + + " e.printStackTrace();\n" + + " }\n" + + " }\n" + + "}\n" + }, + "", + 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 sent to another method affecting also the underlying resource - from a real-world example +public void test061f3() throws IOException { + 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.FileInputStream;\n" + + "import java.io.FileNotFoundException;\n" + + "import java.io.InputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "public class X {\n" + + " String loadProfile(File profileFile) {\n" + + " try {\n" + + " InputStream stream = new BufferedInputStream(new FileInputStream(profileFile));\n" + + " return loadProfile(stream);\n" + + " } catch (FileNotFoundException e) {\n" + + " //null\n" + + " }\n" + + " return null;\n" + + " }\n" + + " private String loadProfile(InputStream stream) {\n" + + " return null;\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 10)\n" + + " return loadProfile(stream);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: \'stream\' may not be closed at this location\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 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 dummy;\n" + + " BufferedInputStream bis2 = (dummy = 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 20)\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 stream2 = new FileInputStream(file);\n" + + " stream2 = new BufferedInputStream(stream2);\n" + + " stream2 = new BufferedInputStream(stream2);\n" + + " System.out.println(stream2.available());\n" + + " stream2.close();\n" + + " }\n" + + " void neverClosed() throws IOException {\n" + + " File file = new File(\"somefile\");\n" + + " InputStream stream3 = new FileInputStream(file);\n" + + " stream3 = new BufferedInputStream(stream3);\n" + + " stream3 = new BufferedInputStream(stream3);\n" + + " System.out.println(stream3.available());\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 26)\n" + + " InputStream stream3 = new FileInputStream(file);\n" + + " ^^^^^^^\n" + + "Resource leak: \'stream3\' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// self-wrapping a method argument (caused NPE UnconditionalFlowInfo.markAsDefinitelyNull(..)). +public void test061j() { + 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.InputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(InputStream stream) throws IOException {\n" + + " stream = new BufferedInputStream(stream);\n" + + " System.out.println(stream.available());\n" + + " stream.close();\n" + + " }\n" + + " void boo(InputStream stream2) throws IOException {\n" + + " stream2 = new BufferedInputStream(stream2);\n" + + " System.out.println(stream2.available());\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a wrapper is created in a return statement +public void test061k() throws IOException { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " BufferedInputStream getReader(File file) throws IOException {\n" + + " FileInputStream stream = new FileInputStream(file);\n" + + " return new BufferedInputStream(stream);\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a closeable is assigned to a field +public void test061l() throws IOException { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " BufferedInputStream stream;\n" + + " void foo(File file) throws IOException {\n" + + " FileInputStream s = new FileInputStream(file);\n" + + " stream = new BufferedInputStream(s);\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a closeable is passed to another method in a return statement +// example constructed after org.eclipse.equinox.internal.p2.artifact.repository.simple.SimpleArtifactRepository#getArtifact(..) +public void test061m() throws IOException { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.InputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " BufferedInputStream stream;\n" + + " BufferedInputStream foo(File file) throws IOException {\n" + + " FileInputStream s = new FileInputStream(file);\n" + + " return check(new BufferedInputStream(s));\n" + + " }\n" + + " BufferedInputStream foo2(FileInputStream s, File file) throws IOException {\n" + + " s = new FileInputStream(file);\n" + + " return check(s);\n" + + " }\n" + + " BufferedInputStream foo3(InputStream s) throws IOException {\n" + + " s = check(s);\n" + + " return check(s);\n" + + " }\n" + + " BufferedInputStream check(InputStream s) { return null; }\n" + + "}\n" + }, + // TODO: also these warnings *might* be avoidable by detecting check(s) as a wrapper creation?? + "----------\n" + + "1. ERROR in X.java (at line 14)\n" + + " return check(s);\n" + + " ^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: \'s\' may not be closed at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 18)\n" + + " return check(s);\n" + + " ^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: \'s\' may not be closed at this location\n" + + "----------\n", + null, + true, + options); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a resource wrapper does not wrap any provided resource +public void test061n() { + 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.PrintWriter;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " PrintWriter writer = new PrintWriter(\"filename\");\n" + + " writer.write(1);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " PrintWriter writer = new PrintWriter(\"filename\");\n" + + " ^^^^^^\n" + + "Resource leak: \'writer\' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a resource wrapper is closed only in its local block, underlying resource may leak +public void test061o() { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean bar) throws IOException {\n" + + " File file = new File(\"somefil\");\n" + + " FileInputStream fileStream = new FileInputStream(file);\n" + + " BufferedInputStream bis = new BufferedInputStream(fileStream); \n" + + " if (bar) {\n" + + " BufferedInputStream doubleWrap = new BufferedInputStream(bis);\n" + + " doubleWrap.close();\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 9)\n" + + " BufferedInputStream bis = new BufferedInputStream(fileStream); \n" + + " ^^^\n" + + "Potential resource leak: \'bis\' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a resource wrapper is conditionally allocated but not closed - from a real-world example +public void test061f4() throws IOException { + 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.FileInputStream;\n" + + "import java.io.FileNotFoundException;\n" + + "import java.io.InputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "public class X {\n" + + " void foo(File location, String adviceFilePath) throws FileNotFoundException {\n" + + " InputStream stream = null;\n" + + " if (location.isDirectory()) {\n" + + " File adviceFile = new File(location, adviceFilePath);\n" + + " stream = new BufferedInputStream(new FileInputStream(adviceFile));\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 11)\n" + + " stream = new BufferedInputStream(new FileInputStream(adviceFile));\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: \'stream\' may not be closed\n" + // message could be stronger, but the enclosing if blurs the picture + "----------\n", + null, + true, + options); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a t-w-r wraps an existing resource +public void test061p() { + if (this.complianceLevel < ClassFileConstants.JDK1_7) return; + 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.PrintWriter;\n" + + "import java.io.BufferedWriter;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " PrintWriter writer = new PrintWriter(\"filename\");\n" + + " try (BufferedWriter bw = new BufferedWriter(writer)) {\n" + + " bw.write(1);\n" + + " }\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a t-w-r potentially wraps an existing resource +// DISABLED, fails because we currently don't include t-w-r managed resources in the analysis +public void _test061q() { + if (this.complianceLevel < ClassFileConstants.JDK1_7) return; + 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.PrintWriter;\n" + + "import java.io.BufferedWriter;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo(boolean b) throws IOException {\n" + + " PrintWriter writer = new PrintWriter(\"filename\");\n" + + " if (b)\n" + + " try (BufferedWriter bw = new BufferedWriter(writer)) {\n" + + " bw.write(1);\n" + + " }\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " PrintWriter writer = new PrintWriter(\\\"filename\\\");\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: \'writer\' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// the inner from a wrapper is returned +public void test061r() { + 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.FileInputStream;\n" + + "import java.io.File;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " FileInputStream foo() throws IOException {\n" + + " File file = new File(\"somefil\");\n" + + " FileInputStream fileStream = new FileInputStream(file);\n" + + " BufferedInputStream bis = new BufferedInputStream(fileStream); \n" + + " return fileStream;\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 358903 - Filter practically unimportant resource leak warnings +// a wrapper is forgotten, the inner is closed afterwards +public void test061s() { + 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.FileInputStream;\n" + + "import java.io.File;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " File file = new File(\"somefil\");\n" + + " FileInputStream fileStream = new FileInputStream(file);\n" + + " BufferedInputStream bis = new BufferedInputStream(fileStream);\n" + + " bis = null;\n" + + " fileStream.close();\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 362331 - Resource leak not detected when closeable not assigned to variable +// a resource is never assigned +public void test062a() throws IOException { + 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.FileOutputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " new FileOutputStream(new File(\"C:\\temp\\foo.txt\")).write(1);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " new FileOutputStream(new File(\"C:\\temp\\foo.txt\")).write(1);\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: \'\' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 362331 - Resource leak not detected when closeable not assigned to variable +// a freshly allocated resource is immediately closed +public void test062b() throws IOException { + 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.FileOutputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " new FileOutputStream(new File(\"C:\\temp\\foo.txt\")).close();\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 362331 - Resource leak not detected when closeable not assigned to variable +// a resource is directly passed to another method +public void test062c() throws IOException { + 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.FileOutputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " writeIt(new FileOutputStream(new File(\"C:\\temp\\foo.txt\")));\n" + + " }\n" + + " void writeIt(FileOutputStream fos) throws IOException {\n" + + " fos.write(1);\n" + + " fos.close();\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 362331 - Resource leak not detected when closeable not assigned to variable +// a resource is not used +public void test062d() throws IOException { + 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.FileOutputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo() throws IOException {\n" + + " new FileOutputStream(new File(\"C:\\temp\\foo.txt\"));\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 6)\n" + + " new FileOutputStream(new File(\"C:\\temp\\foo.txt\"));\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Resource leak: \'\' is never closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// a wrapper is obtained from another method +public void test063a() throws IOException { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void read(File file) throws IOException {\n" + + " FileInputStream stream = new FileInputStream(file);\n" + + " BufferedInputStream bis = new BufferedInputStream(stream); // never since reassigned\n" + + " FileInputStream stream2 = new FileInputStream(file); // unsure since passed to method\n" + + " bis = getReader(stream2); // unsure since obtained from method\n" + + " bis.available();\n" + + " }\n" + + " BufferedInputStream getReader(FileInputStream stream) throws IOException {\n" + + " return new BufferedInputStream(stream);\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " FileInputStream stream = new FileInputStream(file);\n" + + " ^^^^^^\n" + + "Resource leak: \'stream\' is never closed\n" + + "----------\n" + + "2. ERROR in X.java (at line 9)\n" + + " FileInputStream stream2 = new FileInputStream(file); // unsure since passed to method\n" + + " ^^^^^^^\n" + + "Potential resource leak: \'stream2\' may not be closed\n" + + "----------\n" + + "3. ERROR in X.java (at line 10)\n" + + " bis = getReader(stream2); // unsure since obtained from method\n" + + " ^^^^^^^^^^^^^^^^^^^^^^^^\n" + + "Potential resource leak: \'bis\' may not be closed\n" + + "----------\n", + null, + true, + options); +} +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// a wrapper is obtained from a field read +public void test063b() throws IOException { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " FileInputStream stream;\n" + + " void read() throws IOException {\n" + + " FileInputStream s = this.stream;\n" + + " BufferedInputStream bis = new BufferedInputStream(s); // don't complain since s is obtained from a field\n" + + " bis.available();\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// a wrapper is assigned to a field +public void test063c() throws IOException { + 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.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " BufferedInputStream stream;\n" + + " void read() throws IOException {\n" + + " FileInputStream s = new FileInputStream(\"somefile\");\n" + + " BufferedInputStream bis = new BufferedInputStream(s);\n" + + " this.stream = bis;\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// a resource is obtained as a method argument and/or assigned with a cast +public void test063d() throws IOException { + Map options = getCompilerOptions(); + options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); + options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); + runTestsExpectingErrorsOnlyIn17( + new String[] { + "X.java", + "import java.io.FileInputStream;\n" + + "import java.io.BufferedInputStream;\n" + + "import java.io.InputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " void foo( InputStream input) throws IOException {\n" + + " FileInputStream input1 = (FileInputStream)input;\n" + + " System.out.println(input1.read());\n" + + " input.close();\n" + // don't propose t-w-r for argument + " }\n" + + " void foo() throws IOException {\n" + + " InputStream input = new FileInputStream(\"somefile\");\n" + + " FileInputStream input1 = (FileInputStream)input;\n" + + " System.out.println(input1.read());\n" + + " input.close();\n" + // do propose t-w-r, not from a method argument + " }\n" + + " void foo3( InputStream input, InputStream input2) throws IOException {\n" + + " FileInputStream input1 = (FileInputStream)input;\n" + // still don't claim because obtained from outside + " System.out.println(input1.read());\n" + + " BufferedInputStream bis = new BufferedInputStream(input2);\n" + + " System.out.println(bis.read());\n" + + " }\n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 12)\n" + + " InputStream input = new FileInputStream(\"somefile\");\n" + + " ^^^^^\n" + + "Resource \'input\' should be managed by try-with-resource\n" + + "----------\n", + options); +} +// Bug 362332 - Only report potential leak when closeable not created in the local scope +// a resource is obtained from a field read, then re-assigned +public void test063e() { + 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.FileInputStream;\n" + + "import java.io.IOException;\n" + + "public class X {\n" + + " FileInputStream input1;\n" + + " public void foo() throws IOException {\n" + + " FileInputStream input = input1;\n" + + " input = new FileInputStream(\"adfafd\");\n" + + " input.close();\n" + + " }\n" + + "}\n" + }, + "", + null, + true, + null, + options, + null); +} + +} diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java index 4dbf8ba..5f2f3d8 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.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 @@ -7,7 +7,9 @@ * * Contributors: * IBM Corporation - initial API and implementation - * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking + * Stephan Herrmann - Contributions for + * bug 186342 - [compiler][null] Using annotations for null checking + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.core.tests.compiler.regression; @@ -74,6 +76,7 @@ public static Test suite() { standardTests.add(ProgrammingProblemsTest.class); standardTests.add(ManifestAnalyzerTest.class); standardTests.add(InitializationTests.class); + standardTests.add(ResourceLeakTests.class); // add all javadoc tests for (int i=0, l=JavadocTest.ALL_CLASSES.size(); i= 1;\n" + " }\n" + + " }\n" + + " int num() { return 2; }\n" + + " public void close() {\n" + + " isOpen = false;\n" + + " }\n" + "}\n" - }, - "caught", /*output*/ - null/*classLibs*/, - true/*shouldFlush*/, - null/*vmargs*/, - options, - null/*requestor*/); + }, + "false"); } -// Bug 349326 - [1.7] new warning for missing try-with-resources -// a method uses an AutoCloseable properly within try-with-resources. -public void test056c() { - Map options = getCompilerOptions(); - options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); - options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); + +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=367566 - In try-with-resources statement close() method of resource is not called +public void test060() { this.runConformTest( new String[] { "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " try (FileReader fileReader = new FileReader(file)) {\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " }\n" + + "public class X implements AutoCloseable {\n" + + " static int num = 10 ;\n" + + " public static void main(String [] args) throws Exception { \n" + + " System.out.println(foo(1));\n" + + " System.out.println(foo(2));\n" + + " System.out.println(foo(3));\n" + " }\n" + - " public static void main(String[] args) {\n" + - " try {\n" + - " new X().foo();\n" + - " } catch (IOException ioex) {\n" + - " System.out.println(\"caught\");\n" + + " private static boolean foo(int where) throws Exception {\n" + + " final boolean getOut = true;\n" + + " System.out.println(\"Main\");\n" + + " try (X x1 = new X(); X x2 = new X()) {\n" + + " if (where == 1) {\n" + + " return where == 1;\n" + + " }\n" + + " System.out.println(\"Outer Try\");\n" + + " while (true) {\n" + + " try (Y y1 = new Y(); Y y2 = new Y()) { \n" + + " if (where == 2) {\n" + + " return where == 2;\n" + + " } \n" + + " System.out.println(\"Middle Try\");\n" + + " try (Z z1 = new Z(); Z z2 = new Z()) {\n" + + " System.out.println(\"Inner Try\");\n" + + " if (getOut) \n" + + " return num >= 10;\n" + + " else\n" + + " break; \n" + + " }\n" + + " }\n" + + " }\n" + + " System.out.println(\"Out of while\");\n" + " }\n" + + " return false;\n" + + " }\n" + + " public X() {\n" + + " System.out.println(\"X::X\");\n" + " }\n" + - "}\n" - }, - "caught", /*output*/ - null/*classLibs*/, - true/*shouldFlush*/, - null/*vmargs*/, - options, - null/*requestor*/); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// a method uses two AutoCloseables (testing independent analysis) -// - one closeable may be unclosed at a conditional return -// - the other is only conditionally closed -public void test056d() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo(boolean flag1, boolean flag2) throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " char[] in = new char[50];\n" + - " FileReader fileReader1 = new FileReader(file);\n" + - " fileReader1.read(in);\n" + - " FileReader fileReader2 = new FileReader(file);\n" + - " fileReader2.read(in);\n" + - " if (flag1) {\n" + - " fileReader2.close();\n" + - " return;\n" + - " } else if (flag2) {\n" + - " fileReader2.close();\n" + - " }\n" + - " fileReader1.close();\n" + + " @Override\n" + + " public void close() throws Exception {\n" + + " System.out.println(\"X::~X\");\n" + " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo(false, true);\n" + + "}\n" + + "class Y implements AutoCloseable {\n" + + " public Y() {\n" + + " System.out.println(\"Y::Y\");\n" + " }\n" + - "}\n" - }, - "----------\n" + - "1. WARNING in X.java (at line 10)\n" + - " FileReader fileReader2 = new FileReader(file);\n" + - " ^^^^^^^^^^^\n" + - "Potential resource leak: 'fileReader2' may not be closed\n" + - "----------\n" + - "2. ERROR in X.java (at line 14)\n" + - " return;\n" + - " ^^^^^^^\n" + - "Resource leak: 'fileReader1' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -//Bug 349326 - [1.7] new warning for missing try-with-resources -//a method uses two AutoCloseables (testing independent analysis) -//- one closeable may be unclosed at a conditional return -//- the other is only conditionally closed -public void test056d_suppress() { - Map options = getCompilerOptions(); - options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); - options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); - options.put(CompilerOptions.OPTION_SuppressOptionalErrors, CompilerOptions.ENABLED); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo(boolean flag1, boolean flag2) throws IOException {\n" + - " @SuppressWarnings(\"resource\") File file = new File(\"somefile\"); // unnecessary suppress\n" + - " char[] in = new char[50];\n" + - " FileReader fileReader1 = new FileReader(file);\n" + - " fileReader1.read(in);\n" + - " @SuppressWarnings(\"resource\") FileReader fileReader2 = new FileReader(file); // useful suppress\n" + - " fileReader2.read(in);\n" + - " if (flag1) {\n" + - " fileReader2.close();\n" + - " return; // not suppressed\n" + - " } else if (flag2) {\n" + - " fileReader2.close();\n" + - " }\n" + - " fileReader1.close();\n" + + " @Override\n" + + " public void close() throws Exception {\n" + + " System.out.println(\"Y::~Y\");\n" + " }\n" + - " @SuppressWarnings(\"resource\") // useful suppress\n" + - " void bar() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + + "}\n" + + "class Z implements AutoCloseable {\n" + + " public Z() {\n" + + " System.out.println(\"Z::Z\");\n" + " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo(false, true);\n" + + " @Override\n" + + " public void close() throws Exception {\n" + + " System.out.println(\"Z::~Z\");\n" + " }\n" + "}\n" - }, - "----------\n" + - "1. WARNING in X.java (at line 6)\n" + - " @SuppressWarnings(\"resource\") File file = new File(\"somefile\"); // unnecessary suppress\n" + - " ^^^^^^^^^^\n" + - "Unnecessary @SuppressWarnings(\"resource\")\n" + - "----------\n" + - "2. ERROR in X.java (at line 14)\n" + - " return; // not suppressed\n" + - " ^^^^^^^\n" + - "Resource leak: 'fileReader1' is not closed at this location\n" + - "----------\n", - null, - true, - options); + }, + "Main\n" + + "X::X\n" + + "X::X\n" + + "X::~X\n" + + "X::~X\n" + + "true\n" + + "Main\n" + + "X::X\n" + + "X::X\n" + + "Outer Try\n" + + "Y::Y\n" + + "Y::Y\n" + + "Y::~Y\n" + + "Y::~Y\n" + + "X::~X\n" + + "X::~X\n" + + "true\n" + + "Main\n" + + "X::X\n" + + "X::X\n" + + "Outer Try\n" + + "Y::Y\n" + + "Y::Y\n" + + "Middle Try\n" + + "Z::Z\n" + + "Z::Z\n" + + "Inner Try\n" + + "Z::~Z\n" + + "Z::~Z\n" + + "Y::~Y\n" + + "Y::~Y\n" + + "X::~X\n" + + "X::~X\n" + + "true"); } -// Bug 349326 - [1.7] new warning for missing try-with-resources -// one method returns an AutoCleasble, a second method uses this object without ever closing it. -public void test056e() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " FileReader getReader(String filename) throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " return fileReader;\n" + // don't complain here, pass responsibility to caller - " }\n" + - " void foo() throws IOException {\n" + - " FileReader reader = getReader(\"somefile\");\n" + - " char[] in = new char[50];\n" + - " reader.read(in);\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 11)\n" + - " FileReader reader = getReader(\"somefile\");\n" + - " ^^^^^^\n" + - "Resource leak: 'reader' is never closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// a method explicitly closes its AutoCloseable rather than using t-w-r -public void test056f() { - Map options = getCompilerOptions(); - options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); - options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); - options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = null;\n" + - " try {\n" + - " fileReader = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " } finally {\n" + - " fileReader.close();\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " FileReader fileReader = null;\n" + - " ^^^^^^^^^^\n" + - "Resource 'fileReader' should be managed by try-with-resource\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// an AutoCloseable local is re-assigned -public void test056g() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " fileReader = new FileReader(file);\n" + - " fileReader.read(in);\n" + - " fileReader.close();\n" + - " fileReader = null;\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 10)\n" + - " fileReader = new FileReader(file);\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'fileReader' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// an AutoCloseable local is re-assigned after null-assigned -public void test056g2() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " fileReader = null;\n" + - " fileReader = new FileReader(file);\n" + // don't complain again, fileReader is null, so nothing can leak here - " fileReader.read(in);\n" + - " fileReader.close();\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 10)\n" + - " fileReader = null;\n" + - " ^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'fileReader' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// two AutoCloseables at different nesting levels (anonymous local type) -public void test056h() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " final File file = new File(\"somefile\");\n" + - " final FileReader fileReader = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " new Runnable() {\n public void run() {\n" + - " try {\n" + - " fileReader.close();\n" + - " FileReader localReader = new FileReader(file);\n" + - " } catch (IOException ex) { /* nop */ }\n" + - " }}.run();\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. WARNING in X.java (at line 7)\n" + - " final FileReader fileReader = new FileReader(file);\n" + - " ^^^^^^^^^^\n" + - "Potential resource leak: 'fileReader' may not be closed\n" + - "----------\n" + - "2. ERROR in X.java (at line 14)\n" + - " FileReader localReader = new FileReader(file);\n" + - " ^^^^^^^^^^^\n" + - "Resource leak: 'localReader' is never closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// three AutoCloseables in different blocks of the same method -public void test056i() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo(boolean f1, boolean f2) throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " if (f1) {\n" + - " FileReader fileReader = new FileReader(file); // err: not closed\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " while (true) {\n" + - " FileReader loopReader = new FileReader(file); // don't warn, properly closed\n" + - " loopReader.close();" + - " break;\n" + - " }\n" + - " } else {\n" + - " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + - " if (f2)\n" + - " fileReader.close();\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo(true, true);\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 8)\n" + - " FileReader fileReader = new FileReader(file); // err: not closed\n" + - " ^^^^^^^^^^\n" + - "Resource leak: 'fileReader' is never closed\n" + - "----------\n" + - "2. WARNING in X.java (at line 16)\n" + - " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + - " ^^^^^^^^^^\n" + - "Potential resource leak: 'fileReader' may not be closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// three AutoCloseables in different blocks of the same method -public void test056i2() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo(boolean f1, boolean f2) throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " if (f1) {\n" + - " FileReader fileReader = new FileReader(file); // properly closed\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " while (true) {\n" + - " fileReader.close();\n" + - " FileReader loopReader = new FileReader(file); // don't warn, properly closed\n" + - " loopReader.close();\n" + - " break;\n" + - " }\n" + - " } else {\n" + - " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + - " if (f2)\n" + - " fileReader.close();\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo(true, true);\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 18)\n" + - " FileReader fileReader = new FileReader(file); // warn: not closed on all paths\n" + - " ^^^^^^^^^^\n" + - "Potential resource leak: 'fileReader' may not be closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// a method uses an AutoCloseable without closing it locally but passing as arg to another method -public void test056j() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " read(fileReader);\n" + - " }\n" + - " void read(FileReader reader) { }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " FileReader fileReader = new FileReader(file);\n" + - " ^^^^^^^^^^\n" + - "Potential resource leak: 'fileReader' may not be closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// a method uses an AutoCloseable without closing it locally but passing as arg to another method -public void test056jconditional() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo(boolean b) throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " synchronized (b ? this : new X()) {\n" + - " new ReadDelegator(fileReader);\n" + - " }\n" + - " }\n" + - " class ReadDelegator { ReadDelegator(FileReader reader) { } }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo(true);\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " FileReader fileReader = new FileReader(file);\n" + - " ^^^^^^^^^^\n" + - "Potential resource leak: 'fileReader' may not be closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// many locals, some are AutoCloseable. -// Unfortunately analysis cannot respect how exception exits may affect ra3 and rb3, -// doing so would create false positives. -public void test056k() { - Map options = getCompilerOptions(); - options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); - options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.WARNING); - options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " int i01, i02, i03, i04, i05, i06, i07, i08, i09,\n" + - " i11, i12, i13, i14, i15, i16, i17, i18, i19,\n" + - " i21, i22, i23, i24, i25, i26, i27, i28, i29,\n" + - " i31, i32, i33, i34, i35, i36, i37, i38, i39,\n" + - " i41, i42, i43, i44, i45, i46, i47, i48, i49;\n" + - " File file = new File(\"somefile\");\n" + - " FileReader ra1 = null, ra2 = null;\n" + - " try {\n" + - " ra1 = new FileReader(file);\n" + - " ra2 = new FileReader(file);\n" + - " FileReader ra3 = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " ra1.read(in);\n" + - " ra2.read(in);\n" + - " ra3.close();\n" + - " } finally {\n" + - " ra1.close();\n" + - " }\n" + - " int i51, i52, i53, i54, i55, i56, i57, i58, i59, i60;\n" + // beyond this point locals are analyzed using extraBits - " FileReader rb1 = null, rb2 = null;\n" + - " try {\n" + - " rb1 = new FileReader(file);\n" + - " rb2 = new FileReader(file);\n" + - " FileReader rb3 = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " rb1.read(in);\n" + - " rb2.read(in);\n" + - " rb3.close();\n" + - " } finally {\n" + - " rb1.close();\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 12)\n" + - " FileReader ra1 = null, ra2 = null;\n" + - " ^^^\n" + - "Resource 'ra1' should be managed by try-with-resource\n" + - "----------\n" + - "2. ERROR in X.java (at line 15)\n" + - " ra2 = new FileReader(file);\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'ra2' is never closed\n" + - "----------\n" + - "3. ERROR in X.java (at line 16)\n" + - " FileReader ra3 = new FileReader(file);\n" + - " ^^^\n" + - "Resource 'ra3' should be managed by try-with-resource\n" + - "----------\n" + - "4. ERROR in X.java (at line 25)\n" + - " FileReader rb1 = null, rb2 = null;\n" + - " ^^^\n" + - "Resource 'rb1' should be managed by try-with-resource\n" + - "----------\n" + - "5. ERROR in X.java (at line 28)\n" + - " rb2 = new FileReader(file);\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'rb2' is never closed\n" + - "----------\n" + - "6. ERROR in X.java (at line 29)\n" + - " FileReader rb3 = new FileReader(file);\n" + - " ^^^\n" + - "Resource 'rb3' should be managed by try-with-resource\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// various non-problems -public void test056l() { - Map options = getCompilerOptions(); - options.put(CompilerOptions.OPTION_ReportUnclosedCloseable, CompilerOptions.ERROR); - options.put(CompilerOptions.OPTION_ReportPotentiallyUnclosedCloseable, CompilerOptions.ERROR); - options.put(CompilerOptions.OPTION_ReportExplicitlyClosedAutoCloseable, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " X(FileReader r0) {}\n" + // don't complain against argument - " FileReader getReader() { return null; }\n" + - " void foo(FileReader r1) throws IOException {\n" + - " FileReader fileReader = getReader();\n" + - " if (fileReader == null)\n" + - " return;\n" + // don't complain, resource is actually null - " FileReader r3 = getReader();\n" + - " if (r3 == null)\n" + - " r3 = new FileReader(new File(\"absent\"));\n" + // don't complain, previous resource is actually null - " try {\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " r1.read(in);\n" + - " } finally {\n" + - " fileReader.close();\n" + - " r3.close();\n" + // the effect of this close() call might be spoiled by exception in fileReader.close() above, but we ignore exception exits in the analysis - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " FileReader r2 = new FileReader(new File(\"inexist\")); // only potential problem: ctor X below might close r2\n" + - " new X(r2).foo(new FileReader(new File(\"notthere\")));\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 8)\n" + - " FileReader fileReader = getReader();\n" + - " ^^^^^^^^^^\n" + - "Resource 'fileReader' should be managed by try-with-resource\n" + - "----------\n" + - "2. ERROR in X.java (at line 11)\n" + - " FileReader r3 = getReader();\n" + - " ^^\n" + - "Resource 'r3' should be managed by try-with-resource\n" + - "----------\n" + - "3. ERROR in X.java (at line 24)\n" + - " FileReader r2 = new FileReader(new File(\"inexist\")); // only potential problem: ctor X below might close r2\n" + - " ^^\n" + - "Potential resource leak: 'r2' may not be closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// nested try with early exit -public void test056m() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() {\n" + - " File file = new File(\"somefile\");" + - " try {\n" + - " FileReader fileReader = new FileReader(file);\n" + - " try {\n" + - " char[] in = new char[50];\n" + - " if (fileReader.read(in)==0)\n" + - " return;\n" + - " } finally {\n" + - " fileReader.close();\n" + - " }\n" + - " } catch (IOException e) {\n" + - " System.out.println(\"caught\");\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "caught", /*output*/ - null/*classLibs*/, - true/*shouldFlush*/, - null/*vmargs*/, - options, - null/*requestor*/); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// nested try should not interfere with earlier analysis. -public void test056n() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "import java.io.FileNotFoundException;\n" + - "public class X {\n" + - " void foo(File someFile, char[] buf) throws IOException {\n" + - " FileReader fr1 = new FileReader(someFile);\n" + - " try {\n" + - " fr1.read(buf);\n" + - " } finally {\n" + - " fr1.close();\n" + - " }\n" + - " try {\n" + - " FileReader fr3 = new FileReader(someFile);\n" + - " try {\n" + - " } finally {\n" + - " fr3.close();\n" + - " }\n" + - " } catch (IOException e) {\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " try {\n" + - " new X().foo(new File(\"missing\"), new char[100]);\n" + - " } catch (FileNotFoundException e) {\n" + - " System.out.println(\"caught\");\n" + - " }\n" + - " }\n" + - "}\n" - }, - "caught", /*output*/ - null/*classLibs*/, - true/*shouldFlush*/, - null/*vmargs*/, - options, - null/*requestor*/); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// if close is guarded by null check this should still be recognized as definitely closed -public void test056o() { - 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.FileReader;\n" + - "import java.io.IOException;\n" + - "import java.io.FileNotFoundException;\n" + - "public class X {\n" + - " void foo(File someFile, char[] buf) throws IOException {\n" + - " FileReader fr1 = null;\n" + - " try {\n" + - " fr1 = new FileReader(someFile);" + - " fr1.read(buf);\n" + - " } finally {\n" + - " if (fr1 != null)\n" + - " try {\n" + - " fr1.close();\n" + - " } catch (IOException e) { /*do nothing*/ }\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " try {\n" + - " new X().foo(new File(\"missing\"), new char[100]);\n" + - " } catch (FileNotFoundException e) {\n" + - " System.out.println(\"caught\");\n" + - " }\n" + - " }\n" + - "}\n" - }, - "caught", /*output*/ - null/*classLibs*/, - true/*shouldFlush*/, - null/*vmargs*/, - options, - null/*requestor*/); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// a method uses an AutoCloseable without ever closing it, type from a type variable -public void test056p() { - 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.FileReader;\n" + - "import java.io.Reader;\n" + - "import java.io.IOException;\n" + - "public abstract class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " T fileReader = newReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " }\n" + - " abstract T newReader(File file) throws IOException;\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X() {\n" + - " FileReader newReader(File f) throws IOException { return new FileReader(f); }\n" + - " }.foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 8)\n" + - " T fileReader = newReader(file);\n" + - " ^^^^^^^^^^\n" + - "Resource leak: 'fileReader' is never closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// closed in dead code -public void test056q() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fileReader = new FileReader(file);\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " if (2*2 == 4)\n" + - " return;\n" + - " fileReader.close();\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " FileReader fileReader = new FileReader(file);\n" + - " ^^^^^^^^^^\n" + - "Resource leak: 'fileReader' is never closed\n" + - "----------\n" + - "2. WARNING in X.java (at line 10)\n" + - " if (2*2 == 4)\n" + - " ^^^^^^^^\n" + - "Comparing identical expressions\n" + - "----------\n" + - "3. WARNING in X.java (at line 12)\n" + - " fileReader.close();\n" + - " ^^^^^^^^^^^^^^^^^^\n" + - "Dead code\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// closed in dead code -public void test056r() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " FileReader fr = new FileReader(file);\n" + - " Object b = null;\n" + - " fr.close();\n" + - " if (b != null) {\n" + - " fr = new FileReader(file);\n" + - " return;\n" + - " } else {\n" + - " System.out.print(42);\n" + - " }\n" + - " return; // Should not complain about fr\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 10)\n" + - " if (b != null) {\n" + - " fr = new FileReader(file);\n" + - " return;\n" + - " } else {\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Dead code\n" + - "----------\n" + - "2. WARNING in X.java (at line 13)\n" + - " } else {\n" + - " System.out.print(42);\n" + - " }\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Statement unnecessarily nested within else clause. The corresponding then clause does not complete normally\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// resource inside t-w-r is re-assigned, shouldn't even record an errorLocation -public void test056s() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.IGNORE); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.File;\n" + - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo() throws IOException {\n" + - " File file = new File(\"somefile\");\n" + - " try (FileReader fileReader = new FileReader(file);) {\n" + - " char[] in = new char[50];\n" + - " fileReader.read(in);\n" + - " fileReader = new FileReader(file); // debug here\n" + - " fileReader.read(in);\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 10)\n" + - " fileReader = new FileReader(file); // debug here\n" + - " ^^^^^^^^^^\n" + - "The resource fileReader of a try-with-resources statement cannot be assigned\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// resource is closed, dead code follows -public void test056t() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "import java.io.IOException;\n" + - "public class X {\n" + - " void foo31() throws IOException {\n" + - " FileReader reader = new FileReader(\"file\"); //warning\n" + - " if (reader != null) {\n" + - " reader.close();\n" + - " } else {\n" + - " // nop\n" + - " }\n" + - " }\n" + - " public static void main(String[] args) throws IOException {\n" + - " new X().foo31();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 8)\n" + - " } else {\n" + - " // nop\n" + - " }\n" + - " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + - "Dead code\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// resource is reassigned within t-w-r with different resource -// was initially broken due to https://bugs.eclipse.org/358827 -public void test056u() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo() throws Exception {\n" + - " FileReader reader1 = new FileReader(\"file1\");\n" + - " FileReader reader2 = new FileReader(\"file2\");\n" + - " reader2 = reader1;// this disconnects reader 2\n" + - " try (FileReader reader3 = new FileReader(\"file3\")) {\n" + - " int ch;\n" + - " while ((ch = reader2.read()) != -1) {\n" + - " System.out.println(ch);\n" + - " reader1.read();\n" + - " }\n" + - " reader2 = reader1; // warning 1 regarding original reader1\n" + // this warning was missing - " reader2 = reader1; // warning 2 regarding original reader1\n" + - " } finally {\n" + - " if (reader2 != null) {\n" + - " reader2.close();\n" + - " } else {\n" + - " System.out.println();\n" + - " }\n" + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 5)\n" + - " FileReader reader2 = new FileReader(\"file2\");\n" + - " ^^^^^^^\n" + - "Resource leak: 'reader2' is never closed\n" + - "----------\n" + - "2. ERROR in X.java (at line 13)\n" + - " reader2 = reader1; // warning 1 regarding original reader1\n" + - " ^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'reader1' is not closed at this location\n" + - "----------\n" + - "3. ERROR in X.java (at line 14)\n" + - " reader2 = reader1; // warning 2 regarding original reader1\n" + - " ^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'reader1' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// scope-related pbs reported in https://bugs.eclipse.org/349326#c70 and https://bugs.eclipse.org/349326#c82 -public void test056v() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " boolean foo1() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " try {\n" + - " int ch;\n" + - " while ((ch = reader.read()) != -1) {\n" + - " System.out.println(ch);\n" + - " reader.read();\n" + - " }\n" + - " if (ch > 10) {\n" + - " return true;\n" + - " }\n" + - " return false;\n" + // return while resource from enclosing scope remains unclosed - " } finally {\n" + - " }\n" + - " }\n" + - " void foo111() throws Exception {\n" + - " FileReader reader111 = new FileReader(\"file2\");\n" + - " try {\n" + - " int ch;\n" + - " while ((ch = reader111.read()) != -1) {\n" + - " System.out.println(ch);\n" + - " reader111.read();\n" + - " }\n" + - " return;\n" + // this shouldn't spoil the warning "should be managed with t-w-r" - " } finally {\n" + - " if (reader111 != null) {\n" + - " reader111.close();\n" + - " }\n" + - " }\n" + - " }\n" + - " void foo2() throws Exception {\n" + - " FileReader reader2 = new FileReader(\"file\");\n" + - " try {\n" + - " int ch;\n" + - " while ((ch = reader2.read()) != -1) {\n" + - " System.out.println(ch);\n" + - " reader2.read();\n" + - " }\n" + - " if (ch > 10) {\n" + - " return;\n" + // potential leak - " }\n" + - " } finally {\n" + - " }\n" + - " reader2.close();\n" + // due to this close we don't say "never closed" - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " ^^^^^^\n" + - "Resource leak: 'reader' is never closed\n" + - "----------\n" + - "2. WARNING in X.java (at line 19)\n" + - " FileReader reader111 = new FileReader(\"file2\");\n" + - " ^^^^^^^^^\n" + - "Resource 'reader111' should be managed by try-with-resource\n" + - "----------\n" + - "3. ERROR in X.java (at line 42)\n" + - " return;\n" + - " ^^^^^^^\n" + - "Resource leak: 'reader2' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// end of method is dead end, but before we have both a close() and an early return -public void test056w() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " boolean foo1() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " try {\n" + - " int ch;\n" + - " while ((ch = reader.read()) != -1) {\n" + - " System.out.println(ch);\n" + - " reader.read();\n" + - " }\n" + - " if (ch > 10) {\n" + - " reader.close();\n" + - " return true;\n" + - " }\n" + - " return false;\n" + - " } finally {\n" + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 15)\n" + - " return false;\n" + - " ^^^^^^^^^^^^^\n" + - "Resource leak: 'reader' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// different early exits, if no close seen report as definitely unclosed -public void test056x() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo31(boolean b) throws Exception {\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " if (b) {\n" + - " reader.close();\n" + - " } else {\n" + - " return; // warning\n" + - " }\n" + - " }\n" + - " void foo32(boolean b) throws Exception {\n" + - " FileReader reader = new FileReader(\"file\"); // warn here\n" + - " return;\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 8)\n" + - " return; // warning\n" + - " ^^^^^^^\n" + - "Resource leak: 'reader' is not closed at this location\n" + - "----------\n" + - "2. ERROR in X.java (at line 12)\n" + - " FileReader reader = new FileReader(\"file\"); // warn here\n" + - " ^^^^^^\n" + - "Resource leak: 'reader' is never closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// nested method passes the resource to outside code -public void test056y() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.WARNING); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo31(boolean b) throws Exception {\n" + - " final FileReader reader31 = new FileReader(\"file\");\n" + - " new Runnable() {\n" + - " public void run() {\n" + - " foo18(reader31);\n" + - " }\n" + - " }.run();\n" + - " }\n" + - " void foo18(FileReader r18) {\n" + - " // could theoretically close r18;\n" + - " }\n" + - " abstract class ResourceProvider {\n" + - " abstract FileReader provide();" + - " }\n" + - " ResourceProvider provider;" + - " void foo23() throws Exception {\n" + - " final FileReader reader23 = new FileReader(\"file\");\n" + - " provider = new ResourceProvider() {\n" + - " public FileReader provide() {\n" + - " return reader23;\n" + - " }\n" + - " };\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. WARNING in X.java (at line 4)\n" + - " 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, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// resource assigned to second local and is (potentially) closed on the latter -public void test056z() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo17() throws Exception {\n" + - " FileReader reader17 = new FileReader(\"file\");\n" + - " final FileReader readerCopy = reader17;\n" + - " readerCopy.close();\n" + - " }\n" + - " void foo17a() throws Exception {\n" + - " FileReader reader17a = new FileReader(\"file\");\n" + - " FileReader readerCopya;" + - " readerCopya = reader17a;\n" + - " bar(readerCopya);\n" + // potentially closes - " }\n" + - " void bar(FileReader r) {}\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 9)\n" + - " FileReader reader17a = new FileReader(\"file\");\n" + - " ^^^^^^^^^\n" + - "Potential resource leak: 'reader17a' may not be closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// multiple early exists from nested scopes (always closed) -public void test056zz() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo16() throws Exception {\n" + - " FileReader reader16 = new FileReader(\"file\");\n" + - " try {\n" + - " reader16.close();\n " + - " return;\n" + - " } catch (RuntimeException re) {\n" + - " return;\n" + - " } catch (Error e) {\n" + - " return;\n" + - " } finally {\n" + - " reader16.close();\n " + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " FileReader reader16 = new FileReader(\"file\");\n" + - " ^^^^^^^^\n" + - "Resource 'reader16' should be managed by try-with-resource\n" + - "----------\n", - null, - true, - options); -} -// Bug 349326 - [1.7] new warning for missing try-with-resources -// multiple early exists from nested scopes (never closed) -public void test056zzz() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo16() throws Exception {\n" + - " FileReader reader16 = new FileReader(\"file\");\n" + - " try {\n" + - " return;\n" + - " } catch (RuntimeException re) {\n" + - " return;\n" + - " } catch (Error e) {\n" + - " return;\n" + - " } finally {\n" + - " System.out.println();\n " + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " FileReader reader16 = new FileReader(\"file\");\n" + - " ^^^^^^^^\n" + - "Resource leak: 'reader16' is never closed\n" + - "----------\n", - null, - true, - options); -} -// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points -// explicit throw is a true method exit here -public void test056throw1() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo2(boolean a, boolean b, boolean c) throws Exception {\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " if(a)\n" + - " throw new Exception(); //warning 1\n" + - " else if (b)\n" + - " reader.close();\n" + - " else if(c)\n" + - " throw new Exception(); //warning 2\n" + - " reader.close();\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 6)\n" + - " throw new Exception(); //warning 1\n" + - " ^^^^^^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'reader' is not closed at this location\n" + - "----------\n" + - "2. ERROR in X.java (at line 10)\n" + - " throw new Exception(); //warning 2\n" + - " ^^^^^^^^^^^^^^^^^^^^^^\n" + - "Resource leak: 'reader' is not closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points -// close() within finally provides protection for throw -public void test056throw2() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo1() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " try {\n" + - " reader.read();\n" + - " return;\n" + - " } catch (Exception e) {\n" + - " throw new Exception();\n" + - " } finally {\n" + - " reader.close();\n" + - " }\n" + - " }\n" + - "\n" + - " void foo2() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " try {\n" + - " reader.read();\n" + - " throw new Exception(); // should not warn here\n" + - " } catch (Exception e) {\n" + - " throw new Exception();\n" + - " } finally {\n" + - " reader.close();\n" + - " }\n" + - " }\n" + - "\n" + - " void foo3() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " try {\n" + - " reader.read();\n" + - " throw new Exception();\n" + - " } finally {\n" + - " reader.close();\n" + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " ^^^^^^\n" + - "Resource 'reader' should be managed by try-with-resource\n" + - "----------\n" + - "2. ERROR in X.java (at line 16)\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " ^^^^^^\n" + - "Resource 'reader' should be managed by try-with-resource\n" + - "----------\n" + - "3. ERROR in X.java (at line 28)\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " ^^^^^^\n" + - "Resource 'reader' should be managed by try-with-resource\n" + - "----------\n", - null, - true, - options); -} -// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points -// close() nested within finally provides protection for throw -public void test056throw3() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo2x() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " try {\n" + - " reader.read();\n" + - " throw new Exception(); // should not warn here\n" + - " } catch (Exception e) {\n" + - " throw new Exception();\n" + - " } finally {\n" + - " if (reader != null)\n" + - " try {\n" + - " reader.close();\n" + - " } catch (java.io.IOException io) {}\n" + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 4)\n" + - " FileReader reader = new FileReader(\"file\"); // propose t-w-r\n" + - " ^^^^^^\n" + - "Resource 'reader' should be managed by try-with-resource\n" + - "----------\n", - null, - true, - options); -} -// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points -// additional boolean should shed doubt on whether we reach the close() call -public void test056throw4() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo2x(boolean b) throws Exception {\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " try {\n" + - " reader.read();\n" + - " throw new Exception(); // should warn here\n" + - " } catch (Exception e) {\n" + - " throw new Exception(); // should warn here\n" + - " } finally {\n" + - " if (reader != null && b)\n" + // this condition is too strong to protect reader - " try {\n" + - " reader.close();\n" + - " } catch (java.io.IOException io) {}\n" + - " }\n" + - " }\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " throw new Exception(); // should warn here\n" + - " ^^^^^^^^^^^^^^^^^^^^^^\n" + - "Potential resource leak: 'reader' may not be closed at this location\n" + - "----------\n" + - "2. ERROR in X.java (at line 9)\n" + - " throw new Exception(); // should warn here\n" + - " ^^^^^^^^^^^^^^^^^^^^^^\n" + - "Potential resource leak: 'reader' may not be closed at this location\n" + - "----------\n", - null, - true, - options); -} -// Bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points -// similar to test056throw3() but indirectly calling close(), so doubts remain. -public void test056throw5() { - Map options = getCompilerOptions(); - options.put(JavaCore.COMPILER_PB_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE, CompilerOptions.ERROR); - options.put(JavaCore.COMPILER_PB_DEAD_CODE, CompilerOptions.ERROR); - this.runNegativeTest( - new String[] { - "X.java", - "import java.io.FileReader;\n" + - "public class X {\n" + - " void foo2x() throws Exception {\n" + - " FileReader reader = new FileReader(\"file\");\n" + - " try {\n" + - " reader.read();\n" + - " throw new Exception(); // should warn 'may not' here\n" + - " } catch (Exception e) {\n" + - " throw new Exception(); // should warn 'may not' here\n" + - " } finally {\n" + - " doClose(reader);\n" + - " }\n" + - " }\n" + - " void doClose(FileReader r) { try { r.close(); } catch (java.io.IOException ex) {}}\n" + - "}\n" - }, - "----------\n" + - "1. ERROR in X.java (at line 7)\n" + - " throw new Exception(); // should warn \'may not\' here\n" + - " ^^^^^^^^^^^^^^^^^^^^^^\n" + - "Potential resource leak: 'reader' may not be closed at this location\n" + - "----------\n" + - "2. ERROR in X.java (at line 9)\n" + - " throw new Exception(); // should warn \'may not\' here\n" + - " ^^^^^^^^^^^^^^^^^^^^^^\n" + - "Potential resource leak: 'reader' may not be closed at this location\n" + - "----------\n", - null, - true, - options); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=361053 -public void test057() { - this.runConformTest( - new String[] { - "X.java", - "public class X implements AutoCloseable {\n" + - " @Override\n" + - " public void close() throws Exception {\n" + - " throw new Exception();\n" + - " }\n" + - " public static void main(String[] args) {\n" + - " final boolean foo;\n" + - " try (X a = new X(); X b = new X()) {\n" + - " foo = true;\n" + - " } catch (final Exception exception) {\n" + - " return;\n" + - " }\n" + - " }\n" + - "}\n" - }, ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=364008 -public void test058() { - this.runConformTest( - new String[] { - "X.java", - "import java.io.ByteArrayOutputStream;\n" + - "import java.io.FileOutputStream;\n" + - "import java.io.IOException;\n" + - "\n" + - "public class X {\n" + - "\n" + - " public static void main(final String[] args) throws IOException {\n" + - " byte[] data;\n" + - " try (final ByteArrayOutputStream os = new ByteArrayOutputStream();\n" + - " final FileOutputStream out = new FileOutputStream(\"test.dat\")) {\n" + - " data = os.toByteArray();\n" + - " }\n" + - " }\n" + - "}\n" - }, ""); -} -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=367566 - In try-with-resources statement close() method of resource is not called -public void test059() { - this.runConformTest( - new String[] { - "X.java", - "import java.io.IOException;\n" + - "\n" + - "public class X implements java.lang.AutoCloseable {\n" + - " static boolean isOpen = true;\n" + - " public static void main(final String[] args) throws IOException {\n" + - " foo();\n" + - " System.out.println(isOpen);\n" + - " }\n" + - " static boolean foo() {\n" + - " try (final X x = new X()) {\n" + - " return x.num() >= 1;\n" + - " }\n" + - " }\n" + - " int num() { return 2; }\n" + - " public void close() {\n" + - " isOpen = false;\n" + - " }\n" + - "}\n" - }, - "false"); -} - -// https://bugs.eclipse.org/bugs/show_bug.cgi?id=367566 - In try-with-resources statement close() method of resource is not called -public void test060() { - this.runConformTest( - new String[] { - "X.java", - "public class X implements AutoCloseable {\n" + - " static int num = 10 ;\n" + - " public static void main(String [] args) throws Exception { \n" + - " System.out.println(foo(1));\n" + - " System.out.println(foo(2));\n" + - " System.out.println(foo(3));\n" + - " }\n" + - " private static boolean foo(int where) throws Exception {\n" + - " final boolean getOut = true;\n" + - " System.out.println(\"Main\");\n" + - " try (X x1 = new X(); X x2 = new X()) {\n" + - " if (where == 1) {\n" + - " return where == 1;\n" + - " }\n" + - " System.out.println(\"Outer Try\");\n" + - " while (true) {\n" + - " try (Y y1 = new Y(); Y y2 = new Y()) { \n" + - " if (where == 2) {\n" + - " return where == 2;\n" + - " } \n" + - " System.out.println(\"Middle Try\");\n" + - " try (Z z1 = new Z(); Z z2 = new Z()) {\n" + - " System.out.println(\"Inner Try\");\n" + - " if (getOut) \n" + - " return num >= 10;\n" + - " else\n" + - " break; \n" + - " }\n" + - " }\n" + - " }\n" + - " System.out.println(\"Out of while\");\n" + - " }\n" + - " return false;\n" + - " }\n" + - " public X() {\n" + - " System.out.println(\"X::X\");\n" + - " }\n" + - " @Override\n" + - " public void close() throws Exception {\n" + - " System.out.println(\"X::~X\");\n" + - " }\n" + - "}\n" + - "class Y implements AutoCloseable {\n" + - " public Y() {\n" + - " System.out.println(\"Y::Y\");\n" + - " }\n" + - " @Override\n" + - " public void close() throws Exception {\n" + - " System.out.println(\"Y::~Y\");\n" + - " }\n" + - "}\n" + - "class Z implements AutoCloseable {\n" + - " public Z() {\n" + - " System.out.println(\"Z::Z\");\n" + - " }\n" + - " @Override\n" + - " public void close() throws Exception {\n" + - " System.out.println(\"Z::~Z\");\n" + - " }\n" + - "}\n" - }, - "Main\n" + - "X::X\n" + - "X::X\n" + - "X::~X\n" + - "X::~X\n" + - "true\n" + - "Main\n" + - "X::X\n" + - "X::X\n" + - "Outer Try\n" + - "Y::Y\n" + - "Y::Y\n" + - "Y::~Y\n" + - "Y::~Y\n" + - "X::~X\n" + - "X::~X\n" + - "true\n" + - "Main\n" + - "X::X\n" + - "X::X\n" + - "Outer Try\n" + - "Y::Y\n" + - "Y::Y\n" + - "Middle Try\n" + - "Z::Z\n" + - "Z::Z\n" + - "Inner Try\n" + - "Z::~Z\n" + - "Z::~Z\n" + - "Y::~Y\n" + - "Y::~Y\n" + - "X::~X\n" + - "X::~X\n" + - "true"); -} - 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..fd64fd9 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 @@ -12,6 +12,7 @@ * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE * bug 349326 - [1.7] new warning for missing try-with-resources * bug 186342 - [compiler][null] Using annotations for null checking + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -37,25 +38,35 @@ public class AllocationExpression extends Expression implements InvocationSite { protected TypeBinding typeExpected; // for <> inference public boolean inferredReturnType; + public FakedTrackingVariable closeTracker; // when allocation a Closeable store a pre-liminary tracking variable here + public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) { // check captured variables are initialized in current context (26134) checkCapturedLocalInitializationIfNecessary((ReferenceBinding)this.binding.declaringClass.erasure(), currentScope, flowInfo); // process arguments if (this.arguments != null) { + boolean hasResourceWrapperType = this.resolvedType instanceof ReferenceBinding + && ((ReferenceBinding)this.resolvedType).hasTypeBit(TypeIds.BitWrapperCloseable); 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 = this.arguments[i] .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); + // if argument is an AutoCloseable insert info that it *may* be closed (by the target method, i.e.) + if (!hasResourceWrapperType) { // allocation of wrapped closeables is analyzed specially + flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo, false); + } if ((this.arguments[i].implicitConversion & TypeIds.UNBOXING) != 0) { this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); } } 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) { 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..ea1af7b 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 @@ -14,6 +14,7 @@ * bug 335093 - [compiler][null] minimal hook for future null annotation support * bug 349326 - [1.7] new warning for missing try-with-resources * bug 186342 - [compiler][null] Using annotations for null checking + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -46,19 +47,27 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl 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.isAnyCloseable(this.expression.resolvedType) + || this.expression.resolvedType == TypeBinding.NULL); + if (shouldAnalyseResource) { + preInitInfo = flowInfo.unconditionalCopy(); + // analysis of resource leaks needs additional context while analyzing the RHS: + FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, local, this.expression); + } + 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(currentScope, preInitInfo, flowInfo, this, this.expression, local); + else + FakedTrackingVariable.cleanUpAfterAssignment(currentScope, this.lhs.bits, this.expression); + 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..5ece3b7 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 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011 GK Software AG and others. + * Copyright (c) 2011, 2012 GK Software AG 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 @@ -14,8 +14,8 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Set; -import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.codegen.CodeStream; import org.eclipse.jdt.internal.compiler.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; @@ -42,26 +42,42 @@ public class FakedTrackingVariable extends LocalDeclaration { // a call to close() was seen at least on one path: private static final int CLOSE_SEEN = 1; - // the resource was passed to outside code (arg in method/ctor call or as a return value from this method): - private static final int PASSED_TO_OUTSIDE = 2; + // the resource is shared with outside code either by + // - passing as an arg in a method call or + // - obtaining this from a method call or array reference + // Interpret that we may or may not be responsible for closing + private static final int SHARED_WITH_OUTSIDE = 2; + // the resource is likely owned by outside code (owner has responsibility to close): + // - obtained as argument of the current method, or via a field read + // - returned as the result of this method + private static final int OWNED_BY_OUTSIDE = 4; // If close() is invoked from a nested method (inside a local type) report remaining problems only as potential: - 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; - + private static final int CLOSED_IN_NESTED_METHOD = 8; + // explicit closing has been reported already against this resource: + private static final int REPORTED_EXPLICIT_CLOSE = 16; + // a location independent potential problem has been reported against this resource: + private static final int REPORTED_POTENTIAL_LEAK = 32; + // a location independent definitive problem has been reported against this resource: + private static final int REPORTED_DEFINITIVE_LEAK = 64; + /** - * Bitset of {@link #CLOSE_SEEN}, {@link #PASSED_TO_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD} and {@link #REPORTED}. + * Bitset of {@link #CLOSE_SEEN}, {@link #SHARED_WITH_OUTSIDE}, {@link #OWNED_BY_OUTSIDE}, {@link #CLOSED_IN_NESTED_METHOD}, {@link #REPORTED_EXPLICIT_CLOSE}, {@link #REPORTED_POTENTIAL_LEAK} and {@link #REPORTED_DEFINITIVE_LEAK}. */ private int globalClosingState = 0; - MethodScope methodScope; // designates the method declaring this variable - - public LocalVariableBinding originalBinding; // the real local being tracked + public LocalVariableBinding originalBinding; // the real local being tracked, can be null for preliminary track vars for allocation expressions - HashMap recordedLocations; // initially null, ASTNode -> Integer + public FakedTrackingVariable innerTracker; // chained tracking variable of a chained (wrapped) resource + public FakedTrackingVariable outerTracker; // inverse of 'innerTracker' + + MethodScope methodScope; // designates the method declaring this variable + + private HashMap recordedLocations; // initially null, ASTNode -> Integer + // temporary storage while analyzing "res = new Res();": + private ASTNode currentAssignment; // temporarily store the assignment as the location for error reporting - public FakedTrackingVariable(LocalVariableBinding original, Statement location) { + public FakedTrackingVariable(LocalVariableBinding original, ASTNode location) { super(original.name, location.sourceStart, location.sourceEnd); this.type = new SingleTypeReference( TypeConstants.OBJECT, @@ -70,6 +86,17 @@ public class FakedTrackingVariable extends LocalDeclaration { this.originalBinding = original; resolve(original.declaringScope); } + + /* Create an unassigned tracking variable while analyzing an allocation expression: */ + private FakedTrackingVariable(BlockScope scope, ASTNode location) { + super("".toCharArray(), location.sourceStart, location.sourceEnd); //$NON-NLS-1$ + this.type = new SingleTypeReference( + TypeConstants.OBJECT, + ((long)this.sourceStart <<32)+this.sourceEnd); + this.methodScope = scope.methodScope(); + this.originalBinding = null; + resolve(scope); + } public void generateCode(BlockScope currentScope, CodeStream codeStream) { /* NOP - this variable is completely dummy, ie. for analysis only. */ } @@ -81,6 +108,7 @@ public class FakedTrackingVariable extends LocalDeclaration { scope.getJavaLangObject(), // dummy, just needs to be a reference type 0, false); + this.binding.declaringScope = scope; this.binding.setConstant(Constant.NotAConstant); this.binding.useFlag = LocalVariableBinding.USED; // use a free slot without assigning it: @@ -88,86 +116,435 @@ public class FakedTrackingVariable extends LocalDeclaration { } /** - * If expression resolves to a local variable binding of type AutoCloseable, - * answer the variable that tracks closing of that local, creating it if needed. + * If expression resolves to a value of type AutoCloseable answer the variable that tracks closing of that local. + * Covers two cases: + *
    + *
  • value is a local variable reference, create tracking variable it if needed. + *
  • value is an allocation expression, return a preliminary tracking variable if set. + *
* @param expression * @return a new {@link FakedTrackingVariable} or null. */ public static FakedTrackingVariable getCloseTrackingVariable(Expression expression) { + while (true) { + if (expression instanceof CastExpression) + expression = ((CastExpression) expression).expression; + else if (expression instanceof Assignment) + expression = ((Assignment) expression).expression; + else + break; + } if (expression instanceof SingleNameReference) { SingleNameReference name = (SingleNameReference) expression; if (name.binding instanceof LocalVariableBinding) { LocalVariableBinding local = (LocalVariableBinding)name.binding; if (local.closeTracker != null) return local.closeTracker; - if (local.isParameter() || !isAutoCloseable(expression.resolvedType)) + if (!isAnyCloseable(expression.resolvedType)) return null; // tracking var doesn't yet exist. This happens in finally block // which is analyzed before the corresponding try block Statement location = local.declaration; - return local.closeTracker = new FakedTrackingVariable(local, location); + local.closeTracker = new FakedTrackingVariable(local, location); + if (local.isParameter()) { + local.closeTracker.globalClosingState |= OWNED_BY_OUTSIDE; + // status of this tracker is now UNKNOWN + } + return local.closeTracker; } - } + } else if (expression instanceof AllocationExpression) { + // return any preliminary tracking variable from analyseCloseableAllocation + return ((AllocationExpression) expression).closeTracker; + } return null; } - /** if 'invocationSite' is a call to close() that has a registered tracking variable, answer that variable's binding. */ - public static LocalVariableBinding getTrackerForCloseCall(ASTNode invocationSite) { - if (invocationSite instanceof MessageSend) { - MessageSend send = (MessageSend) invocationSite; - if (CharOperation.equals(TypeConstants.CLOSE, send.selector) && send.receiver instanceof SingleNameReference) { - Binding receiverBinding = ((SingleNameReference)send.receiver).binding; - if (receiverBinding instanceof LocalVariableBinding) { - FakedTrackingVariable trackingVariable = ((LocalVariableBinding)receiverBinding).closeTracker; - if (trackingVariable != null) - return trackingVariable.binding; + /** + * Before analyzing an assignment of this shape: singleName = new Allocation() + * connect any tracking variable of the LHS with the allocation on the RHS. + * Also the assignment is temporarily stored in the tracking variable in case we need to + * report errors because the assignment leaves the old LHS value unclosed. + * In this case the assignment should be used as the error location. + * + * @param location the assignment/local declaration being analyzed + * @param local the local variable being assigned to + * @param rhs the rhs of the assignment resp. the initialization of the local variable declaration. + * Precondition: client has already checked that the resolved type of this expression is either a closeable type or NULL. + */ + public static void preConnectTrackerAcrossAssignment(ASTNode location, LocalVariableBinding local, Expression rhs) { + FakedTrackingVariable closeTracker = null; + if (rhs instanceof AllocationExpression) { + closeTracker = local.closeTracker; + if (closeTracker == null) { + if (rhs.resolvedType != TypeBinding.NULL) { // not NULL means valid closeable as per method precondition + closeTracker = new FakedTrackingVariable(local, location); + if (local.isParameter()) { + closeTracker.globalClosingState |= OWNED_BY_OUTSIDE; + } + } + } + if (closeTracker != null) { + closeTracker.currentAssignment = location; + ((AllocationExpression)rhs).closeTracker = closeTracker; + } + } + } + + /** + * Compute/assign a tracking variable for a freshly allocated closeable value, using information from our white lists. + * See Bug 358903 - Filter practically unimportant resource leak warnings + */ + public static void analyseCloseableAllocation(BlockScope scope, FlowInfo flowInfo, AllocationExpression allocation) { + // client has checked that the resolvedType is an AutoCloseable, hence the following cast is safe: + if (((ReferenceBinding)allocation.resolvedType).hasTypeBit(TypeIds.BitResourceFreeCloseable)) { + // remove unnecessary attempts (closeable is not relevant) + if (allocation.closeTracker != null) { + scope.removeTrackingVar(allocation.closeTracker); + allocation.closeTracker = null; + } + } else if (((ReferenceBinding)allocation.resolvedType).hasTypeBit(TypeIds.BitWrapperCloseable)) { + boolean isWrapper = true; + if (allocation.arguments != null && allocation.arguments.length > 0) { + // find the wrapped resource represented by its tracking var: + FakedTrackingVariable innerTracker = findCloseTracker(scope, flowInfo, allocation, allocation.arguments[0]); + if (innerTracker != null) { + if (innerTracker == allocation.closeTracker) + return; // self wrap (res = new Res(res)) -> neither change (here) nor remove (below) + int newStatus = FlowInfo.NULL; + if (allocation.closeTracker == null) { + allocation.closeTracker = new FakedTrackingVariable(scope, allocation); // no local available, closeable is unassigned + } else { + if (scope.finallyInfo != null) { + // inject results from analysing a finally block onto the newly connected wrapper + int finallyStatus = scope.finallyInfo.nullStatus(allocation.closeTracker.binding); + if (finallyStatus != FlowInfo.UNKNOWN) + newStatus = finallyStatus; + } + } + allocation.closeTracker.innerTracker = innerTracker; + innerTracker.outerTracker = allocation.closeTracker; + flowInfo.markNullStatus(allocation.closeTracker.binding, newStatus); + if (newStatus != FlowInfo.NULL) { + // propagate results from a finally block also into nested resources: + FakedTrackingVariable currentTracker = innerTracker; + while (currentTracker != null) { + flowInfo.markNullStatus(currentTracker.binding, newStatus); + currentTracker.globalClosingState |= allocation.closeTracker.globalClosingState; + currentTracker = currentTracker.innerTracker; + } + } + return; // keep chaining wrapper (by avoiding to fall through to removeTrackingVar below) + } else { + if (!isAnyCloseable(allocation.arguments[0].resolvedType)) { + isWrapper = false; // argument is not closeable + } + } + } else { + isWrapper = false; // no argument + } + // successful wrapper detection has exited above, let's see why that failed + if (isWrapper) { + // remove unnecessary attempts (wrapper has no relevant inner) + if (allocation.closeTracker != null) { + scope.removeTrackingVar(allocation.closeTracker); + allocation.closeTracker = null; } + } else { + // allocation does not provide a resource as the first argument -> don't treat as a wrapper + handleRegularResource(scope, flowInfo, allocation); } + } else { // regular resource + handleRegularResource(scope, flowInfo, allocation); } - return null; + } + + private static void handleRegularResource(BlockScope scope, FlowInfo flowInfo, AllocationExpression allocation) { + FakedTrackingVariable presetTracker = allocation.closeTracker; + if (presetTracker != null && presetTracker.originalBinding != null) { + // the current assignment forgets a previous resource in the LHS, may cause a leak + // report now because handleResourceAssignment can't distinguish this from a self-wrap situation + int closeStatus = flowInfo.nullStatus(presetTracker.binding); + if (closeStatus != FlowInfo.NON_NULL // old resource was not closed + && closeStatus != FlowInfo.UNKNOWN // old resource had some flow information + && !flowInfo.isDefinitelyNull(presetTracker.originalBinding) // old resource was not null + && !(presetTracker.currentAssignment instanceof LocalDeclaration)) // forgetting old val in local decl is syntactically impossible + allocation.closeTracker.recordErrorLocation(presetTracker.currentAssignment, closeStatus); + } else { + allocation.closeTracker = new FakedTrackingVariable(scope, allocation); // no local available, closeable is unassigned + } + flowInfo.markAsDefinitelyNull(allocation.closeTracker.binding); + } + + /** Find an existing tracking variable for the argument of an allocation for a resource wrapper. */ + private static FakedTrackingVariable findCloseTracker(BlockScope scope, FlowInfo flowInfo, AllocationExpression allocation, Expression arg) + { + while (arg instanceof Assignment) { + Assignment assign = (Assignment)arg; + LocalVariableBinding innerLocal = assign.localVariableBinding(); + if (innerLocal != null) { + // nested assignment has already been processed + return innerLocal.closeTracker; + } else { + arg = assign.expression; // unwrap assignment and fall through + } + } + if (arg instanceof SingleNameReference) { + // is allocation arg a reference to an existing closeable? + LocalVariableBinding local = arg.localVariableBinding(); + if (local != null) { + return local.closeTracker; + } + } else if (arg instanceof AllocationExpression) { + // nested allocation + return ((AllocationExpression)arg).closeTracker; + } + return null; // not a tracked expression } /** * 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 scope scope containing the assignment + * @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(BlockScope scope, FlowInfo upstreamInfo, FlowInfo flowInfo, ASTNode location, Expression rhs, LocalVariableBinding local) { - if (isAutoCloseable(rhs.resolvedType)) { + // does the LHS (local) already have a tracker, indicating we may leak a resource by the assignment? + 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 nullStatus = upstreamInfo.nullStatus(local); + if (nullStatus != FlowInfo.NULL && nullStatus != FlowInfo.UNKNOWN) // only if previous value may be relevant + disconnectedTracker = previousTracker; // report error below, unless we have a self-wrap assignment + } + + if (rhs.resolvedType != TypeBinding.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? - // re-assigning from a fresh, mark as not-closed again: - flowInfo.markAsDefinitelyNull(previousTrackerBinding); + if (rhsTrackVar != null) { // 1. if RHS has a tracking variable... + if (local.closeTracker == null) { + // null shouldn't occur but let's play safe + if (rhsTrackVar.originalBinding != null) + local.closeTracker = rhsTrackVar; // a.: let fresh LHS share it + } else { + if (rhsTrackVar == disconnectedTracker && rhs instanceof AllocationExpression) + return; // b.: self wrapper: res = new Wrap(res); -> done! + local.closeTracker = rhsTrackVar; // c.: conflicting LHS and RHS, proceed with recordErrorLocation below + } + // keep close-status of RHS unchanged across this assignment + } else if (previousTracker != null) { // 2. re-use tracking variable from the LHS? + // re-assigning from a fresh value, mark as not-closed again: + if ((previousTracker.globalClosingState & (SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE)) == 0) + flowInfo.markAsDefinitelyNull(previousTracker.binding); + local.closeTracker = analyseCloseableExpression(flowInfo, local, location, rhs, previousTracker); } 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: + if ((rhsTrackVar.globalClosingState & (SHARED_WITH_OUTSIDE|OWNED_BY_OUTSIDE)) == 0) + flowInfo.markAsDefinitelyNull(rhsTrackVar.binding); // TODO(stephan): this might be useful, but I could not find a test case for it: -// if (flowContext.initsOnFinally != null) -// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); +// if (flowContext.initsOnFinally != null) +// flowContext.initsOnFinally.markAsDefinitelyNonNull(trackerBinding); + } + } + } + + if (disconnectedTracker != null) { + if (disconnectedTracker.innerTracker != null && disconnectedTracker.innerTracker.binding.declaringScope == scope) { + // discard tracker for the wrapper but keep the inner: + disconnectedTracker.innerTracker.outerTracker = null; + scope.pruneWrapperTrackingVar(disconnectedTracker); + } else { + int upstreamStatus = upstreamInfo.nullStatus(disconnectedTracker.binding); + if (upstreamStatus != FlowInfo.NON_NULL) + disconnectedTracker.recordErrorLocation(location, upstreamStatus); + } + } + } + /** + * Analyze structure of a closeable expression, matching (chained) resources against our white lists. + * @param flowInfo where to record close status + * @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) + { + // unwrap uninteresting nodes: + while (true) { + if (expression instanceof Assignment) + expression = ((Assignment)expression).expression; + else if (expression instanceof CastExpression) + expression = ((CastExpression) expression).expression; + else + break; + } + + // analyze by node type: + if (expression instanceof AllocationExpression) { + // allocation expressions already have their tracking variables analyzed by analyseCloseableAllocation(..) + FakedTrackingVariable tracker = ((AllocationExpression) expression).closeTracker; + if (tracker != null && tracker.originalBinding == null) { + // tracker without original binding (unassigned closeable) shouldn't reach here but let's play safe + return null; + } + return tracker; + } else if (expression instanceof MessageSend + || expression instanceof ArrayReference) + { + // we *might* be responsible for the resource obtained + 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) + { + // responsibility for this resource probably lies at a higher level + FakedTrackingVariable tracker = new FakedTrackingVariable(local, location); + tracker.globalClosingState |= OWNED_BY_OUTSIDE; + // leave state as UNKNOWN, the bit OWNED_BY_OUTSIDE will prevent spurious warnings + return tracker; + } + + if (expression.resolvedType instanceof ReferenceBinding) { + ReferenceBinding resourceType = (ReferenceBinding) expression.resolvedType; + if (resourceType.hasTypeBit(TypeIds.BitResourceFreeCloseable)) { + // (a) resource-free closeable: -> null + return null; } } + if (local.closeTracker != null) + // (c): inner has already been analyzed: -> re-use track var + return local.closeTracker; + return new FakedTrackingVariable(local, location); + } + + public static void cleanUpAfterAssignment(BlockScope currentScope, int lhsBits, Expression expression) { + // remove all remaining track vars with no original binding + + // unwrap uninteresting nodes: + while (true) { + if (expression instanceof Assignment) + expression = ((Assignment)expression).expression; + else if (expression instanceof CastExpression) + expression = ((CastExpression) expression).expression; + else + break; + } + if (expression instanceof AllocationExpression) { + FakedTrackingVariable tracker = ((AllocationExpression) expression).closeTracker; + if (tracker != null && tracker.originalBinding == null) { + currentScope.removeTrackingVar(tracker); + ((AllocationExpression) expression).closeTracker = null; + } + } else { + // assignment passing a local into a field? + LocalVariableBinding local = expression.localVariableBinding(); + if (local != null && local.closeTracker != null && ((lhsBits & Binding.FIELD) != 0)) + currentScope.removeTrackingVar(local.closeTracker); // TODO: may want to use local.closeTracker.markPassedToOutside(..,true) + } } /** Answer wither the given type binding is a subtype of java.lang.AutoCloseable. */ - public static boolean isAutoCloseable(TypeBinding typeBinding) { + public static boolean isAnyCloseable(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. */ public void markClose(FlowInfo flowInfo, FlowContext flowContext) { - flowInfo.markAsDefinitelyNonNull(this.binding); - this.globalClosingState |= CLOSE_SEEN; + FakedTrackingVariable current = this; + do { + flowInfo.markAsDefinitelyNonNull(current.binding); + current.globalClosingState |= CLOSE_SEEN; //TODO(stephan): this might be useful, but I could not find a test case for it: -// if (flowContext.initsOnFinally != null) -// flowContext.initsOnFinally.markAsDefinitelyNonNull(this.binding); +// if (flowContext.initsOnFinally != null) +// flowContext.initsOnFinally.markAsDefinitelyNonNull(this.binding); + current = current.innerTracker; + } while (current != null); } /** Mark that this resource is closed from a nested method (inside a local class). */ @@ -179,59 +556,165 @@ public class FakedTrackingVariable extends LocalDeclaration { * Mark that this resource is passed to some outside code * (as argument to a method/ctor call or as a return value from the current method), * and thus should be considered as potentially closed. + * @param owned should the resource be considered owned by some outside? */ - public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo) { + public static FlowInfo markPassedToOutside(BlockScope scope, Expression expression, FlowInfo flowInfo, boolean owned) { + FakedTrackingVariable trackVar = getCloseTrackingVariable(expression); if (trackVar != null) { - trackVar.globalClosingState |= PASSED_TO_OUTSIDE; - if (scope.methodScope() != trackVar.methodScope) - trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD; + if (trackVar.originalBinding == null) { + // an allocation that never was assigned to a local variable -> drop it completely as we're not responsible + scope.removeTrackingVar(trackVar); + return flowInfo; + } // insert info that the tracked resource *may* be closed (by the target method, i.e.) FlowInfo infoResourceIsClosed = flowInfo.copy(); - infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding); + int flag = owned ? OWNED_BY_OUTSIDE : SHARED_WITH_OUTSIDE; + do { + trackVar.globalClosingState |= flag; + if (scope.methodScope() != trackVar.methodScope) + trackVar.globalClosingState |= CLOSED_IN_NESTED_METHOD; + infoResourceIsClosed.markAsDefinitelyNonNull(trackVar.binding); + } while ((trackVar = trackVar.innerTracker) != null); return FlowInfo.conditional(flowInfo, infoResourceIsClosed); } return flowInfo; } - + + /** + * Pick tracking variables from 'varsOfScope' to establish a proper order of processing: + * As much as possible pick wrapper resources before their inner resources. + * Also consider cases of wrappers and their inners being declared at different scopes. + */ + public static FakedTrackingVariable pickVarForReporting(Set varsOfScope, BlockScope scope, boolean atExit) { + if (varsOfScope.isEmpty()) return null; + FakedTrackingVariable trackingVar = (FakedTrackingVariable) varsOfScope.iterator().next(); + while (trackingVar.outerTracker != null) { + // resource is wrapped, is wrapper defined in this scope? + if (varsOfScope.contains(trackingVar.outerTracker)) { + // resource from same scope, travel up the wrapper chain + trackingVar = trackingVar.outerTracker; + } else if (atExit) { + // at an exit point we report against inner despite a wrapper that may/may not be closed later + break; + } else { + BlockScope outerTrackerScope = trackingVar.outerTracker.binding.declaringScope; + if (outerTrackerScope == scope) { + // outerTracker is from same scope and already processed -> pick trackingVar now + break; + } else { + // outer resource is from other (outer?) scope + Scope currentScope = scope; + while ((currentScope = currentScope.parent) instanceof BlockScope) { + if (outerTrackerScope == currentScope) { + // at end of block pass responsibility for inner resource to outer scope holding a wrapper + varsOfScope.remove(trackingVar); // drop this one + // pick a next candidate: + return pickVarForReporting(varsOfScope, scope, atExit); + } + } + break; // not parent owned -> pick this var + } + } + } + varsOfScope.remove(trackingVar); + return trackingVar; + } + + /** + * If current is the same as 'returnedResource' or a wrapper thereof, + * mark as reported and return true, otherwise false. + */ + public boolean isResourceBeingReturned(FakedTrackingVariable returnedResource) { + FakedTrackingVariable current = this; + do { + if (current == returnedResource) { + this.globalClosingState |= REPORTED_DEFINITIVE_LEAK; + return true; + } + current = current.innerTracker; + } while (current != null); + return false; + } + public void recordErrorLocation(ASTNode location, int nullStatus) { if (this.recordedLocations == null) this.recordedLocations = new HashMap(); this.recordedLocations.put(location, new Integer(nullStatus)); } - public boolean reportRecordedErrors(Scope scope) { - if (this.globalClosingState == 0) { - reportError(scope.problemReporter(), null, FlowInfo.NULL); - return true; + public boolean reportRecordedErrors(Scope scope, int mergedStatus) { + FakedTrackingVariable current = this; + while (current.globalClosingState == 0) { + current = current.innerTracker; + if (current == null) { + // no relevant state found -> report: + reportError(scope.problemReporter(), null, mergedStatus); + return true; + } } boolean hasReported = false; if (this.recordedLocations != null) { Iterator locations = this.recordedLocations.entrySet().iterator(); + int reportFlags = 0; while (locations.hasNext()) { Map.Entry entry = (Entry) locations.next(); - reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue()); + reportFlags |= reportError(scope.problemReporter(), (ASTNode)entry.getKey(), ((Integer)entry.getValue()).intValue()); hasReported = true; } + if (reportFlags != 0) { + // after all locations have been reported, mark as reported to prevent duplicate report via an outer wrapper + current = this; + do { + current.globalClosingState |= reportFlags; + } while ((current = current.innerTracker) != null); + } } return hasReported; } - public void reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) { + public int reportError(ProblemReporter problemReporter, ASTNode location, int nullStatus) { + // which degree of problem? + boolean isPotentialProblem = false; if (nullStatus == FlowInfo.NULL) { if ((this.globalClosingState & CLOSED_IN_NESTED_METHOD) != 0) - problemReporter.potentiallyUnclosedCloseable(this, location); - else - problemReporter.unclosedCloseable(this, location); + isPotentialProblem = true; } else if (nullStatus == FlowInfo.POTENTIALLY_NULL) { - problemReporter.potentiallyUnclosedCloseable(this, location); - } + isPotentialProblem = true; + } + // report: + if (isPotentialProblem) { + if ((this.globalClosingState & (REPORTED_POTENTIAL_LEAK|REPORTED_DEFINITIVE_LEAK)) != 0) + return 0; + problemReporter.potentiallyUnclosedCloseable(this, location); + } else { + if ((this.globalClosingState & (REPORTED_DEFINITIVE_LEAK)) != 0) + return 0; + problemReporter.unclosedCloseable(this, location); + } + // propagate flag to inners: + int reportFlag = isPotentialProblem ? REPORTED_POTENTIAL_LEAK : REPORTED_DEFINITIVE_LEAK; + if (location == null) { // if location != null flags will be set after the loop over locations + FakedTrackingVariable current = this; + do { + current.globalClosingState |= reportFlag; + } while ((current = current.innerTracker) != null); + } + return reportFlag; } public void reportExplicitClosing(ProblemReporter problemReporter) { - if ((this.globalClosingState & REPORTED) == 0) { - this.globalClosingState |= REPORTED; + if ((this.globalClosingState & (OWNED_BY_OUTSIDE|REPORTED_EXPLICIT_CLOSE)) == 0) { // can't use t-w-r for OWNED_BY_OUTSIDE + this.globalClosingState |= REPORTED_EXPLICIT_CLOSE; problemReporter.explicitlyClosedAutoCloseable(this); } } + + public void resetReportingBits() { + FakedTrackingVariable current = this; + do { + current.globalClosingState &= ~(REPORTED_POTENTIAL_LEAK|REPORTED_DEFINITIVE_LEAK); + current = current.innerTracker; + } while (current != null); + } } 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..91808db 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 @@ -13,6 +13,7 @@ * bug 335093 - [compiler][null] minimal hook for future null annotation support * bug 349326 - [1.7] new warning for missing try-with-resources * bug 186342 - [compiler][null] Using annotations for null checking + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -74,11 +75,26 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl this.initialization.checkNPE(currentScope, flowContext, flowInfo); } + FlowInfo preInitInfo = null; + boolean shouldAnalyseResource = this.binding != null + && flowInfo.reachMode() == FlowInfo.REACHABLE + && FakedTrackingVariable.isAnyCloseable(this.initialization.resolvedType); + if (shouldAnalyseResource) { + preInitInfo = flowInfo.unconditionalCopy(); + // analysis of resource leaks needs additional context while analyzing the RHS: + FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, this.binding, this.initialization); + } + flowInfo = this.initialization .analyseCode(currentScope, flowContext, flowInfo) .unconditionalInits(); - FakedTrackingVariable.handleResourceAssignment(flowInfo, this, this.initialization, this.binding, null); + + if (shouldAnalyseResource) + FakedTrackingVariable.handleResourceAssignment(currentScope, preInitInfo, flowInfo, this, this.initialization, this.binding); + else + FakedTrackingVariable.cleanUpAfterAssignment(currentScope, Binding.LOCAL, this.initialization); + 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..0dda53f 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 @@ -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 @@ -12,6 +12,7 @@ * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE * bug 349326 - [1.7] new warning for missing try-with-resources * bug 186342 - [compiler][null] Using annotations for null checking + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -96,9 +97,9 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl if ((this.arguments[i].implicitConversion & TypeIds.UNBOXING) != 0) { 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 = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); + // 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, false); } 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..6739bbc 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 @@ public class QualifiedAllocationExpression extends AllocationExpression { 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, false); 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/ast/ReturnStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java index 18a1bc1..610f55b 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 @@ -13,6 +13,7 @@ * bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop) * bug 186342 - [compiler][null] Using annotations for null checking * bug 365835 - [compiler][null] inconsistent error reporting. + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -52,9 +53,8 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl if (trackingVariable != null) { if (methodScope != trackingVariable.methodScope) trackingVariable.markClosedInNestedMethod(); - // don't report issues concerning this local, since by returning - // the method passes the responsibility to the caller: - currentScope.removeTrackingVar(trackingVariable); + // by returning the method passes the responsibility to the caller: + flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.expression, flowInfo, true); } } this.initStateIndex = diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java index 9fae009..0534970 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TryStatement.java @@ -12,6 +12,7 @@ * bug 358827 - [1.7] exception analysis for t-w-r spoils null analysis * bug 349326 - [1.7] new warning for missing try-with-resources * bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; @@ -134,7 +135,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl if (resourceBinding.closeTracker != null) { // this was false alarm, we don't need to track the resource this.tryBlock.scope.removeTrackingVar(resourceBinding.closeTracker); - resourceBinding.closeTracker = null; + // keep the tracking variable in the resourceBinding in order to prevent creating a new one while analyzing the try block } TypeBinding type = resourceBinding.type; if (type != null && type.isValidBinding()) { @@ -276,7 +277,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl if (resourceBinding.closeTracker != null) { // this was false alarm, we don't need to track the resource this.tryBlock.scope.removeTrackingVar(resourceBinding.closeTracker); - resourceBinding.closeTracker = null; + // keep the tracking variable in the resourceBinding in order to prevent creating a new one while analyzing the try block } TypeBinding type = resourceBinding.type; if (type != null && type.isValidBinding()) { 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..de17c07 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 @@ -11,6 +11,7 @@ * bug 349326 - [1.7] new warning for missing try-with-resources * bug 186342 - [compiler][null] Using annotations for null checking * bug 364890 - BinaryTypeBinding should use char constants from Util + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; @@ -1275,7 +1276,9 @@ public ReferenceBinding superclass() { 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 +1301,7 @@ public ReferenceBinding[] superInterfaces() { 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..33c3b6f 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 @@ -10,11 +10,14 @@ * Stephan Herrmann - Contributions for * bug 349326 - [1.7] new warning for missing try-with-resources * bug 359334 - Analysis for resource leak warnings does not consider exceptions as method exit points + * bug 358903 - Filter practically unimportant resource leak warnings *******************************************************************************/ package org.eclipse.jdt.internal.compiler.lookup; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.ast.*; @@ -976,17 +979,24 @@ public int registerTrackingVariable(FakedTrackingVariable fakedTrackingVariable) this.trackingVariables = new ArrayList(3); this.trackingVariables.add(fakedTrackingVariable); MethodScope outerMethodScope = outerMostMethodScope(); - return outerMethodScope.analysisIndex + (outerMethodScope.trackVarCount++); - + return outerMethodScope.analysisIndex++; } /** When are no longer interested in this tracking variable - remove it. */ public void removeTrackingVar(FakedTrackingVariable trackingVariable) { + if (trackingVariable.innerTracker != null) { + removeTrackingVar(trackingVariable.innerTracker); + trackingVariable.innerTracker = null; + } if (this.trackingVariables != null) if (this.trackingVariables.remove(trackingVariable)) return; if (this.parent instanceof BlockScope) ((BlockScope)this.parent).removeTrackingVar(trackingVariable); } +/** Unregister a wrapper resource without affecting its inner. */ +public void pruneWrapperTrackingVar(FakedTrackingVariable trackingVariable) { + this.trackingVariables.remove(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. @@ -999,14 +1009,25 @@ public void checkUnclosedCloseables(FlowInfo flowInfo, ASTNode location, BlockSc return; } if (location != null && flowInfo.reachMode() != 0) return; - for (int i=0; i call it POTENTIALLY_NULL - status = FlowInfo.POTENTIALLY_NULL; + } else { + int size = this.trackingVariables.size(); + for (int i=0; i