[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