From 70813b336a137fceae0450c0a41fd4037a397450 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Mon, 27 Jul 2026 12:33:49 +0100 Subject: [PATCH 1/2] Suppress clang-tidy coroutine-capture warnings in pathfinding tests The Path_test and TestHelpers coroutine lambdas capture locals by reference, but the caller blocks on Gate::waitFor() until the coroutine signals completion, so the captures cannot dangle. Add NOLINTNEXTLINE for cppcoreguidelines-avoid-capturing-lambda-coroutines with a comment explaining the lifetime guarantee. --- src/test/app/Path_test.cpp | 15 +++++++++++++++ src/test/jtx/impl/TestHelpers.cpp | 3 +++ 2 files changed, 18 insertions(+) diff --git a/src/test/app/Path_test.cpp b/src/test/app/Path_test.cpp index 9783879ede..21f3c36a5d 100644 --- a/src/test/app/Path_test.cpp +++ b/src/test/app/Path_test.cpp @@ -187,6 +187,9 @@ public: json::Value result; Gate g; + // Safe capture: the test blocks on g.waitFor() until the coroutine + // completes, so the captured locals outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) app.getJobQueue().postCoroTask(JtClient, "RPC-Client", [&](auto) -> CoroTask { context.params = std::move(params); RPC::doCommand(context, result); @@ -280,6 +283,9 @@ public: json::Value result; Gate g; // Test RPC::Tuning::max_src_cur source currencies. + // Safe capture: the test blocks on g.waitFor() until the coroutine + // completes, so the captured locals outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) app.getJobQueue().postCoroTask(JtClient, "RPC-Client", [&](auto) -> CoroTask { context.params = rpf(Account("alice"), Account("bob"), RPC::Tuning::kMaxSrcCur); RPC::doCommand(context, result); @@ -290,6 +296,9 @@ public: BEAST_EXPECT(!result.isMember(jss::error)); // Test more than RPC::Tuning::max_src_cur source currencies. + // Safe capture: the test blocks on g.waitFor() until the coroutine + // completes, so the captured locals outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) app.getJobQueue().postCoroTask(JtClient, "RPC-Client", [&](auto) -> CoroTask { context.params = rpf(Account("alice"), Account("bob"), RPC::Tuning::kMaxSrcCur + 1); RPC::doCommand(context, result); @@ -302,6 +311,9 @@ public: // Test RPC::Tuning::max_auto_src_cur source currencies. for (auto i = 0; i < (RPC::Tuning::kMaxAutoSrcCur - 1); ++i) env.trust(Account("alice")[std::to_string(i + 100)](100), "bob"); + // Safe capture: the test blocks on g.waitFor() until the coroutine + // completes, so the captured locals outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) app.getJobQueue().postCoroTask(JtClient, "RPC-Client", [&](auto) -> CoroTask { context.params = rpf(Account("alice"), Account("bob"), 0); RPC::doCommand(context, result); @@ -313,6 +325,9 @@ public: // Test more than RPC::Tuning::max_auto_src_cur source currencies. env.trust(Account("alice")["AUD"](100), "bob"); + // Safe capture: the test blocks on g.waitFor() until the coroutine + // completes, so the captured locals outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) app.getJobQueue().postCoroTask(JtClient, "RPC-Client", [&](auto) -> CoroTask { context.params = rpf(Account("alice"), Account("bob"), 0); RPC::doCommand(context, result); diff --git a/src/test/jtx/impl/TestHelpers.cpp b/src/test/jtx/impl/TestHelpers.cpp index 8173aeae87..34b6bd5aa9 100644 --- a/src/test/jtx/impl/TestHelpers.cpp +++ b/src/test/jtx/impl/TestHelpers.cpp @@ -250,6 +250,9 @@ findPathsRequest( json::Value result; Gate g; + // Safe capture: the caller blocks on g.waitFor() until the coroutine + // completes, so the captured locals outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) app.getJobQueue().postCoroTask(JtClient, "RPC-Client", [&](auto) -> CoroTask { context.params = std::move(params); RPC::doCommand(context, result); From a6fea1227f84151420688999bf6c4e3f34703620 Mon Sep 17 00:00:00 2001 From: Pratik Mankawde <3397372+pratikmankawde@users.noreply.github.com> Date: Mon, 27 Jul 2026 12:36:10 +0100 Subject: [PATCH 2/2] Suppress clang-tidy coroutine-capture warnings in core tests The JobQueue_test and Coroutine_test coroutine lambdas capture pointers to test-scope locals, but each test drives the coroutine to completion (or, for the stopped-queue case, guarantees it never starts) before the locals go out of scope. Add NOLINTNEXTLINE for cppcoreguidelines-avoid-capturing-lambda-coroutines with comments explaining the lifetime guarantee, and drop the unused include flagged by misc-include-cleaner. --- src/test/core/Coroutine_test.cpp | 10 ++++++++++ src/test/core/JobQueue_test.cpp | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/test/core/Coroutine_test.cpp b/src/test/core/Coroutine_test.cpp index e6180f2161..a0065ecf45 100644 --- a/src/test/core/Coroutine_test.cpp +++ b/src/test/core/Coroutine_test.cpp @@ -70,6 +70,9 @@ public: Gate g1, g2; std::shared_ptr c; + // Safe capture: the test blocks on the Gates until the coroutine + // completes, so the captured pointers outlive the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) env.app().getJobQueue().postCoroTask( JtClient, "CoroTest", [cp = &c, g1p = &g1, g2p = &g2](auto runner) -> CoroTask { *cp = runner; @@ -99,6 +102,9 @@ public: })); Gate g; + // Safe capture: the test blocks on the Gate until the coroutine + // completes, so the captured pointer outlives the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) env.app().getJobQueue().postCoroTask( JtClient, "CoroTest", [gp = &g](auto runner) -> CoroTask { // Schedule a resume before suspending. The posted job @@ -145,6 +151,10 @@ public: jq.postCoroTask( JtClient, "CoroTest", + // Safe capture: the test drives every coroutine to completion + // via the Gate/post()/join() sequences below, so the captured + // pointers outlive the coroutines. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) [this, ap = &a, gp = &g, lvp = &lv, id = i](auto runner) -> CoroTask { (*ap)[id] = runner; gp->signal(); diff --git a/src/test/core/JobQueue_test.cpp b/src/test/core/JobQueue_test.cpp index 1ab52e59f4..20ff285e56 100644 --- a/src/test/core/JobQueue_test.cpp +++ b/src/test/core/JobQueue_test.cpp @@ -6,7 +6,6 @@ #include #include -#include namespace xrpl::test { @@ -61,6 +60,9 @@ class JobQueue_test : public beast::unit_test::Suite { // Test repeated post()s until the coroutine completes. std::atomic yieldCount{0}; + // Safe capture: the test blocks below until the coroutine + // completes, so the captured pointer outlives the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) auto const runner = jQueue.postCoroTask( JtClient, "PostCoroTest1", [ycp = &yieldCount](auto runner) -> CoroTask { while (++(*ycp) < 4) @@ -89,6 +91,9 @@ class JobQueue_test : public beast::unit_test::Suite { // Test repeated post()+join()s until the coroutine completes. int yieldCount{0}; + // Safe capture: the test blocks below until the coroutine + // completes, so the captured pointer outlives the coroutine. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) auto const runner = jQueue.postCoroTask( JtClient, "PostCoroTest2", [ycp = &yieldCount](auto runner) -> CoroTask { while (++(*ycp) < 4) @@ -133,6 +138,9 @@ class JobQueue_test : public beast::unit_test::Suite // unprotected variable on the stack should be completely safe. // Not recommended for the faint of heart... bool unprotected = false; + // Safe capture: the JobQueue is stopped, so the coroutine is + // never started and the captured pointer is never dereferenced. + // NOLINTNEXTLINE(cppcoreguidelines-avoid-capturing-lambda-coroutines) auto const runner = jQueue.postCoroTask( JtClient, "PostCoroTest3", [up = &unprotected](auto) -> CoroTask { *up = true;