// RUN: %check_clang_tidy %s bugprone-unused-return-value %t -- -- -fexceptions namespace std { struct future {}; enum class launch { async, deferred }; template future async(Function &&, Args &&...); template future async(launch, Function &&, Args &&...); template ForwardIt remove(ForwardIt, ForwardIt, const T &); template ForwardIt remove_if(ForwardIt, ForwardIt, UnaryPredicate); template ForwardIt unique(ForwardIt, ForwardIt); template struct default_delete; template > struct unique_ptr { T *release() noexcept; }; template struct char_traits; template struct allocator; template , typename Allocator = allocator> struct basic_string { bool empty() const; }; typedef basic_string string; template > struct vector { bool empty() const noexcept; }; // the check should be able to match std lib calls even if the functions are // declared inside inline namespaces inline namespace v1 { template T *launder(T *); } // namespace v1 } // namespace std struct Foo { void f(); }; int increment(int i) { return i + 1; } void useFuture(const std::future &fut); void warning() { std::async(increment, 42); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning std::async(std::launch::async, increment, 42); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning Foo F; std::launder(&F); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::remove_if(nullptr, nullptr, nullptr); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::unique(nullptr, nullptr); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::unique_ptr UPtr; UPtr.release(); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::string Str; Str.empty(); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning std::vector Vec; Vec.empty(); // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning // test discarding return values inside different kinds of statements auto Lambda = [] { std::remove(nullptr, nullptr, 1); }; // CHECK-NOTES: [[@LINE-1]]:22: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:22: note: cast {{.*}} this warning if (true) std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning else if (true) std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning else std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning while (true) std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning do std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning while (true); for (;;) std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning for (std::remove(nullptr, nullptr, 1);;) // CHECK-NOTES: [[@LINE-1]]:8: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:8: note: cast {{.*}} this warning ; for (;; std::remove(nullptr, nullptr, 1)) // CHECK-NOTES: [[@LINE-1]]:11: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:11: note: cast {{.*}} this warning ; for (auto C : "foo") std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning switch (1) { case 1: std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning break; default: std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning break; } try { std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning } catch (...) { std::remove(nullptr, nullptr, 1); // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning } } void noWarning() { auto AsyncRetval1 = std::async(increment, 42); auto AsyncRetval2 = std::async(std::launch::async, increment, 42); Foo FNoWarning; auto LaunderRetval = std::launder(&FNoWarning); auto RemoveRetval = std::remove(nullptr, nullptr, 1); auto RemoveIfRetval = std::remove_if(nullptr, nullptr, nullptr); auto UniqueRetval = std::unique(nullptr, nullptr); std::unique_ptr UPtrNoWarning; auto ReleaseRetval = UPtrNoWarning.release(); std::string StrNoWarning; auto StrEmptyRetval = StrNoWarning.empty(); std::vector VecNoWarning; auto VecEmptyRetval = VecNoWarning.empty(); // test using the return value in different kinds of expressions useFuture(std::async(increment, 42)); std::launder(&FNoWarning)->f(); delete std::launder(&FNoWarning); if (std::launder(&FNoWarning)) ; for (; std::launder(&FNoWarning);) ; while (std::launder(&FNoWarning)) ; do ; while (std::launder(&FNoWarning)); switch (std::unique(1, 1)) ; // cast to void should allow ignoring the return value (void)std::async(increment, 42); // test discarding return value of functions that are not configured to be checked increment(1); // test that the check is disabled inside GNU statement expressions ({ std::async(increment, 42); }); auto StmtExprRetval = ({ std::async(increment, 42); }); }