diff --git a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll index 21cfc54fdf8a..361b4feb54a8 100644 --- a/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll +++ b/java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll @@ -202,6 +202,13 @@ private module Input implements TypeFlowInput { t1e = t2e and unbound(t2) and not unbound(t1) + or + t1e = t2e and + exists(int pos | + partiallyUnbound(t2, pos) and + not partiallyUnbound(t1, pos) and + not unbound(t1) + ) ) } @@ -370,6 +377,11 @@ private module Input implements TypeFlowInput { ) } + /** Holds if `t` is a parameterised type with unrestricted type argument at position `pos`. */ + private predicate partiallyUnbound(ParameterizedType t, int pos) { + unconstrained(t.getTypeArgument(pos)) + } + Type getErasure(Type t) { result = t.getErasure() } Type getAnAncestor(Type sub) { result = sub.getAnAncestor() } diff --git a/java/ql/test/library-tests/typeflow/A.java b/java/ql/test/library-tests/typeflow/A.java index ad19901f1514..751ad525d153 100644 --- a/java/ql/test/library-tests/typeflow/A.java +++ b/java/ql/test/library-tests/typeflow/A.java @@ -118,4 +118,12 @@ public void m10(Object o) { default -> { } } } + + private static T lookupFoo(Map m) { + return m.get("foo"); + } + + public void m11(Map m) { + lookupFoo(m); + } } diff --git a/java/ql/test/library-tests/typeflow/typeflow.expected b/java/ql/test/library-tests/typeflow/typeflow.expected index f0cb2356cb81..03dcb0e07666 100644 --- a/java/ql/test/library-tests/typeflow/typeflow.expected +++ b/java/ql/test/library-tests/typeflow/typeflow.expected @@ -18,5 +18,6 @@ | A.java:112:23:112:24 | cs | String | false | | A.java:116:13:116:14 | o2 | String | false | | A.java:117:49:117:50 | cs | String | false | +| A.java:123:12:123:12 | m | Map | false | | UnionTypes.java:45:7:45:7 | x | Inter | false | | UnionTypes.java:48:23:48:23 | x | Inter | false |