|
| 1 | +/** |
| 2 | + * Finds conversion of an array or a `Collection<CharSequence>` to a `Stream` |
| 3 | + * just to join the elements to a single string. Such code can be simplified |
| 4 | + * by using `String.join`. For example: |
| 5 | + * ```java |
| 6 | + * List<String> words = ...; |
| 7 | + * String sentence = words.stream().collect(Collectors.joining(" ")); |
| 8 | + * |
| 9 | + * // Can be simplified as |
| 10 | + * String sentence = String.join(" ", words); |
| 11 | + * ``` |
| 12 | + * |
| 13 | + * @kind problem |
| 14 | + */ |
| 15 | + |
| 16 | +import java |
| 17 | + |
| 18 | +class CollectionStreamMethod extends Method { |
| 19 | + CollectionStreamMethod() { |
| 20 | + getDeclaringType().hasQualifiedName("java.util", "Collection") and |
| 21 | + hasStringSignature("stream()") |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +class ArrayAsStreamMethod extends Method { |
| 26 | + ArrayAsStreamMethod() { |
| 27 | + isStatic() and |
| 28 | + ( |
| 29 | + getDeclaringType().hasQualifiedName("java.util", "Arrays") and |
| 30 | + hasName("stream") |
| 31 | + or |
| 32 | + getDeclaringType().hasQualifiedName("java.util.stream", "Stream") and |
| 33 | + hasName("of") |
| 34 | + ) |
| 35 | + } |
| 36 | +} |
| 37 | + |
| 38 | +class CollectMethod extends Method { |
| 39 | + CollectMethod() { |
| 40 | + getDeclaringType().hasQualifiedName("java.util.stream", "Stream") and |
| 41 | + hasName("collect") |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +class JoiningCollectorMethod extends Method { |
| 46 | + JoiningCollectorMethod() { |
| 47 | + getDeclaringType().hasQualifiedName("java.util.stream", "Collectors") and |
| 48 | + hasName("joining") and |
| 49 | + // Ignore variant with additional prefix and suffix arguments |
| 50 | + getNumberOfParameters() = [0, 1] |
| 51 | + } |
| 52 | +} |
| 53 | + |
| 54 | +from |
| 55 | + Expr collectionExpr, MethodAccess charSeqStreamExpr, MethodAccess collectCall, |
| 56 | + MethodAccess joiningCall |
| 57 | +where |
| 58 | + ( |
| 59 | + exists(MethodAccess collectionStreamCall | charSeqStreamExpr = collectionStreamCall | |
| 60 | + collectionStreamCall.getQualifier() = collectionExpr and |
| 61 | + collectionStreamCall.getMethod().getSourceDeclaration().getASourceOverriddenMethod*() |
| 62 | + instanceof CollectionStreamMethod |
| 63 | + ) |
| 64 | + or |
| 65 | + exists(MethodAccess arrayStreamCall | charSeqStreamExpr = arrayStreamCall | |
| 66 | + arrayStreamCall.getArgument(0) = collectionExpr and |
| 67 | + arrayStreamCall.getMethod() instanceof ArrayAsStreamMethod |
| 68 | + ) |
| 69 | + ) and |
| 70 | + // Has type Stream<CharSequence> |
| 71 | + charSeqStreamExpr |
| 72 | + .getType() |
| 73 | + .(ParameterizedType) |
| 74 | + .getTypeArgument(0) |
| 75 | + .getErasure() |
| 76 | + .(RefType) |
| 77 | + .getASourceSupertype*() |
| 78 | + .hasQualifiedName("java.lang", "CharSequence") and |
| 79 | + // Calls `stream.collect(...)` |
| 80 | + collectCall.getQualifier() = charSeqStreamExpr and |
| 81 | + collectCall.getMethod().getSourceDeclaration().getASourceOverriddenMethod*() instanceof |
| 82 | + CollectMethod and |
| 83 | + // Calls `collect(Collectors.joining(...))` |
| 84 | + collectCall.getArgument(0) = joiningCall and |
| 85 | + joiningCall.getMethod().getSourceDeclaration().getASourceOverriddenMethod*() instanceof |
| 86 | + JoiningCollectorMethod |
| 87 | +select joiningCall, "Could instead use `String.join` to join elements of $@", collectionExpr, |
| 88 | + "this collection" |
0 commit comments