xref: /llvm-project/clang-tools-extra/test/clang-tidy/checkers/misc/coroutine-hostile-raii.cpp (revision 729657d6e15d0455557f35485deb87313ccdde10)
131512811SUtkarsh Saxena // RUN: %check_clang_tidy -std=c++20 %s misc-coroutine-hostile-raii %t \
2c9444439SUtkarsh Saxena // RUN:   -config="{CheckOptions: {\
3c9444439SUtkarsh Saxena // RUN:             misc-coroutine-hostile-raii.RAIITypesList: 'my::Mutex; ::my::other::Mutex', \
4*729657d6SUtkarsh Saxena // RUN:             misc-coroutine-hostile-raii.AllowedAwaitablesList: 'safe::awaitable; ::transformable::awaitable' \
5c9444439SUtkarsh Saxena // RUN:             }}"
631512811SUtkarsh Saxena 
731512811SUtkarsh Saxena namespace std {
831512811SUtkarsh Saxena 
931512811SUtkarsh Saxena template <typename R, typename...> struct coroutine_traits {
1031512811SUtkarsh Saxena   using promise_type = typename R::promise_type;
1131512811SUtkarsh Saxena };
1231512811SUtkarsh Saxena 
1331512811SUtkarsh Saxena template <typename Promise = void> struct coroutine_handle;
1431512811SUtkarsh Saxena 
1531512811SUtkarsh Saxena template <> struct coroutine_handle<void> {
from_addressstd::coroutine_handle1631512811SUtkarsh Saxena   static coroutine_handle from_address(void *addr) noexcept {
1731512811SUtkarsh Saxena     coroutine_handle me;
1831512811SUtkarsh Saxena     me.ptr = addr;
1931512811SUtkarsh Saxena     return me;
2031512811SUtkarsh Saxena   }
operator ()std::coroutine_handle2131512811SUtkarsh Saxena   void operator()() { resume(); }
addressstd::coroutine_handle2231512811SUtkarsh Saxena   void *address() const noexcept { return ptr; }
resumestd::coroutine_handle2331512811SUtkarsh Saxena   void resume() const {  }
destroystd::coroutine_handle2431512811SUtkarsh Saxena   void destroy() const { }
donestd::coroutine_handle2531512811SUtkarsh Saxena   bool done() const { return true; }
operator =std::coroutine_handle2631512811SUtkarsh Saxena   coroutine_handle &operator=(decltype(nullptr)) {
2731512811SUtkarsh Saxena     ptr = nullptr;
2831512811SUtkarsh Saxena     return *this;
2931512811SUtkarsh Saxena   }
coroutine_handlestd::coroutine_handle3031512811SUtkarsh Saxena   coroutine_handle(decltype(nullptr)) : ptr(nullptr) {}
coroutine_handlestd::coroutine_handle3131512811SUtkarsh Saxena   coroutine_handle() : ptr(nullptr) {}
3231512811SUtkarsh Saxena   //  void reset() { ptr = nullptr; } // add to P0057?
operator boolstd::coroutine_handle3331512811SUtkarsh Saxena   explicit operator bool() const { return ptr; }
3431512811SUtkarsh Saxena 
3531512811SUtkarsh Saxena protected:
3631512811SUtkarsh Saxena   void *ptr;
3731512811SUtkarsh Saxena };
3831512811SUtkarsh Saxena 
3931512811SUtkarsh Saxena template <typename Promise> struct coroutine_handle : coroutine_handle<> {
4031512811SUtkarsh Saxena   using coroutine_handle<>::operator=;
4131512811SUtkarsh Saxena 
from_addressstd::coroutine_handle4231512811SUtkarsh Saxena   static coroutine_handle from_address(void *addr) noexcept {
4331512811SUtkarsh Saxena     coroutine_handle me;
4431512811SUtkarsh Saxena     me.ptr = addr;
4531512811SUtkarsh Saxena     return me;
4631512811SUtkarsh Saxena   }
4731512811SUtkarsh Saxena 
promisestd::coroutine_handle4831512811SUtkarsh Saxena   Promise &promise() const {
4931512811SUtkarsh Saxena     return *reinterpret_cast<Promise *>(
5031512811SUtkarsh Saxena         __builtin_coro_promise(ptr, alignof(Promise), false));
5131512811SUtkarsh Saxena   }
from_promisestd::coroutine_handle5231512811SUtkarsh Saxena   static coroutine_handle from_promise(Promise &promise) {
5331512811SUtkarsh Saxena     coroutine_handle p;
5431512811SUtkarsh Saxena     p.ptr = __builtin_coro_promise(&promise, alignof(Promise), true);
5531512811SUtkarsh Saxena     return p;
5631512811SUtkarsh Saxena   }
5731512811SUtkarsh Saxena };
5831512811SUtkarsh Saxena 
5931512811SUtkarsh Saxena struct suspend_always {
await_readystd::suspend_always6031512811SUtkarsh Saxena   bool await_ready() noexcept { return false; }
await_suspendstd::suspend_always6131512811SUtkarsh Saxena   void await_suspend(std::coroutine_handle<>) noexcept {}
await_resumestd::suspend_always6231512811SUtkarsh Saxena   void await_resume() noexcept {}
6331512811SUtkarsh Saxena };
6431512811SUtkarsh Saxena } // namespace std
6531512811SUtkarsh Saxena 
6631512811SUtkarsh Saxena struct ReturnObject {
6731512811SUtkarsh Saxena     struct promise_type {
get_return_objectReturnObject::promise_type6831512811SUtkarsh Saxena         ReturnObject get_return_object() { return {}; }
initial_suspendReturnObject::promise_type6931512811SUtkarsh Saxena         std::suspend_always initial_suspend() { return {}; }
final_suspendReturnObject::promise_type7031512811SUtkarsh Saxena         std::suspend_always final_suspend() noexcept { return {}; }
unhandled_exceptionReturnObject::promise_type7131512811SUtkarsh Saxena         void unhandled_exception() {}
yield_valueReturnObject::promise_type7231512811SUtkarsh Saxena         std::suspend_always yield_value(int value) { return {}; }
7331512811SUtkarsh Saxena     };
7431512811SUtkarsh Saxena };
7531512811SUtkarsh Saxena 
7631512811SUtkarsh Saxena #define SCOPED_LOCKABLE __attribute__ ((scoped_lockable))
7731512811SUtkarsh Saxena 
7831512811SUtkarsh Saxena namespace absl {
7931512811SUtkarsh Saxena class SCOPED_LOCKABLE Mutex {};
8031512811SUtkarsh Saxena using Mutex2 = Mutex;
8131512811SUtkarsh Saxena } // namespace absl
8231512811SUtkarsh Saxena 
BasicWarning()8331512811SUtkarsh Saxena ReturnObject BasicWarning() {
8431512811SUtkarsh Saxena   absl::Mutex mtx;
8531512811SUtkarsh Saxena   // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'mtx' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
8631512811SUtkarsh Saxena   int no_warning;
8731512811SUtkarsh Saxena   {
8831512811SUtkarsh Saxena     co_yield 1;
8931512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here
9031512811SUtkarsh Saxena   }
9131512811SUtkarsh Saxena }
9231512811SUtkarsh Saxena 
BasicNoWarning()9331512811SUtkarsh Saxena ReturnObject BasicNoWarning() {
9431512811SUtkarsh Saxena   co_yield 1;
9531512811SUtkarsh Saxena   {  absl::Mutex no_warning; }
9631512811SUtkarsh Saxena   int no_warning;
9731512811SUtkarsh Saxena   {
9831512811SUtkarsh Saxena     co_yield 1;
9931512811SUtkarsh Saxena     absl::Mutex no_warning;
10031512811SUtkarsh Saxena   }
10131512811SUtkarsh Saxena   co_yield 1;
10231512811SUtkarsh Saxena }
10331512811SUtkarsh Saxena 
scopedLockableTest()10431512811SUtkarsh Saxena ReturnObject scopedLockableTest() {
10531512811SUtkarsh Saxena     co_yield 0;
10631512811SUtkarsh Saxena     absl::Mutex a;
10731512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
10831512811SUtkarsh Saxena     absl::Mutex2 b;
10931512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: 'b' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
11031512811SUtkarsh Saxena     {
11131512811SUtkarsh Saxena         absl::Mutex no_warning_1;
11231512811SUtkarsh Saxena         { absl::Mutex no_warning_2; }
11331512811SUtkarsh Saxena     }
11431512811SUtkarsh Saxena 
11531512811SUtkarsh Saxena     co_yield 1;
11631512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here
11731512811SUtkarsh Saxena     absl::Mutex c;
11831512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'c' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
11931512811SUtkarsh Saxena     co_await std::suspend_always{};
12031512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here
12131512811SUtkarsh Saxena     for(int i=1; i<=10; ++i ) {
12231512811SUtkarsh Saxena       absl::Mutex d;
12331512811SUtkarsh Saxena       // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'd' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
12431512811SUtkarsh Saxena       co_await std::suspend_always{};
12531512811SUtkarsh Saxena       // CHECK-MESSAGES: :[[@LINE-1]]:7: note: suspension point is here
12631512811SUtkarsh Saxena       co_yield 1;
12731512811SUtkarsh Saxena       absl::Mutex no_warning_3;
12831512811SUtkarsh Saxena     }
12931512811SUtkarsh Saxena     if (true) {
13031512811SUtkarsh Saxena       absl::Mutex e;
13131512811SUtkarsh Saxena       // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: 'e' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
13231512811SUtkarsh Saxena       co_yield 1;
13331512811SUtkarsh Saxena       // CHECK-MESSAGES: :[[@LINE-1]]:7: note: suspension point is here
13431512811SUtkarsh Saxena       absl::Mutex no_warning_4;
13531512811SUtkarsh Saxena     }
13631512811SUtkarsh Saxena     absl::Mutex no_warning_5;
13731512811SUtkarsh Saxena }
13831512811SUtkarsh Saxena 
139*729657d6SUtkarsh Saxena // ================================================================================
140*729657d6SUtkarsh Saxena // Safe awaitable
141*729657d6SUtkarsh Saxena // ================================================================================
142c9444439SUtkarsh Saxena namespace safe {
143c9444439SUtkarsh Saxena   struct awaitable {
await_readysafe::awaitable144c9444439SUtkarsh Saxena   bool await_ready() noexcept { return false; }
await_suspendsafe::awaitable145c9444439SUtkarsh Saxena   void await_suspend(std::coroutine_handle<>) noexcept {}
await_resumesafe::awaitable146c9444439SUtkarsh Saxena   void await_resume() noexcept {}
147c9444439SUtkarsh Saxena };
148c9444439SUtkarsh Saxena } // namespace safe
RAIISafeSuspendTest()149c9444439SUtkarsh Saxena ReturnObject RAIISafeSuspendTest() {
150c9444439SUtkarsh Saxena   absl::Mutex a;
151c9444439SUtkarsh Saxena   co_await safe::awaitable{};
152c9444439SUtkarsh Saxena   using other = safe::awaitable;
153c9444439SUtkarsh Saxena   co_await other{};
154c9444439SUtkarsh Saxena }
155c9444439SUtkarsh Saxena 
156*729657d6SUtkarsh Saxena // ================================================================================
157*729657d6SUtkarsh Saxena // Safe transformable awaitable
158*729657d6SUtkarsh Saxena // ================================================================================
159*729657d6SUtkarsh Saxena struct transformable { struct awaitable{}; };
160*729657d6SUtkarsh Saxena using alias_transformable_awaitable = transformable::awaitable;
161*729657d6SUtkarsh Saxena struct UseTransformAwaitable {
162*729657d6SUtkarsh Saxena   struct promise_type {
get_return_objectUseTransformAwaitable::promise_type163*729657d6SUtkarsh Saxena     UseTransformAwaitable get_return_object() { return {}; }
initial_suspendUseTransformAwaitable::promise_type164*729657d6SUtkarsh Saxena     std::suspend_always initial_suspend() { return {}; }
final_suspendUseTransformAwaitable::promise_type165*729657d6SUtkarsh Saxena     std::suspend_always final_suspend() noexcept { return {}; }
unhandled_exceptionUseTransformAwaitable::promise_type166*729657d6SUtkarsh Saxena     void unhandled_exception() {}
await_transformUseTransformAwaitable::promise_type167*729657d6SUtkarsh Saxena     std::suspend_always await_transform(transformable::awaitable) { return {}; }
168*729657d6SUtkarsh Saxena   };
169*729657d6SUtkarsh Saxena };
170*729657d6SUtkarsh Saxena 
retAwaitable()171*729657d6SUtkarsh Saxena auto retAwaitable() { return transformable::awaitable{}; }
RAIISafeSuspendTest2()172*729657d6SUtkarsh Saxena UseTransformAwaitable RAIISafeSuspendTest2() {
173*729657d6SUtkarsh Saxena   absl::Mutex a;
174*729657d6SUtkarsh Saxena   co_await retAwaitable();
175*729657d6SUtkarsh Saxena   co_await transformable::awaitable{};
176*729657d6SUtkarsh Saxena   co_await alias_transformable_awaitable{};
177*729657d6SUtkarsh Saxena }
178*729657d6SUtkarsh Saxena 
179*729657d6SUtkarsh Saxena // ================================================================================
180*729657d6SUtkarsh Saxena // Lambdas
181*729657d6SUtkarsh Saxena // ================================================================================
lambda()18231512811SUtkarsh Saxena void lambda() {
18331512811SUtkarsh Saxena   absl::Mutex no_warning;
18431512811SUtkarsh Saxena   auto lambda = []() -> ReturnObject {
18531512811SUtkarsh Saxena     co_await std::suspend_always{};
18631512811SUtkarsh Saxena     absl::Mutex a;
18731512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
18831512811SUtkarsh Saxena     co_yield 1;
18931512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here
19031512811SUtkarsh Saxena     co_await std::suspend_always{};
19131512811SUtkarsh Saxena     co_yield 1;
19231512811SUtkarsh Saxena   };
19331512811SUtkarsh Saxena   absl::Mutex no_warning_2;
19431512811SUtkarsh Saxena }
19531512811SUtkarsh Saxena 
196*729657d6SUtkarsh Saxena // ================================================================================
197*729657d6SUtkarsh Saxena // Denylisted RAII
198*729657d6SUtkarsh Saxena // ================================================================================
19931512811SUtkarsh Saxena template<class T>
raii_in_template()20031512811SUtkarsh Saxena ReturnObject raii_in_template(){
20131512811SUtkarsh Saxena   T a;
20231512811SUtkarsh Saxena   // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: 'a' holds a lock across a suspension point of coroutine and could be unlocked by a different thread [misc-coroutine-hostile-raii]
20331512811SUtkarsh Saxena   co_yield 1;
20431512811SUtkarsh Saxena   // CHECK-MESSAGES: :[[@LINE-1]]:3: note: suspension point is here
20531512811SUtkarsh Saxena }
foo_template()20631512811SUtkarsh Saxena void foo_template() { raii_in_template<absl::Mutex>(); }
20731512811SUtkarsh Saxena 
20831512811SUtkarsh Saxena namespace my {
20931512811SUtkarsh Saxena class Mutex{};
21031512811SUtkarsh Saxena namespace other {
21131512811SUtkarsh Saxena class Mutex{};
21231512811SUtkarsh Saxena } // namespace other
21331512811SUtkarsh Saxena 
21431512811SUtkarsh Saxena using Mutex2 = Mutex;
21531512811SUtkarsh Saxena } // namespace my
21631512811SUtkarsh Saxena 
denyListTest()21731512811SUtkarsh Saxena ReturnObject denyListTest() {
21831512811SUtkarsh Saxena     my::Mutex a;
21931512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: 'a' persists across a suspension point of coroutine [misc-coroutine-hostile-raii]
22031512811SUtkarsh Saxena     my::other::Mutex b;
22131512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: 'b' persists across a suspension point of coroutine [misc-coroutine-hostile-raii]
22231512811SUtkarsh Saxena     my::Mutex2 c;
22331512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: 'c' persists across a suspension point of coroutine [misc-coroutine-hostile-raii]
22431512811SUtkarsh Saxena     co_yield 1;
22531512811SUtkarsh Saxena     // CHECK-MESSAGES: :[[@LINE-1]]:5: note: suspension point is here
22631512811SUtkarsh Saxena }
22731512811SUtkarsh Saxena 
referenceTest(my::Mutex & ref)22831512811SUtkarsh Saxena ReturnObject referenceTest(my::Mutex& ref) {
22931512811SUtkarsh Saxena     my::Mutex& a = ref;
23031512811SUtkarsh Saxena     co_yield 1;
23131512811SUtkarsh Saxena }
pointerTest(my::Mutex * ref)23231512811SUtkarsh Saxena ReturnObject pointerTest(my::Mutex* ref) {
23331512811SUtkarsh Saxena     my::Mutex* a = ref;
23431512811SUtkarsh Saxena     co_yield 1;
23531512811SUtkarsh Saxena }
23631512811SUtkarsh Saxena 
functionArgTest(my::Mutex ref)23731512811SUtkarsh Saxena ReturnObject functionArgTest(my::Mutex ref) {
23831512811SUtkarsh Saxena     co_yield 1;
23931512811SUtkarsh Saxena }
240