forked from OSchip/llvm-project
239 lines
7.2 KiB
C++
239 lines
7.2 KiB
C++
// RUN: %check_clang_tidy %s bugprone-unused-return-value %t -- -- -fexceptions
|
|
|
|
namespace std {
|
|
|
|
struct future {};
|
|
|
|
enum class launch {
|
|
async,
|
|
deferred
|
|
};
|
|
|
|
template <typename Function, typename... Args>
|
|
future async(Function &&, Args &&...);
|
|
|
|
template <typename Function, typename... Args>
|
|
future async(launch, Function &&, Args &&...);
|
|
|
|
template <typename ForwardIt, typename T>
|
|
ForwardIt remove(ForwardIt, ForwardIt, const T &);
|
|
|
|
template <typename ForwardIt, typename UnaryPredicate>
|
|
ForwardIt remove_if(ForwardIt, ForwardIt, UnaryPredicate);
|
|
|
|
template <typename ForwardIt>
|
|
ForwardIt unique(ForwardIt, ForwardIt);
|
|
|
|
template <typename T>
|
|
struct default_delete;
|
|
|
|
template <typename T, typename Deleter = std::default_delete<T>>
|
|
struct unique_ptr {
|
|
T *release() noexcept;
|
|
};
|
|
|
|
template <typename T>
|
|
struct char_traits;
|
|
|
|
template <typename T>
|
|
struct allocator;
|
|
|
|
template <typename CharT,
|
|
typename Traits = char_traits<CharT>,
|
|
typename Allocator = allocator<CharT>>
|
|
struct basic_string {
|
|
bool empty() const;
|
|
};
|
|
|
|
typedef basic_string<char> string;
|
|
|
|
template <typename T, typename Allocator = std::allocator<T>>
|
|
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 <typename T>
|
|
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<Foo> 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<Foo> 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<Foo> UPtrNoWarning;
|
|
auto ReleaseRetval = UPtrNoWarning.release();
|
|
|
|
std::string StrNoWarning;
|
|
auto StrEmptyRetval = StrNoWarning.empty();
|
|
|
|
std::vector<Foo> 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); });
|
|
}
|