1// RUN: %check_clang_tidy -std=c++20 %s cppcoreguidelines-no-suspend-with-lock %t -- -- -fno-delayed-template-parsing -fexceptions
2
3// NOLINTBEGIN
4namespace std {
5template <typename T, typename... Args>
6struct coroutine_traits {
7 using promise_type = typename T::promise_type;
8};
9template <typename T = void>
10struct coroutine_handle;
11template <>
12struct coroutine_handle<void> {
13 coroutine_handle() noexcept;
14 coroutine_handle(decltype(nullptr)) noexcept;
15 static constexpr coroutine_handle from_address(void*);
16};
17template <typename T>
18struct 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
25template <class Mutex>
26class unique_lock {
27public:
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
37class mutex {
38public:
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
49class my_own_mutex {
50public:
51 void lock();
52 void unlock();
53};
54
55struct Awaiter {
56 bool await_ready() noexcept;
57 void await_suspend(std::coroutine_handle<>) noexcept;
58 void await_resume() noexcept;
59};
60
61struct 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
73std::mutex mtx;
74std::mutex mtx2;
75
76Coro 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
98Coro 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
104Coro 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(other&: 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(other&: 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
153Coro 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
166Coro 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
172Coro await_before_lock() {
173 co_await Awaiter{};
174 std::unique_lock<std::mutex> lock(mtx);
175}
176
177Coro await_with_lock_different_scope() {
178 {
179 std::unique_lock<std::mutex> lock(mtx);
180 }
181 co_await Awaiter{};
182}
183
184Coro await_with_goto() {
185first:
186 co_await Awaiter{};
187 std::unique_lock<std::mutex> lock(mtx);
188 goto first;
189}
190
191void 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
205void 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
217Coro 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
223template <class Mutex>
224Coro 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
230template <class T>
231Coro 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
237template <class Mutex>
238Coro 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
245template <class T>
246Coro 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
253template <class T>
254struct my_container {
255
256 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>
263 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
272void calls_templated_functions() {
273 my_own_mutex m2;
274 awaits_templated_type(m&: mtx);
275 awaits_templated_type(m&: m2);
276
277 awaits_in_template_function(1);
278 awaits_in_template_function(1.0);
279}
280

source code of clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/no-suspend-with-lock.cpp