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>
move(_Tp && __t)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 {
operator ++Iterator21 void operator++() {}
operator *Iterator22 const T& operator*() {
23 static T* TT = new T();
24 return *TT;
25 }
operator !=Iterator26 bool operator!=(const Iterator &) { return false; }
27 typedef const T& const_reference;
28 };
29 template <typename T>
30 struct View {
31 View() = default;
beginView32 T begin() { return T(); }
beginView33 T begin() const { return T(); }
endView34 T end() { return T(); }
endView35 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 &);
SS45 S(const ConstructorConvertible&) {}
46 ~S();
47 S &operator=(const S &);
48 };
49
50 struct Point {
~PointPoint51 ~Point() {}
52 int x, y;
53 };
54
55 struct Convertible {
operator SConvertible56 operator S() const {
57 return S();
58 }
59 };
60
negativeConstReference()61 void negativeConstReference() {
62 for (const S &S1 : View<Iterator<S>>()) {
63 }
64 }
65
negativeUserDefinedConversion()66 void negativeUserDefinedConversion() {
67 Convertible C[0];
68 for (const S S1 : C) {
69 }
70 }
71
negativeImplicitConstructorConversion()72 void negativeImplicitConstructorConversion() {
73 ConstructorConvertible C[0];
74 for (const S S1 : C) {
75 }
76 }
77
78 template <typename T>
uninstantiated()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>
instantiated()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>
instantiatedNegativeTypedefConstReference()105 void instantiatedNegativeTypedefConstReference() {
106 for (typename T::const_reference T2 : T()) {
107 S S1 = T2;
108 }
109 }
110
f()111 void f() {
112 instantiated<int>();
113 instantiated<S>();
114 instantiatedNegativeTypedefConstReference<View<Iterator<S>>>();
115 }
116
117 struct Mutable {
MutableMutable118 Mutable() {}
119 Mutable(const Mutable &) = default;
120 Mutable(Mutable&&) = default;
MutableMutable121 Mutable(const Mutable &, const Mutable &) {}
setBoolMutable122 void setBool(bool B) {}
constMethodMutable123 bool constMethod() const {
124 return true;
125 }
operator []Mutable126 Mutable& operator[](int I) {
127 return *this;
128 }
operator ==Mutable129 bool operator==(const Mutable &Other) const {
130 return true;
131 }
~MutableMutable132 ~Mutable() {}
133 };
134
operator <<(Mutable & Out,bool B)135 Mutable& operator<<(Mutable &Out, bool B) {
136 Out.setBool(B);
137 return Out;
138 }
139
operator !=(const Mutable & M1,const Mutable & M2)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
negativeVariableIsMutated()154 void negativeVariableIsMutated() {
155 for (auto M : View<Iterator<Mutable>>()) {
156 mutate(M);
157 }
158 for (auto M : View<Iterator<Mutable>>()) {
159 mutate(&M);
160 }
161 for (auto M : View<Iterator<Mutable>>()) {
162 M.setBool(true);
163 }
164 }
165
negativeOnceConstOnceMutated()166 void negativeOnceConstOnceMutated() {
167 for (auto M : View<Iterator<Mutable>>()) {
168 onceConstOnceMutated(M, M);
169 }
170 }
171
negativeVarIsMoved()172 void negativeVarIsMoved() {
173 for (auto M : View<Iterator<Mutable>>()) {
174 auto Moved = std::move(M);
175 }
176 }
177
negativeNonConstOperatorIsInvoked()178 void negativeNonConstOperatorIsInvoked() {
179 for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
180 auto& N = NonConstOperatorInvokee[0];
181 }
182 }
183
negativeNonConstNonMemberOperatorInvoked()184 void negativeNonConstNonMemberOperatorInvoked() {
185 for (auto NonConstOperatorInvokee : View<Iterator<Mutable>>()) {
186 NonConstOperatorInvokee << true;
187 }
188 }
189
negativeConstCheapToCopy()190 void negativeConstCheapToCopy() {
191 for (const int I : View<Iterator<int>>()) {
192 }
193 }
194
negativeConstCheapToCopyTypedef()195 void negativeConstCheapToCopyTypedef() {
196 typedef const int ConstInt;
197 for (ConstInt C : View<Iterator<ConstInt>>()) {
198 }
199 }
200
negativeCheapToCopy()201 void negativeCheapToCopy() {
202 for (int I : View<Iterator<int>>()) {
203 use(I);
204 }
205 }
206
negativeCheapToCopyTypedef()207 void negativeCheapToCopyTypedef() {
208 typedef int Int;
209 for (Int I : View<Iterator<Int>>()) {
210 use(I);
211 }
212 }
213
positiveOnlyConstMethodInvoked()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
positiveOnlyUsedAsConstArguments()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(UsedAsConst);
227 useTwice(UsedAsConst, UsedAsConst);
228 useByValue(UsedAsConst);
229 useByConstValue(UsedAsConst);
230 }
231 }
232
positiveOnlyAccessedFieldAsConst()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(UsedAsConst.x);
238 use(UsedAsConst.y);
239 }
240 }
241
positiveOnlyUsedAsConstBinding()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(X);
247 use(Y);
248 }
249 }
250
negativeMutatedBinding()251 void negativeMutatedBinding() {
252 for (auto [X, Y] : View<Iterator<Point>>()) {
253 use(X);
254 mutate(Y);
255 }
256 }
257
positiveOnlyUsedInCopyConstructor()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
positiveTwoConstConstructorArgs()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
PositiveConstMemberOperatorInvoked()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
PositiveConstNonMemberOperatorInvoked()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
IgnoreLoopVariableNotUsedInLoopBody()291 void IgnoreLoopVariableNotUsedInLoopBody() {
292 for (auto _ : View<Iterator<S>>()) {
293 }
294 }
295
296 template <typename T>
297 struct ValueReturningIterator {
operator ++ValueReturningIterator298 void operator++() {}
operator *ValueReturningIterator299 T operator*() { return T(); }
operator !=ValueReturningIterator300 bool operator!=(const ValueReturningIterator &) { return false; }
301 typedef const T &const_reference;
302 };
303
negativeValueIterator()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
createView(S)310 View<Iterator<S>> createView(S) { return View<Iterator<S>>(); }
311
positiveValueIteratorUsedElseWhere()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
positiveConstMemberExpr()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(SS.Member);
332 useByConstValue(SS.Member);
333 useByValue(SS.Member);
334 }
335 }
336
negativeNonConstMemberExpr()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(SS.Member);
349 }
350 for (Struct SS : View<Iterator<Struct>>()) {
351 mutate(&SS.Member);
352 }
353 }
354
355