xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/performance/for-range-copy.cpp (revision 8fd32b96caf37113dd425cd9d0ff8c839c6a048a)
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