diff --git a/infer/models/java/src/java/util/HashMap.java b/infer/models/java/src/java/util/HashMap.java index 0b5e5738d..2cb2a6aaa 100644 --- a/infer/models/java/src/java/util/HashMap.java +++ b/infer/models/java/src/java/util/HashMap.java @@ -29,6 +29,7 @@ public abstract class HashMap { private K lastKey1 = null; private K lastKey2 = null; + private boolean containsResources = false; public boolean containsKey(K key) { // doesn't actually check if _containsKey(key). If you just put a @@ -53,6 +54,12 @@ public abstract class HashMap { } public V put(K key, V value) { + if (value instanceof Closeable) { + // Transfer the resource ownership to the container + InferBuiltins.__set_mem_attribute(value); + InferBuiltins.__set_file_attribute(this); + containsResources = true; + } pushKey(key); if (InferUndefined.boolean_undefined()) { @@ -70,6 +77,10 @@ public abstract class HashMap { public void clear() { lastKey1 = null; lastKey2 = null; + if (containsResources) { + InferBuiltins.__set_mem_attribute(this); + } + containsResources = false; } /** some sort of circular buffer simulator */ diff --git a/infer/tests/build_systems/ant/issues.exp b/infer/tests/build_systems/ant/issues.exp index e7ffa55a5..9f7a71575 100644 --- a/infer/tests/build_systems/ant/issues.exp +++ b/infer/tests/build_systems/ant/issues.exp @@ -6,6 +6,7 @@ codetoanalyze/java/infer/Builtins.java, void Builtins.doNotBlockError(Object), 3 codetoanalyze/java/infer/CloseableAsResourceExample.java, T CloseableAsResourceExample.sourceOfNullWithResourceLeak(), 1, RESOURCE_LEAK, [start of procedure sourceOfNullWithResourceLeak(),start of procedure SomeResource(),return from a call to SomeResource.()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.failToCloseWithCloseQuietly(), 5, RESOURCE_LEAK, [start of procedure failToCloseWithCloseQuietly(),start of procedure SomeResource(),return from a call to SomeResource.(),start of procedure doSomething(),Taking true branch,start of procedure LocalException(),return from a call to LocalException.(),exception codetoanalyze.java.infer.LocalException,return from a call to void SomeResource.doSomething()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.leakFoundWhenIndirectlyImplementingCloseable(), 1, RESOURCE_LEAK, [start of procedure leakFoundWhenIndirectlyImplementingCloseable(),start of procedure CloseableAsResourceExample$MyResource(...),return from a call to CloseableAsResourceExample$MyResource.(CloseableAsResourceExample)] +codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.notClearinglocalMapContainingResourcesBad(), 4, RESOURCE_LEAK, [start of procedure notClearinglocalMapContainingResourcesBad(),start of procedure SomeResource(),return from a call to SomeResource.()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.notClosingCloseable(), 1, RESOURCE_LEAK, [start of procedure notClosingCloseable(),start of procedure SomeResource(),return from a call to SomeResource.()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.notClosingWrapper(), 2, RESOURCE_LEAK, [start of procedure notClosingWrapper(),start of procedure Resource(),return from a call to Resource.(),start of procedure Sub(...),start of procedure Wrapper(...),return from a call to Wrapper.(Resource),return from a call to Sub.(Resource),start of procedure close(),return from a call to void Resource.close()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.skippedVritualCallDoesNotCloseResourceOnReceiver(), 2, RESOURCE_LEAK, [start of procedure skippedVritualCallDoesNotCloseResourceOnReceiver(),start of procedure SomeResource(),return from a call to SomeResource.(),Skipping foo(...): method has no implementation] diff --git a/infer/tests/codetoanalyze/java/infer/CloseableAsResourceExample.java b/infer/tests/codetoanalyze/java/infer/CloseableAsResourceExample.java index 402981be8..5d17e32e1 100644 --- a/infer/tests/codetoanalyze/java/infer/CloseableAsResourceExample.java +++ b/infer/tests/codetoanalyze/java/infer/CloseableAsResourceExample.java @@ -14,7 +14,8 @@ import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.StringReader; - +import java.util.Map; +import java.util.HashMap; class LocalException extends IOException { } @@ -173,4 +174,35 @@ public class CloseableAsResourceExample { res.foo(42); } + Map returnsLocalMapContainingResourcesOk() { + HashMap map = new HashMap<>(); + SomeResource res = new SomeResource(); + Integer key = 42; + map.put(key, res); + return map; + } + + void createsLocalMapContainingResourcesOk() { + HashMap map = new HashMap<>(); + SomeResource res = new SomeResource(); + Integer key = 42; + map.put(key, res); + map.clear(); + } + + HashMap resourceMap = new HashMap<>(); + + void fieldMapContainingResourcesOk() { + Integer key = 42; + SomeResource res = new SomeResource(); + resourceMap.put(key, res); + } + + void notClearinglocalMapContainingResourcesBad() { + HashMap map = new HashMap<>(); + SomeResource res = new SomeResource(); + Integer key = 42; + map.put(key, res); + } + } diff --git a/infer/tests/codetoanalyze/java/infer/issues.exp b/infer/tests/codetoanalyze/java/infer/issues.exp index f400fdabe..a80fcd7a9 100644 --- a/infer/tests/codetoanalyze/java/infer/issues.exp +++ b/infer/tests/codetoanalyze/java/infer/issues.exp @@ -25,6 +25,7 @@ codetoanalyze/java/infer/ClassCastExceptions.java, void ClassCastExceptions.clas codetoanalyze/java/infer/CloseableAsResourceExample.java, T CloseableAsResourceExample.sourceOfNullWithResourceLeak(), 1, RESOURCE_LEAK, [start of procedure sourceOfNullWithResourceLeak(),start of procedure SomeResource(),return from a call to SomeResource.()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.failToCloseWithCloseQuietly(), 5, RESOURCE_LEAK, [start of procedure failToCloseWithCloseQuietly(),start of procedure SomeResource(),return from a call to SomeResource.(),start of procedure doSomething(),Taking true branch,start of procedure LocalException(),return from a call to LocalException.(),exception codetoanalyze.java.infer.LocalException,return from a call to void SomeResource.doSomething()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.leakFoundWhenIndirectlyImplementingCloseable(), 1, RESOURCE_LEAK, [start of procedure leakFoundWhenIndirectlyImplementingCloseable(),start of procedure CloseableAsResourceExample$MyResource(...),return from a call to CloseableAsResourceExample$MyResource.(CloseableAsResourceExample)] +codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.notClearinglocalMapContainingResourcesBad(), 4, RESOURCE_LEAK, [start of procedure notClearinglocalMapContainingResourcesBad(),start of procedure SomeResource(),return from a call to SomeResource.()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.notClosingCloseable(), 1, RESOURCE_LEAK, [start of procedure notClosingCloseable(),start of procedure SomeResource(),return from a call to SomeResource.()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.notClosingWrapper(), 2, RESOURCE_LEAK, [start of procedure notClosingWrapper(),start of procedure Resource(),return from a call to Resource.(),start of procedure Sub(...),start of procedure Wrapper(...),return from a call to Wrapper.(Resource),return from a call to Sub.(Resource),start of procedure close(),return from a call to void Resource.close()] codetoanalyze/java/infer/CloseableAsResourceExample.java, void CloseableAsResourceExample.skippedVritualCallDoesNotCloseResourceOnReceiver(), 2, RESOURCE_LEAK, [start of procedure skippedVritualCallDoesNotCloseResourceOnReceiver(),start of procedure SomeResource(),return from a call to SomeResource.(),Skipping foo(...): method has no implementation]