brintos

brintos / llvm-project-archived public Read only

0
0
Text · 9.8 KiB · e11ced7 Raw
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