[clang-tools-extra] [clang-tidy] Add concurrency-lambda-coroutine-capture check (PR #182916)

Willem Kaufmann via cfe-commits cfe-commits at lists.llvm.org
Mon Feb 23 11:00:58 PST 2026


https://github.com/WillemKauf created https://github.com/llvm/llvm-project/pull/182916

Adds a new check which finds lambda coroutines that capture variables without using the C++23 "deducing this" explicit object parameter, which can cause use-after-free when the lambda is destroyed while the coroutine is still suspended ([the "lambda coroutine fiasco"](https://github.com/scylladb/seastar/blob/master/doc/lambda-coroutine-fiasco.md)).

There is an existing clang-tidy check based on the [CP.53](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#rcoro-reference-parameters),  `cppcoreguidelines-avoid-capturing-lambda-coroutines`.  This check warns on all lambda coroutines with captures, including C++23 lambdas that use explicit object parameters (deducing this) which are safefrom the lifetime issue. Since we now have a way of avoiding this problem in C++23 and up (along with the added `clang-tidy` check), we can suppress the warning when the lambda uses `this auto`, and cross-reference the two related checks in their documentation.

>From 3bbe094077a65b3e78c44a38ce473b1dcc5b0510 Mon Sep 17 00:00:00 2001
From: Willem Kaufmann <willem.kaufmann at gmail.com>
Date: Mon, 23 Feb 2026 13:53:00 -0500
Subject: [PATCH 1/2] [clang-tidy] Add `concurrency-lambda-coroutine-capture
 check`

Adds a new check which finds lambda coroutines that capture variables
without using the C++23 "deducing this" explicit object parameter,
which can cause use-after-free when the lambda is destroyed while
the coroutine is still suspended (the "lambda coroutine fiasco") [1].

The check provides fix-it hints to insert `this auto` as the first
parameter, which moves captures into the coroutine frame and
decouples their lifetime from the lambda object.

[1]: https://github.com/scylladb/seastar/blob/master/doc/lambda-coroutine-fiasco.md
---
 .../clang-tidy/concurrency/CMakeLists.txt     |   1 +
 .../concurrency/ConcurrencyTidyModule.cpp     |   4 +
 .../LambdaCoroutineCaptureCheck.cpp           |  72 ++++++++++
 .../concurrency/LambdaCoroutineCaptureCheck.h |  41 ++++++
 clang-tools-extra/docs/ReleaseNotes.rst       |   8 ++
 .../concurrency/lambda-coroutine-capture.rst  |  58 ++++++++
 .../docs/clang-tidy/checks/list.rst           |   1 +
 .../concurrency/lambda-coroutine-capture.cpp  | 132 ++++++++++++++++++
 8 files changed, 317 insertions(+)
 create mode 100644 clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.cpp
 create mode 100644 clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.h
 create mode 100644 clang-tools-extra/docs/clang-tidy/checks/concurrency/lambda-coroutine-capture.rst
 create mode 100644 clang-tools-extra/test/clang-tidy/checkers/concurrency/lambda-coroutine-capture.cpp

diff --git a/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt b/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
index 91c6cedabcc80..ffd871bb1059c 100644
--- a/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/concurrency/CMakeLists.txt
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
 
 add_clang_library(clangTidyConcurrencyModule STATIC
   ConcurrencyTidyModule.cpp
+  LambdaCoroutineCaptureCheck.cpp
   MtUnsafeCheck.cpp
   ThreadCanceltypeAsynchronousCheck.cpp
 
diff --git a/clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp b/clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
index 0d02a3d9b6e2b..9626a438a07b0 100644
--- a/clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/concurrency/ConcurrencyTidyModule.cpp
@@ -8,6 +8,8 @@
 
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
+#include "../ClangTidyModuleRegistry.h"
+#include "LambdaCoroutineCaptureCheck.h"
 #include "MtUnsafeCheck.h"
 #include "ThreadCanceltypeAsynchronousCheck.h"
 
@@ -18,6 +20,8 @@ namespace {
 class ConcurrencyModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<LambdaCoroutineCaptureCheck>(
+        "concurrency-lambda-coroutine-capture");
     CheckFactories.registerCheck<concurrency::MtUnsafeCheck>(
         "concurrency-mt-unsafe");
     CheckFactories.registerCheck<ThreadCanceltypeAsynchronousCheck>(
diff --git a/clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.cpp b/clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.cpp
new file mode 100644
index 0000000000000..dedb41066c618
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "LambdaCoroutineCaptureCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::concurrency {
+
+namespace {
+
+AST_MATCHER(LambdaExpr, hasCoroutineBody) {
+  const Stmt *Body = Node.getBody();
+  return Body != nullptr && isa<CoroutineBodyStmt>(Body);
+}
+
+AST_MATCHER(LambdaExpr, capturesWithoutDeducingThis) {
+  if (Node.capture_size() == 0U)
+    return false;
+  const auto *Call = Node.getCallOperator();
+  return !Call->isExplicitObjectMemberFunction();
+}
+
+} // namespace
+
+void LambdaCoroutineCaptureCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(
+      lambdaExpr(hasCoroutineBody(), capturesWithoutDeducingThis())
+          .bind("lambda"),
+      this);
+}
+
+void LambdaCoroutineCaptureCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedLambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda");
+  auto *Call = MatchedLambda->getCallOperator();
+  bool HasExplicitParams = MatchedLambda->hasExplicitParameters();
+
+  auto DiagBuilder =
+      diag(MatchedLambda->getExprLoc(),
+           "lambda coroutine with captures may cause use-after-free; use "
+           "'this auto' as the first parameter to move captures into the "
+           "coroutine frame");
+
+  if (HasExplicitParams) {
+    bool HasParams = !Call->param_empty();
+    if (HasParams) {
+      const ParmVarDecl *FirstParam = Call->parameters().front();
+      DiagBuilder << FixItHint::CreateInsertion(FirstParam->getBeginLoc(),
+                                                "this auto, ");
+    } else {
+      // Empty parens `()` — insert `this auto` before the closing paren.
+      DiagBuilder << FixItHint::CreateInsertion(
+          Call->getFunctionTypeLoc().getRParenLoc(), "this auto");
+    }
+  } else {
+    // No explicit parameter list — insert `(this auto)` after the
+    // capture list closing `]`.
+    auto IntroRange = MatchedLambda->getIntroducerRange();
+    DiagBuilder << FixItHint::CreateInsertion(
+        IntroRange.getEnd().getLocWithOffset(1), "(this auto)");
+  }
+}
+
+} // namespace clang::tidy::concurrency
diff --git a/clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.h b/clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.h
new file mode 100644
index 0000000000000..aaf51b5b4fefc
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/concurrency/LambdaCoroutineCaptureCheck.h
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CONCURRENCY_LAMBDACOROUTINECAPTURECHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CONCURRENCY_LAMBDACOROUTINECAPTURECHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::concurrency {
+
+/// Finds lambda coroutines that capture variables without using the C++23
+/// "deducing this" syntax, which can lead to use-after-free bugs.
+///
+/// When a lambda coroutine is passed to APIs that store the lambda temporarily
+/// (e.g. ``seastar::future::then()``), the lambda object may be destroyed while
+/// the coroutine is still suspended. Captures reference the lambda's storage,
+/// so accessing them after the lambda is destroyed causes undefined behavior.
+/// The C++23 explicit object parameter (``this auto``) moves captures into the
+/// coroutine frame, decoupling their lifetime from the lambda object.
+///
+/// For the user-facing documentation see:
+/// https://clang.llvm.org/extra/clang-tidy/checks/concurrency/lambda-coroutine-capture.html
+class LambdaCoroutineCaptureCheck : public ClangTidyCheck {
+public:
+  LambdaCoroutineCaptureCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+  bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+    return LangOpts.CPlusPlus23;
+  }
+};
+
+} // namespace clang::tidy::concurrency
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CONCURRENCY_LAMBDACOROUTINECAPTURECHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 64c8fbbe2f07a..b9d6dd8456b19 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -117,6 +117,14 @@ New checks
   Finds ``llvm::TypeSwitch::Case`` calls with redundant explicit template
   arguments that can be inferred from the lambda parameter type.
 
+- New :doc:`concurrency-lambda-coroutine-capture
+  <clang-tidy/checks/concurrency/lambda-coroutine-capture>` check.
+
+  Finds lambda coroutines that capture variables without using the C++23
+  "deducing this" (explicit object parameter) syntax, which can lead to
+  use-after-free bugs when the lambda object is destroyed while the coroutine
+  is still suspended.
+
 - New :doc:`llvm-use-vector-utils
   <clang-tidy/checks/llvm/use-vector-utils>` check.
 
diff --git a/clang-tools-extra/docs/clang-tidy/checks/concurrency/lambda-coroutine-capture.rst b/clang-tools-extra/docs/clang-tidy/checks/concurrency/lambda-coroutine-capture.rst
new file mode 100644
index 0000000000000..836c02e9bb974
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/concurrency/lambda-coroutine-capture.rst
@@ -0,0 +1,58 @@
+.. title:: clang-tidy - concurrency-lambda-coroutine-capture
+
+concurrency-lambda-coroutine-capture
+====================================
+
+Finds lambda coroutines that capture variables without using the C++23
+"deducing this" (explicit object parameter) syntax, which can lead to
+use-after-free bugs.
+
+When a lambda coroutine is passed to APIs that store the lambda temporarily
+(e.g. continuation-passing style like ``future::then()``), the lambda object
+may be destroyed while the coroutine is still suspended. Since captures
+reference the lambda's storage, accessing them after destruction causes
+undefined behavior. This is known as the
+`lambda coroutine fiasco <https://github.com/scylladb/seastar/blob/master/doc/lambda-coroutine-fiasco.md>`_.
+
+The C++23 explicit object parameter (``this auto``) solves this by moving
+captures directly into the coroutine frame, decoupling their lifetime from
+the lambda object.
+
+Example
+-------
+
+Before:
+
+.. code-block:: c++
+
+  auto handler = [&data](int x) -> task {
+    co_await process(data, x);  // 'data' may be dangling
+  };
+
+After:
+
+.. code-block:: c++
+
+  auto handler = [&data](this auto, int x) -> task {
+    co_await process(data, x);  // captures live in the coroutine frame
+  };
+
+The check handles three lambda forms:
+
+- Lambdas with explicit parameters: inserts ``this auto, `` before the first
+  parameter.
+- Lambdas with empty parentheses: inserts ``this auto`` inside the
+  parentheses.
+- Lambdas without a parameter list: inserts ``(this auto)`` after the capture
+  list.
+
+Options
+-------
+
+This check has no configurable options.
+
+This check requires C++23 or later (``-std=c++23``). For pre-C++23 code, see
+:doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines
+<../cppcoreguidelines/avoid-capturing-lambda-coroutines>` which flags the same
+problem but recommends avoiding captures entirely rather than using deducing
+this.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c475870ed7b31..815093f2f55fa 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -183,6 +183,7 @@ Clang-Tidy Checks
    :doc:`bugprone-use-after-move <bugprone/use-after-move>`,
    :doc:`bugprone-virtual-near-miss <bugprone/virtual-near-miss>`, "Yes"
    :doc:`cert-err33-c <cert/err33-c>`,
+   :doc:`concurrency-lambda-coroutine-capture <concurrency/lambda-coroutine-capture>`, "Yes"
    :doc:`concurrency-mt-unsafe <concurrency/mt-unsafe>`,
    :doc:`concurrency-thread-canceltype-asynchronous <concurrency/thread-canceltype-asynchronous>`,
    :doc:`cppcoreguidelines-avoid-capturing-lambda-coroutines <cppcoreguidelines/avoid-capturing-lambda-coroutines>`,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/concurrency/lambda-coroutine-capture.cpp b/clang-tools-extra/test/clang-tidy/checkers/concurrency/lambda-coroutine-capture.cpp
new file mode 100644
index 0000000000000..0d2e1b6c4ec28
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/concurrency/lambda-coroutine-capture.cpp
@@ -0,0 +1,132 @@
+// RUN: %check_clang_tidy -std=c++23 %s concurrency-lambda-coroutine-capture %t
+
+// Minimal coroutine support types.
+namespace std {
+
+template <typename R, typename...> struct coroutine_traits {
+  using promise_type = typename R::promise_type;
+};
+
+template <typename Promise = void> struct coroutine_handle;
+
+template <> struct coroutine_handle<void> {
+  static coroutine_handle from_address(void *);
+  coroutine_handle() = default;
+  coroutine_handle(decltype(nullptr)) {}
+};
+
+template <typename Promise> struct coroutine_handle : coroutine_handle<> {
+  static coroutine_handle from_address(void *);
+};
+
+struct suspend_never {
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+} // namespace std
+
+struct task {
+  struct promise_type {
+    task get_return_object() { return {}; }
+    std::suspend_never initial_suspend() { return {}; }
+    std::suspend_never final_suspend() noexcept { return {}; }
+    void return_void() {}
+    void unhandled_exception() {}
+  };
+};
+
+template <typename F> void invoke(F &&f) {}
+
+// --- Cases that SHOULD trigger the warning ---
+
+void test_capture_no_parens() {
+  int x = 42;
+  invoke([&x] -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free; use 'this auto' as the first parameter to move captures into the coroutine frame [concurrency-lambda-coroutine-capture]
+    // CHECK-FIXES: {{^}}  invoke([&x](this auto) -> task {{{$}}
+    co_return;
+  });
+}
+
+void test_capture_empty_parens() {
+  int x = 42;
+  invoke([&x]() -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free
+    // CHECK-FIXES: {{^}}  invoke([&x](this auto) -> task {{{$}}
+    co_return;
+  });
+}
+
+void test_capture_with_params() {
+  int x = 42;
+  invoke([&x](int a) -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free
+    // CHECK-FIXES: {{^}}  invoke([&x](this auto, int a) -> task {{{$}}
+    co_return;
+  });
+}
+
+void test_capture_with_multiple_params() {
+  int x = 42;
+  invoke([&x](int a, int b) -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free
+    // CHECK-FIXES: {{^}}  invoke([&x](this auto, int a, int b) -> task {{{$}}
+    co_return;
+  });
+}
+
+void test_capture_by_value() {
+  int x = 42;
+  invoke([x]() -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free
+    // CHECK-FIXES: {{^}}  invoke([x](this auto) -> task {{{$}}
+    co_return;
+  });
+}
+
+void test_default_capture_ref() {
+  int x = 42;
+  invoke([&]() -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free
+    // CHECK-FIXES: {{^}}  invoke([&](this auto) -> task {{{$}}
+    (void)x;
+    co_return;
+  });
+}
+
+void test_default_capture_copy() {
+  int x = 42;
+  invoke([=]() -> task {
+    // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: lambda coroutine with captures may cause use-after-free
+    // CHECK-FIXES: {{^}}  invoke([=](this auto) -> task {{{$}}
+    (void)x;
+    co_return;
+  });
+}
+
+// --- Cases that should NOT trigger the warning ---
+
+void test_no_captures_no_coroutine() {
+  invoke([]() { return; });
+}
+
+void test_no_captures_coroutine() {
+  invoke([]() -> task { co_return; });
+}
+
+void test_deducing_this_coroutine() {
+  int x = 42;
+  invoke([&x](this auto) -> task { co_return; });
+}
+
+void test_deducing_this_with_params() {
+  int x = 42;
+  invoke([&x](this auto, int a) -> task { co_return; });
+}
+
+void test_captures_not_coroutine() {
+  int x = 42;
+  invoke([&x]() { (void)x; });
+}

>From 638b66eeb3020d888e944838db5e74b8c2720698 Mon Sep 17 00:00:00 2001
From: Willem Kaufmann <willem.kaufmann at gmail.com>
Date: Mon, 23 Feb 2026 13:53:22 -0500
Subject: [PATCH 2/2] [clang-tidy] Suppress `avoid-capturing-lambda-coroutines`
 for `deducing this`

The `cppcoreguidelines-avoid-capturing-lambda-coroutines` check warns
on all lambda coroutines with captures, including C++23 lambdas
that use explicit object parameters (deducing this) which are safe
from the lifetime issue. Suppress the warning when the lambda uses
`this auto`, and cross-reference the two related checks in their
documentation.
---
 .../AvoidCapturingLambdaCoroutinesCheck.cpp              | 9 ++++++++-
 .../avoid-capturing-lambda-coroutines.rst                | 7 +++++++
 .../avoid-capturing-lambda-coroutines.cpp                | 6 ++++++
 3 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp
index 618554663ab91..a6c969c6feb9e 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/AvoidCapturingLambdaCoroutinesCheck.cpp
@@ -21,12 +21,19 @@ AST_MATCHER(LambdaExpr, hasCoroutineBody) {
 }
 
 AST_MATCHER(LambdaExpr, hasCaptures) { return Node.capture_size() != 0U; }
+
+AST_MATCHER(LambdaExpr, hasDeducingThis) {
+  return Node.getCallOperator()->isExplicitObjectMemberFunction();
+}
 } // namespace
 
 void AvoidCapturingLambdaCoroutinesCheck::registerMatchers(
     MatchFinder *Finder) {
   Finder->addMatcher(
-      lambdaExpr(hasCaptures(), hasCoroutineBody()).bind("lambda"), this);
+      lambdaExpr(hasCaptures(), hasCoroutineBody(),
+                 unless(hasDeducingThis()))
+          .bind("lambda"),
+      this);
 }
 
 bool AvoidCapturingLambdaCoroutinesCheck::isLanguageVersionSupported(
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst
index 58bfc35c557dc..c617a7715f6d1 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/avoid-capturing-lambda-coroutines.rst
@@ -52,3 +52,10 @@ captures or ensuring the lambda closure object has a guaranteed lifetime.
 
 Following these guidelines can help ensure the safe and reliable use of
 coroutine lambdas in C++ code.
+
+In C++23, the "deducing this" (explicit object parameter) syntax provides a
+solution to this problem by moving captures into the coroutine frame. Lambda
+coroutines using ``this auto`` as their first parameter are not flagged by
+this check. See :doc:`concurrency-lambda-coroutine-capture
+<../concurrency/lambda-coroutine-capture>` for a check that can automatically
+apply this fix.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp
index 6670f4a5420be..3e83fbefba7cf 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/avoid-capturing-lambda-coroutines.cpp
@@ -33,4 +33,10 @@ void Safe() {
     [=] () -> task { co_return; };
 
     [&v]{++v;}();
+
+#if __cplusplus >= 202302L
+    // Lambda coroutines using C++23 deducing this are safe.
+    [&v] (this auto) -> task { co_return; };
+    [v] (this auto, int x) -> task { co_return; };
+#endif
 }



More information about the cfe-commits mailing list