[clang-tools-extra] a563ced - [clang-tidy] Implement cppcoreguidelines CP.52
Piotr Zegar via cfe-commits
cfe-commits at lists.llvm.org
Fri Aug 11 00:14:21 PDT 2023
Author: Chris Cotter
Date: 2023-08-11T07:13:59Z
New Revision: a563ced78bb4bda31babb85e88a89b49ffd61a90
URL: https://github.com/llvm/llvm-project/commit/a563ced78bb4bda31babb85e88a89b49ffd61a90
DIFF: https://github.com/llvm/llvm-project/commit/a563ced78bb4bda31babb85e88a89b49ffd61a90.diff
LOG: [clang-tidy] Implement cppcoreguidelines CP.52
Flag code that suspends a coroutine while a lock is held.
Reviewed By: PiotrZSL
Differential Revision: https://reviews.llvm.org/D157057
Added:
clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.h
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock.rst
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/no-suspend-with-lock.cpp
Modified:
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
index c2cde34fb1336d..eb35bbc6a538fe 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -18,6 +18,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
MissingStdForwardCheck.cpp
NarrowingConversionsCheck.cpp
NoMallocCheck.cpp
+ NoSuspendWithLockCheck.cpp
OwningMemoryCheck.cpp
PreferMemberInitializerCheck.cpp
ProBoundsArrayToPointerDecayCheck.cpp
@@ -50,6 +51,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule
clang_target_link_libraries(clangTidyCppCoreGuidelinesModule
PRIVATE
+ clangAnalysis
clangAST
clangASTMatchers
clangBasic
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
index fdbf0e11f3f5f8..e9f0201615616f 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -32,6 +32,7 @@
#include "MissingStdForwardCheck.h"
#include "NarrowingConversionsCheck.h"
#include "NoMallocCheck.h"
+#include "NoSuspendWithLockCheck.h"
#include "OwningMemoryCheck.h"
#include "PreferMemberInitializerCheck.h"
#include "ProBoundsArrayToPointerDecayCheck.h"
@@ -89,6 +90,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
CheckFactories.registerCheck<NarrowingConversionsCheck>(
"cppcoreguidelines-narrowing-conversions");
CheckFactories.registerCheck<NoMallocCheck>("cppcoreguidelines-no-malloc");
+ CheckFactories.registerCheck<NoSuspendWithLockCheck>(
+ "cppcoreguidelines-no-suspend-with-lock");
CheckFactories.registerCheck<performance::NoexceptDestructorCheck>(
"cppcoreguidelines-noexcept-destructor");
CheckFactories.registerCheck<performance::NoexceptMoveConstructorCheck>(
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp
new file mode 100644
index 00000000000000..ca293178c78b47
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.cpp
@@ -0,0 +1,73 @@
+//===--- NoSuspendWithLockCheck.cpp - clang-tidy --------------------------===//
+//
+// 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 "NoSuspendWithLockCheck.h"
+#include "../utils/ExprSequence.h"
+#include "../utils/Matchers.h"
+#include "../utils/OptionsUtils.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/CFG.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::cppcoreguidelines {
+
+void NoSuspendWithLockCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "LockGuards", LockGuards);
+}
+
+void NoSuspendWithLockCheck::registerMatchers(MatchFinder *Finder) {
+ auto LockType = elaboratedType(namesType(templateSpecializationType(
+ hasDeclaration(namedDecl(matchers::matchesAnyListedName(
+ utils::options::parseStringList(LockGuards)))))));
+
+ StatementMatcher Lock =
+ declStmt(has(varDecl(hasType(LockType)).bind("lock-decl")))
+ .bind("lock-decl-stmt");
+ Finder->addMatcher(
+ expr(anyOf(coawaitExpr(), coyieldExpr(), dependentCoawaitExpr()),
+ forCallable(functionDecl().bind("function")),
+ unless(isInTemplateInstantiation()),
+ hasAncestor(
+ compoundStmt(has(Lock), forCallable(equalsBoundNode("function")))
+ .bind("block")))
+ .bind("suspend"),
+ this);
+}
+
+void NoSuspendWithLockCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Block = Result.Nodes.getNodeAs<CompoundStmt>("block");
+ const auto *Suspend = Result.Nodes.getNodeAs<Expr>("suspend");
+ const auto *LockDecl = Result.Nodes.getNodeAs<VarDecl>("lock-decl");
+ const auto *LockStmt = Result.Nodes.getNodeAs<Stmt>("lock-decl-stmt");
+
+ if (!Block || !Suspend || !LockDecl || !LockStmt)
+ return;
+
+ ASTContext &Context = *Result.Context;
+ CFG::BuildOptions Options;
+ Options.AddImplicitDtors = true;
+ Options.AddTemporaryDtors = true;
+
+ std::unique_ptr<CFG> TheCFG = CFG::buildCFG(
+ nullptr, const_cast<clang::CompoundStmt *>(Block), &Context, Options);
+ if (!TheCFG)
+ return;
+
+ utils::ExprSequence Sequence(TheCFG.get(), Block, &Context);
+ const Stmt *LastBlockStmt = Block->body_back();
+ if (Sequence.inSequence(LockStmt, Suspend) &&
+ (Suspend == LastBlockStmt ||
+ Sequence.inSequence(Suspend, LastBlockStmt))) {
+ diag(Suspend->getBeginLoc(), "coroutine suspended with lock %0 held")
+ << LockDecl;
+ }
+}
+
+} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.h
new file mode 100644
index 00000000000000..c7b7f476003fb5
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/NoSuspendWithLockCheck.h
@@ -0,0 +1,44 @@
+//===--- NoSuspendWithLockCheck.h - clang-tidy ------------------*- C++ -*-===//
+//
+// 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_CPPCOREGUIDELINES_NOSUSPENDWITHLOCKCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NOSUSPENDWITHLOCKCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::cppcoreguidelines {
+
+/// Flag coroutines that suspend while any lock guard is alive.
+/// This check implements CppCoreGuideline CP.52.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock.html
+class NoSuspendWithLockCheck : public ClangTidyCheck {
+public:
+ NoSuspendWithLockCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ LockGuards(Options.get("LockGuards",
+ "::std::unique_lock;::std::scoped_lock;::"
+ "std::shared_lock;::std::lock_guard")) {}
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ 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.CPlusPlus20;
+ }
+
+private:
+ /// Semicolon-separated list of fully qualified names of lock guard template
+ /// types. Defaults to
+ /// `::std::unique_lock;::std::scoped_lock;::std::shared_lock;::std::lock_guard`.
+ const StringRef LockGuards;
+};
+
+} // namespace clang::tidy::cppcoreguidelines
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_NOSUSPENDWITHLOCKCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 703d8b0edd8cc9..122ec5fe6e0d31 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -136,6 +136,12 @@ New checks
extracted from an optional-like type and then used to create a new instance
of the same optional-like type.
+- New :doc:`cppcoreguidelines-no-suspend-with-lock
+ <clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock>` check.
+
+ Flags coroutines that suspend while a lock guard is in scope at the
+ suspension point.
+
- New :doc:`modernize-use-constraints
<clang-tidy/checks/modernize/use-constraints>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock.rst
new file mode 100644
index 00000000000000..59981f2c8d6d34
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock.rst
@@ -0,0 +1,40 @@
+.. title:: clang-tidy - cppcoreguidelines-no-suspend-with-lock
+
+cppcoreguidelines-no-suspend-with-lock
+======================================
+
+Flags coroutines that suspend while a lock guard is in scope at the
+suspension point.
+
+When a coroutine suspends, any mutexes held by the coroutine will remain
+locked until the coroutine resumes and eventually destructs the lock guard.
+This can lead to long periods with a mutex held and runs the risk of deadlock.
+
+Instead, locks should be released before suspending a coroutine.
+
+This check only checks suspending coroutines while a lock_guard is in scope;
+it does not consider manual locking or unlocking of mutexes, e.g., through
+calls to ``std::mutex::lock()``.
+
+Examples:
+
+.. code-block:: c++
+
+ future bad_coro() {
+ std::lock_guard lock{mtx};
+ ++some_counter;
+ co_await something(); // Suspending while holding a mutex
+ }
+
+ future good_coro() {
+ {
+ std::lock_guard lock{mtx};
+ ++some_counter;
+ }
+ // Destroy the lock_guard to release the mutex before suspending the coroutine
+ co_await something(); // Suspending while holding a mutex
+ }
+
+This check implements `CP.52
+<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rcoro-locks>`_
+from the C++ Core Guidelines.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 2e2fe0dfa51fe8..9de7989c264fb5 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -200,6 +200,7 @@ Clang-Tidy Checks
`cppcoreguidelines-missing-std-forward <cppcoreguidelines/missing-std-forward.html>`_,
`cppcoreguidelines-narrowing-conversions <cppcoreguidelines/narrowing-conversions.html>`_,
`cppcoreguidelines-no-malloc <cppcoreguidelines/no-malloc.html>`_,
+ `cppcoreguidelines-no-suspend-with-lock <cppcoreguidelines/no-suspend-with-lock.html>`_,
`cppcoreguidelines-owning-memory <cppcoreguidelines/owning-memory.html>`_,
`cppcoreguidelines-prefer-member-initializer <cppcoreguidelines/prefer-member-initializer.html>`_, "Yes"
`cppcoreguidelines-pro-bounds-array-to-pointer-decay <cppcoreguidelines/pro-bounds-array-to-pointer-decay.html>`_,
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/no-suspend-with-lock.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/no-suspend-with-lock.cpp
new file mode 100644
index 00000000000000..6944864302dce0
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/no-suspend-with-lock.cpp
@@ -0,0 +1,279 @@
+// RUN: %check_clang_tidy -std=c++20 %s cppcoreguidelines-no-suspend-with-lock %t -- -- -fno-delayed-template-parsing
+
+// NOLINTBEGIN
+namespace std {
+template <typename T, typename... Args>
+struct coroutine_traits {
+ using promise_type = typename T::promise_type;
+};
+template <typename T = void>
+struct coroutine_handle;
+template <>
+struct coroutine_handle<void> {
+ coroutine_handle() noexcept;
+ coroutine_handle(decltype(nullptr)) noexcept;
+ static constexpr coroutine_handle from_address(void*);
+};
+template <typename T>
+struct coroutine_handle {
+ coroutine_handle() noexcept;
+ coroutine_handle(decltype(nullptr)) noexcept;
+ static constexpr coroutine_handle from_address(void*);
+ operator coroutine_handle<>() const noexcept;
+};
+
+template <class Mutex>
+class unique_lock {
+public:
+ unique_lock() noexcept;
+ explicit unique_lock(Mutex &m);
+ unique_lock& operator=(unique_lock&&);
+ void unlock();
+ Mutex* release() noexcept;
+ Mutex* mutex() const noexcept;
+ void swap(unique_lock& other) noexcept;
+};
+
+class mutex {
+public:
+ mutex() noexcept;
+ ~mutex();
+ mutex(const mutex &) = delete;
+ mutex &operator=(const mutex &) = delete;
+
+ void lock();
+ void unlock();
+};
+} // namespace std
+
+class my_own_mutex {
+public:
+ void lock();
+ void unlock();
+};
+
+struct Awaiter {
+ bool await_ready() noexcept;
+ void await_suspend(std::coroutine_handle<>) noexcept;
+ void await_resume() noexcept;
+};
+
+struct Coro {
+ struct promise_type {
+ Awaiter initial_suspend();
+ Awaiter final_suspend() noexcept;
+ void return_void();
+ Coro get_return_object();
+ void unhandled_exception();
+ Awaiter yield_value(int);
+ };
+};
+// NOLINTEND
+
+std::mutex mtx;
+std::mutex mtx2;
+
+Coro awaits_with_lock() {
+ std::unique_lock<std::mutex> lock(mtx);
+
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+
+ if (true) {
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ if (true) {
+ std::unique_lock<std::mutex> lock2;
+ lock2.unlock();
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock2' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+}
+
+Coro awaits_with_lock_in_try() try {
+ std::unique_lock<std::mutex> lock(mtx);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+} catch (...) {}
+
+Coro lock_possibly_unlocked() {
+ // CppCoreGuideline CP.52's enforcement strictly requires flagging
+ // code that suspends while any lock guard is not destructed.
+
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ lock.unlock();
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ lock.release();
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ std::unique_lock<std::mutex> lock2;
+ lock.swap(lock2);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ std::unique_lock<std::mutex> lock2{mtx2};
+ lock.swap(lock2);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ lock = std::unique_lock<std::mutex>{};
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ lock = std::unique_lock<std::mutex>{mtx2};
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+}
+
+Coro await_with_underlying_mutex_unlocked() {
+ std::unique_lock<std::mutex> lock(mtx);
+
+ // Even though we unlock the mutex here, 'lock' is still active unless
+ // there is a call to lock.unlock(). This is a bug in the program since
+ // it will result in locking the mutex twice. The check does not track
+ // unlock calls on the underlying mutex held by a lock guard object.
+ mtx.unlock();
+
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+Coro await_with_empty_lock() {
+ std::unique_lock<std::mutex> lock;
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+Coro await_before_lock() {
+ co_await Awaiter{};
+ std::unique_lock<std::mutex> lock(mtx);
+}
+
+Coro await_with_lock_
diff erent_scope() {
+ {
+ std::unique_lock<std::mutex> lock(mtx);
+ }
+ co_await Awaiter{};
+}
+
+Coro await_with_goto() {
+first:
+ co_await Awaiter{};
+ std::unique_lock<std::mutex> lock(mtx);
+ goto first;
+}
+
+void await_in_lambda() {
+ auto f1 = []() -> Coro {
+ std::unique_lock<std::mutex> lock(mtx);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ };
+
+ auto f2 = [](auto& m) -> Coro {
+ std::unique_lock<decltype(m)> lock(m);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ };
+}
+
+void await_in_lambda_without_immediate_mutex() {
+ std::unique_lock<std::mutex> lock(mtx);
+
+ auto f1 = []() -> Coro {
+ co_await Awaiter{};
+ };
+
+ // The check only finds suspension points where there is a lock held in the
+ // immediate callable.
+ f1();
+}
+
+Coro yields_with_lock() {
+ std::unique_lock<std::mutex> lock(mtx);
+ co_yield 0;
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+template <class Mutex>
+Coro awaits_templated_type(Mutex& m) {
+ std::unique_lock<Mutex> lock(m);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+template <class T>
+Coro awaits_in_template_function(T) {
+ std::unique_lock<std::mutex> lock(mtx);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+template <class Mutex>
+Coro awaits_in_never_instantiated_template_of_mutex(Mutex& m) {
+ // Nothing should instantiate this function
+ std::unique_lock<Mutex> lock(m);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+template <class T>
+Coro awaits_in_never_instantiated_templated_function(T) {
+ // Nothing should instantiate this function
+ std::unique_lock<std::mutex> lock(mtx);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+}
+
+template <class T>
+struct my_container {
+
+ Coro push_back() {
+ std::unique_lock<std::mutex> lock(mtx_);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ template <class... Args>
+ Coro emplace_back(Args&&...) {
+ std::unique_lock<std::mutex> lock(mtx_);
+ co_await Awaiter{};
+ // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
+ }
+
+ std::mutex mtx_;
+};
+
+void calls_templated_functions() {
+ my_own_mutex m2;
+ awaits_templated_type(mtx);
+ awaits_templated_type(m2);
+
+ awaits_in_template_function(1);
+ awaits_in_template_function(1.0);
+}
More information about the cfe-commits
mailing list