1 // RUN: %check_clang_tidy %s bugprone-unused-return-value %t -- -- -fexceptions
2 
3 namespace std {
4 
5 struct future {};
6 
7 enum class launch {
8   async,
9   deferred
10 };
11 
12 template <typename Function, typename... Args>
13 future async(Function &&, Args &&...);
14 
15 template <typename Function, typename... Args>
16 future async(launch, Function &&, Args &&...);
17 
18 template <typename ForwardIt, typename T>
19 ForwardIt remove(ForwardIt, ForwardIt, const T &);
20 
21 template <typename ForwardIt, typename UnaryPredicate>
22 ForwardIt remove_if(ForwardIt, ForwardIt, UnaryPredicate);
23 
24 template <typename ForwardIt>
25 ForwardIt unique(ForwardIt, ForwardIt);
26 
27 template <typename T>
28 struct default_delete;
29 
30 template <typename T, typename Deleter = std::default_delete<T>>
31 struct unique_ptr {
32   T *release() noexcept;
33 };
34 
35 template <typename T>
36 struct char_traits;
37 
38 template <typename T>
39 struct allocator;
40 
41 template <typename CharT,
42           typename Traits = char_traits<CharT>,
43           typename Allocator = allocator<CharT>>
44 struct basic_string {
45   bool empty() const;
46 };
47 
48 typedef basic_string<char> string;
49 
50 template <typename T, typename Allocator = std::allocator<T>>
51 struct vector {
52   bool empty() const noexcept;
53 };
54 
55 // the check should be able to match std lib calls even if the functions are
56 // declared inside inline namespaces
57 inline namespace v1 {
58 
59 template <typename T>
60 T *launder(T *);
61 
62 } // namespace v1
63 } // namespace std
64 
65 struct Foo {
66   void f();
67 };
68 
increment(int i)69 int increment(int i) {
70   return i + 1;
71 }
72 
73 void useFuture(const std::future &fut);
74 
warning()75 void warning() {
76   std::async(increment, 42);
77   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value returned by this function should be used
78   // CHECK-NOTES: [[@LINE-2]]:3: note: cast the expression to void to silence this warning
79 
80   std::async(std::launch::async, increment, 42);
81   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
82   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
83 
84   Foo F;
85   std::launder(&F);
86   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
87   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
88 
89   std::remove(nullptr, nullptr, 1);
90   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
91   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
92 
93   std::remove_if(nullptr, nullptr, nullptr);
94   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
95   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
96 
97   std::unique(nullptr, nullptr);
98   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
99   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
100 
101   std::unique_ptr<Foo> UPtr;
102   UPtr.release();
103   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
104   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
105 
106   std::string Str;
107   Str.empty();
108   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
109   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
110 
111   std::vector<Foo> Vec;
112   Vec.empty();
113   // CHECK-NOTES: [[@LINE-1]]:3: warning: the value {{.*}} should be used
114   // CHECK-NOTES: [[@LINE-2]]:3: note: cast {{.*}} this warning
115 
116   // test discarding return values inside different kinds of statements
117 
118   auto Lambda = [] { std::remove(nullptr, nullptr, 1); };
119   // CHECK-NOTES: [[@LINE-1]]:22: warning: the value {{.*}} should be used
120   // CHECK-NOTES: [[@LINE-2]]:22: note: cast {{.*}} this warning
121 
122   if (true)
123     std::remove(nullptr, nullptr, 1);
124   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
125   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
126   else if (true)
127     std::remove(nullptr, nullptr, 1);
128   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
129   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
130   else
131     std::remove(nullptr, nullptr, 1);
132   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
133   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
134 
135   while (true)
136     std::remove(nullptr, nullptr, 1);
137   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
138   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
139 
140   do
141     std::remove(nullptr, nullptr, 1);
142   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
143   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
144   while (true);
145 
146   for (;;)
147     std::remove(nullptr, nullptr, 1);
148   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
149   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
150 
151   for (std::remove(nullptr, nullptr, 1);;)
152     // CHECK-NOTES: [[@LINE-1]]:8: warning: the value {{.*}} should be used
153     // CHECK-NOTES: [[@LINE-2]]:8: note: cast {{.*}} this warning
154     ;
155 
156   for (;; std::remove(nullptr, nullptr, 1))
157     // CHECK-NOTES: [[@LINE-1]]:11: warning: the value {{.*}} should be used
158     // CHECK-NOTES: [[@LINE-2]]:11: note: cast {{.*}} this warning
159     ;
160 
161   for (auto C : "foo")
162     std::remove(nullptr, nullptr, 1);
163   // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
164   // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
165 
166   switch (1) {
167   case 1:
168     std::remove(nullptr, nullptr, 1);
169     // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
170     // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
171     break;
172   default:
173     std::remove(nullptr, nullptr, 1);
174     // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
175     // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
176     break;
177   }
178 
179   try {
180     std::remove(nullptr, nullptr, 1);
181     // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
182     // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
183   } catch (...) {
184     std::remove(nullptr, nullptr, 1);
185     // CHECK-NOTES: [[@LINE-1]]:5: warning: the value {{.*}} should be used
186     // CHECK-NOTES: [[@LINE-2]]:5: note: cast {{.*}} this warning
187   }
188 }
189 
noWarning()190 void noWarning() {
191   auto AsyncRetval1 = std::async(increment, 42);
192   auto AsyncRetval2 = std::async(std::launch::async, increment, 42);
193 
194   Foo FNoWarning;
195   auto LaunderRetval = std::launder(&FNoWarning);
196 
197   auto RemoveRetval = std::remove(nullptr, nullptr, 1);
198 
199   auto RemoveIfRetval = std::remove_if(nullptr, nullptr, nullptr);
200 
201   auto UniqueRetval = std::unique(nullptr, nullptr);
202 
203   std::unique_ptr<Foo> UPtrNoWarning;
204   auto ReleaseRetval = UPtrNoWarning.release();
205 
206   std::string StrNoWarning;
207   auto StrEmptyRetval = StrNoWarning.empty();
208 
209   std::vector<Foo> VecNoWarning;
210   auto VecEmptyRetval = VecNoWarning.empty();
211 
212   // test using the return value in different kinds of expressions
213   useFuture(std::async(increment, 42));
214   std::launder(&FNoWarning)->f();
215   delete std::launder(&FNoWarning);
216 
217   if (std::launder(&FNoWarning))
218     ;
219   for (; std::launder(&FNoWarning);)
220     ;
221   while (std::launder(&FNoWarning))
222     ;
223   do
224     ;
225   while (std::launder(&FNoWarning));
226   switch (std::unique(1, 1))
227     ;
228 
229   // cast to void should allow ignoring the return value
230   (void)std::async(increment, 42);
231 
232   // test discarding return value of functions that are not configured to be checked
233   increment(1);
234 
235   // test that the check is disabled inside GNU statement expressions
236   ({ std::async(increment, 42); });
237   auto StmtExprRetval = ({ std::async(increment, 42); });
238 }
239