55 lines · plain
1.. title:: clang-tidy - cppcoreguidelines-avoid-capturing-lambda-coroutines2 3cppcoreguidelines-avoid-capturing-lambda-coroutines4===================================================5 6Flags C++20 coroutine lambdas with non-empty capture lists that may cause7use-after-free errors and suggests avoiding captures or ensuring the lambda8closure object has a guaranteed lifetime.9 10This check implements `CP.5111<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rcoro-capture>`_12from the C++ Core Guidelines.13 14Using coroutine lambdas with non-empty capture lists can be risky, as capturing15variables can lead to accessing freed memory after the first suspension point.16This issue can occur even with refcounted smart pointers and copyable types.17When a lambda expression creates a coroutine, it results in a closure object18with storage, which is often on the stack and will eventually go out of scope.19When the closure object goes out of scope, its captures also go out of scope.20While normal lambdas finish executing before this happens, coroutine lambdas21may resume from suspension after the closure object has been destructed,22resulting in use-after-free memory access for all captures.23 24Consider the following example:25 26.. code-block:: c++27 28 int value = get_value();29 std::shared_ptr<Foo> sharedFoo = get_foo();30 {31 const auto lambda = [value, sharedFoo]() -> std::future<void>32 {33 co_await something();34 // "sharedFoo" and "value" have already been destroyed35 // the "shared" pointer didn't accomplish anything36 };37 lambda();38 } // the lambda closure object has now gone out of scope39 40In this example, the lambda object is defined with two captures: value and41``sharedFoo``. When ``lambda()`` is called, the lambda object is created on the42stack, and the captures are copied into the closure object. When the coroutine43is suspended, the lambda object goes out of scope, and the closure object is44destroyed. When the coroutine is resumed, the captured variables may have been45destroyed, resulting in use-after-free bugs.46 47In conclusion, the use of coroutine lambdas with non-empty capture lists can48lead to use-after-free errors when resuming the coroutine after the closure49object has been destroyed. This check helps prevent such errors by flagging50C++20 coroutine lambdas with non-empty capture lists and suggesting avoiding51captures or ensuring the lambda closure object has a guaranteed lifetime.52 53Following these guidelines can help ensure the safe and reliable use of54coroutine lambdas in C++ code.55