-
Notifications
You must be signed in to change notification settings - Fork 720
Expand file tree
/
Copy pathCollectorsToListCheckSample.java
More file actions
216 lines (159 loc) · 7.27 KB
/
CollectorsToListCheckSample.java
File metadata and controls
216 lines (159 loc) · 7.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
package checks;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.val;
public class CollectorsToListCheckSample {
void lombok_val() {
// Lombok val is not supported, in this case the symbolType of the collect method invocation is unknown
val unknownSymbolStream = Stream.of("A", "B", "C").collect(Collectors.toList()); // Compliant
}
static class ListWrapper {
List<String> strings;
}
ListWrapper listWrapper = new ListWrapper();
void noncompliant() {
List<String> list1 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Noncompliant {{Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.}}
// ^^^^^^^^^^^^^^^^^^^
// Not modifying the list
list1.contains("B");
List<String> list2 = Stream.of("A", "B", "C")
.collect(Collectors.toUnmodifiableList()); // Noncompliant {{Replace this usage of 'Stream.collect(Collectors.toUnmodifiableList())' with 'Stream.toList()'.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Noncompliant {{Replace this usage of 'Stream.collect(Collectors.toList())' with 'Stream.toList()' and ensure that the list is unmodified.}}
// ^^^^^^^^^^^^^^^^^^^
Stream.of("A", "B", "C")
.collect(Collectors.toUnmodifiableList()); // Noncompliant {{Replace this usage of 'Stream.collect(Collectors.toUnmodifiableList())' with 'Stream.toList()'.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
List<List<String>> listOfLists = new ArrayList<>();
// list1 appears in a call to List.add, but it is not the receiver, so it should not be interpreted as mutable:
listOfLists.add(list1);
listWrapper.strings = Stream.of("A", "B", "C").collect(Collectors.toList());
// listWrapper.strings appears in a call to List.add, but it is not the receiver, so it should not be interpreted as mutable:
listOfLists.add(listWrapper.strings);
}
void compliant_collections_methods() {
var myMutableList = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant
Collections.shuffle(myMutableList);
var myMutableList2 = Stream.of("A", "B")
.collect(Collectors.toList()); // Compliant
Collections.rotate(myMutableList2, 2);
var myMutableList3 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant
Collections.addAll(myMutableList3, "D", "E");
var myMutableList4 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant
Collections.replaceAll(myMutableList4, "B", "D");
var myMutableList5 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant
Collections.reverse(myMutableList5);
}
void noncompliant_collections_methods() {
var myMutableList = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Noncompliant
Collections.max(myMutableList);
Collections.frequency(myMutableList, "A");
Collections.synchronizedList(myMutableList);
Collections.unmodifiableList(myMutableList);
}
private List<String> memberList;
private List<String> memberListAccessedWithThis;
List<String>[] arr;
ListWrapper listWrapper2 = new ListWrapper();
void compliant() {
List<String> list1 = Stream.of("A", "B", "C").toList(); // Compliant
List<String> list2 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, list2 needs to be mutable
list2.add("X");
List<String> list3 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, list3 needs to be mutable
list3.retainAll(Arrays.asList("C", "D"));
memberList = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, memberList needs to be mutable as its modified in addX
this.memberListAccessedWithThis = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, memberListAccessedWithThis needs to be mutable as its modified in addX
arr[0] = Stream.of("A", "B", "C").collect(Collectors.toList()); // Compliant, list is modified in addX
listWrapper2.strings = Stream.of("A", "B", "C").collect(Collectors.toList()); // Compliant, list is modified in addX
List<String> list4 = Stream.of("A", "B", "C")
.collect(Collectors.toCollection(ArrayList::new)); // Compliant because it's creating a specific list type instead of using toList
List<String> list5 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, list5 needs to be mutable
list5.removeIf(s -> true);
List<String> list6 = Stream.of("B", "C", "D")
.collect(Collectors.toList()); // Compliant, list6 needs to be mutable
list6.addFirst("A");
List<String> list7 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, list7 needs to be mutable
list7.addLast("D");
List<String> list8 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, list8 needs to be mutable
list8.removeFirst();
List<String> list9 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Compliant, list9 needs to be mutable
list9.removeLast();
}
void addX() {
memberList.add("X");
this.memberListAccessedWithThis.add("X");
arr[0].add("X");
listWrapper2.strings.add("X");
}
void FNs() {
Collector<String, ?, List<String>> collector = Collectors.toUnmodifiableList();
List<String> list1 = Stream.of("A", "B", "C").collect(collector); // FN because we don't track the collector through variables
}
private List<String> memberList2;
List<String> FPs() {
List<String> list1 = Stream.of("A", "B", "C")
.collect(Collectors.toList()); // Noncompliant
addX(list1);
addX(Stream.of("A", "B", "C").collect(Collectors.toList())); // Noncompliant
memberList2 = Stream.of("A", "B", "C").collect(Collectors.toList()); // Noncompliant
return Stream.of("A", "B", "C").collect(Collectors.toList()); // Noncompliant
}
void useFPs() {
FPs().add("X");
}
void addX(List<String> string) {
memberList.add("X");
}
void addX2() {
getMemberList2().add("X"); // We don't detect this modification on memberList2 because we don't follow through the getter
}
List<String> getMemberList2() {
return memberList2;
}
// Test that we don't throw an exception when List.add is called without a receiver
private static class MyList extends ArrayList<String> {
void addX() {
add("X");
}
}
Collection<CharSequence> upcast(Stream<String> stream) {
return stream.collect(Collectors.toList()); // Compliant
}
Collection<CharSequence> noUpcast(Stream<CharSequence> stream) {
return stream.collect(Collectors.toList()); // Noncompliant
}
Collection<CharSequence> upcastInlineStream() {
return Stream.of(1, 2)
.map(String::valueOf)
.collect(Collectors.toList()); // Compliant
}
Collection rawReceiver() {
return Stream.of(1, 2)
.map(String::valueOf)
.collect(Collectors.toList()); // Noncompliant
}
Object rawReceiverAndArgument(Stream stream) {
return stream.collect(Collectors.toList()); // Noncompliant
}
}