Skip to content

Commit b6aa86f

Browse files
committed
Add method-ref-creating-collection-with-capacity.ql
1 parent 80a7c05 commit b6aa86f

File tree

1 file changed

+56
-0
lines changed

1 file changed

+56
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/**
2+
* Finds method reference expressions which might by accident refer to collection constructors
3+
* or factory methods which take an `int` as capacity argument. The method reference expression
4+
* might have been intended to just create a new collection, and the argument is accidentally
5+
* misinterpreted as capacity.
6+
*
7+
* If usage of the referenced constructor or method with capacity parameter is intended, the code
8+
* should be rewritten as lambda expression to make this explicit. For example:
9+
* ```java
10+
* Map<Integer, List<String>> map = ...;
11+
* // Actually calls `new ArrayList<>(capacity)`
12+
* map.computeIfAbsent(1, ArrayList::new);
13+
*
14+
* // If that is intended, it should be written like this:
15+
* map.computeIfAbsent(1, capacity -> new ArrayList<>(capacity));
16+
* // Or otherwise the argument should be ignored:
17+
* map.computeIfAbsent(1, key -> new ArrayList<>());
18+
* ```
19+
*
20+
* Based on [Java Collections Puzzlers by Maurice Naftalin And José Paumard](https://youtu.be/w6hhjg_gt_M?t=1873).
21+
*
22+
* @kind problem
23+
*/
24+
25+
import java
26+
27+
from MethodAccess enclosingCall, MemberRefExpr methodRef, Callable referencedCallable
28+
where
29+
// To reduce false positives only consider method ref which is argument to an instance method call (e.g. `Map.computeIfAbsent`)
30+
enclosingCall.getAnArgument() = methodRef
31+
and not enclosingCall.getMethod().isStatic()
32+
and methodRef.getReferencedCallable() = referencedCallable
33+
and (
34+
referencedCallable instanceof Constructor
35+
// By convention Collection and Map implementations often have constructor which takes an `int` initial capacity
36+
and referencedCallable.getDeclaringType().getSourceDeclaration().getASourceSupertype*().hasQualifiedName("java.util", ["Collection", "Map"])
37+
or
38+
// Or static factory methods which create collection or map with capacity
39+
// TODO: Maybe remove this again if it causes too many false positives; though from the method names alone, it is
40+
// not immediately obvious that they take a capacity argument
41+
referencedCallable.(Method).isStatic()
42+
and exists(string type, string methodName |
43+
referencedCallable.getDeclaringType().getQualifiedName() = type
44+
and referencedCallable.hasName(methodName)
45+
|
46+
type = "java.util.concurrent.ConcurrentHashMap" and methodName = "newKeySet"
47+
or type = "java.util.HashMap" and methodName = "newHashMap"
48+
or type = "java.util.HashSet" and methodName = "newHashSet"
49+
or type = "java.util.WeakHashMap" and methodName = "newWeakHashMap"
50+
or type = "java.util.LinkedHashMap" and methodName = "newLinkedHashMap"
51+
or type = "java.util.LinkedHashSet" and methodName = "newLinkedHashSet"
52+
)
53+
)
54+
and referencedCallable.getNumberOfParameters() = 1
55+
and referencedCallable.getParameterType(0).(PrimitiveType).hasName("int")
56+
select methodRef, "Might by accident create collection with certain capacity"

0 commit comments

Comments
 (0)