1 // RUN: %check_clang_tidy -std=c++20 %s cppcoreguidelines-no-suspend-with-lock %t -- -- -fno-delayed-template-parsing -fexceptions
2 
3 // NOLINTBEGIN
4 namespace std {
5 template <typename T, typename... Args>
6 struct coroutine_traits {
7   using promise_type = typename T::promise_type;
8 };
9 template <typename T = void>
10 struct coroutine_handle;
11 template <>
12 struct coroutine_handle<void> {
13   coroutine_handle() noexcept;
14   coroutine_handle(decltype(nullptr)) noexcept;
15   static constexpr coroutine_handle from_address(void*);
16 };
17 template <typename T>
18 struct coroutine_handle {
19   coroutine_handle() noexcept;
20   coroutine_handle(decltype(nullptr)) noexcept;
21   static constexpr coroutine_handle from_address(void*);
22   operator coroutine_handle<>() const noexcept;
23 };
24 
25 template <class Mutex>
26 class unique_lock {
27 public:
28   unique_lock() noexcept;
29   explicit unique_lock(Mutex &m);
30   unique_lock& operator=(unique_lock&&);
31   void unlock();
32   Mutex* release() noexcept;
33   Mutex* mutex() const noexcept;
34   void swap(unique_lock& other) noexcept;
35 };
36 
37 class mutex {
38 public:
39   mutex() noexcept;
40   ~mutex();
41   mutex(const mutex &) = delete;
42   mutex &operator=(const mutex &) = delete;
43 
44   void lock();
45   void unlock();
46 };
47 } // namespace std
48 
49 class my_own_mutex {
50 public:
51   void lock();
52   void unlock();
53 };
54 
55 struct Awaiter {
56   bool await_ready() noexcept;
57   void await_suspend(std::coroutine_handle<>) noexcept;
58   void await_resume() noexcept;
59 };
60 
61 struct Coro {
62   struct promise_type {
63     Awaiter initial_suspend();
64     Awaiter final_suspend() noexcept;
65     void return_void();
66     Coro get_return_object();
67     void unhandled_exception();
68     Awaiter yield_value(int);
69   };
70 };
71 // NOLINTEND
72 
73 std::mutex mtx;
74 std::mutex mtx2;
75 
awaits_with_lock()76 Coro awaits_with_lock() {
77   std::unique_lock<std::mutex> lock(mtx);
78 
79   co_await Awaiter{};
80   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
81 
82   co_await Awaiter{};
83   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
84 
85   if (true) {
86     co_await Awaiter{};
87     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
88   }
89 
90   if (true) {
91     std::unique_lock<std::mutex> lock2;
92     lock2.unlock();
93     co_await Awaiter{};
94     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock2' held [cppcoreguidelines-no-suspend-with-lock]
95   }
96 }
97 
awaits_with_lock_in_try()98 Coro awaits_with_lock_in_try() try {
99   std::unique_lock<std::mutex> lock(mtx);
100   co_await Awaiter{};
101   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
102 } catch (...) {}
103 
lock_possibly_unlocked()104 Coro lock_possibly_unlocked() {
105   // CppCoreGuideline CP.52's enforcement strictly requires flagging
106   // code that suspends while any lock guard is not destructed.
107 
108   {
109     std::unique_lock<std::mutex> lock(mtx);
110     lock.unlock();
111     co_await Awaiter{};
112     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
113   }
114 
115   {
116     std::unique_lock<std::mutex> lock(mtx);
117     lock.release();
118     co_await Awaiter{};
119     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
120   }
121 
122   {
123     std::unique_lock<std::mutex> lock(mtx);
124     std::unique_lock<std::mutex> lock2;
125     lock.swap(lock2);
126     co_await Awaiter{};
127     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
128   }
129 
130   {
131     std::unique_lock<std::mutex> lock(mtx);
132     std::unique_lock<std::mutex> lock2{mtx2};
133     lock.swap(lock2);
134     co_await Awaiter{};
135     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
136   }
137 
138   {
139     std::unique_lock<std::mutex> lock(mtx);
140     lock = std::unique_lock<std::mutex>{};
141     co_await Awaiter{};
142     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
143   }
144 
145   {
146     std::unique_lock<std::mutex> lock(mtx);
147     lock = std::unique_lock<std::mutex>{mtx2};
148     co_await Awaiter{};
149     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
150   }
151 }
152 
await_with_underlying_mutex_unlocked()153 Coro await_with_underlying_mutex_unlocked() {
154   std::unique_lock<std::mutex> lock(mtx);
155 
156   // Even though we unlock the mutex here, 'lock' is still active unless
157   // there is a call to lock.unlock(). This is a bug in the program since
158   // it will result in locking the mutex twice. The check does not track
159   // unlock calls on the underlying mutex held by a lock guard object.
160   mtx.unlock();
161 
162   co_await Awaiter{};
163   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
164 }
165 
await_with_empty_lock()166 Coro await_with_empty_lock() {
167   std::unique_lock<std::mutex> lock;
168   co_await Awaiter{};
169   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
170 }
171 
await_before_lock()172 Coro await_before_lock() {
173   co_await Awaiter{};
174   std::unique_lock<std::mutex> lock(mtx);
175 }
176 
await_with_lock_different_scope()177 Coro await_with_lock_different_scope() {
178   {
179     std::unique_lock<std::mutex> lock(mtx);
180   }
181   co_await Awaiter{};
182 }
183 
await_with_goto()184 Coro await_with_goto() {
185 first:
186   co_await Awaiter{};
187   std::unique_lock<std::mutex> lock(mtx);
188   goto first;
189 }
190 
await_in_lambda()191 void await_in_lambda() {
192   auto f1 = []() -> Coro {
193     std::unique_lock<std::mutex> lock(mtx);
194     co_await Awaiter{};
195     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
196   };
197 
198   auto f2 = [](auto& m) -> Coro {
199     std::unique_lock<decltype(m)> lock(m);
200     co_await Awaiter{};
201     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
202   };
203 }
204 
await_in_lambda_without_immediate_mutex()205 void await_in_lambda_without_immediate_mutex() {
206   std::unique_lock<std::mutex> lock(mtx);
207 
208   auto f1 = []() -> Coro {
209     co_await Awaiter{};
210   };
211 
212   // The check only finds suspension points where there is a lock held in the
213   // immediate callable.
214   f1();
215 }
216 
yields_with_lock()217 Coro yields_with_lock() {
218   std::unique_lock<std::mutex> lock(mtx);
219   co_yield 0;
220   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
221 }
222 
223 template <class Mutex>
awaits_templated_type(Mutex & m)224 Coro awaits_templated_type(Mutex& m) {
225   std::unique_lock<Mutex> lock(m);
226   co_await Awaiter{};
227   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
228 }
229 
230 template <class T>
awaits_in_template_function(T)231 Coro awaits_in_template_function(T) {
232   std::unique_lock<std::mutex> lock(mtx);
233   co_await Awaiter{};
234   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
235 }
236 
237 template <class Mutex>
awaits_in_never_instantiated_template_of_mutex(Mutex & m)238 Coro awaits_in_never_instantiated_template_of_mutex(Mutex& m) {
239   // Nothing should instantiate this function
240   std::unique_lock<Mutex> lock(m);
241   co_await Awaiter{};
242   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
243 }
244 
245 template <class T>
awaits_in_never_instantiated_templated_function(T)246 Coro awaits_in_never_instantiated_templated_function(T) {
247   // Nothing should instantiate this function
248   std::unique_lock<std::mutex> lock(mtx);
249   co_await Awaiter{};
250   // CHECK-MESSAGES: [[@LINE-1]]:3: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
251 }
252 
253 template <class T>
254 struct my_container {
255 
push_backmy_container256   Coro push_back() {
257     std::unique_lock<std::mutex> lock(mtx_);
258     co_await Awaiter{};
259     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
260   }
261 
262   template <class... Args>
emplace_backmy_container263   Coro emplace_back(Args&&...) {
264     std::unique_lock<std::mutex> lock(mtx_);
265     co_await Awaiter{};
266     // CHECK-MESSAGES: [[@LINE-1]]:5: warning: coroutine suspended with lock 'lock' held [cppcoreguidelines-no-suspend-with-lock]
267   }
268 
269   std::mutex mtx_;
270 };
271 
calls_templated_functions()272 void calls_templated_functions() {
273   my_own_mutex m2;
274   awaits_templated_type(mtx);
275   awaits_templated_type(m2);
276 
277   awaits_in_template_function(1);
278   awaits_in_template_function(1.0);
279 }
280