327 lines · plain
1.. title:: clang-tidy - modernize-loop-convert2 3modernize-loop-convert4======================5 6This check converts ``for(...; ...; ...)`` loops to use the new range-based7loops 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 option16--------------------17 18risky19^^^^^20 21In loops where the container expression is more complex than just a22reference to a declared expression (a variable, function, enum, etc.),23and some part of it appears elsewhere in the loop, we lower our confidence24in the transformation due to the increased risk of changing semantics.25Transformations for these loops are marked as `risky`, and thus will only26be 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 risky35 36 for (int i = 0; i < obj.getVector().size(); ++i)37 obj.foo(10); // using 'obj' is considered risky38 39See40:ref:`Range-based loops evaluate end() only once<IncorrectRiskyTransformation>`41for an example of an incorrect transformation when the minimum required confidence42level is set to `risky`.43 44reasonable (Default)45^^^^^^^^^^^^^^^^^^^^46 47If a loop calls ``.end()`` or ``.size()`` after each iteration, the48transformation for that loop is marked as `reasonable`, and thus will49be 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 reasonable55 for (int i = 0; i < container.size(); ++i)56 cout << container[i];57 58safe59^^^^60 61Any other loops that do not match the above criteria to be marked as62`risky` or `reasonable` are marked `safe`, and thus will be converted63if 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 72Example73-------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 conversion87 for (int i = 0; i < N; ++i)88 cout << arr[i];89 90 // reasonable conversion91 for (vector<int>::iterator it = v.begin(); it != v.end(); ++it)92 cout << *it;93 94 // reasonable conversion95 for (vector<int>::iterator it = begin(v); it != end(v); ++it)96 cout << *it;97 98 // reasonable conversion99 for (vector<int>::iterator it = std::begin(v); it != std::end(v); ++it)100 cout << *it;101 102 // reasonable conversion103 for (int i = 0; i < v.size(); ++i)104 cout << v[i];105 106 // reasonable conversion107 for (int i = 0; i < size(v); ++i)108 cout << v[i];109 110After applying the check with minimum confidence level set to111`reasonable` (default):112 113.. code-block:: c++114 115 const int N = 5;116 int arr[] = {1,2,3,4,5};117 vector<int> v;118 v.push_back(1);119 v.push_back(2);120 v.push_back(3);121 122 // safe conversion123 for (auto & elem : arr)124 cout << elem;125 126 // reasonable conversion127 for (auto & elem : v)128 cout << elem;129 130 // reasonable conversion131 for (auto & elem : v)132 cout << elem;133 134Reverse Iterator Support135------------------------136 137The converter is also capable of transforming iterator loops which use138``rbegin`` and ``rend`` for looping backwards over a container. Out of the box139this will automatically happen in C++20 mode using the ``ranges`` library,140however the check can be configured to work without C++20 by specifying a141function to reverse a range and optionally the header file where that function142lives.143 144Options145-------146 147.. option:: UseCxx20ReverseRanges148 149 When set to true convert loops when in C++20 or later mode using150 ``std::ranges::reverse_view``.151 Default value is `true`.152 153.. option:: MakeReverseRangeFunction154 155 Specify the function used to reverse an iterator pair, the function should156 accept a class with ``rbegin`` and ``rend`` methods and return a157 class with ``begin`` and ``end`` methods that call the ``rbegin`` and158 ``rend`` methods respectively. Common examples are ``ranges::reverse_view``159 and ``llvm::reverse``.160 Default value is an empty string.161 162.. option:: MakeReverseRangeHeader163 164 Specifies the header file where :option:`MakeReverseRangeFunction` is165 declared. For the previous examples this option would be set to166 ``range/v3/view/reverse.hpp`` and ``llvm/ADT/STLExtras.h`` respectively.167 If this is an empty string and :option:`MakeReverseRangeFunction` is set,168 the check will proceed on the assumption that the function is already169 available in the translation unit.170 This can be wrapped in angle brackets to signify to add the include as a171 system include.172 Default value is an empty string.173 174.. option:: IncludeStyle175 176 A string specifying which include-style is used, `llvm` or `google`. Default177 is `llvm`.178 179 180Limitations181-----------182 183There are certain situations where the tool may erroneously perform184transformations that remove information and change semantics. Users of the tool185should be aware of the behavior and limitations of the check outlined by186the cases below.187 188Comments inside loop headers189^^^^^^^^^^^^^^^^^^^^^^^^^^^^190 191Comments inside the original loop header are ignored and deleted when192transformed.193 194.. code-block:: c++195 196 for (int i = 0; i < N; /* This will be deleted */ ++i) { }197 198Range-based loops evaluate end() only once199^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^200 201The C++11 range-based for loop calls ``.end()`` only once during the202initialization of the loop. If in the original loop ``.end()`` is called after203each iteration the semantics of the transformed loop may differ.204 205.. code-block:: c++206 207 // The following is semantically equivalent to the C++11 range-based for loop,208 // therefore the semantics of the header will not change.209 for (iterator it = container.begin(), e = container.end(); it != e; ++it) { }210 211 // Instead of calling .end() after each iteration, this loop will be212 // transformed to call .end() only once during the initialization of the loop,213 // which may affect semantics.214 for (iterator it = container.begin(); it != container.end(); ++it) { }215 216.. _IncorrectRiskyTransformation:217 218As explained above, calling member functions of the container in the body219of the loop is considered `risky`. If the called member function modifies the220container the semantics of the converted loop will differ due to ``.end()``221being called only once.222 223.. code-block:: c++224 225 bool flag = false;226 for (vector<T>::iterator it = vec.begin(); it != vec.end(); ++it) {227 // Add a copy of the first element to the end of the vector.228 if (!flag) {229 // This line makes this transformation 'risky'.230 vec.push_back(*it);231 flag = true;232 }233 cout << *it;234 }235 236The original code above prints out the contents of the container including the237newly added element while the converted loop, shown below, will only print the238original contents and not the newly added element.239 240.. code-block:: c++241 242 bool flag = false;243 for (auto & elem : vec) {244 // Add a copy of the first element to the end of the vector.245 if (!flag) {246 // This line makes this transformation 'risky'247 vec.push_back(elem);248 flag = true;249 }250 cout << elem;251 }252 253Semantics will also be affected if ``.end()`` has side effects. For example, in254the case where calls to ``.end()`` are logged the semantics will change in the255transformed loop if ``.end()`` was originally called after each iteration.256 257.. code-block:: c++258 259 iterator end() {260 num_of_end_calls++;261 return container.end();262 }263 264Overloaded operator->() with side effects265^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^266 267Similarly, if ``operator->()`` was overloaded to have side effects, such as268logging, the semantics will change. If the iterator's ``operator->()`` was used269in the original loop it will be replaced with ``<container element>.<member>``270instead due to the implicit dereference as part of the range-based for loop.271Therefore any side effect of the overloaded ``operator->()`` will no longer be272performed.273 274.. code-block:: c++275 276 for (iterator it = c.begin(); it != c.end(); ++it) {277 it->func(); // Using operator->()278 }279 // Will be transformed to:280 for (auto & elem : c) {281 elem.func(); // No longer using operator->()282 }283 284Pointers and references to containers285^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^286 287While most of the check's risk analysis is dedicated to determining whether288the iterator or container was modified within the loop, it is possible to289circumvent the analysis by accessing and modifying the container through a290pointer or reference.291 292If the container were directly used instead of using the pointer or reference293the following transformation would have only been applied at the `risky`294level since calling a member function of the container is considered `risky`.295The check cannot identify expressions associated with the container that are296different than the one used in the loop header, therefore the transformation297below ends up being performed at the `safe` level.298 299.. code-block:: c++300 301 vector<int> vec;302 303 vector<int> *ptr = &vec;304 vector<int> &ref = vec;305 306 for (vector<int>::iterator it = vec.begin(), e = vec.end(); it != e; ++it) {307 if (!flag) {308 // Accessing and modifying the container is considered risky, but the risk309 // level is not raised here.310 ptr->push_back(*it);311 ref.push_back(*it);312 flag = true;313 }314 }315 316OpenMP317^^^^^^318 319As range-based for loops are only available since OpenMP 5, this check should320not be used on code with a compatibility requirement of OpenMP prior to321version 5. It is **intentional** that this check does not make any attempts to322exclude incorrect diagnostics on OpenMP for loops prior to OpenMP 5.323 324To prevent this check to be applied (and to break) OpenMP for loops325but still be applied to non-OpenMP for loops the usage of ``NOLINT``326(see :ref:`clang-tidy-nolint`) on the specific for loops is recommended.327