xref: /llvm-project/clang-tools-extra/docs/clang-tidy/checks/modernize/loop-convert.rst (revision 6a1f8ef8a7aaefea80ef0bc7c6c462a96215b50e)
1.. title:: clang-tidy - modernize-loop-convert
2
3modernize-loop-convert
4======================
5
6This check converts ``for(...; ...; ...)`` loops to use the new range-based
7loops in C++11.
8
9Three kinds of loops can be converted:
10
11-  Loops over statically allocated arrays.
12-  Loops over containers, using iterators.
13-  Loops over array-like containers, using ``operator[]`` and ``at()``.
14
15MinConfidence option
16--------------------
17
18risky
19^^^^^
20
21In loops where the container expression is more complex than just a
22reference to a declared expression (a variable, function, enum, etc.),
23and some part of it appears elsewhere in the loop, we lower our confidence
24in the transformation due to the increased risk of changing semantics.
25Transformations for these loops are marked as `risky`, and thus will only
26be converted if the minimum required confidence level is set to `risky`.
27
28.. code-block:: c++
29
30  int arr[10][20];
31  int l = 5;
32
33  for (int j = 0; j < 20; ++j)
34    int k = arr[l][j] + l; // using l outside arr[l] is considered risky
35
36  for (int i = 0; i < obj.getVector().size(); ++i)
37    obj.foo(10); // using 'obj' is considered risky
38
39See
40:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`
41for an example of an incorrect transformation when the minimum required confidence
42level is set to `risky`.
43
44reasonable (Default)
45^^^^^^^^^^^^^^^^^^^^
46
47If a loop calls ``.end()`` or ``.size()`` after each iteration, the
48transformation for that loop is marked as `reasonable`, and thus will
49be converted if the required confidence level is set to `reasonable`
50(default) or lower.
51
52.. code-block:: c++
53
54  // using size() is considered reasonable
55  for (int i = 0; i < container.size(); ++i)
56    cout << container[i];
57
58safe
59^^^^
60
61Any other loops that do not match the above criteria to be marked as
62`risky` or `reasonable` are marked `safe`, and thus will be converted
63if the required confidence level is set to `safe` or lower.
64
65.. code-block:: c++
66
67  int arr[] = {1,2,3};
68
69  for (int i = 0; i < 3; ++i)
70    cout << arr[i];
71
72Example
73-------
74
75Original:
76
77.. code-block:: c++
78
79  const int N = 5;
80  int arr[] = {1,2,3,4,5};
81  vector<int> v;
82  v.push_back(1);
83  v.push_back(2);
84  v.push_back(3);
85
86  // safe conversion
87  for (int i = 0; i < N; ++i)
88    cout << arr[i];
89
90  // reasonable conversion
91  for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)
92    cout << *it;
93
94  // reasonable conversion
95  for (vector<int>::iterator it = begin(v); it != end(v); ++it)
96    cout << *it;
97
98  // reasonable conversion
99  for (vector<int>::iterator it = std::begin(v); it != std::end(v); ++it)
100    cout << *it;
101
102  // reasonable conversion
103  for (int i = 0; i < v.size(); ++i)
104    cout << v[i];
105
106  // reasonable conversion
107  for (int i = 0; i < size(v); ++i)
108    cout << v[i];
109
110After applying the check with minimum confidence level set to `reasonable` (default):
111
112.. code-block:: c++
113
114  const int N = 5;
115  int arr[] = {1,2,3,4,5};
116  vector<int> v;
117  v.push_back(1);
118  v.push_back(2);
119  v.push_back(3);
120
121  // safe conversion
122  for (auto & elem : arr)
123    cout << elem;
124
125  // reasonable conversion
126  for (auto & elem : v)
127    cout << elem;
128
129  // reasonable conversion
130  for (auto & elem : v)
131    cout << elem;
132
133Reverse Iterator Support
134------------------------
135
136The converter is also capable of transforming iterator loops which use
137``rbegin`` and ``rend`` for looping backwards over a container. Out of the box
138this will automatically happen in C++20 mode using the ``ranges`` library,
139however the check can be configured to work without C++20 by specifying a
140function to reverse a range and optionally the header file where that function
141lives.
142
143.. option:: UseCxx20ReverseRanges
144
145   When set to true convert loops when in C++20 or later mode using
146   ``std::ranges::reverse_view``.
147   Default value is ``true``.
148
149.. option:: MakeReverseRangeFunction
150
151   Specify the function used to reverse an iterator pair, the function should
152   accept a class with ``rbegin`` and ``rend`` methods and return a
153   class with ``begin`` and ``end`` methods that call the ``rbegin`` and
154   ``rend`` methods respectively. Common examples are ``ranges::reverse_view``
155   and ``llvm::reverse``.
156   Default value is an empty string.
157
158.. option:: MakeReverseRangeHeader
159
160   Specifies the header file where :option:`MakeReverseRangeFunction` is
161   declared. For the previous examples this option would be set to
162   ``range/v3/view/reverse.hpp`` and ``llvm/ADT/STLExtras.h`` respectively.
163   If this is an empty string and :option:`MakeReverseRangeFunction` is set,
164   the check will proceed on the assumption that the function is already
165   available in the translation unit.
166   This can be wrapped in angle brackets to signify to add the include as a
167   system include.
168   Default value is an empty string.
169
170.. option:: IncludeStyle
171
172   A string specifying which include-style is used, `llvm` or `google`. Default
173   is `llvm`.
174
175Limitations
176-----------
177
178There are certain situations where the tool may erroneously perform
179transformations that remove information and change semantics. Users of the tool
180should be aware of the behavior and limitations of the check outlined by
181the cases below.
182
183Comments inside loop headers
184^^^^^^^^^^^^^^^^^^^^^^^^^^^^
185
186Comments inside the original loop header are ignored and deleted when
187transformed.
188
189.. code-block:: c++
190
191  for (int i = 0; i < N; /* This will be deleted */ ++i) { }
192
193Range-based loops evaluate end() only once
194^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
195
196The C++11 range-based for loop calls ``.end()`` only once during the
197initialization of the loop. If in the original loop ``.end()`` is called after
198each iteration the semantics of the transformed loop may differ.
199
200.. code-block:: c++
201
202  // The following is semantically equivalent to the C++11 range-based for loop,
203  // therefore the semantics of the header will not change.
204  for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }
205
206  // Instead of calling .end() after each iteration, this loop will be
207  // transformed to call .end() only once during the initialization of the loop,
208  // which may affect semantics.
209  for (iterator it = container.begin(); it != container.end(); ++it) { }
210
211.. _IncorrectRiskyTransformation:
212
213As explained above, calling member functions of the container in the body
214of the loop is considered `risky`. If the called member function modifies the
215container the semantics of the converted loop will differ due to ``.end()``
216being called only once.
217
218.. code-block:: c++
219
220  bool flag = false;
221  for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {
222    // Add a copy of the first element to the end of the vector.
223    if (!flag) {
224      // This line makes this transformation 'risky'.
225      vec.push_back(*it);
226      flag = true;
227    }
228    cout << *it;
229  }
230
231The original code above prints out the contents of the container including the
232newly added element while the converted loop, shown below, will only print the
233original contents and not the newly added element.
234
235.. code-block:: c++
236
237  bool flag = false;
238  for (auto & elem : vec) {
239    // Add a copy of the first element to the end of the vector.
240    if (!flag) {
241      // This line makes this transformation 'risky'
242      vec.push_back(elem);
243      flag = true;
244    }
245    cout << elem;
246  }
247
248Semantics will also be affected if ``.end()`` has side effects. For example, in
249the case where calls to ``.end()`` are logged the semantics will change in the
250transformed loop if ``.end()`` was originally called after each iteration.
251
252.. code-block:: c++
253
254  iterator end() {
255    num_of_end_calls++;
256    return container.end();
257  }
258
259Overloaded operator->() with side effects
260^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
261
262Similarly, if ``operator->()`` was overloaded to have side effects, such as
263logging, the semantics will change. If the iterator's ``operator->()`` was used
264in the original loop it will be replaced with ``<container element>.<member>``
265instead due to the implicit dereference as part of the range-based for loop.
266Therefore any side effect of the overloaded ``operator->()`` will no longer be
267performed.
268
269.. code-block:: c++
270
271  for (iterator it = c.begin(); it != c.end(); ++it) {
272    it->func(); // Using operator->()
273  }
274  // Will be transformed to:
275  for (auto & elem : c) {
276    elem.func(); // No longer using operator->()
277  }
278
279Pointers and references to containers
280^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
281
282While most of the check's risk analysis is dedicated to determining whether
283the iterator or container was modified within the loop, it is possible to
284circumvent the analysis by accessing and modifying the container through a
285pointer or reference.
286
287If the container were directly used instead of using the pointer or reference
288the following transformation would have only been applied at the `risky`
289level since calling a member function of the container is considered `risky`.
290The check cannot identify expressions associated with the container that are
291different than the one used in the loop header, therefore the transformation
292below ends up being performed at the `safe` level.
293
294.. code-block:: c++
295
296  vector<int> vec;
297
298  vector<int> *ptr = &vec;
299  vector<int> &ref = vec;
300
301  for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {
302    if (!flag) {
303      // Accessing and modifying the container is considered risky, but the risk
304      // level is not raised here.
305      ptr->push_back(*it);
306      ref.push_back(*it);
307      flag = true;
308    }
309  }
310
311OpenMP
312^^^^^^
313
314As range-based for loops are only available since OpenMP 5, this check should
315not be used on code with a compatibility requirement of OpenMP prior to
316version 5. It is **intentional** that this check does not make any attempts to
317exclude incorrect diagnostics on OpenMP for loops prior to OpenMP 5.
318
319To prevent this check to be applied (and to break) OpenMP for loops but still be
320applied to non-OpenMP for loops the usage of ``NOLINT`` (see
321:ref:`clang-tidy-nolint`) on the specific for loops is recommended.
322