diff --git a/checker/build.gradle b/checker/build.gradle index f1de668420d..4cd19612b82 100644 --- a/checker/build.gradle +++ b/checker/build.gradle @@ -64,6 +64,8 @@ dependencies { testImplementation platform('com.amazonaws:aws-java-sdk-bom:1.12.293') // For the Resource Leak Checker's support for JavaEE. testImplementation 'javax.servlet:javax.servlet-api:3.1.0' + // For the Resource Leak Checker's support for IOUtils. + testImplementation 'commons-io:commons-io:2.11.0' testImplementation group: 'junit', name: 'junit', version: '4.13.2' testImplementation project(':framework-test') diff --git a/checker/src/main/java/org/checkerframework/checker/mustcall/IOUtils.astub b/checker/src/main/java/org/checkerframework/checker/mustcall/IOUtils.astub new file mode 100644 index 00000000000..8f35d8febbd --- /dev/null +++ b/checker/src/main/java/org/checkerframework/checker/mustcall/IOUtils.astub @@ -0,0 +1,14 @@ +package org.apache.commons.io; + +import org.checkerframework.checker.mustcall.qual.*; + +class IOUtils { + @NotOwning static InputStream toBufferedInputStream(InputStream arg0) throws IOException; + @NotOwning static BufferedReader toBufferedReader(Reader arg0); + @NotOwning static InputStream toInputStream(CharSequence arg0); + @NotOwning static InputStream toInputStream(CharSequence arg0, Charset arg1); + @NotOwning static InputStream toInputStream(CharSequence arg0, String arg1) throws IOException; + @NotOwning static InputStream toInputStream(String arg0); + @NotOwning static InputStream toInputStream(String arg0, Charset arg1); + @NotOwning static InputStream toInputStream(String arg0, String arg1) throws IOException; +} diff --git a/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallChecker.java b/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallChecker.java index fc89dfa5733..617fe571db6 100644 --- a/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallChecker.java +++ b/checker/src/main/java/org/checkerframework/checker/mustcall/MustCallChecker.java @@ -10,6 +10,7 @@ * another. The Resource Leak Checker verifies that the given methods are actually called. */ @StubFiles({ + "IOUtils.astub", "JavaEE.astub", "JdkCompiler.astub", "Reflection.astub", diff --git a/checker/src/main/java/org/checkerframework/checker/resourceleak/IOUtils.astub b/checker/src/main/java/org/checkerframework/checker/resourceleak/IOUtils.astub new file mode 100644 index 00000000000..9caed0051ee --- /dev/null +++ b/checker/src/main/java/org/checkerframework/checker/resourceleak/IOUtils.astub @@ -0,0 +1,25 @@ +package org.apache.commons.io; + +import org.checkerframework.checker.calledmethods.qual.*; + +class IOUtils { + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(Reader arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(Writer arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(InputStream arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(OutputStream arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(Closeable arg0); + @SuppressWarnings("ensuresvarargs.unverified") + @EnsuresCalledMethodsVarArgs("close") + static void closeQuietly(Closeable... arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(Socket arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(Selector arg0); + @EnsuresCalledMethods(value = "#1", methods = "close") + static void closeQuietly(ServerSocket arg0); +} diff --git a/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakChecker.java b/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakChecker.java index 5f5401a6b49..b5a30070da5 100644 --- a/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakChecker.java +++ b/checker/src/main/java/org/checkerframework/checker/resourceleak/ResourceLeakChecker.java @@ -8,6 +8,7 @@ import org.checkerframework.checker.mustcall.MustCallNoCreatesMustCallForChecker; import org.checkerframework.common.basetype.BaseTypeChecker; import org.checkerframework.common.basetype.BaseTypeVisitor; +import org.checkerframework.framework.qual.StubFiles; import org.checkerframework.framework.source.SupportedOptions; /** @@ -23,8 +24,12 @@ MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP, MustCallChecker.NO_RESOURCE_ALIASES }) +@StubFiles("IOUtils.astub") public class ResourceLeakChecker extends CalledMethodsChecker { + /** Creates a ResourceLeakChecker. */ + public ResourceLeakChecker() {} + /** * Command-line option for counting how many must-call obligations were checked by the Resource * Leak Checker, and emitting the number after processing all files. Used for generating tables diff --git a/checker/tests/resourceleak/IOUtilsTest.java b/checker/tests/resourceleak/IOUtilsTest.java new file mode 100644 index 00000000000..757320760c8 --- /dev/null +++ b/checker/tests/resourceleak/IOUtilsTest.java @@ -0,0 +1,16 @@ +import org.checkerframework.checker.mustcall.qual.*; +import java.io.*; + +class IOUtilsTest { + static void test1(@Owning InputStream inputStream) { + org.apache.commons.io.IOUtils.closeQuietly(inputStream); + } + + static void test2(@Owning InputStream inputStream) throws IOException { + try { + InputStream other = org.apache.commons.io.IOUtils.toBufferedInputStream(inputStream); + } finally { + inputStream.close(); + } + } +}