1 | // RUN: %check_clang_tidy %s performance-for-range-copy %t -- -- -fno-delayed-template-parsing |
2 | |
3 | namespace std { |
4 | |
5 | template <typename _Tp> |
6 | struct remove_reference { typedef _Tp type; }; |
7 | template <typename _Tp> |
8 | struct remove_reference<_Tp&> { typedef _Tp type; }; |
9 | template <typename _Tp> |
10 | struct remove_reference<_Tp&&> { typedef _Tp type; }; |
11 | |
12 | template <typename _Tp> |
13 | constexpr typename std::remove_reference<_Tp>::type &&move(_Tp &&__t) { |
14 | return static_cast<typename std::remove_reference<_Tp>::type &&>(__t); |
15 | } |
16 | |
17 | } // std |
18 | |
19 | template <typename T> |
20 | struct Iterator { |
21 | void operator++() {} |
22 | const T& operator*() { |
23 | static T* TT = new T(); |
24 | return *TT; |
25 | } |
26 | bool operator!=(const Iterator &) { return false; } |
27 | typedef const T& const_reference; |
28 | }; |
29 | template <typename T> |
30 | struct View { |
31 | View() = default; |
32 | T begin() { return T(); } |
33 | T begin() const { return T(); } |
34 | T end() { return T(); } |
35 | T end() const { return T(); } |
36 | typedef typename T::const_reference const_reference; |
37 | }; |
38 | |
39 | struct ConstructorConvertible { |
40 | }; |
41 | |
42 | struct S { |
43 | S(); |
44 | S(const S &); |
45 | S(const ConstructorConvertible&) {} |
46 | ~S(); |
47 | S &operator=(const S &); |
48 | }; |
49 | |
50 | struct Point { |
51 | ~Point() {} |
52 | int x, y; |
53 | }; |
54 | |
55 | struct Convertible { |
56 | operator S() const { |
57 | return S(); |
58 | } |
59 | }; |
60 | |
61 | void negativeConstReference() { |
62 | for (const S &S1 : View<Iterator<S>>()) { |
63 | } |
64 | } |
65 | |
66 | void negativeUserDefinedConversion() { |
67 | Convertible C[0]; |
68 | for (const S S1 : C) { |
69 | } |
70 | } |
71 | |
72 | void negativeImplicitConstructorConversion() { |
73 | ConstructorConvertible C[0]; |
74 | for (const S S1 : C) { |
75 | } |
76 | } |
77 | |
78 | template <typename T> |
79 | void uninstantiated() { |
80 | for (const S S1 : View<Iterator<S>>()) {} |
81 | // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is not a reference type; this creates a copy in each iteration; consider making this a reference [performance-for-range-copy] |
82 | // CHECK-FIXES: {{^}} for (const S& S1 : View<Iterator<S>>()) {} |
83 | |
84 | // Don't warn on dependent types. |
85 | for (const T t1 : View<Iterator<T>>()) { |
86 | } |
87 | } |
88 | |
89 | template <typename T> |
90 | void instantiated() { |
91 | for (const S S2 : View<Iterator<S>>()) {} |
92 | // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}} |
93 | // CHECK-FIXES: {{^}} for (const S& S2 : View<Iterator<S>>()) {} |
94 | |
95 | for (const auto [X, Y] : View<Iterator<Point>>()) {} |
96 | // CHECK-MESSAGES: [[@LINE-1]]:19: warning: the loop variable's type is |
97 | // CHECK-FIXES: {{^}} for (const auto& [X, Y] : View<Iterator<Point>>()) {} |
98 | |
99 | for (const T T2 : View<Iterator<T>>()) {} |
100 | // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is {{.*}} |
101 | // CHECK-FIXES: {{^}} for (const T& T2 : View<Iterator<T>>()) {} |
102 | } |
103 | |
104 | template <typename T> |
105 | void instantiatedNegativeTypedefConstReference() { |
106 | for (typename T::const_reference T2 : T()) { |
107 | S S1 = T2; |
108 | } |
109 | } |
110 | |
111 | void f() { |
112 | instantiated<int>(); |
113 | instantiated<S>(); |
114 | instantiatedNegativeTypedefConstReference<View<Iterator<S>>>(); |
115 | } |
116 | |
117 | struct Mutable { |
118 | Mutable() {} |
119 | Mutable(const Mutable &) = default; |
120 | Mutable(Mutable&&) = default; |
121 | Mutable(const Mutable &, const Mutable &) {} |
122 | void setBool(bool B) {} |
123 | bool constMethod() const { |
124 | return true; |
125 | } |
126 | Mutable& operator[](int I) { |
127 | return *this; |
128 | } |
129 | bool operator==(const Mutable &Other) const { |
130 | return true; |
131 | } |
132 | ~Mutable() {} |
133 | }; |
134 | |
135 | Mutable& operator<<(Mutable &Out, bool B) { |
136 | Out.setBool(B); |
137 | return Out; |
138 | } |
139 | |
140 | bool operator!=(const Mutable& M1, const Mutable& M2) { |
141 | return false; |
142 | } |
143 | |
144 | void use(const Mutable &M); |
145 | void use(int I); |
146 | void useTwice(const Mutable &M1, const Mutable &M2); |
147 | void useByValue(Mutable M); |
148 | void useByConstValue(const Mutable M); |
149 | void mutate(Mutable *M); |
150 | void mutate(Mutable &M); |
151 | void mutate(int &); |
152 | void onceConstOnceMutated(const Mutable &M1, Mutable &M2); |
153 | |
154 | void negativeVariableIsMutated() { |
155 | for (auto M : View<Iterator<Mutable>>()) { |
156 | mutate(M); |
157 | } |
158 | for (auto M : View<Iterator<Mutable>>()) { |
159 | mutate(M: &M); |
160 | } |
161 | for (auto M : View<Iterator<Mutable>>()) { |
162 | M.setBool(true); |
163 | } |
164 | } |
165 | |
166 | void negativeOnceConstOnceMutated() { |
167 | for (auto M : View<Iterator<Mutable>>()) { |
168 | onceConstOnceMutated(M1: M, M2&: M); |
169 | } |
170 | } |
171 | |
172 | void negativeVarIsMoved() { |
173 | for (auto M : View<Iterator<Mutable>>()) { |
174 | auto Moved = std::move(M); |
175 | } |
176 | } |
177 | |
178 | void negativeNonConstOperatorIsInvoked() { |
179 | for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) { |
180 | auto& N = NonConstOperatorInvokee[0]; |
181 | } |
182 | } |
183 | |
184 | void negativeNonConstNonMemberOperatorInvoked() { |
185 | for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) { |
186 | NonConstOperatorInvokee << true; |
187 | } |
188 | } |
189 | |
190 | void negativeConstCheapToCopy() { |
191 | for (const int I : View<Iterator<int>>()) { |
192 | } |
193 | } |
194 | |
195 | void negativeConstCheapToCopyTypedef() { |
196 | typedef const int ConstInt; |
197 | for (ConstInt C : View<Iterator<ConstInt>>()) { |
198 | } |
199 | } |
200 | |
201 | void negativeCheapToCopy() { |
202 | for (int I : View<Iterator<int>>()) { |
203 | use(I); |
204 | } |
205 | } |
206 | |
207 | void negativeCheapToCopyTypedef() { |
208 | typedef int Int; |
209 | for (Int I : View<Iterator<Int>>()) { |
210 | use(I); |
211 | } |
212 | } |
213 | |
214 | void positiveOnlyConstMethodInvoked() { |
215 | for (auto M : View<Iterator<Mutable>>()) { |
216 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
217 | // CHECK-FIXES: for (const auto& M : View<Iterator<Mutable>>()) { |
218 | M.constMethod(); |
219 | } |
220 | } |
221 | |
222 | void positiveOnlyUsedAsConstArguments() { |
223 | for (auto UsedAsConst : View<Iterator<Mutable>>()) { |
224 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
225 | // CHECK-FIXES: for (const auto& UsedAsConst : View<Iterator<Mutable>>()) { |
226 | use(M: UsedAsConst); |
227 | useTwice(M1: UsedAsConst, M2: UsedAsConst); |
228 | useByValue(M: UsedAsConst); |
229 | useByConstValue(M: UsedAsConst); |
230 | } |
231 | } |
232 | |
233 | void positiveOnlyAccessedFieldAsConst() { |
234 | for (auto UsedAsConst : View<Iterator<Point>>()) { |
235 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
236 | // CHECK-FIXES: for (const auto& UsedAsConst : View<Iterator<Point>>()) { |
237 | use(I: UsedAsConst.x); |
238 | use(I: UsedAsConst.y); |
239 | } |
240 | } |
241 | |
242 | void positiveOnlyUsedAsConstBinding() { |
243 | for (auto [X, Y] : View<Iterator<Point>>()) { |
244 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but |
245 | // CHECK-FIXES: for (const auto& [X, Y] : View<Iterator<Point>>()) { |
246 | use(I: X); |
247 | use(I: Y); |
248 | } |
249 | } |
250 | |
251 | void negativeMutatedBinding() { |
252 | for (auto [X, Y] : View<Iterator<Point>>()) { |
253 | use(I: X); |
254 | mutate(Y); |
255 | } |
256 | } |
257 | |
258 | void positiveOnlyUsedInCopyConstructor() { |
259 | for (auto A : View<Iterator<Mutable>>()) { |
260 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
261 | // CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) { |
262 | Mutable Copy = A; |
263 | Mutable Copy2(A); |
264 | } |
265 | } |
266 | |
267 | void positiveTwoConstConstructorArgs() { |
268 | for (auto A : View<Iterator<Mutable>>()) { |
269 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
270 | // CHECK-FIXES: for (const auto& A : View<Iterator<Mutable>>()) { |
271 | Mutable Copy(A, A); |
272 | } |
273 | } |
274 | |
275 | void PositiveConstMemberOperatorInvoked() { |
276 | for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) { |
277 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
278 | // CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) { |
279 | bool result = ConstOperatorInvokee == Mutable(); |
280 | } |
281 | } |
282 | |
283 | void PositiveConstNonMemberOperatorInvoked() { |
284 | for (auto ConstOperatorInvokee : View<Iterator<Mutable>>()) { |
285 | // CHECK-MESSAGES: [[@LINE-1]]:13: warning: loop variable is copied but only used as const reference; consider making it a const reference [performance-for-range-copy] |
286 | // CHECK-FIXES: for (const auto& ConstOperatorInvokee : View<Iterator<Mutable>>()) { |
287 | bool result = ConstOperatorInvokee != Mutable(); |
288 | } |
289 | } |
290 | |
291 | void IgnoreLoopVariableNotUsedInLoopBody() { |
292 | for (auto _ : View<Iterator<S>>()) { |
293 | } |
294 | } |
295 | |
296 | template <typename T> |
297 | struct ValueReturningIterator { |
298 | void operator++() {} |
299 | T operator*() { return T(); } |
300 | bool operator!=(const ValueReturningIterator &) { return false; } |
301 | typedef const T &const_reference; |
302 | }; |
303 | |
304 | void negativeValueIterator() { |
305 | // Check does not trigger for iterators that return elements by value. |
306 | for (const S SS : View<ValueReturningIterator<S>>()) { |
307 | } |
308 | } |
309 | |
310 | View<Iterator<S>> createView(S) { return View<Iterator<S>>(); } |
311 | |
312 | void positiveValueIteratorUsedElseWhere() { |
313 | for (const S SS : createView(*ValueReturningIterator<S>())) { |
314 | // CHECK-MESSAGES: [[@LINE-1]]:16: warning: the loop variable's type is not |
315 | // a reference type; this creates a copy in each iteration; consider making |
316 | // this a reference [performance-for-range-copy] CHECK-FIXES: for (const S& |
317 | // SS : createView(*ValueReturningIterator<S>())) { |
318 | } |
319 | } |
320 | |
321 | void positiveConstMemberExpr() { |
322 | struct Struct { |
323 | Mutable Member; |
324 | }; |
325 | for (Struct SS : View<Iterator<Struct>>()) { |
326 | // CHECK-MESSAGES: [[@LINE-1]]:15: warning: loop variable is copied |
327 | // CHECK-FIXES: for (const Struct& SS : View<Iterator<Struct>>()) { |
328 | auto MemberCopy = SS.Member; |
329 | const auto &ConstRef = SS.Member; |
330 | bool b = SS.Member.constMethod(); |
331 | use(M: SS.Member); |
332 | useByConstValue(M: SS.Member); |
333 | useByValue(M: SS.Member); |
334 | } |
335 | } |
336 | |
337 | void negativeNonConstMemberExpr() { |
338 | struct Struct { |
339 | Mutable Member; |
340 | }; |
341 | for (Struct SS : View<Iterator<Struct>>()) { |
342 | SS.Member.setBool(true); |
343 | } |
344 | for (Struct SS : View<Iterator<Struct>>()) { |
345 | SS.Member[1]; |
346 | } |
347 | for (Struct SS : View<Iterator<Struct>>()) { |
348 | mutate(M&: SS.Member); |
349 | } |
350 | for (Struct SS : View<Iterator<Struct>>()) { |
351 | mutate(M: &SS.Member); |
352 | } |
353 | } |
354 | |
355 | |