1 | //===- UncheckedOptionalAccessModelTest.cpp -------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // FIXME: Move this to clang/unittests/Analysis/FlowSensitive/Models. |
9 | |
10 | #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" |
11 | #include "TestingSupport.h" |
12 | #include "clang/AST/ASTContext.h" |
13 | #include "clang/ASTMatchers/ASTMatchers.h" |
14 | #include "clang/Basic/SourceLocation.h" |
15 | #include "clang/Frontend/TextDiagnostic.h" |
16 | #include "clang/Tooling/Tooling.h" |
17 | #include "llvm/ADT/DenseSet.h" |
18 | #include "llvm/ADT/STLExtras.h" |
19 | #include "llvm/Support/Error.h" |
20 | #include "gmock/gmock.h" |
21 | #include "gtest/gtest.h" |
22 | #include <optional> |
23 | #include <string> |
24 | #include <utility> |
25 | #include <vector> |
26 | |
27 | using namespace clang; |
28 | using namespace dataflow; |
29 | using namespace test; |
30 | |
31 | using ::testing::ContainerEq; |
32 | |
33 | // FIXME: Move header definitions in separate file(s). |
34 | static constexpr char [] = R"( |
35 | #ifndef CSTDDEF_H |
36 | #define CSTDDEF_H |
37 | |
38 | namespace std { |
39 | |
40 | typedef decltype(sizeof(char)) size_t; |
41 | |
42 | using nullptr_t = decltype(nullptr); |
43 | |
44 | } // namespace std |
45 | |
46 | #endif // CSTDDEF_H |
47 | )" ; |
48 | |
49 | static constexpr char [] = R"( |
50 | #ifndef STD_TYPE_TRAITS_H |
51 | #define STD_TYPE_TRAITS_H |
52 | |
53 | #include "cstddef.h" |
54 | |
55 | namespace std { |
56 | |
57 | template <typename T, T V> |
58 | struct integral_constant { |
59 | static constexpr T value = V; |
60 | }; |
61 | |
62 | using true_type = integral_constant<bool, true>; |
63 | using false_type = integral_constant<bool, false>; |
64 | |
65 | template< class T > struct remove_reference {typedef T type;}; |
66 | template< class T > struct remove_reference<T&> {typedef T type;}; |
67 | template< class T > struct remove_reference<T&&> {typedef T type;}; |
68 | |
69 | template <class T> |
70 | using remove_reference_t = typename remove_reference<T>::type; |
71 | |
72 | template <class T> |
73 | struct remove_extent { |
74 | typedef T type; |
75 | }; |
76 | |
77 | template <class T> |
78 | struct remove_extent<T[]> { |
79 | typedef T type; |
80 | }; |
81 | |
82 | template <class T, size_t N> |
83 | struct remove_extent<T[N]> { |
84 | typedef T type; |
85 | }; |
86 | |
87 | template <class T> |
88 | struct is_array : false_type {}; |
89 | |
90 | template <class T> |
91 | struct is_array<T[]> : true_type {}; |
92 | |
93 | template <class T, size_t N> |
94 | struct is_array<T[N]> : true_type {}; |
95 | |
96 | template <class> |
97 | struct is_function : false_type {}; |
98 | |
99 | template <class Ret, class... Args> |
100 | struct is_function<Ret(Args...)> : true_type {}; |
101 | |
102 | namespace detail { |
103 | |
104 | template <class T> |
105 | struct type_identity { |
106 | using type = T; |
107 | }; // or use type_identity (since C++20) |
108 | |
109 | template <class T> |
110 | auto try_add_pointer(int) -> type_identity<typename remove_reference<T>::type*>; |
111 | template <class T> |
112 | auto try_add_pointer(...) -> type_identity<T>; |
113 | |
114 | } // namespace detail |
115 | |
116 | template <class T> |
117 | struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {}; |
118 | |
119 | template <bool B, class T, class F> |
120 | struct conditional { |
121 | typedef T type; |
122 | }; |
123 | |
124 | template <class T, class F> |
125 | struct conditional<false, T, F> { |
126 | typedef F type; |
127 | }; |
128 | |
129 | template <class T> |
130 | struct remove_cv { |
131 | typedef T type; |
132 | }; |
133 | template <class T> |
134 | struct remove_cv<const T> { |
135 | typedef T type; |
136 | }; |
137 | template <class T> |
138 | struct remove_cv<volatile T> { |
139 | typedef T type; |
140 | }; |
141 | template <class T> |
142 | struct remove_cv<const volatile T> { |
143 | typedef T type; |
144 | }; |
145 | |
146 | template <class T> |
147 | using remove_cv_t = typename remove_cv<T>::type; |
148 | |
149 | template <class T> |
150 | struct decay { |
151 | private: |
152 | typedef typename remove_reference<T>::type U; |
153 | |
154 | public: |
155 | typedef typename conditional< |
156 | is_array<U>::value, typename remove_extent<U>::type*, |
157 | typename conditional<is_function<U>::value, typename add_pointer<U>::type, |
158 | typename remove_cv<U>::type>::type>::type type; |
159 | }; |
160 | |
161 | template <bool B, class T = void> |
162 | struct enable_if {}; |
163 | |
164 | template <class T> |
165 | struct enable_if<true, T> { |
166 | typedef T type; |
167 | }; |
168 | |
169 | template <bool B, class T = void> |
170 | using enable_if_t = typename enable_if<B, T>::type; |
171 | |
172 | template <class T, class U> |
173 | struct is_same : false_type {}; |
174 | |
175 | template <class T> |
176 | struct is_same<T, T> : true_type {}; |
177 | |
178 | template <class T> |
179 | struct is_void : is_same<void, typename remove_cv<T>::type> {}; |
180 | |
181 | namespace detail { |
182 | |
183 | template <class T> |
184 | auto try_add_lvalue_reference(int) -> type_identity<T&>; |
185 | template <class T> |
186 | auto try_add_lvalue_reference(...) -> type_identity<T>; |
187 | |
188 | template <class T> |
189 | auto try_add_rvalue_reference(int) -> type_identity<T&&>; |
190 | template <class T> |
191 | auto try_add_rvalue_reference(...) -> type_identity<T>; |
192 | |
193 | } // namespace detail |
194 | |
195 | template <class T> |
196 | struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) { |
197 | }; |
198 | |
199 | template <class T> |
200 | struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) { |
201 | }; |
202 | |
203 | template <class T> |
204 | typename add_rvalue_reference<T>::type declval() noexcept; |
205 | |
206 | namespace detail { |
207 | |
208 | template <class T> |
209 | auto test_returnable(int) |
210 | -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{}); |
211 | template <class> |
212 | auto test_returnable(...) -> false_type; |
213 | |
214 | template <class From, class To> |
215 | auto test_implicitly_convertible(int) |
216 | -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{}); |
217 | template <class, class> |
218 | auto test_implicitly_convertible(...) -> false_type; |
219 | |
220 | } // namespace detail |
221 | |
222 | template <class From, class To> |
223 | struct is_convertible |
224 | : integral_constant<bool, |
225 | (decltype(detail::test_returnable<To>(0))::value && |
226 | decltype(detail::test_implicitly_convertible<From, To>( |
227 | 0))::value) || |
228 | (is_void<From>::value && is_void<To>::value)> {}; |
229 | |
230 | template <class From, class To> |
231 | inline constexpr bool is_convertible_v = is_convertible<From, To>::value; |
232 | |
233 | template <class...> |
234 | using void_t = void; |
235 | |
236 | template <class, class T, class... Args> |
237 | struct is_constructible_ : false_type {}; |
238 | |
239 | template <class T, class... Args> |
240 | struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...> |
241 | : true_type {}; |
242 | |
243 | template <class T, class... Args> |
244 | using is_constructible = is_constructible_<void_t<>, T, Args...>; |
245 | |
246 | template <class T, class... Args> |
247 | inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value; |
248 | |
249 | template <class _Tp> |
250 | struct __uncvref { |
251 | typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type; |
252 | }; |
253 | |
254 | template <class _Tp> |
255 | using __uncvref_t = typename __uncvref<_Tp>::type; |
256 | |
257 | template <bool _Val> |
258 | using _BoolConstant = integral_constant<bool, _Val>; |
259 | |
260 | template <class _Tp, class _Up> |
261 | using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>; |
262 | |
263 | template <class _Tp, class _Up> |
264 | using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>; |
265 | |
266 | template <bool> |
267 | struct _MetaBase; |
268 | template <> |
269 | struct _MetaBase<true> { |
270 | template <class _Tp, class _Up> |
271 | using _SelectImpl = _Tp; |
272 | template <template <class...> class _FirstFn, template <class...> class, |
273 | class... _Args> |
274 | using _SelectApplyImpl = _FirstFn<_Args...>; |
275 | template <class _First, class...> |
276 | using _FirstImpl = _First; |
277 | template <class, class _Second, class...> |
278 | using _SecondImpl = _Second; |
279 | template <class _Result, class _First, class... _Rest> |
280 | using _OrImpl = |
281 | typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>:: |
282 | template _OrImpl<_First, _Rest...>; |
283 | }; |
284 | |
285 | template <> |
286 | struct _MetaBase<false> { |
287 | template <class _Tp, class _Up> |
288 | using _SelectImpl = _Up; |
289 | template <template <class...> class, template <class...> class _SecondFn, |
290 | class... _Args> |
291 | using _SelectApplyImpl = _SecondFn<_Args...>; |
292 | template <class _Result, class...> |
293 | using _OrImpl = _Result; |
294 | }; |
295 | |
296 | template <bool _Cond, class _IfRes, class _ElseRes> |
297 | using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>; |
298 | |
299 | template <class... _Rest> |
300 | using _Or = typename _MetaBase<sizeof...(_Rest) != |
301 | 0>::template _OrImpl<false_type, _Rest...>; |
302 | |
303 | template <bool _Bp, class _Tp = void> |
304 | using __enable_if_t = typename enable_if<_Bp, _Tp>::type; |
305 | |
306 | template <class...> |
307 | using __expand_to_true = true_type; |
308 | template <class... _Pred> |
309 | __expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int); |
310 | template <class...> |
311 | false_type __and_helper(...); |
312 | template <class... _Pred> |
313 | using _And = decltype(__and_helper<_Pred...>(0)); |
314 | |
315 | template <class _Pred> |
316 | struct _Not : _BoolConstant<!_Pred::value> {}; |
317 | |
318 | struct __check_tuple_constructor_fail { |
319 | static constexpr bool __enable_explicit_default() { return false; } |
320 | static constexpr bool __enable_implicit_default() { return false; } |
321 | template <class...> |
322 | static constexpr bool __enable_explicit() { |
323 | return false; |
324 | } |
325 | template <class...> |
326 | static constexpr bool __enable_implicit() { |
327 | return false; |
328 | } |
329 | }; |
330 | |
331 | template <typename, typename _Tp> |
332 | struct __select_2nd { |
333 | typedef _Tp type; |
334 | }; |
335 | template <class _Tp, class _Arg> |
336 | typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())), |
337 | true_type>::type |
338 | __is_assignable_test(int); |
339 | template <class, class> |
340 | false_type __is_assignable_test(...); |
341 | template <class _Tp, class _Arg, |
342 | bool = is_void<_Tp>::value || is_void<_Arg>::value> |
343 | struct __is_assignable_imp |
344 | : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {}; |
345 | template <class _Tp, class _Arg> |
346 | struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {}; |
347 | template <class _Tp, class _Arg> |
348 | struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {}; |
349 | |
350 | template <class _Tp> |
351 | struct __libcpp_is_integral : public false_type {}; |
352 | template <> |
353 | struct __libcpp_is_integral<bool> : public true_type {}; |
354 | template <> |
355 | struct __libcpp_is_integral<char> : public true_type {}; |
356 | template <> |
357 | struct __libcpp_is_integral<signed char> : public true_type {}; |
358 | template <> |
359 | struct __libcpp_is_integral<unsigned char> : public true_type {}; |
360 | template <> |
361 | struct __libcpp_is_integral<wchar_t> : public true_type {}; |
362 | template <> |
363 | struct __libcpp_is_integral<short> : public true_type {}; // NOLINT |
364 | template <> |
365 | struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT |
366 | template <> |
367 | struct __libcpp_is_integral<int> : public true_type {}; |
368 | template <> |
369 | struct __libcpp_is_integral<unsigned int> : public true_type {}; |
370 | template <> |
371 | struct __libcpp_is_integral<long> : public true_type {}; // NOLINT |
372 | template <> |
373 | struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT |
374 | template <> |
375 | struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT |
376 | template <> // NOLINTNEXTLINE |
377 | struct __libcpp_is_integral<unsigned long long> : public true_type {}; |
378 | template <class _Tp> |
379 | struct is_integral |
380 | : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {}; |
381 | |
382 | template <class _Tp> |
383 | struct __libcpp_is_floating_point : public false_type {}; |
384 | template <> |
385 | struct __libcpp_is_floating_point<float> : public true_type {}; |
386 | template <> |
387 | struct __libcpp_is_floating_point<double> : public true_type {}; |
388 | template <> |
389 | struct __libcpp_is_floating_point<long double> : public true_type {}; |
390 | template <class _Tp> |
391 | struct is_floating_point |
392 | : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {}; |
393 | |
394 | template <class _Tp> |
395 | struct is_arithmetic |
396 | : public integral_constant<bool, is_integral<_Tp>::value || |
397 | is_floating_point<_Tp>::value> {}; |
398 | |
399 | template <class _Tp> |
400 | struct __libcpp_is_pointer : public false_type {}; |
401 | template <class _Tp> |
402 | struct __libcpp_is_pointer<_Tp*> : public true_type {}; |
403 | template <class _Tp> |
404 | struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> { |
405 | }; |
406 | |
407 | template <class _Tp> |
408 | struct __libcpp_is_member_pointer : public false_type {}; |
409 | template <class _Tp, class _Up> |
410 | struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {}; |
411 | template <class _Tp> |
412 | struct is_member_pointer |
413 | : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {}; |
414 | |
415 | template <class _Tp> |
416 | struct __libcpp_union : public false_type {}; |
417 | template <class _Tp> |
418 | struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {}; |
419 | |
420 | template <class T> |
421 | struct is_reference : false_type {}; |
422 | template <class T> |
423 | struct is_reference<T&> : true_type {}; |
424 | template <class T> |
425 | struct is_reference<T&&> : true_type {}; |
426 | |
427 | template <class T> |
428 | inline constexpr bool is_reference_v = is_reference<T>::value; |
429 | |
430 | struct __two { |
431 | char __lx[2]; |
432 | }; |
433 | |
434 | namespace __is_class_imp { |
435 | template <class _Tp> |
436 | char __test(int _Tp::*); |
437 | template <class _Tp> |
438 | __two __test(...); |
439 | } // namespace __is_class_imp |
440 | template <class _Tp> |
441 | struct is_class |
442 | : public integral_constant<bool, |
443 | sizeof(__is_class_imp::__test<_Tp>(0)) == 1 && |
444 | !is_union<_Tp>::value> {}; |
445 | |
446 | template <class _Tp> |
447 | struct __is_nullptr_t_impl : public false_type {}; |
448 | template <> |
449 | struct __is_nullptr_t_impl<nullptr_t> : public true_type {}; |
450 | template <class _Tp> |
451 | struct __is_nullptr_t |
452 | : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; |
453 | template <class _Tp> |
454 | struct is_null_pointer |
455 | : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; |
456 | |
457 | template <class _Tp> |
458 | struct is_enum |
459 | : public integral_constant< |
460 | bool, !is_void<_Tp>::value && !is_integral<_Tp>::value && |
461 | !is_floating_point<_Tp>::value && !is_array<_Tp>::value && |
462 | !is_pointer<_Tp>::value && !is_reference<_Tp>::value && |
463 | !is_member_pointer<_Tp>::value && !is_union<_Tp>::value && |
464 | !is_class<_Tp>::value && !is_function<_Tp>::value> {}; |
465 | |
466 | template <class _Tp> |
467 | struct is_scalar |
468 | : public integral_constant< |
469 | bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value || |
470 | is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value || |
471 | is_enum<_Tp>::value> {}; |
472 | template <> |
473 | struct is_scalar<nullptr_t> : public true_type {}; |
474 | |
475 | } // namespace std |
476 | |
477 | #endif // STD_TYPE_TRAITS_H |
478 | )" ; |
479 | |
480 | static constexpr char [] = R"( |
481 | #ifndef ABSL_TYPE_TRAITS_H |
482 | #define ABSL_TYPE_TRAITS_H |
483 | |
484 | #include "std_type_traits.h" |
485 | |
486 | namespace absl { |
487 | |
488 | template <typename... Ts> |
489 | struct conjunction : std::true_type {}; |
490 | |
491 | template <typename T, typename... Ts> |
492 | struct conjunction<T, Ts...> |
493 | : std::conditional<T::value, conjunction<Ts...>, T>::type {}; |
494 | |
495 | template <typename T> |
496 | struct conjunction<T> : T {}; |
497 | |
498 | template <typename T> |
499 | struct negation : std::integral_constant<bool, !T::value> {}; |
500 | |
501 | template <bool B, typename T = void> |
502 | using enable_if_t = typename std::enable_if<B, T>::type; |
503 | |
504 | } // namespace absl |
505 | |
506 | #endif // ABSL_TYPE_TRAITS_H |
507 | )" ; |
508 | |
509 | static constexpr char [] = R"( |
510 | #ifndef STRING_H |
511 | #define STRING_H |
512 | |
513 | namespace std { |
514 | |
515 | struct string { |
516 | string(const char*); |
517 | ~string(); |
518 | bool empty(); |
519 | }; |
520 | bool operator!=(const string &LHS, const char *RHS); |
521 | |
522 | } // namespace std |
523 | |
524 | #endif // STRING_H |
525 | )" ; |
526 | |
527 | static constexpr char [] = R"( |
528 | #ifndef UTILITY_H |
529 | #define UTILITY_H |
530 | |
531 | #include "std_type_traits.h" |
532 | |
533 | namespace std { |
534 | |
535 | template <typename T> |
536 | constexpr remove_reference_t<T>&& move(T&& x); |
537 | |
538 | template <typename T> |
539 | void swap(T& a, T& b) noexcept; |
540 | |
541 | } // namespace std |
542 | |
543 | #endif // UTILITY_H |
544 | )" ; |
545 | |
546 | static constexpr char [] = R"( |
547 | #ifndef INITIALIZER_LIST_H |
548 | #define INITIALIZER_LIST_H |
549 | |
550 | namespace std { |
551 | |
552 | template <typename T> |
553 | class initializer_list { |
554 | public: |
555 | initializer_list() noexcept; |
556 | }; |
557 | |
558 | } // namespace std |
559 | |
560 | #endif // INITIALIZER_LIST_H |
561 | )" ; |
562 | |
563 | static constexpr char [] = R"( |
564 | #include "std_initializer_list.h" |
565 | #include "std_type_traits.h" |
566 | #include "std_utility.h" |
567 | |
568 | namespace std { |
569 | |
570 | struct in_place_t {}; |
571 | constexpr in_place_t in_place; |
572 | |
573 | struct nullopt_t { |
574 | constexpr explicit nullopt_t() {} |
575 | }; |
576 | constexpr nullopt_t nullopt; |
577 | |
578 | template <class _Tp> |
579 | struct __optional_destruct_base { |
580 | constexpr void reset() noexcept; |
581 | }; |
582 | |
583 | template <class _Tp> |
584 | struct __optional_storage_base : __optional_destruct_base<_Tp> { |
585 | constexpr bool has_value() const noexcept; |
586 | }; |
587 | |
588 | template <typename _Tp> |
589 | class optional : private __optional_storage_base<_Tp> { |
590 | using __base = __optional_storage_base<_Tp>; |
591 | |
592 | public: |
593 | using value_type = _Tp; |
594 | |
595 | private: |
596 | struct _CheckOptionalArgsConstructor { |
597 | template <class _Up> |
598 | static constexpr bool __enable_implicit() { |
599 | return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>; |
600 | } |
601 | |
602 | template <class _Up> |
603 | static constexpr bool __enable_explicit() { |
604 | return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>; |
605 | } |
606 | }; |
607 | template <class _Up> |
608 | using _CheckOptionalArgsCtor = |
609 | _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value && |
610 | _IsNotSame<__uncvref_t<_Up>, optional>::value, |
611 | _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>; |
612 | template <class _QualUp> |
613 | struct _CheckOptionalLikeConstructor { |
614 | template <class _Up, class _Opt = optional<_Up>> |
615 | using __check_constructible_from_opt = |
616 | _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>, |
617 | is_constructible<_Tp, _Opt&&>, is_constructible<_Tp, _Opt const&&>, |
618 | is_convertible<_Opt&, _Tp>, is_convertible<_Opt const&, _Tp>, |
619 | is_convertible<_Opt&&, _Tp>, is_convertible<_Opt const&&, _Tp>>; |
620 | template <class _Up, class _QUp = _QualUp> |
621 | static constexpr bool __enable_implicit() { |
622 | return is_convertible<_QUp, _Tp>::value && |
623 | !__check_constructible_from_opt<_Up>::value; |
624 | } |
625 | template <class _Up, class _QUp = _QualUp> |
626 | static constexpr bool __enable_explicit() { |
627 | return !is_convertible<_QUp, _Tp>::value && |
628 | !__check_constructible_from_opt<_Up>::value; |
629 | } |
630 | }; |
631 | |
632 | template <class _Up, class _QualUp> |
633 | using _CheckOptionalLikeCtor = |
634 | _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value, |
635 | _CheckOptionalLikeConstructor<_QualUp>, |
636 | __check_tuple_constructor_fail>; |
637 | |
638 | |
639 | template <class _Up, class _QualUp> |
640 | using _CheckOptionalLikeAssign = _If< |
641 | _And< |
642 | _IsNotSame<_Up, _Tp>, |
643 | is_constructible<_Tp, _QualUp>, |
644 | is_assignable<_Tp&, _QualUp> |
645 | >::value, |
646 | _CheckOptionalLikeConstructor<_QualUp>, |
647 | __check_tuple_constructor_fail |
648 | >; |
649 | |
650 | public: |
651 | constexpr optional() noexcept {} |
652 | constexpr optional(const optional&) = default; |
653 | constexpr optional(optional&&) = default; |
654 | constexpr optional(nullopt_t) noexcept {} |
655 | |
656 | template < |
657 | class _InPlaceT, class... _Args, |
658 | class = enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, |
659 | is_constructible<value_type, _Args...>>::value>> |
660 | constexpr explicit optional(_InPlaceT, _Args&&... __args); |
661 | |
662 | template <class _Up, class... _Args, |
663 | class = enable_if_t<is_constructible_v< |
664 | value_type, initializer_list<_Up>&, _Args...>>> |
665 | constexpr explicit optional(in_place_t, initializer_list<_Up> __il, |
666 | _Args&&... __args); |
667 | |
668 | template < |
669 | class _Up = value_type, |
670 | enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), |
671 | int> = 0> |
672 | constexpr optional(_Up&& __v); |
673 | |
674 | template < |
675 | class _Up, |
676 | enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), |
677 | int> = 0> |
678 | constexpr explicit optional(_Up&& __v); |
679 | |
680 | template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: |
681 | template __enable_implicit<_Up>(), |
682 | int> = 0> |
683 | constexpr optional(const optional<_Up>& __v); |
684 | |
685 | template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: |
686 | template __enable_explicit<_Up>(), |
687 | int> = 0> |
688 | constexpr explicit optional(const optional<_Up>& __v); |
689 | |
690 | template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: |
691 | template __enable_implicit<_Up>(), |
692 | int> = 0> |
693 | constexpr optional(optional<_Up>&& __v); |
694 | |
695 | template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: |
696 | template __enable_explicit<_Up>(), |
697 | int> = 0> |
698 | constexpr explicit optional(optional<_Up>&& __v); |
699 | |
700 | constexpr optional& operator=(nullopt_t) noexcept; |
701 | |
702 | optional& operator=(const optional&); |
703 | |
704 | optional& operator=(optional&&); |
705 | |
706 | template <class _Up = value_type, |
707 | class = enable_if_t<_And<_IsNotSame<__uncvref_t<_Up>, optional>, |
708 | _Or<_IsNotSame<__uncvref_t<_Up>, value_type>, |
709 | _Not<is_scalar<value_type>>>, |
710 | is_constructible<value_type, _Up>, |
711 | is_assignable<value_type&, _Up>>::value>> |
712 | constexpr optional& operator=(_Up&& __v); |
713 | |
714 | template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>:: |
715 | template __enable_assign<_Up>(), |
716 | int> = 0> |
717 | constexpr optional& operator=(const optional<_Up>& __v); |
718 | |
719 | template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: |
720 | template __enable_assign<_Up>(), |
721 | int> = 0> |
722 | constexpr optional& operator=(optional<_Up>&& __v); |
723 | |
724 | const _Tp& operator*() const&; |
725 | _Tp& operator*() &; |
726 | const _Tp&& operator*() const&&; |
727 | _Tp&& operator*() &&; |
728 | |
729 | const _Tp* operator->() const; |
730 | _Tp* operator->(); |
731 | |
732 | const _Tp& value() const&; |
733 | _Tp& value() &; |
734 | const _Tp&& value() const&&; |
735 | _Tp&& value() &&; |
736 | |
737 | template <typename U> |
738 | constexpr _Tp value_or(U&& v) const&; |
739 | template <typename U> |
740 | _Tp value_or(U&& v) &&; |
741 | |
742 | template <typename... Args> |
743 | _Tp& emplace(Args&&... args); |
744 | |
745 | template <typename U, typename... Args> |
746 | _Tp& emplace(std::initializer_list<U> ilist, Args&&... args); |
747 | |
748 | using __base::reset; |
749 | |
750 | constexpr explicit operator bool() const noexcept; |
751 | using __base::has_value; |
752 | |
753 | constexpr void swap(optional& __opt) noexcept; |
754 | }; |
755 | |
756 | template <typename T> |
757 | constexpr optional<typename std::decay<T>::type> make_optional(T&& v); |
758 | |
759 | template <typename T, typename... Args> |
760 | constexpr optional<T> make_optional(Args&&... args); |
761 | |
762 | template <typename T, typename U, typename... Args> |
763 | constexpr optional<T> make_optional(std::initializer_list<U> il, |
764 | Args&&... args); |
765 | |
766 | template <typename T, typename U> |
767 | constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs); |
768 | template <typename T, typename U> |
769 | constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs); |
770 | |
771 | template <typename T> |
772 | constexpr bool operator==(const optional<T> &opt, nullopt_t); |
773 | |
774 | // C++20 and later do not define the following overloads because they are |
775 | // provided by rewritten candidates instead. |
776 | #if __cplusplus < 202002L |
777 | template <typename T> |
778 | constexpr bool operator==(nullopt_t, const optional<T> &opt); |
779 | template <typename T> |
780 | constexpr bool operator!=(const optional<T> &opt, nullopt_t); |
781 | template <typename T> |
782 | constexpr bool operator!=(nullopt_t, const optional<T> &opt); |
783 | #endif // __cplusplus < 202002L |
784 | |
785 | template <typename T, typename U> |
786 | constexpr bool operator==(const optional<T> &opt, const U &value); |
787 | template <typename T, typename U> |
788 | constexpr bool operator==(const T &value, const optional<U> &opt); |
789 | template <typename T, typename U> |
790 | constexpr bool operator!=(const optional<T> &opt, const U &value); |
791 | template <typename T, typename U> |
792 | constexpr bool operator!=(const T &value, const optional<U> &opt); |
793 | |
794 | } // namespace std |
795 | )" ; |
796 | |
797 | static constexpr char [] = R"( |
798 | #include "absl_type_traits.h" |
799 | #include "std_initializer_list.h" |
800 | #include "std_type_traits.h" |
801 | #include "std_utility.h" |
802 | |
803 | namespace absl { |
804 | |
805 | struct nullopt_t { |
806 | constexpr explicit nullopt_t() {} |
807 | }; |
808 | constexpr nullopt_t nullopt; |
809 | |
810 | struct in_place_t {}; |
811 | constexpr in_place_t in_place; |
812 | |
813 | template <typename T> |
814 | class optional; |
815 | |
816 | namespace optional_internal { |
817 | |
818 | template <typename T, typename U> |
819 | struct is_constructible_convertible_from_optional |
820 | : std::integral_constant< |
821 | bool, std::is_constructible<T, optional<U>&>::value || |
822 | std::is_constructible<T, optional<U>&&>::value || |
823 | std::is_constructible<T, const optional<U>&>::value || |
824 | std::is_constructible<T, const optional<U>&&>::value || |
825 | std::is_convertible<optional<U>&, T>::value || |
826 | std::is_convertible<optional<U>&&, T>::value || |
827 | std::is_convertible<const optional<U>&, T>::value || |
828 | std::is_convertible<const optional<U>&&, T>::value> {}; |
829 | |
830 | template <typename T, typename U> |
831 | struct is_constructible_convertible_assignable_from_optional |
832 | : std::integral_constant< |
833 | bool, is_constructible_convertible_from_optional<T, U>::value || |
834 | std::is_assignable<T&, optional<U>&>::value || |
835 | std::is_assignable<T&, optional<U>&&>::value || |
836 | std::is_assignable<T&, const optional<U>&>::value || |
837 | std::is_assignable<T&, const optional<U>&&>::value> {}; |
838 | |
839 | } // namespace optional_internal |
840 | |
841 | template <typename T> |
842 | class optional { |
843 | public: |
844 | constexpr optional() noexcept; |
845 | |
846 | constexpr optional(nullopt_t) noexcept; |
847 | |
848 | optional(const optional&) = default; |
849 | |
850 | optional(optional&&) = default; |
851 | |
852 | template <typename InPlaceT, typename... Args, |
853 | absl::enable_if_t<absl::conjunction< |
854 | std::is_same<InPlaceT, in_place_t>, |
855 | std::is_constructible<T, Args&&...>>::value>* = nullptr> |
856 | constexpr explicit optional(InPlaceT, Args&&... args); |
857 | |
858 | template <typename U, typename... Args, |
859 | typename = typename std::enable_if<std::is_constructible< |
860 | T, std::initializer_list<U>&, Args&&...>::value>::type> |
861 | constexpr explicit optional(in_place_t, std::initializer_list<U> il, |
862 | Args&&... args); |
863 | |
864 | template < |
865 | typename U = T, |
866 | typename std::enable_if< |
867 | absl::conjunction<absl::negation<std::is_same< |
868 | in_place_t, typename std::decay<U>::type>>, |
869 | absl::negation<std::is_same< |
870 | optional<T>, typename std::decay<U>::type>>, |
871 | std::is_convertible<U&&, T>, |
872 | std::is_constructible<T, U&&>>::value, |
873 | bool>::type = false> |
874 | constexpr optional(U&& v); |
875 | |
876 | template < |
877 | typename U = T, |
878 | typename std::enable_if< |
879 | absl::conjunction<absl::negation<std::is_same< |
880 | in_place_t, typename std::decay<U>::type>>, |
881 | absl::negation<std::is_same< |
882 | optional<T>, typename std::decay<U>::type>>, |
883 | absl::negation<std::is_convertible<U&&, T>>, |
884 | std::is_constructible<T, U&&>>::value, |
885 | bool>::type = false> |
886 | explicit constexpr optional(U&& v); |
887 | |
888 | template <typename U, |
889 | typename std::enable_if< |
890 | absl::conjunction< |
891 | absl::negation<std::is_same<T, U>>, |
892 | std::is_constructible<T, const U&>, |
893 | absl::negation< |
894 | optional_internal:: |
895 | is_constructible_convertible_from_optional<T, U>>, |
896 | std::is_convertible<const U&, T>>::value, |
897 | bool>::type = false> |
898 | optional(const optional<U>& rhs); |
899 | |
900 | template <typename U, |
901 | typename std::enable_if< |
902 | absl::conjunction< |
903 | absl::negation<std::is_same<T, U>>, |
904 | std::is_constructible<T, const U&>, |
905 | absl::negation< |
906 | optional_internal:: |
907 | is_constructible_convertible_from_optional<T, U>>, |
908 | absl::negation<std::is_convertible<const U&, T>>>::value, |
909 | bool>::type = false> |
910 | explicit optional(const optional<U>& rhs); |
911 | |
912 | template < |
913 | typename U, |
914 | typename std::enable_if< |
915 | absl::conjunction< |
916 | absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, |
917 | absl::negation< |
918 | optional_internal::is_constructible_convertible_from_optional< |
919 | T, U>>, |
920 | std::is_convertible<U&&, T>>::value, |
921 | bool>::type = false> |
922 | optional(optional<U>&& rhs); |
923 | |
924 | template < |
925 | typename U, |
926 | typename std::enable_if< |
927 | absl::conjunction< |
928 | absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, |
929 | absl::negation< |
930 | optional_internal::is_constructible_convertible_from_optional< |
931 | T, U>>, |
932 | absl::negation<std::is_convertible<U&&, T>>>::value, |
933 | bool>::type = false> |
934 | explicit optional(optional<U>&& rhs); |
935 | |
936 | optional& operator=(nullopt_t) noexcept; |
937 | |
938 | optional& operator=(const optional& src); |
939 | |
940 | optional& operator=(optional&& src); |
941 | |
942 | template < |
943 | typename U = T, |
944 | typename = typename std::enable_if<absl::conjunction< |
945 | absl::negation< |
946 | std::is_same<optional<T>, typename std::decay<U>::type>>, |
947 | absl::negation< |
948 | absl::conjunction<std::is_scalar<T>, |
949 | std::is_same<T, typename std::decay<U>::type>>>, |
950 | std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type> |
951 | optional& operator=(U&& v); |
952 | |
953 | template < |
954 | typename U, |
955 | typename = typename std::enable_if<absl::conjunction< |
956 | absl::negation<std::is_same<T, U>>, |
957 | std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>, |
958 | absl::negation< |
959 | optional_internal:: |
960 | is_constructible_convertible_assignable_from_optional< |
961 | T, U>>>::value>::type> |
962 | optional& operator=(const optional<U>& rhs); |
963 | |
964 | template <typename U, |
965 | typename = typename std::enable_if<absl::conjunction< |
966 | absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>, |
967 | std::is_assignable<T&, U>, |
968 | absl::negation< |
969 | optional_internal:: |
970 | is_constructible_convertible_assignable_from_optional< |
971 | T, U>>>::value>::type> |
972 | optional& operator=(optional<U>&& rhs); |
973 | |
974 | const T& operator*() const&; |
975 | T& operator*() &; |
976 | const T&& operator*() const&&; |
977 | T&& operator*() &&; |
978 | |
979 | const T* operator->() const; |
980 | T* operator->(); |
981 | |
982 | const T& value() const&; |
983 | T& value() &; |
984 | const T&& value() const&&; |
985 | T&& value() &&; |
986 | |
987 | template <typename U> |
988 | constexpr T value_or(U&& v) const&; |
989 | template <typename U> |
990 | T value_or(U&& v) &&; |
991 | |
992 | template <typename... Args> |
993 | T& emplace(Args&&... args); |
994 | |
995 | template <typename U, typename... Args> |
996 | T& emplace(std::initializer_list<U> ilist, Args&&... args); |
997 | |
998 | void reset() noexcept; |
999 | |
1000 | constexpr explicit operator bool() const noexcept; |
1001 | constexpr bool has_value() const noexcept; |
1002 | |
1003 | void swap(optional& rhs) noexcept; |
1004 | }; |
1005 | |
1006 | template <typename T> |
1007 | constexpr optional<typename std::decay<T>::type> make_optional(T&& v); |
1008 | |
1009 | template <typename T, typename... Args> |
1010 | constexpr optional<T> make_optional(Args&&... args); |
1011 | |
1012 | template <typename T, typename U, typename... Args> |
1013 | constexpr optional<T> make_optional(std::initializer_list<U> il, |
1014 | Args&&... args); |
1015 | |
1016 | template <typename T, typename U> |
1017 | constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs); |
1018 | template <typename T, typename U> |
1019 | constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs); |
1020 | |
1021 | template <typename T> |
1022 | constexpr bool operator==(const optional<T> &opt, nullopt_t); |
1023 | template <typename T> |
1024 | constexpr bool operator==(nullopt_t, const optional<T> &opt); |
1025 | template <typename T> |
1026 | constexpr bool operator!=(const optional<T> &opt, nullopt_t); |
1027 | template <typename T> |
1028 | constexpr bool operator!=(nullopt_t, const optional<T> &opt); |
1029 | |
1030 | template <typename T, typename U> |
1031 | constexpr bool operator==(const optional<T> &opt, const U &value); |
1032 | template <typename T, typename U> |
1033 | constexpr bool operator==(const T &value, const optional<U> &opt); |
1034 | template <typename T, typename U> |
1035 | constexpr bool operator!=(const optional<T> &opt, const U &value); |
1036 | template <typename T, typename U> |
1037 | constexpr bool operator!=(const T &value, const optional<U> &opt); |
1038 | |
1039 | } // namespace absl |
1040 | )" ; |
1041 | |
1042 | static constexpr char [] = R"( |
1043 | #include "std_initializer_list.h" |
1044 | #include "std_type_traits.h" |
1045 | #include "std_utility.h" |
1046 | |
1047 | namespace base { |
1048 | |
1049 | struct in_place_t {}; |
1050 | constexpr in_place_t in_place; |
1051 | |
1052 | struct nullopt_t { |
1053 | constexpr explicit nullopt_t() {} |
1054 | }; |
1055 | constexpr nullopt_t nullopt; |
1056 | |
1057 | template <typename T> |
1058 | class Optional; |
1059 | |
1060 | namespace internal { |
1061 | |
1062 | template <typename T> |
1063 | using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>; |
1064 | |
1065 | template <typename T, typename U> |
1066 | struct IsConvertibleFromOptional |
1067 | : std::integral_constant< |
1068 | bool, std::is_constructible<T, Optional<U>&>::value || |
1069 | std::is_constructible<T, const Optional<U>&>::value || |
1070 | std::is_constructible<T, Optional<U>&&>::value || |
1071 | std::is_constructible<T, const Optional<U>&&>::value || |
1072 | std::is_convertible<Optional<U>&, T>::value || |
1073 | std::is_convertible<const Optional<U>&, T>::value || |
1074 | std::is_convertible<Optional<U>&&, T>::value || |
1075 | std::is_convertible<const Optional<U>&&, T>::value> {}; |
1076 | |
1077 | template <typename T, typename U> |
1078 | struct IsAssignableFromOptional |
1079 | : std::integral_constant< |
1080 | bool, IsConvertibleFromOptional<T, U>::value || |
1081 | std::is_assignable<T&, Optional<U>&>::value || |
1082 | std::is_assignable<T&, const Optional<U>&>::value || |
1083 | std::is_assignable<T&, Optional<U>&&>::value || |
1084 | std::is_assignable<T&, const Optional<U>&&>::value> {}; |
1085 | |
1086 | } // namespace internal |
1087 | |
1088 | template <typename T> |
1089 | class Optional { |
1090 | public: |
1091 | using value_type = T; |
1092 | |
1093 | constexpr Optional() = default; |
1094 | constexpr Optional(const Optional& other) noexcept = default; |
1095 | constexpr Optional(Optional&& other) noexcept = default; |
1096 | |
1097 | constexpr Optional(nullopt_t); |
1098 | |
1099 | template <typename U, |
1100 | typename std::enable_if< |
1101 | std::is_constructible<T, const U&>::value && |
1102 | !internal::IsConvertibleFromOptional<T, U>::value && |
1103 | std::is_convertible<const U&, T>::value, |
1104 | bool>::type = false> |
1105 | Optional(const Optional<U>& other) noexcept; |
1106 | |
1107 | template <typename U, |
1108 | typename std::enable_if< |
1109 | std::is_constructible<T, const U&>::value && |
1110 | !internal::IsConvertibleFromOptional<T, U>::value && |
1111 | !std::is_convertible<const U&, T>::value, |
1112 | bool>::type = false> |
1113 | explicit Optional(const Optional<U>& other) noexcept; |
1114 | |
1115 | template <typename U, |
1116 | typename std::enable_if< |
1117 | std::is_constructible<T, U&&>::value && |
1118 | !internal::IsConvertibleFromOptional<T, U>::value && |
1119 | std::is_convertible<U&&, T>::value, |
1120 | bool>::type = false> |
1121 | Optional(Optional<U>&& other) noexcept; |
1122 | |
1123 | template <typename U, |
1124 | typename std::enable_if< |
1125 | std::is_constructible<T, U&&>::value && |
1126 | !internal::IsConvertibleFromOptional<T, U>::value && |
1127 | !std::is_convertible<U&&, T>::value, |
1128 | bool>::type = false> |
1129 | explicit Optional(Optional<U>&& other) noexcept; |
1130 | |
1131 | template <class... Args> |
1132 | constexpr explicit Optional(in_place_t, Args&&... args); |
1133 | |
1134 | template <class U, class... Args, |
1135 | class = typename std::enable_if<std::is_constructible< |
1136 | value_type, std::initializer_list<U>&, Args...>::value>::type> |
1137 | constexpr explicit Optional(in_place_t, std::initializer_list<U> il, |
1138 | Args&&... args); |
1139 | |
1140 | template < |
1141 | typename U = value_type, |
1142 | typename std::enable_if< |
1143 | std::is_constructible<T, U&&>::value && |
1144 | !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && |
1145 | !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && |
1146 | std::is_convertible<U&&, T>::value, |
1147 | bool>::type = false> |
1148 | constexpr Optional(U&& value); |
1149 | |
1150 | template < |
1151 | typename U = value_type, |
1152 | typename std::enable_if< |
1153 | std::is_constructible<T, U&&>::value && |
1154 | !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && |
1155 | !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && |
1156 | !std::is_convertible<U&&, T>::value, |
1157 | bool>::type = false> |
1158 | constexpr explicit Optional(U&& value); |
1159 | |
1160 | Optional& operator=(const Optional& other) noexcept; |
1161 | |
1162 | Optional& operator=(Optional&& other) noexcept; |
1163 | |
1164 | Optional& operator=(nullopt_t); |
1165 | |
1166 | template <typename U> |
1167 | typename std::enable_if< |
1168 | !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && |
1169 | std::is_constructible<T, U>::value && |
1170 | std::is_assignable<T&, U>::value && |
1171 | (!std::is_scalar<T>::value || |
1172 | !std::is_same<typename std::decay<U>::type, T>::value), |
1173 | Optional&>::type |
1174 | operator=(U&& value) noexcept; |
1175 | |
1176 | template <typename U> |
1177 | typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && |
1178 | std::is_constructible<T, const U&>::value && |
1179 | std::is_assignable<T&, const U&>::value, |
1180 | Optional&>::type |
1181 | operator=(const Optional<U>& other) noexcept; |
1182 | |
1183 | template <typename U> |
1184 | typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && |
1185 | std::is_constructible<T, U>::value && |
1186 | std::is_assignable<T&, U>::value, |
1187 | Optional&>::type |
1188 | operator=(Optional<U>&& other) noexcept; |
1189 | |
1190 | const T& operator*() const&; |
1191 | T& operator*() &; |
1192 | const T&& operator*() const&&; |
1193 | T&& operator*() &&; |
1194 | |
1195 | const T* operator->() const; |
1196 | T* operator->(); |
1197 | |
1198 | const T& value() const&; |
1199 | T& value() &; |
1200 | const T&& value() const&&; |
1201 | T&& value() &&; |
1202 | |
1203 | template <typename U> |
1204 | constexpr T value_or(U&& v) const&; |
1205 | template <typename U> |
1206 | T value_or(U&& v) &&; |
1207 | |
1208 | template <typename... Args> |
1209 | T& emplace(Args&&... args); |
1210 | |
1211 | template <typename U, typename... Args> |
1212 | T& emplace(std::initializer_list<U> ilist, Args&&... args); |
1213 | |
1214 | void reset() noexcept; |
1215 | |
1216 | constexpr explicit operator bool() const noexcept; |
1217 | constexpr bool has_value() const noexcept; |
1218 | |
1219 | void swap(Optional& other); |
1220 | }; |
1221 | |
1222 | template <typename T> |
1223 | constexpr Optional<typename std::decay<T>::type> make_optional(T&& v); |
1224 | |
1225 | template <typename T, typename... Args> |
1226 | constexpr Optional<T> make_optional(Args&&... args); |
1227 | |
1228 | template <typename T, typename U, typename... Args> |
1229 | constexpr Optional<T> make_optional(std::initializer_list<U> il, |
1230 | Args&&... args); |
1231 | |
1232 | template <typename T, typename U> |
1233 | constexpr bool operator==(const Optional<T> &lhs, const Optional<U> &rhs); |
1234 | template <typename T, typename U> |
1235 | constexpr bool operator!=(const Optional<T> &lhs, const Optional<U> &rhs); |
1236 | |
1237 | template <typename T> |
1238 | constexpr bool operator==(const Optional<T> &opt, nullopt_t); |
1239 | template <typename T> |
1240 | constexpr bool operator==(nullopt_t, const Optional<T> &opt); |
1241 | template <typename T> |
1242 | constexpr bool operator!=(const Optional<T> &opt, nullopt_t); |
1243 | template <typename T> |
1244 | constexpr bool operator!=(nullopt_t, const Optional<T> &opt); |
1245 | |
1246 | template <typename T, typename U> |
1247 | constexpr bool operator==(const Optional<T> &opt, const U &value); |
1248 | template <typename T, typename U> |
1249 | constexpr bool operator==(const T &value, const Optional<U> &opt); |
1250 | template <typename T, typename U> |
1251 | constexpr bool operator!=(const Optional<T> &opt, const U &value); |
1252 | template <typename T, typename U> |
1253 | constexpr bool operator!=(const T &value, const Optional<U> &opt); |
1254 | |
1255 | } // namespace base |
1256 | )" ; |
1257 | |
1258 | /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. |
1259 | static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern, |
1260 | const std::string &Replacement) { |
1261 | size_t Pos = 0; |
1262 | while (true) { |
1263 | Pos = S.find(str: Pattern, pos: Pos); |
1264 | if (Pos == std::string::npos) |
1265 | break; |
1266 | S.replace(pos: Pos, n: Pattern.size(), str: Replacement); |
1267 | } |
1268 | } |
1269 | |
1270 | struct OptionalTypeIdentifier { |
1271 | std::string NamespaceName; |
1272 | std::string TypeName; |
1273 | }; |
1274 | |
1275 | static raw_ostream &operator<<(raw_ostream &OS, |
1276 | const OptionalTypeIdentifier &TypeId) { |
1277 | OS << TypeId.NamespaceName << "::" << TypeId.TypeName; |
1278 | return OS; |
1279 | } |
1280 | |
1281 | class UncheckedOptionalAccessTest |
1282 | : public ::testing::TestWithParam<OptionalTypeIdentifier> { |
1283 | protected: |
1284 | void ExpectDiagnosticsFor(std::string SourceCode) { |
1285 | ExpectDiagnosticsFor(SourceCode, FuncMatcher: ast_matchers::hasName(Name: "target" )); |
1286 | } |
1287 | |
1288 | void ExpectDiagnosticsForLambda(std::string SourceCode) { |
1289 | ExpectDiagnosticsFor( |
1290 | SourceCode, FuncMatcher: ast_matchers::hasDeclContext( |
1291 | InnerMatcher: ast_matchers::cxxRecordDecl(ast_matchers::isLambda()))); |
1292 | } |
1293 | |
1294 | template <typename FuncDeclMatcher> |
1295 | void ExpectDiagnosticsFor(std::string SourceCode, |
1296 | FuncDeclMatcher FuncMatcher) { |
1297 | // Run in C++17 and C++20 mode to cover differences in the AST between modes |
1298 | // (e.g. C++20 can contain `CXXRewrittenBinaryOperator`). |
1299 | for (const char *CxxMode : {"-std=c++17" , "-std=c++20" }) |
1300 | ExpectDiagnosticsFor(SourceCode, FuncMatcher, CxxMode); |
1301 | } |
1302 | |
1303 | template <typename FuncDeclMatcher> |
1304 | void ExpectDiagnosticsFor(std::string SourceCode, FuncDeclMatcher FuncMatcher, |
1305 | const char *CxxMode) { |
1306 | ReplaceAllOccurrences(S&: SourceCode, Pattern: "$ns" , Replacement: GetParam().NamespaceName); |
1307 | ReplaceAllOccurrences(S&: SourceCode, Pattern: "$optional" , Replacement: GetParam().TypeName); |
1308 | |
1309 | std::vector<std::pair<std::string, std::string>> ; |
1310 | Headers.emplace_back(args: "cstddef.h" , args: CSDtdDefHeader); |
1311 | Headers.emplace_back(args: "std_initializer_list.h" , args: StdInitializerListHeader); |
1312 | Headers.emplace_back(args: "std_string.h" , args: StdStringHeader); |
1313 | Headers.emplace_back(args: "std_type_traits.h" , args: StdTypeTraitsHeader); |
1314 | Headers.emplace_back(args: "std_utility.h" , args: StdUtilityHeader); |
1315 | Headers.emplace_back(args: "std_optional.h" , args: StdOptionalHeader); |
1316 | Headers.emplace_back(args: "absl_type_traits.h" , args: AbslTypeTraitsHeader); |
1317 | Headers.emplace_back(args: "absl_optional.h" , args: AbslOptionalHeader); |
1318 | Headers.emplace_back(args: "base_optional.h" , args: BaseOptionalHeader); |
1319 | Headers.emplace_back(args: "unchecked_optional_access_test.h" , args: R"( |
1320 | #include "absl_optional.h" |
1321 | #include "base_optional.h" |
1322 | #include "std_initializer_list.h" |
1323 | #include "std_optional.h" |
1324 | #include "std_string.h" |
1325 | #include "std_utility.h" |
1326 | |
1327 | template <typename T> |
1328 | T Make(); |
1329 | )" ); |
1330 | UncheckedOptionalAccessModelOptions Options{ |
1331 | /*IgnoreSmartPointerDereference=*/true}; |
1332 | std::vector<SourceLocation> Diagnostics; |
1333 | llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>( |
1334 | AnalysisInputs<UncheckedOptionalAccessModel>( |
1335 | SourceCode, std::move(FuncMatcher), |
1336 | [](ASTContext &Ctx, Environment &Env) { |
1337 | return UncheckedOptionalAccessModel(Ctx, Env); |
1338 | }) |
1339 | .withPostVisitCFG( |
1340 | Arg: [&Diagnostics, |
1341 | Diagnoser = UncheckedOptionalAccessDiagnoser(Options)]( |
1342 | ASTContext &Ctx, const CFGElement &Elt, |
1343 | const TransferStateForDiagnostics<NoopLattice> |
1344 | &State) mutable { |
1345 | auto EltDiagnostics = Diagnoser(Elt, Ctx, State); |
1346 | llvm::move(Range&: EltDiagnostics, Out: std::back_inserter(x&: Diagnostics)); |
1347 | }) |
1348 | .withASTBuildArgs( |
1349 | {"-fsyntax-only" , CxxMode, "-Wno-undefined-inline" }) |
1350 | .withASTBuildVirtualMappedFiles( |
1351 | tooling::FileContentMappings(Headers.begin(), Headers.end())), |
1352 | /*VerifyResults=*/[&Diagnostics]( |
1353 | const llvm::DenseMap<unsigned, std::string> |
1354 | &Annotations, |
1355 | const AnalysisOutputs &AO) { |
1356 | llvm::DenseSet<unsigned> AnnotationLines; |
1357 | for (const auto &[Line, _] : Annotations) { |
1358 | AnnotationLines.insert(V: Line); |
1359 | } |
1360 | auto &SrcMgr = AO.ASTCtx.getSourceManager(); |
1361 | llvm::DenseSet<unsigned> DiagnosticLines; |
1362 | for (SourceLocation &Loc : Diagnostics) { |
1363 | unsigned Line = SrcMgr.getPresumedLineNumber(Loc); |
1364 | DiagnosticLines.insert(V: Line); |
1365 | if (!AnnotationLines.contains(V: Line)) { |
1366 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts( |
1367 | new DiagnosticOptions()); |
1368 | TextDiagnostic TD(llvm::errs(), AO.ASTCtx.getLangOpts(), |
1369 | DiagOpts.get()); |
1370 | TD.emitDiagnostic( |
1371 | Loc: FullSourceLoc(Loc, SrcMgr), Level: DiagnosticsEngine::Error, |
1372 | Message: "unexpected diagnostic" , Ranges: std::nullopt, FixItHints: std::nullopt); |
1373 | } |
1374 | } |
1375 | |
1376 | EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines)); |
1377 | }); |
1378 | if (Error) |
1379 | FAIL() << llvm::toString(E: std::move(Error)); |
1380 | } |
1381 | }; |
1382 | |
1383 | INSTANTIATE_TEST_SUITE_P( |
1384 | UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest, |
1385 | ::testing::Values(OptionalTypeIdentifier{"std" , "optional" }, |
1386 | OptionalTypeIdentifier{"absl" , "optional" }, |
1387 | OptionalTypeIdentifier{"base" , "Optional" }), |
1388 | [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) { |
1389 | return Info.param.NamespaceName; |
1390 | }); |
1391 | |
1392 | // Verifies that similarly-named types are ignored. |
1393 | TEST_P(UncheckedOptionalAccessTest, NonTrackedOptionalType) { |
1394 | ExpectDiagnosticsFor( |
1395 | SourceCode: R"( |
1396 | namespace other { |
1397 | namespace $ns { |
1398 | template <typename T> |
1399 | struct $optional { |
1400 | T value(); |
1401 | }; |
1402 | } |
1403 | |
1404 | void target($ns::$optional<int> opt) { |
1405 | opt.value(); |
1406 | } |
1407 | } |
1408 | )" ); |
1409 | } |
1410 | |
1411 | TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { |
1412 | ExpectDiagnosticsFor(SourceCode: R"( |
1413 | void target() { |
1414 | (void)0; |
1415 | } |
1416 | )" ); |
1417 | } |
1418 | |
1419 | TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { |
1420 | ExpectDiagnosticsFor( |
1421 | SourceCode: R"( |
1422 | #include "unchecked_optional_access_test.h" |
1423 | |
1424 | void target($ns::$optional<int> opt) { |
1425 | opt.value(); // [[unsafe]] |
1426 | } |
1427 | )" ); |
1428 | |
1429 | ExpectDiagnosticsFor( |
1430 | SourceCode: R"( |
1431 | #include "unchecked_optional_access_test.h" |
1432 | |
1433 | void target($ns::$optional<int> opt) { |
1434 | std::move(opt).value(); // [[unsafe]] |
1435 | } |
1436 | )" ); |
1437 | } |
1438 | |
1439 | TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { |
1440 | ExpectDiagnosticsFor( |
1441 | SourceCode: R"( |
1442 | #include "unchecked_optional_access_test.h" |
1443 | |
1444 | void target($ns::$optional<int> opt) { |
1445 | *opt; // [[unsafe]] |
1446 | } |
1447 | )" ); |
1448 | |
1449 | ExpectDiagnosticsFor( |
1450 | SourceCode: R"( |
1451 | #include "unchecked_optional_access_test.h" |
1452 | |
1453 | void target($ns::$optional<int> opt) { |
1454 | *std::move(opt); // [[unsafe]] |
1455 | } |
1456 | )" ); |
1457 | } |
1458 | |
1459 | TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { |
1460 | ExpectDiagnosticsFor( |
1461 | SourceCode: R"( |
1462 | #include "unchecked_optional_access_test.h" |
1463 | |
1464 | struct Foo { |
1465 | void foo(); |
1466 | }; |
1467 | |
1468 | void target($ns::$optional<Foo> opt) { |
1469 | opt->foo(); // [[unsafe]] |
1470 | } |
1471 | )" ); |
1472 | |
1473 | ExpectDiagnosticsFor( |
1474 | SourceCode: R"( |
1475 | #include "unchecked_optional_access_test.h" |
1476 | |
1477 | struct Foo { |
1478 | void foo(); |
1479 | }; |
1480 | |
1481 | void target($ns::$optional<Foo> opt) { |
1482 | std::move(opt)->foo(); // [[unsafe]] |
1483 | } |
1484 | )" ); |
1485 | } |
1486 | |
1487 | TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { |
1488 | ExpectDiagnosticsFor(SourceCode: R"( |
1489 | #include "unchecked_optional_access_test.h" |
1490 | |
1491 | void target($ns::$optional<int> opt) { |
1492 | if (opt.has_value()) { |
1493 | opt.value(); |
1494 | } |
1495 | } |
1496 | )" ); |
1497 | } |
1498 | |
1499 | TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { |
1500 | ExpectDiagnosticsFor(SourceCode: R"( |
1501 | #include "unchecked_optional_access_test.h" |
1502 | |
1503 | void target($ns::$optional<int> opt) { |
1504 | if (opt) { |
1505 | opt.value(); |
1506 | } |
1507 | } |
1508 | )" ); |
1509 | } |
1510 | |
1511 | TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { |
1512 | ExpectDiagnosticsFor( |
1513 | SourceCode: R"( |
1514 | #include "unchecked_optional_access_test.h" |
1515 | |
1516 | void target() { |
1517 | Make<$ns::$optional<int>>().value(); // [[unsafe]] |
1518 | (void)0; |
1519 | } |
1520 | )" ); |
1521 | |
1522 | ExpectDiagnosticsFor( |
1523 | SourceCode: R"( |
1524 | #include "unchecked_optional_access_test.h" |
1525 | |
1526 | void target($ns::$optional<int> opt) { |
1527 | std::move(opt).value(); // [[unsafe]] |
1528 | } |
1529 | )" ); |
1530 | } |
1531 | |
1532 | TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { |
1533 | ExpectDiagnosticsFor( |
1534 | SourceCode: R"( |
1535 | #include "unchecked_optional_access_test.h" |
1536 | |
1537 | void target() { |
1538 | $ns::$optional<int> opt; |
1539 | opt.value(); // [[unsafe]] |
1540 | } |
1541 | )" ); |
1542 | } |
1543 | |
1544 | TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { |
1545 | ExpectDiagnosticsFor( |
1546 | SourceCode: R"( |
1547 | #include "unchecked_optional_access_test.h" |
1548 | |
1549 | void target() { |
1550 | $ns::$optional<int> opt($ns::nullopt); |
1551 | opt.value(); // [[unsafe]] |
1552 | } |
1553 | )" ); |
1554 | } |
1555 | |
1556 | TEST_P(UncheckedOptionalAccessTest, NulloptConstructorWithSugaredType) { |
1557 | ExpectDiagnosticsFor( |
1558 | SourceCode: R"( |
1559 | #include "unchecked_optional_access_test.h" |
1560 | template <typename T> |
1561 | using wrapper = T; |
1562 | |
1563 | template <typename T> |
1564 | wrapper<T> wrap(T); |
1565 | |
1566 | void target() { |
1567 | $ns::$optional<int> opt(wrap($ns::nullopt)); |
1568 | opt.value(); // [[unsafe]] |
1569 | } |
1570 | )" ); |
1571 | } |
1572 | |
1573 | TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { |
1574 | ExpectDiagnosticsFor(SourceCode: R"( |
1575 | #include "unchecked_optional_access_test.h" |
1576 | |
1577 | void target() { |
1578 | $ns::$optional<int> opt($ns::in_place, 3); |
1579 | opt.value(); |
1580 | } |
1581 | )" ); |
1582 | |
1583 | ExpectDiagnosticsFor(SourceCode: R"( |
1584 | #include "unchecked_optional_access_test.h" |
1585 | |
1586 | struct Foo {}; |
1587 | |
1588 | void target() { |
1589 | $ns::$optional<Foo> opt($ns::in_place); |
1590 | opt.value(); |
1591 | } |
1592 | )" ); |
1593 | |
1594 | ExpectDiagnosticsFor(SourceCode: R"( |
1595 | #include "unchecked_optional_access_test.h" |
1596 | |
1597 | struct Foo { |
1598 | explicit Foo(int, bool); |
1599 | }; |
1600 | |
1601 | void target() { |
1602 | $ns::$optional<Foo> opt($ns::in_place, 3, false); |
1603 | opt.value(); |
1604 | } |
1605 | )" ); |
1606 | |
1607 | ExpectDiagnosticsFor(SourceCode: R"( |
1608 | #include "unchecked_optional_access_test.h" |
1609 | |
1610 | struct Foo { |
1611 | explicit Foo(std::initializer_list<int>); |
1612 | }; |
1613 | |
1614 | void target() { |
1615 | $ns::$optional<Foo> opt($ns::in_place, {3}); |
1616 | opt.value(); |
1617 | } |
1618 | )" ); |
1619 | } |
1620 | |
1621 | TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { |
1622 | ExpectDiagnosticsFor(SourceCode: R"( |
1623 | #include "unchecked_optional_access_test.h" |
1624 | |
1625 | void target() { |
1626 | $ns::$optional<int> opt(21); |
1627 | opt.value(); |
1628 | } |
1629 | )" ); |
1630 | |
1631 | ExpectDiagnosticsFor(SourceCode: R"( |
1632 | #include "unchecked_optional_access_test.h" |
1633 | |
1634 | void target() { |
1635 | $ns::$optional<int> opt = $ns::$optional<int>(21); |
1636 | opt.value(); |
1637 | } |
1638 | )" ); |
1639 | ExpectDiagnosticsFor(SourceCode: R"( |
1640 | #include "unchecked_optional_access_test.h" |
1641 | |
1642 | void target() { |
1643 | $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>()); |
1644 | opt.value(); |
1645 | } |
1646 | )" ); |
1647 | |
1648 | ExpectDiagnosticsFor(SourceCode: R"( |
1649 | #include "unchecked_optional_access_test.h" |
1650 | |
1651 | struct MyString { |
1652 | MyString(const char*); |
1653 | }; |
1654 | |
1655 | void target() { |
1656 | $ns::$optional<MyString> opt("foo"); |
1657 | opt.value(); |
1658 | } |
1659 | )" ); |
1660 | |
1661 | ExpectDiagnosticsFor(SourceCode: R"( |
1662 | #include "unchecked_optional_access_test.h" |
1663 | |
1664 | struct Foo {}; |
1665 | |
1666 | struct Bar { |
1667 | Bar(const Foo&); |
1668 | }; |
1669 | |
1670 | void target() { |
1671 | $ns::$optional<Bar> opt(Make<Foo>()); |
1672 | opt.value(); |
1673 | } |
1674 | )" ); |
1675 | |
1676 | ExpectDiagnosticsFor(SourceCode: R"( |
1677 | #include "unchecked_optional_access_test.h" |
1678 | |
1679 | struct Foo { |
1680 | explicit Foo(int); |
1681 | }; |
1682 | |
1683 | void target() { |
1684 | $ns::$optional<Foo> opt(3); |
1685 | opt.value(); |
1686 | } |
1687 | )" ); |
1688 | } |
1689 | |
1690 | TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { |
1691 | ExpectDiagnosticsFor( |
1692 | SourceCode: R"( |
1693 | #include "unchecked_optional_access_test.h" |
1694 | |
1695 | struct Foo {}; |
1696 | |
1697 | struct Bar { |
1698 | Bar(const Foo&); |
1699 | }; |
1700 | |
1701 | void target() { |
1702 | $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); |
1703 | opt.value(); // [[unsafe]] |
1704 | } |
1705 | )" ); |
1706 | |
1707 | ExpectDiagnosticsFor( |
1708 | SourceCode: R"( |
1709 | #include "unchecked_optional_access_test.h" |
1710 | |
1711 | struct Foo {}; |
1712 | |
1713 | struct Bar { |
1714 | explicit Bar(const Foo&); |
1715 | }; |
1716 | |
1717 | void target() { |
1718 | $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); |
1719 | opt.value(); // [[unsafe]] |
1720 | } |
1721 | )" ); |
1722 | |
1723 | ExpectDiagnosticsFor( |
1724 | SourceCode: R"( |
1725 | #include "unchecked_optional_access_test.h" |
1726 | |
1727 | struct Foo {}; |
1728 | |
1729 | struct Bar { |
1730 | Bar(const Foo&); |
1731 | }; |
1732 | |
1733 | void target() { |
1734 | $ns::$optional<Foo> opt1 = $ns::nullopt; |
1735 | $ns::$optional<Bar> opt2(opt1); |
1736 | opt2.value(); // [[unsafe]] |
1737 | } |
1738 | )" ); |
1739 | |
1740 | ExpectDiagnosticsFor(SourceCode: R"( |
1741 | #include "unchecked_optional_access_test.h" |
1742 | |
1743 | struct Foo {}; |
1744 | |
1745 | struct Bar { |
1746 | Bar(const Foo&); |
1747 | }; |
1748 | |
1749 | void target() { |
1750 | $ns::$optional<Foo> opt1(Make<Foo>()); |
1751 | $ns::$optional<Bar> opt2(opt1); |
1752 | opt2.value(); |
1753 | } |
1754 | )" ); |
1755 | |
1756 | ExpectDiagnosticsFor(SourceCode: R"( |
1757 | #include "unchecked_optional_access_test.h" |
1758 | |
1759 | struct Foo {}; |
1760 | |
1761 | struct Bar { |
1762 | explicit Bar(const Foo&); |
1763 | }; |
1764 | |
1765 | void target() { |
1766 | $ns::$optional<Foo> opt1(Make<Foo>()); |
1767 | $ns::$optional<Bar> opt2(opt1); |
1768 | opt2.value(); |
1769 | } |
1770 | )" ); |
1771 | } |
1772 | |
1773 | TEST_P(UncheckedOptionalAccessTest, MakeOptional) { |
1774 | ExpectDiagnosticsFor(SourceCode: R"( |
1775 | #include "unchecked_optional_access_test.h" |
1776 | |
1777 | void target() { |
1778 | $ns::$optional<int> opt = $ns::make_optional(0); |
1779 | opt.value(); |
1780 | } |
1781 | )" ); |
1782 | |
1783 | ExpectDiagnosticsFor(SourceCode: R"( |
1784 | #include "unchecked_optional_access_test.h" |
1785 | |
1786 | struct Foo { |
1787 | Foo(int, int); |
1788 | }; |
1789 | |
1790 | void target() { |
1791 | $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22); |
1792 | opt.value(); |
1793 | } |
1794 | )" ); |
1795 | |
1796 | ExpectDiagnosticsFor(SourceCode: R"( |
1797 | #include "unchecked_optional_access_test.h" |
1798 | |
1799 | struct Foo { |
1800 | constexpr Foo(std::initializer_list<char>); |
1801 | }; |
1802 | |
1803 | void target() { |
1804 | char a = 'a'; |
1805 | $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a}); |
1806 | opt.value(); |
1807 | } |
1808 | )" ); |
1809 | } |
1810 | |
1811 | TEST_P(UncheckedOptionalAccessTest, ValueOr) { |
1812 | ExpectDiagnosticsFor(SourceCode: R"( |
1813 | #include "unchecked_optional_access_test.h" |
1814 | |
1815 | void target() { |
1816 | $ns::$optional<int> opt; |
1817 | opt.value_or(0); |
1818 | (void)0; |
1819 | } |
1820 | )" ); |
1821 | } |
1822 | |
1823 | TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointers) { |
1824 | ExpectDiagnosticsFor( |
1825 | SourceCode: R"code( |
1826 | #include "unchecked_optional_access_test.h" |
1827 | |
1828 | void target($ns::$optional<int*> opt) { |
1829 | if (opt.value_or(nullptr) != nullptr) { |
1830 | opt.value(); |
1831 | } else { |
1832 | opt.value(); // [[unsafe]] |
1833 | } |
1834 | } |
1835 | )code" ); |
1836 | } |
1837 | |
1838 | TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonIntegers) { |
1839 | ExpectDiagnosticsFor( |
1840 | SourceCode: R"code( |
1841 | #include "unchecked_optional_access_test.h" |
1842 | |
1843 | void target($ns::$optional<int> opt) { |
1844 | if (opt.value_or(0) != 0) { |
1845 | opt.value(); |
1846 | } else { |
1847 | opt.value(); // [[unsafe]] |
1848 | } |
1849 | } |
1850 | )code" ); |
1851 | } |
1852 | |
1853 | TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonStrings) { |
1854 | ExpectDiagnosticsFor( |
1855 | SourceCode: R"code( |
1856 | #include "unchecked_optional_access_test.h" |
1857 | |
1858 | void target($ns::$optional<std::string> opt) { |
1859 | if (!opt.value_or("").empty()) { |
1860 | opt.value(); |
1861 | } else { |
1862 | opt.value(); // [[unsafe]] |
1863 | } |
1864 | } |
1865 | )code" ); |
1866 | |
1867 | ExpectDiagnosticsFor( |
1868 | SourceCode: R"code( |
1869 | #include "unchecked_optional_access_test.h" |
1870 | |
1871 | void target($ns::$optional<std::string> opt) { |
1872 | if (opt.value_or("") != "") { |
1873 | opt.value(); |
1874 | } else { |
1875 | opt.value(); // [[unsafe]] |
1876 | } |
1877 | } |
1878 | )code" ); |
1879 | } |
1880 | |
1881 | TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointerToOptional) { |
1882 | // FIXME: make `opt` a parameter directly, once we ensure that all `optional` |
1883 | // values have a `has_value` property. |
1884 | ExpectDiagnosticsFor( |
1885 | SourceCode: R"code( |
1886 | #include "unchecked_optional_access_test.h" |
1887 | |
1888 | void target($ns::$optional<int> p) { |
1889 | $ns::$optional<int> *opt = &p; |
1890 | if (opt->value_or(0) != 0) { |
1891 | opt->value(); |
1892 | } else { |
1893 | opt->value(); // [[unsafe]] |
1894 | } |
1895 | } |
1896 | )code" ); |
1897 | } |
1898 | |
1899 | TEST_P(UncheckedOptionalAccessTest, Emplace) { |
1900 | ExpectDiagnosticsFor(SourceCode: R"( |
1901 | #include "unchecked_optional_access_test.h" |
1902 | |
1903 | void target() { |
1904 | $ns::$optional<int> opt; |
1905 | opt.emplace(0); |
1906 | opt.value(); |
1907 | } |
1908 | )" ); |
1909 | |
1910 | ExpectDiagnosticsFor(SourceCode: R"( |
1911 | #include "unchecked_optional_access_test.h" |
1912 | |
1913 | void target($ns::$optional<int> *opt) { |
1914 | opt->emplace(0); |
1915 | opt->value(); |
1916 | } |
1917 | )" ); |
1918 | |
1919 | // FIXME: Add tests that call `emplace` in conditional branches: |
1920 | // ExpectDiagnosticsFor( |
1921 | // R"( |
1922 | // #include "unchecked_optional_access_test.h" |
1923 | // |
1924 | // void target($ns::$optional<int> opt, bool b) { |
1925 | // if (b) { |
1926 | // opt.emplace(0); |
1927 | // } |
1928 | // if (b) { |
1929 | // opt.value(); |
1930 | // } else { |
1931 | // opt.value(); // [[unsafe]] |
1932 | // } |
1933 | // } |
1934 | // )"); |
1935 | } |
1936 | |
1937 | TEST_P(UncheckedOptionalAccessTest, Reset) { |
1938 | ExpectDiagnosticsFor( |
1939 | SourceCode: R"( |
1940 | #include "unchecked_optional_access_test.h" |
1941 | |
1942 | void target() { |
1943 | $ns::$optional<int> opt = $ns::make_optional(0); |
1944 | opt.reset(); |
1945 | opt.value(); // [[unsafe]] |
1946 | } |
1947 | )" ); |
1948 | |
1949 | ExpectDiagnosticsFor( |
1950 | SourceCode: R"( |
1951 | #include "unchecked_optional_access_test.h" |
1952 | |
1953 | void target($ns::$optional<int> &opt) { |
1954 | if (opt.has_value()) { |
1955 | opt.reset(); |
1956 | opt.value(); // [[unsafe]] |
1957 | } |
1958 | } |
1959 | )" ); |
1960 | |
1961 | // FIXME: Add tests that call `reset` in conditional branches: |
1962 | // ExpectDiagnosticsFor( |
1963 | // R"( |
1964 | // #include "unchecked_optional_access_test.h" |
1965 | // |
1966 | // void target(bool b) { |
1967 | // $ns::$optional<int> opt = $ns::make_optional(0); |
1968 | // if (b) { |
1969 | // opt.reset(); |
1970 | // } |
1971 | // if (b) { |
1972 | // opt.value(); // [[unsafe]] |
1973 | // } else { |
1974 | // opt.value(); |
1975 | // } |
1976 | // } |
1977 | // )"); |
1978 | } |
1979 | |
1980 | TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { |
1981 | ExpectDiagnosticsFor(SourceCode: R"( |
1982 | #include "unchecked_optional_access_test.h" |
1983 | |
1984 | struct Foo {}; |
1985 | |
1986 | void target() { |
1987 | $ns::$optional<Foo> opt; |
1988 | opt = Foo(); |
1989 | opt.value(); |
1990 | } |
1991 | )" ); |
1992 | |
1993 | ExpectDiagnosticsFor(SourceCode: R"( |
1994 | #include "unchecked_optional_access_test.h" |
1995 | |
1996 | struct Foo {}; |
1997 | |
1998 | void target() { |
1999 | $ns::$optional<Foo> opt; |
2000 | (opt = Foo()).value(); |
2001 | (void)0; |
2002 | } |
2003 | )" ); |
2004 | |
2005 | ExpectDiagnosticsFor(SourceCode: R"( |
2006 | #include "unchecked_optional_access_test.h" |
2007 | |
2008 | struct MyString { |
2009 | MyString(const char*); |
2010 | }; |
2011 | |
2012 | void target() { |
2013 | $ns::$optional<MyString> opt; |
2014 | opt = "foo"; |
2015 | opt.value(); |
2016 | } |
2017 | )" ); |
2018 | |
2019 | ExpectDiagnosticsFor(SourceCode: R"( |
2020 | #include "unchecked_optional_access_test.h" |
2021 | |
2022 | struct MyString { |
2023 | MyString(const char*); |
2024 | }; |
2025 | |
2026 | void target() { |
2027 | $ns::$optional<MyString> opt; |
2028 | (opt = "foo").value(); |
2029 | } |
2030 | )" ); |
2031 | } |
2032 | |
2033 | TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { |
2034 | ExpectDiagnosticsFor( |
2035 | SourceCode: R"( |
2036 | #include "unchecked_optional_access_test.h" |
2037 | |
2038 | struct Foo {}; |
2039 | |
2040 | struct Bar { |
2041 | Bar(const Foo&); |
2042 | }; |
2043 | |
2044 | void target() { |
2045 | $ns::$optional<Foo> opt1 = Foo(); |
2046 | $ns::$optional<Bar> opt2; |
2047 | opt2 = opt1; |
2048 | opt2.value(); |
2049 | } |
2050 | )" ); |
2051 | |
2052 | ExpectDiagnosticsFor( |
2053 | SourceCode: R"( |
2054 | #include "unchecked_optional_access_test.h" |
2055 | |
2056 | struct Foo {}; |
2057 | |
2058 | struct Bar { |
2059 | Bar(const Foo&); |
2060 | }; |
2061 | |
2062 | void target() { |
2063 | $ns::$optional<Foo> opt1; |
2064 | $ns::$optional<Bar> opt2; |
2065 | if (opt2.has_value()) { |
2066 | opt2 = opt1; |
2067 | opt2.value(); // [[unsafe]] |
2068 | } |
2069 | } |
2070 | )" ); |
2071 | |
2072 | ExpectDiagnosticsFor( |
2073 | SourceCode: R"( |
2074 | #include "unchecked_optional_access_test.h" |
2075 | |
2076 | struct Foo {}; |
2077 | |
2078 | struct Bar { |
2079 | Bar(const Foo&); |
2080 | }; |
2081 | |
2082 | void target() { |
2083 | $ns::$optional<Foo> opt1 = Foo(); |
2084 | $ns::$optional<Bar> opt2; |
2085 | (opt2 = opt1).value(); |
2086 | (void)0; |
2087 | } |
2088 | )" ); |
2089 | } |
2090 | |
2091 | TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { |
2092 | ExpectDiagnosticsFor( |
2093 | SourceCode: R"( |
2094 | #include "unchecked_optional_access_test.h" |
2095 | |
2096 | void target() { |
2097 | $ns::$optional<int> opt = 3; |
2098 | opt = $ns::nullopt; |
2099 | opt.value(); // [[unsafe]] |
2100 | } |
2101 | )" ); |
2102 | |
2103 | ExpectDiagnosticsFor( |
2104 | SourceCode: R"( |
2105 | #include "unchecked_optional_access_test.h" |
2106 | |
2107 | void target() { |
2108 | $ns::$optional<int> opt = 3; |
2109 | (opt = $ns::nullopt).value(); // [[unsafe]] |
2110 | } |
2111 | )" ); |
2112 | } |
2113 | |
2114 | TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { |
2115 | ExpectDiagnosticsFor( |
2116 | SourceCode: R"( |
2117 | #include "unchecked_optional_access_test.h" |
2118 | |
2119 | void target() { |
2120 | $ns::$optional<int> opt1 = $ns::nullopt; |
2121 | $ns::$optional<int> opt2 = 3; |
2122 | |
2123 | opt1.swap(opt2); |
2124 | |
2125 | opt1.value(); |
2126 | |
2127 | opt2.value(); // [[unsafe]] |
2128 | } |
2129 | )" ); |
2130 | |
2131 | ExpectDiagnosticsFor( |
2132 | SourceCode: R"( |
2133 | #include "unchecked_optional_access_test.h" |
2134 | |
2135 | void target() { |
2136 | $ns::$optional<int> opt1 = $ns::nullopt; |
2137 | $ns::$optional<int> opt2 = 3; |
2138 | |
2139 | opt2.swap(opt1); |
2140 | |
2141 | opt1.value(); |
2142 | |
2143 | opt2.value(); // [[unsafe]] |
2144 | } |
2145 | )" ); |
2146 | } |
2147 | |
2148 | TEST_P(UncheckedOptionalAccessTest, OptionalReturnedFromFuntionCall) { |
2149 | ExpectDiagnosticsFor( |
2150 | SourceCode: R"( |
2151 | #include "unchecked_optional_access_test.h" |
2152 | |
2153 | struct S { |
2154 | $ns::$optional<float> x; |
2155 | } s; |
2156 | S getOptional() { |
2157 | return s; |
2158 | } |
2159 | |
2160 | void target() { |
2161 | getOptional().x = 0; |
2162 | } |
2163 | )" ); |
2164 | } |
2165 | |
2166 | TEST_P(UncheckedOptionalAccessTest, StdSwap) { |
2167 | ExpectDiagnosticsFor( |
2168 | SourceCode: R"( |
2169 | #include "unchecked_optional_access_test.h" |
2170 | |
2171 | void target() { |
2172 | $ns::$optional<int> opt1 = $ns::nullopt; |
2173 | $ns::$optional<int> opt2 = 3; |
2174 | |
2175 | std::swap(opt1, opt2); |
2176 | |
2177 | opt1.value(); |
2178 | |
2179 | opt2.value(); // [[unsafe]] |
2180 | } |
2181 | )" ); |
2182 | |
2183 | ExpectDiagnosticsFor( |
2184 | SourceCode: R"( |
2185 | #include "unchecked_optional_access_test.h" |
2186 | |
2187 | void target() { |
2188 | $ns::$optional<int> opt1 = $ns::nullopt; |
2189 | $ns::$optional<int> opt2 = 3; |
2190 | |
2191 | std::swap(opt2, opt1); |
2192 | |
2193 | opt1.value(); |
2194 | |
2195 | opt2.value(); // [[unsafe]] |
2196 | } |
2197 | )" ); |
2198 | } |
2199 | |
2200 | TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocLeft) { |
2201 | ExpectDiagnosticsFor( |
2202 | SourceCode: R"( |
2203 | #include "unchecked_optional_access_test.h" |
2204 | |
2205 | struct L { $ns::$optional<int> hd; L* tl; }; |
2206 | |
2207 | void target() { |
2208 | $ns::$optional<int> foo = 3; |
2209 | L bar; |
2210 | |
2211 | // Any `tl` beyond the first is not modeled. |
2212 | bar.tl->tl->hd.swap(foo); |
2213 | |
2214 | bar.tl->tl->hd.value(); // [[unsafe]] |
2215 | foo.value(); // [[unsafe]] |
2216 | } |
2217 | )" ); |
2218 | } |
2219 | |
2220 | TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocRight) { |
2221 | ExpectDiagnosticsFor( |
2222 | SourceCode: R"( |
2223 | #include "unchecked_optional_access_test.h" |
2224 | |
2225 | struct L { $ns::$optional<int> hd; L* tl; }; |
2226 | |
2227 | void target() { |
2228 | $ns::$optional<int> foo = 3; |
2229 | L bar; |
2230 | |
2231 | // Any `tl` beyond the first is not modeled. |
2232 | foo.swap(bar.tl->tl->hd); |
2233 | |
2234 | bar.tl->tl->hd.value(); // [[unsafe]] |
2235 | foo.value(); // [[unsafe]] |
2236 | } |
2237 | )" ); |
2238 | } |
2239 | |
2240 | TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftSet) { |
2241 | ExpectDiagnosticsFor( |
2242 | SourceCode: R"( |
2243 | #include "unchecked_optional_access_test.h" |
2244 | |
2245 | struct S { int x; }; |
2246 | struct A { $ns::$optional<S> late; }; |
2247 | struct B { A f3; }; |
2248 | struct C { B f2; }; |
2249 | struct D { C f1; }; |
2250 | |
2251 | void target() { |
2252 | $ns::$optional<S> foo = S{3}; |
2253 | D bar; |
2254 | |
2255 | bar.f1.f2.f3.late.swap(foo); |
2256 | |
2257 | bar.f1.f2.f3.late.value(); |
2258 | foo.value(); // [[unsafe]] |
2259 | } |
2260 | )" ); |
2261 | } |
2262 | |
2263 | TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftUnset) { |
2264 | ExpectDiagnosticsFor( |
2265 | SourceCode: R"( |
2266 | #include "unchecked_optional_access_test.h" |
2267 | |
2268 | struct S { int x; }; |
2269 | struct A { $ns::$optional<S> late; }; |
2270 | struct B { A f3; }; |
2271 | struct C { B f2; }; |
2272 | struct D { C f1; }; |
2273 | |
2274 | void target() { |
2275 | $ns::$optional<S> foo; |
2276 | D bar; |
2277 | |
2278 | bar.f1.f2.f3.late.swap(foo); |
2279 | |
2280 | bar.f1.f2.f3.late.value(); // [[unsafe]] |
2281 | foo.value(); // [[unsafe]] |
2282 | } |
2283 | )" ); |
2284 | } |
2285 | |
2286 | // fixme: use recursion instead of depth. |
2287 | TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightSet) { |
2288 | ExpectDiagnosticsFor( |
2289 | SourceCode: R"( |
2290 | #include "unchecked_optional_access_test.h" |
2291 | |
2292 | struct S { int x; }; |
2293 | struct A { $ns::$optional<S> late; }; |
2294 | struct B { A f3; }; |
2295 | struct C { B f2; }; |
2296 | struct D { C f1; }; |
2297 | |
2298 | void target() { |
2299 | $ns::$optional<S> foo = S{3}; |
2300 | D bar; |
2301 | |
2302 | foo.swap(bar.f1.f2.f3.late); |
2303 | |
2304 | bar.f1.f2.f3.late.value(); |
2305 | foo.value(); // [[unsafe]] |
2306 | } |
2307 | )" ); |
2308 | } |
2309 | |
2310 | TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightUnset) { |
2311 | ExpectDiagnosticsFor( |
2312 | SourceCode: R"( |
2313 | #include "unchecked_optional_access_test.h" |
2314 | |
2315 | struct S { int x; }; |
2316 | struct A { $ns::$optional<S> late; }; |
2317 | struct B { A f3; }; |
2318 | struct C { B f2; }; |
2319 | struct D { C f1; }; |
2320 | |
2321 | void target() { |
2322 | $ns::$optional<S> foo; |
2323 | D bar; |
2324 | |
2325 | foo.swap(bar.f1.f2.f3.late); |
2326 | |
2327 | bar.f1.f2.f3.late.value(); // [[unsafe]] |
2328 | foo.value(); // [[unsafe]] |
2329 | } |
2330 | )" ); |
2331 | } |
2332 | |
2333 | TEST_P(UncheckedOptionalAccessTest, UniquePtrToOptional) { |
2334 | // We suppress diagnostics for optionals in smart pointers (other than |
2335 | // `optional` itself). |
2336 | ExpectDiagnosticsFor( |
2337 | SourceCode: R"( |
2338 | #include "unchecked_optional_access_test.h" |
2339 | |
2340 | template <typename T> |
2341 | struct smart_ptr { |
2342 | T& operator*() &; |
2343 | T* operator->(); |
2344 | }; |
2345 | |
2346 | void target() { |
2347 | smart_ptr<$ns::$optional<bool>> foo; |
2348 | foo->value(); |
2349 | (*foo).value(); |
2350 | } |
2351 | )" ); |
2352 | } |
2353 | |
2354 | TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { |
2355 | // We suppress diagnostics for optional fields reachable from smart pointers |
2356 | // (other than `optional` itself) through (exactly) one member access. |
2357 | ExpectDiagnosticsFor( |
2358 | SourceCode: R"( |
2359 | #include "unchecked_optional_access_test.h" |
2360 | |
2361 | template <typename T> |
2362 | struct smart_ptr { |
2363 | T& operator*() &; |
2364 | T* operator->(); |
2365 | }; |
2366 | |
2367 | struct Foo { |
2368 | $ns::$optional<int> opt; |
2369 | }; |
2370 | |
2371 | void target() { |
2372 | smart_ptr<Foo> foo; |
2373 | *foo->opt; |
2374 | *(*foo).opt; |
2375 | } |
2376 | )" ); |
2377 | } |
2378 | |
2379 | TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { |
2380 | ExpectDiagnosticsFor( |
2381 | SourceCode: R"( |
2382 | #include "unchecked_optional_access_test.h" |
2383 | |
2384 | $ns::$optional<int> MakeOpt(); |
2385 | |
2386 | void target() { |
2387 | $ns::$optional<int> opt = 0; |
2388 | opt = MakeOpt(); |
2389 | opt.value(); // [[unsafe]] |
2390 | } |
2391 | )" ); |
2392 | ExpectDiagnosticsFor( |
2393 | SourceCode: R"( |
2394 | #include "unchecked_optional_access_test.h" |
2395 | |
2396 | const $ns::$optional<int>& MakeOpt(); |
2397 | |
2398 | void target() { |
2399 | $ns::$optional<int> opt = 0; |
2400 | opt = MakeOpt(); |
2401 | opt.value(); // [[unsafe]] |
2402 | } |
2403 | )" ); |
2404 | |
2405 | ExpectDiagnosticsFor( |
2406 | SourceCode: R"( |
2407 | #include "unchecked_optional_access_test.h" |
2408 | |
2409 | using IntOpt = $ns::$optional<int>; |
2410 | IntOpt MakeOpt(); |
2411 | |
2412 | void target() { |
2413 | IntOpt opt = 0; |
2414 | opt = MakeOpt(); |
2415 | opt.value(); // [[unsafe]] |
2416 | } |
2417 | )" ); |
2418 | |
2419 | ExpectDiagnosticsFor( |
2420 | SourceCode: R"( |
2421 | #include "unchecked_optional_access_test.h" |
2422 | |
2423 | using IntOpt = $ns::$optional<int>; |
2424 | const IntOpt& MakeOpt(); |
2425 | |
2426 | void target() { |
2427 | IntOpt opt = 0; |
2428 | opt = MakeOpt(); |
2429 | opt.value(); // [[unsafe]] |
2430 | } |
2431 | )" ); |
2432 | } |
2433 | |
2434 | |
2435 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftSet) { |
2436 | ExpectDiagnosticsFor( |
2437 | SourceCode: R"( |
2438 | #include "unchecked_optional_access_test.h" |
2439 | |
2440 | void target() { |
2441 | $ns::$optional<int> opt1 = 3; |
2442 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2443 | |
2444 | if (opt1 == opt2) { |
2445 | opt2.value(); |
2446 | } else { |
2447 | opt2.value(); // [[unsafe]] |
2448 | } |
2449 | } |
2450 | )" ); |
2451 | } |
2452 | |
2453 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightSet) { |
2454 | ExpectDiagnosticsFor( |
2455 | SourceCode: R"( |
2456 | #include "unchecked_optional_access_test.h" |
2457 | |
2458 | void target() { |
2459 | $ns::$optional<int> opt1 = 3; |
2460 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2461 | |
2462 | if (opt2 == opt1) { |
2463 | opt2.value(); |
2464 | } else { |
2465 | opt2.value(); // [[unsafe]] |
2466 | } |
2467 | } |
2468 | )" ); |
2469 | } |
2470 | |
2471 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckVerifySetAfterEq) { |
2472 | ExpectDiagnosticsFor( |
2473 | SourceCode: R"( |
2474 | #include "unchecked_optional_access_test.h" |
2475 | |
2476 | void target() { |
2477 | $ns::$optional<int> opt1 = Make<$ns::$optional<int>>(); |
2478 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2479 | |
2480 | if (opt1 == opt2) { |
2481 | if (opt1.has_value()) |
2482 | opt2.value(); |
2483 | if (opt2.has_value()) |
2484 | opt1.value(); |
2485 | } |
2486 | } |
2487 | )" ); |
2488 | } |
2489 | |
2490 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftUnset) { |
2491 | ExpectDiagnosticsFor( |
2492 | SourceCode: R"( |
2493 | #include "unchecked_optional_access_test.h" |
2494 | |
2495 | void target() { |
2496 | $ns::$optional<int> opt1 = $ns::nullopt; |
2497 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2498 | |
2499 | if (opt1 == opt2) { |
2500 | opt2.value(); // [[unsafe]] |
2501 | } else { |
2502 | opt2.value(); |
2503 | } |
2504 | } |
2505 | )" ); |
2506 | } |
2507 | |
2508 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightUnset) { |
2509 | ExpectDiagnosticsFor( |
2510 | SourceCode: R"( |
2511 | #include "unchecked_optional_access_test.h" |
2512 | |
2513 | void target() { |
2514 | $ns::$optional<int> opt1 = $ns::nullopt; |
2515 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2516 | |
2517 | if (opt2 == opt1) { |
2518 | opt2.value(); // [[unsafe]] |
2519 | } else { |
2520 | opt2.value(); |
2521 | } |
2522 | } |
2523 | )" ); |
2524 | } |
2525 | |
2526 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightNullopt) { |
2527 | ExpectDiagnosticsFor( |
2528 | SourceCode: R"( |
2529 | #include "unchecked_optional_access_test.h" |
2530 | |
2531 | void target() { |
2532 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2533 | |
2534 | if (opt == $ns::nullopt) { |
2535 | opt.value(); // [[unsafe]] |
2536 | } else { |
2537 | opt.value(); |
2538 | } |
2539 | } |
2540 | )" ); |
2541 | } |
2542 | |
2543 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftNullopt) { |
2544 | ExpectDiagnosticsFor( |
2545 | SourceCode: R"( |
2546 | #include "unchecked_optional_access_test.h" |
2547 | |
2548 | void target() { |
2549 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2550 | |
2551 | if ($ns::nullopt == opt) { |
2552 | opt.value(); // [[unsafe]] |
2553 | } else { |
2554 | opt.value(); |
2555 | } |
2556 | } |
2557 | )" ); |
2558 | } |
2559 | |
2560 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightValue) { |
2561 | ExpectDiagnosticsFor( |
2562 | SourceCode: R"( |
2563 | #include "unchecked_optional_access_test.h" |
2564 | |
2565 | void target() { |
2566 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2567 | |
2568 | if (opt == 3) { |
2569 | opt.value(); |
2570 | } else { |
2571 | opt.value(); // [[unsafe]] |
2572 | } |
2573 | } |
2574 | )" ); |
2575 | } |
2576 | |
2577 | TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftValue) { |
2578 | ExpectDiagnosticsFor( |
2579 | SourceCode: R"( |
2580 | #include "unchecked_optional_access_test.h" |
2581 | |
2582 | void target() { |
2583 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2584 | |
2585 | if (3 == opt) { |
2586 | opt.value(); |
2587 | } else { |
2588 | opt.value(); // [[unsafe]] |
2589 | } |
2590 | } |
2591 | )" ); |
2592 | } |
2593 | |
2594 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftSet) { |
2595 | ExpectDiagnosticsFor( |
2596 | SourceCode: R"( |
2597 | #include "unchecked_optional_access_test.h" |
2598 | |
2599 | void target() { |
2600 | $ns::$optional<int> opt1 = 3; |
2601 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2602 | |
2603 | if (opt1 != opt2) { |
2604 | opt2.value(); // [[unsafe]] |
2605 | } else { |
2606 | opt2.value(); |
2607 | } |
2608 | } |
2609 | )" ); |
2610 | } |
2611 | |
2612 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightSet) { |
2613 | ExpectDiagnosticsFor( |
2614 | SourceCode: R"( |
2615 | #include "unchecked_optional_access_test.h" |
2616 | |
2617 | void target() { |
2618 | $ns::$optional<int> opt1 = 3; |
2619 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2620 | |
2621 | if (opt2 != opt1) { |
2622 | opt2.value(); // [[unsafe]] |
2623 | } else { |
2624 | opt2.value(); |
2625 | } |
2626 | } |
2627 | )" ); |
2628 | } |
2629 | |
2630 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckVerifySetAfterEq) { |
2631 | ExpectDiagnosticsFor( |
2632 | SourceCode: R"( |
2633 | #include "unchecked_optional_access_test.h" |
2634 | |
2635 | void target() { |
2636 | $ns::$optional<int> opt1 = Make<$ns::$optional<int>>(); |
2637 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2638 | |
2639 | if (opt1 != opt2) { |
2640 | if (opt1.has_value()) |
2641 | opt2.value(); // [[unsafe]] |
2642 | if (opt2.has_value()) |
2643 | opt1.value(); // [[unsafe]] |
2644 | } |
2645 | } |
2646 | )" ); |
2647 | } |
2648 | |
2649 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftUnset) { |
2650 | ExpectDiagnosticsFor( |
2651 | SourceCode: R"( |
2652 | #include "unchecked_optional_access_test.h" |
2653 | |
2654 | void target() { |
2655 | $ns::$optional<int> opt1 = $ns::nullopt; |
2656 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2657 | |
2658 | if (opt1 != opt2) { |
2659 | opt2.value(); |
2660 | } else { |
2661 | opt2.value(); // [[unsafe]] |
2662 | } |
2663 | } |
2664 | )" ); |
2665 | } |
2666 | |
2667 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightUnset) { |
2668 | ExpectDiagnosticsFor( |
2669 | SourceCode: R"( |
2670 | #include "unchecked_optional_access_test.h" |
2671 | |
2672 | void target() { |
2673 | $ns::$optional<int> opt1 = $ns::nullopt; |
2674 | $ns::$optional<int> opt2 = Make<$ns::$optional<int>>(); |
2675 | |
2676 | if (opt2 != opt1) { |
2677 | opt2.value(); |
2678 | } else { |
2679 | opt2.value(); // [[unsafe]] |
2680 | } |
2681 | } |
2682 | )" ); |
2683 | } |
2684 | |
2685 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightNullopt) { |
2686 | ExpectDiagnosticsFor( |
2687 | SourceCode: R"( |
2688 | #include "unchecked_optional_access_test.h" |
2689 | |
2690 | void target() { |
2691 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2692 | |
2693 | if (opt != $ns::nullopt) { |
2694 | opt.value(); |
2695 | } else { |
2696 | opt.value(); // [[unsafe]] |
2697 | } |
2698 | } |
2699 | )" ); |
2700 | } |
2701 | |
2702 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftNullopt) { |
2703 | ExpectDiagnosticsFor( |
2704 | SourceCode: R"( |
2705 | #include "unchecked_optional_access_test.h" |
2706 | |
2707 | void target() { |
2708 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2709 | |
2710 | if ($ns::nullopt != opt) { |
2711 | opt.value(); |
2712 | } else { |
2713 | opt.value(); // [[unsafe]] |
2714 | } |
2715 | } |
2716 | )" ); |
2717 | } |
2718 | |
2719 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightValue) { |
2720 | ExpectDiagnosticsFor( |
2721 | SourceCode: R"( |
2722 | #include "unchecked_optional_access_test.h" |
2723 | |
2724 | void target() { |
2725 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2726 | |
2727 | if (opt != 3) { |
2728 | opt.value(); // [[unsafe]] |
2729 | } else { |
2730 | opt.value(); |
2731 | } |
2732 | } |
2733 | )" ); |
2734 | } |
2735 | |
2736 | TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftValue) { |
2737 | ExpectDiagnosticsFor( |
2738 | SourceCode: R"( |
2739 | #include "unchecked_optional_access_test.h" |
2740 | |
2741 | void target() { |
2742 | $ns::$optional<int> opt = Make<$ns::$optional<int>>(); |
2743 | |
2744 | if (3 != opt) { |
2745 | opt.value(); // [[unsafe]] |
2746 | } else { |
2747 | opt.value(); |
2748 | } |
2749 | } |
2750 | )" ); |
2751 | } |
2752 | |
2753 | // Verifies that the model sees through aliases. |
2754 | TEST_P(UncheckedOptionalAccessTest, WithAlias) { |
2755 | ExpectDiagnosticsFor( |
2756 | SourceCode: R"( |
2757 | #include "unchecked_optional_access_test.h" |
2758 | |
2759 | template <typename T> |
2760 | using MyOptional = $ns::$optional<T>; |
2761 | |
2762 | void target(MyOptional<int> opt) { |
2763 | opt.value(); // [[unsafe]] |
2764 | } |
2765 | )" ); |
2766 | } |
2767 | |
2768 | TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) { |
2769 | // Basic test that nested values are populated. We nest an optional because |
2770 | // its easy to use in a test, but the type of the nested value shouldn't |
2771 | // matter. |
2772 | ExpectDiagnosticsFor( |
2773 | SourceCode: R"( |
2774 | #include "unchecked_optional_access_test.h" |
2775 | |
2776 | using Foo = $ns::$optional<std::string>; |
2777 | |
2778 | void target($ns::$optional<Foo> foo) { |
2779 | if (foo && *foo) { |
2780 | foo->value(); |
2781 | } |
2782 | } |
2783 | )" ); |
2784 | |
2785 | // Mutation is supported for nested values. |
2786 | ExpectDiagnosticsFor( |
2787 | SourceCode: R"( |
2788 | #include "unchecked_optional_access_test.h" |
2789 | |
2790 | using Foo = $ns::$optional<std::string>; |
2791 | |
2792 | void target($ns::$optional<Foo> foo) { |
2793 | if (foo && *foo) { |
2794 | foo->reset(); |
2795 | foo->value(); // [[unsafe]] |
2796 | } |
2797 | } |
2798 | )" ); |
2799 | } |
2800 | |
2801 | TEST_P(UncheckedOptionalAccessTest, NestedOptionalAssignValue) { |
2802 | ExpectDiagnosticsFor( |
2803 | SourceCode: R"( |
2804 | #include "unchecked_optional_access_test.h" |
2805 | |
2806 | using OptionalInt = $ns::$optional<int>; |
2807 | |
2808 | void target($ns::$optional<OptionalInt> opt) { |
2809 | if (!opt) return; |
2810 | |
2811 | // Accessing the outer optional is OK now. |
2812 | *opt; |
2813 | |
2814 | // But accessing the nested optional is still unsafe because we haven't |
2815 | // checked it. |
2816 | **opt; // [[unsafe]] |
2817 | |
2818 | *opt = 1; |
2819 | |
2820 | // Accessing the nested optional is safe after assigning a value to it. |
2821 | **opt; |
2822 | } |
2823 | )" ); |
2824 | } |
2825 | |
2826 | TEST_P(UncheckedOptionalAccessTest, NestedOptionalAssignOptional) { |
2827 | ExpectDiagnosticsFor( |
2828 | SourceCode: R"( |
2829 | #include "unchecked_optional_access_test.h" |
2830 | |
2831 | using OptionalInt = $ns::$optional<int>; |
2832 | |
2833 | void target($ns::$optional<OptionalInt> opt) { |
2834 | if (!opt) return; |
2835 | |
2836 | // Accessing the outer optional is OK now. |
2837 | *opt; |
2838 | |
2839 | // But accessing the nested optional is still unsafe because we haven't |
2840 | // checked it. |
2841 | **opt; // [[unsafe]] |
2842 | |
2843 | // Assign from `optional<short>` so that we trigger conversion assignment |
2844 | // instead of move assignment. |
2845 | *opt = $ns::$optional<short>(); |
2846 | |
2847 | // Accessing the nested optional is still unsafe after assigning an empty |
2848 | // optional to it. |
2849 | **opt; // [[unsafe]] |
2850 | } |
2851 | )" ); |
2852 | } |
2853 | |
2854 | // Tests that structs can be nested. We use an optional field because its easy |
2855 | // to use in a test, but the type of the field shouldn't matter. |
2856 | TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) { |
2857 | ExpectDiagnosticsFor( |
2858 | SourceCode: R"( |
2859 | #include "unchecked_optional_access_test.h" |
2860 | |
2861 | struct Foo { |
2862 | $ns::$optional<std::string> opt; |
2863 | }; |
2864 | |
2865 | void target($ns::$optional<Foo> foo) { |
2866 | if (foo && foo->opt) { |
2867 | foo->opt.value(); |
2868 | } |
2869 | } |
2870 | )" ); |
2871 | } |
2872 | |
2873 | TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) { |
2874 | ExpectDiagnosticsFor( |
2875 | SourceCode: R"( |
2876 | #include "unchecked_optional_access_test.h" |
2877 | |
2878 | using Foo = $ns::$optional<std::string>; |
2879 | |
2880 | void target($ns::$optional<Foo> foo, bool b) { |
2881 | if (!foo.has_value()) return; |
2882 | if (b) { |
2883 | if (!foo->has_value()) return; |
2884 | // We have created `foo.value()`. |
2885 | foo->value(); |
2886 | } else { |
2887 | if (!foo->has_value()) return; |
2888 | // We have created `foo.value()` again, in a different environment. |
2889 | foo->value(); |
2890 | } |
2891 | // Now we merge the two values. UncheckedOptionalAccessModel::merge() will |
2892 | // throw away the "value" property. |
2893 | foo->value(); |
2894 | } |
2895 | )" ); |
2896 | } |
2897 | |
2898 | // This test is aimed at the core model, not the diagnostic. It is a regression |
2899 | // test against a crash when using non-trivial smart pointers, like |
2900 | // `std::unique_ptr`. As such, it doesn't test the access itself, which would be |
2901 | // ignored regardless because of `IgnoreSmartPointerDereference = true`, above. |
2902 | TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) { |
2903 | ExpectDiagnosticsFor( |
2904 | SourceCode: R"( |
2905 | #include "unchecked_optional_access_test.h" |
2906 | |
2907 | template <typename T> |
2908 | struct smart_ptr { |
2909 | typename std::add_lvalue_reference<T>::type operator*() &; |
2910 | }; |
2911 | |
2912 | void target() { |
2913 | smart_ptr<$ns::$optional<int>> x; |
2914 | // Verify that this assignment does not crash. |
2915 | *x = 3; |
2916 | } |
2917 | )" ); |
2918 | } |
2919 | |
2920 | TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) { |
2921 | ExpectDiagnosticsFor(SourceCode: R"code( |
2922 | #include "unchecked_optional_access_test.h" |
2923 | |
2924 | void target(bool b, $ns::$optional<int> opt) { |
2925 | if (b || opt.has_value()) { |
2926 | if (!b) { |
2927 | opt.value(); |
2928 | } |
2929 | } |
2930 | } |
2931 | )code" ); |
2932 | |
2933 | ExpectDiagnosticsFor(SourceCode: R"code( |
2934 | #include "unchecked_optional_access_test.h" |
2935 | |
2936 | void target(bool b, $ns::$optional<int> opt) { |
2937 | if (b && !opt.has_value()) return; |
2938 | if (b) { |
2939 | opt.value(); |
2940 | } |
2941 | } |
2942 | )code" ); |
2943 | |
2944 | ExpectDiagnosticsFor( |
2945 | SourceCode: R"code( |
2946 | #include "unchecked_optional_access_test.h" |
2947 | |
2948 | void target(bool b, $ns::$optional<int> opt) { |
2949 | if (opt.has_value()) b = true; |
2950 | if (b) { |
2951 | opt.value(); // [[unsafe]] |
2952 | } |
2953 | } |
2954 | )code" ); |
2955 | |
2956 | ExpectDiagnosticsFor(SourceCode: R"code( |
2957 | #include "unchecked_optional_access_test.h" |
2958 | |
2959 | void target(bool b, $ns::$optional<int> opt) { |
2960 | if (b) return; |
2961 | if (opt.has_value()) b = true; |
2962 | if (b) { |
2963 | opt.value(); |
2964 | } |
2965 | } |
2966 | )code" ); |
2967 | |
2968 | ExpectDiagnosticsFor(SourceCode: R"( |
2969 | #include "unchecked_optional_access_test.h" |
2970 | |
2971 | void target(bool b, $ns::$optional<int> opt) { |
2972 | if (opt.has_value() == b) { |
2973 | if (b) { |
2974 | opt.value(); |
2975 | } |
2976 | } |
2977 | } |
2978 | )" ); |
2979 | |
2980 | ExpectDiagnosticsFor(SourceCode: R"( |
2981 | #include "unchecked_optional_access_test.h" |
2982 | |
2983 | void target(bool b, $ns::$optional<int> opt) { |
2984 | if (opt.has_value() != b) { |
2985 | if (!b) { |
2986 | opt.value(); |
2987 | } |
2988 | } |
2989 | } |
2990 | )" ); |
2991 | |
2992 | ExpectDiagnosticsFor(SourceCode: R"( |
2993 | #include "unchecked_optional_access_test.h" |
2994 | |
2995 | void target(bool b) { |
2996 | $ns::$optional<int> opt1 = $ns::nullopt; |
2997 | $ns::$optional<int> opt2; |
2998 | if (b) { |
2999 | opt2 = $ns::nullopt; |
3000 | } else { |
3001 | opt2 = $ns::nullopt; |
3002 | } |
3003 | if (opt2.has_value()) { |
3004 | opt1.value(); |
3005 | } |
3006 | } |
3007 | )" ); |
3008 | } |
3009 | |
3010 | TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) { |
3011 | ExpectDiagnosticsFor( |
3012 | SourceCode: R"code( |
3013 | #include "unchecked_optional_access_test.h" |
3014 | |
3015 | void target(bool b) { |
3016 | $ns::$optional<int> opt; |
3017 | if (b) { |
3018 | opt = Make<$ns::$optional<int>>(); |
3019 | } else { |
3020 | opt = Make<$ns::$optional<int>>(); |
3021 | } |
3022 | if (opt.has_value()) { |
3023 | opt.value(); |
3024 | } else { |
3025 | opt.value(); // [[unsafe]] |
3026 | } |
3027 | } |
3028 | )code" ); |
3029 | |
3030 | ExpectDiagnosticsFor(SourceCode: R"code( |
3031 | #include "unchecked_optional_access_test.h" |
3032 | |
3033 | void target(bool b) { |
3034 | $ns::$optional<int> opt; |
3035 | if (b) { |
3036 | opt = Make<$ns::$optional<int>>(); |
3037 | if (!opt.has_value()) return; |
3038 | } else { |
3039 | opt = Make<$ns::$optional<int>>(); |
3040 | if (!opt.has_value()) return; |
3041 | } |
3042 | opt.value(); |
3043 | } |
3044 | )code" ); |
3045 | |
3046 | ExpectDiagnosticsFor( |
3047 | SourceCode: R"code( |
3048 | #include "unchecked_optional_access_test.h" |
3049 | |
3050 | void target(bool b) { |
3051 | $ns::$optional<int> opt; |
3052 | if (b) { |
3053 | opt = Make<$ns::$optional<int>>(); |
3054 | if (!opt.has_value()) return; |
3055 | } else { |
3056 | opt = Make<$ns::$optional<int>>(); |
3057 | } |
3058 | opt.value(); // [[unsafe]] |
3059 | } |
3060 | )code" ); |
3061 | |
3062 | ExpectDiagnosticsFor( |
3063 | SourceCode: R"code( |
3064 | #include "unchecked_optional_access_test.h" |
3065 | |
3066 | void target(bool b) { |
3067 | $ns::$optional<int> opt; |
3068 | if (b) { |
3069 | opt = 1; |
3070 | } else { |
3071 | opt = 2; |
3072 | } |
3073 | opt.value(); |
3074 | } |
3075 | )code" ); |
3076 | |
3077 | ExpectDiagnosticsFor( |
3078 | SourceCode: R"code( |
3079 | #include "unchecked_optional_access_test.h" |
3080 | |
3081 | void target(bool b) { |
3082 | $ns::$optional<int> opt; |
3083 | if (b) { |
3084 | opt = 1; |
3085 | } else { |
3086 | opt = Make<$ns::$optional<int>>(); |
3087 | } |
3088 | opt.value(); // [[unsafe]] |
3089 | } |
3090 | )code" ); |
3091 | } |
3092 | |
3093 | TEST_P(UncheckedOptionalAccessTest, AccessValueInLoop) { |
3094 | ExpectDiagnosticsFor(SourceCode: R"( |
3095 | #include "unchecked_optional_access_test.h" |
3096 | |
3097 | void target() { |
3098 | $ns::$optional<int> opt = 3; |
3099 | while (Make<bool>()) { |
3100 | opt.value(); |
3101 | } |
3102 | } |
3103 | )" ); |
3104 | } |
3105 | |
3106 | TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopWithCheckSafe) { |
3107 | ExpectDiagnosticsFor(SourceCode: R"( |
3108 | #include "unchecked_optional_access_test.h" |
3109 | |
3110 | void target() { |
3111 | $ns::$optional<int> opt = 3; |
3112 | while (Make<bool>()) { |
3113 | opt.value(); |
3114 | |
3115 | opt = Make<$ns::$optional<int>>(); |
3116 | if (!opt.has_value()) return; |
3117 | } |
3118 | } |
3119 | )" ); |
3120 | } |
3121 | |
3122 | TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopNoCheckUnsafe) { |
3123 | ExpectDiagnosticsFor( |
3124 | SourceCode: R"( |
3125 | #include "unchecked_optional_access_test.h" |
3126 | |
3127 | void target() { |
3128 | $ns::$optional<int> opt = 3; |
3129 | while (Make<bool>()) { |
3130 | opt.value(); // [[unsafe]] |
3131 | |
3132 | opt = Make<$ns::$optional<int>>(); |
3133 | } |
3134 | } |
3135 | )" ); |
3136 | } |
3137 | |
3138 | TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnsetUnsafe) { |
3139 | ExpectDiagnosticsFor( |
3140 | SourceCode: R"( |
3141 | #include "unchecked_optional_access_test.h" |
3142 | |
3143 | void target() { |
3144 | $ns::$optional<int> opt = 3; |
3145 | while (Make<bool>()) |
3146 | opt = $ns::nullopt; |
3147 | $ns::$optional<int> opt2 = $ns::nullopt; |
3148 | if (opt.has_value()) |
3149 | opt2 = $ns::$optional<int>(3); |
3150 | opt2.value(); // [[unsafe]] |
3151 | } |
3152 | )" ); |
3153 | } |
3154 | |
3155 | TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToSetUnsafe) { |
3156 | ExpectDiagnosticsFor( |
3157 | SourceCode: R"( |
3158 | #include "unchecked_optional_access_test.h" |
3159 | |
3160 | void target() { |
3161 | $ns::$optional<int> opt = $ns::nullopt; |
3162 | while (Make<bool>()) |
3163 | opt = $ns::$optional<int>(3); |
3164 | $ns::$optional<int> opt2 = $ns::nullopt; |
3165 | if (!opt.has_value()) |
3166 | opt2 = $ns::$optional<int>(3); |
3167 | opt2.value(); // [[unsafe]] |
3168 | } |
3169 | )" ); |
3170 | } |
3171 | |
3172 | TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnknownUnsafe) { |
3173 | ExpectDiagnosticsFor( |
3174 | SourceCode: R"( |
3175 | #include "unchecked_optional_access_test.h" |
3176 | |
3177 | void target() { |
3178 | $ns::$optional<int> opt = $ns::nullopt; |
3179 | while (Make<bool>()) |
3180 | opt = Make<$ns::$optional<int>>(); |
3181 | $ns::$optional<int> opt2 = $ns::nullopt; |
3182 | if (!opt.has_value()) |
3183 | opt2 = $ns::$optional<int>(3); |
3184 | opt2.value(); // [[unsafe]] |
3185 | } |
3186 | )" ); |
3187 | } |
3188 | |
3189 | TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopBadConditionUnsafe) { |
3190 | ExpectDiagnosticsFor( |
3191 | SourceCode: R"( |
3192 | #include "unchecked_optional_access_test.h" |
3193 | |
3194 | void target() { |
3195 | $ns::$optional<int> opt = 3; |
3196 | while (Make<bool>()) { |
3197 | opt.value(); // [[unsafe]] |
3198 | |
3199 | opt = Make<$ns::$optional<int>>(); |
3200 | if (!opt.has_value()) continue; |
3201 | } |
3202 | } |
3203 | )" ); |
3204 | } |
3205 | |
3206 | TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromStruct) { |
3207 | ExpectDiagnosticsFor(SourceCode: R"( |
3208 | #include "unchecked_optional_access_test.h" |
3209 | |
3210 | struct kv { $ns::$optional<int> opt; int x; }; |
3211 | int target() { |
3212 | auto [contents, x] = Make<kv>(); |
3213 | return contents ? *contents : x; |
3214 | } |
3215 | )" ); |
3216 | |
3217 | ExpectDiagnosticsFor(SourceCode: R"( |
3218 | #include "unchecked_optional_access_test.h" |
3219 | |
3220 | template <typename T1, typename T2> |
3221 | struct pair { T1 fst; T2 snd; }; |
3222 | int target() { |
3223 | auto [contents, x] = Make<pair<$ns::$optional<int>, int>>(); |
3224 | return contents ? *contents : x; |
3225 | } |
3226 | )" ); |
3227 | } |
3228 | |
3229 | TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromTupleLikeType) { |
3230 | ExpectDiagnosticsFor(SourceCode: R"( |
3231 | #include "unchecked_optional_access_test.h" |
3232 | |
3233 | namespace std { |
3234 | template <class> struct tuple_size; |
3235 | template <size_t, class> struct tuple_element; |
3236 | template <class...> class tuple; |
3237 | |
3238 | template <class... T> |
3239 | struct tuple_size<tuple<T...>> : integral_constant<size_t, sizeof...(T)> {}; |
3240 | |
3241 | template <size_t I, class... T> |
3242 | struct tuple_element<I, tuple<T...>> { |
3243 | using type = __type_pack_element<I, T...>; |
3244 | }; |
3245 | |
3246 | template <class...> class tuple {}; |
3247 | template <size_t I, class... T> |
3248 | typename tuple_element<I, tuple<T...>>::type get(tuple<T...>); |
3249 | } // namespace std |
3250 | |
3251 | std::tuple<$ns::$optional<const char *>, int> get_opt(); |
3252 | void target() { |
3253 | auto [content, ck] = get_opt(); |
3254 | content ? *content : ""; |
3255 | } |
3256 | )" ); |
3257 | } |
3258 | |
3259 | TEST_P(UncheckedOptionalAccessTest, CtorInitializerNullopt) { |
3260 | using namespace ast_matchers; |
3261 | ExpectDiagnosticsFor( |
3262 | SourceCode: R"( |
3263 | #include "unchecked_optional_access_test.h" |
3264 | |
3265 | struct Target { |
3266 | Target(): opt($ns::nullopt) { |
3267 | opt.value(); // [[unsafe]] |
3268 | } |
3269 | $ns::$optional<int> opt; |
3270 | }; |
3271 | )" , |
3272 | FuncMatcher: cxxConstructorDecl(ofClass(InnerMatcher: hasName(Name: "Target" )))); |
3273 | } |
3274 | |
3275 | TEST_P(UncheckedOptionalAccessTest, CtorInitializerValue) { |
3276 | using namespace ast_matchers; |
3277 | ExpectDiagnosticsFor( |
3278 | SourceCode: R"( |
3279 | #include "unchecked_optional_access_test.h" |
3280 | |
3281 | struct Target { |
3282 | Target(): opt(3) { |
3283 | opt.value(); |
3284 | } |
3285 | $ns::$optional<int> opt; |
3286 | }; |
3287 | )" , |
3288 | FuncMatcher: cxxConstructorDecl(ofClass(InnerMatcher: hasName(Name: "Target" )))); |
3289 | } |
3290 | |
3291 | // This is regression test, it shouldn't crash. |
3292 | TEST_P(UncheckedOptionalAccessTest, Bitfield) { |
3293 | using namespace ast_matchers; |
3294 | ExpectDiagnosticsFor( |
3295 | SourceCode: R"( |
3296 | #include "unchecked_optional_access_test.h" |
3297 | struct Dst { |
3298 | unsigned int n : 1; |
3299 | }; |
3300 | void target() { |
3301 | $ns::$optional<bool> v; |
3302 | Dst d; |
3303 | if (v.has_value()) |
3304 | d.n = v.value(); |
3305 | } |
3306 | )" ); |
3307 | } |
3308 | |
3309 | TEST_P(UncheckedOptionalAccessTest, LambdaParam) { |
3310 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3311 | #include "unchecked_optional_access_test.h" |
3312 | |
3313 | void target() { |
3314 | []($ns::$optional<int> opt) { |
3315 | if (opt.has_value()) { |
3316 | opt.value(); |
3317 | } else { |
3318 | opt.value(); // [[unsafe]] |
3319 | } |
3320 | }(Make<$ns::$optional<int>>()); |
3321 | } |
3322 | )" ); |
3323 | } |
3324 | |
3325 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopy) { |
3326 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3327 | #include "unchecked_optional_access_test.h" |
3328 | |
3329 | void target($ns::$optional<int> opt) { |
3330 | [opt]() { |
3331 | if (opt.has_value()) { |
3332 | opt.value(); |
3333 | } else { |
3334 | opt.value(); // [[unsafe]] |
3335 | } |
3336 | }(); |
3337 | } |
3338 | )" ); |
3339 | } |
3340 | |
3341 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReference) { |
3342 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3343 | #include "unchecked_optional_access_test.h" |
3344 | |
3345 | void target($ns::$optional<int> opt) { |
3346 | [&opt]() { |
3347 | if (opt.has_value()) { |
3348 | opt.value(); |
3349 | } else { |
3350 | opt.value(); // [[unsafe]] |
3351 | } |
3352 | }(); |
3353 | } |
3354 | )" ); |
3355 | } |
3356 | |
3357 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureWithInitializer) { |
3358 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3359 | #include "unchecked_optional_access_test.h" |
3360 | |
3361 | void target($ns::$optional<int> opt) { |
3362 | [opt2=opt]() { |
3363 | if (opt2.has_value()) { |
3364 | opt2.value(); |
3365 | } else { |
3366 | opt2.value(); // [[unsafe]] |
3367 | } |
3368 | }(); |
3369 | } |
3370 | )" ); |
3371 | } |
3372 | |
3373 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopyImplicit) { |
3374 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3375 | #include "unchecked_optional_access_test.h" |
3376 | |
3377 | void target($ns::$optional<int> opt) { |
3378 | [=]() { |
3379 | if (opt.has_value()) { |
3380 | opt.value(); |
3381 | } else { |
3382 | opt.value(); // [[unsafe]] |
3383 | } |
3384 | }(); |
3385 | } |
3386 | )" ); |
3387 | } |
3388 | |
3389 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReferenceImplicit) { |
3390 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3391 | #include "unchecked_optional_access_test.h" |
3392 | |
3393 | void target($ns::$optional<int> opt) { |
3394 | [&]() { |
3395 | if (opt.has_value()) { |
3396 | opt.value(); |
3397 | } else { |
3398 | opt.value(); // [[unsafe]] |
3399 | } |
3400 | }(); |
3401 | } |
3402 | )" ); |
3403 | } |
3404 | |
3405 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureThis) { |
3406 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3407 | #include "unchecked_optional_access_test.h" |
3408 | |
3409 | struct Foo { |
3410 | $ns::$optional<int> opt; |
3411 | |
3412 | void target() { |
3413 | [this]() { |
3414 | if (opt.has_value()) { |
3415 | opt.value(); |
3416 | } else { |
3417 | opt.value(); // [[unsafe]] |
3418 | } |
3419 | }(); |
3420 | } |
3421 | }; |
3422 | )" ); |
3423 | } |
3424 | |
3425 | TEST_P(UncheckedOptionalAccessTest, LambdaCaptureStateNotPropagated) { |
3426 | // We can't propagate information from the surrounding context. |
3427 | ExpectDiagnosticsForLambda(SourceCode: R"( |
3428 | #include "unchecked_optional_access_test.h" |
3429 | |
3430 | void target($ns::$optional<int> opt) { |
3431 | if (opt.has_value()) { |
3432 | [&opt]() { |
3433 | opt.value(); // [[unsafe]] |
3434 | }(); |
3435 | } |
3436 | } |
3437 | )" ); |
3438 | } |
3439 | |
3440 | TEST_P(UncheckedOptionalAccessTest, ClassDerivedFromOptional) { |
3441 | ExpectDiagnosticsFor(SourceCode: R"( |
3442 | #include "unchecked_optional_access_test.h" |
3443 | |
3444 | struct Derived : public $ns::$optional<int> {}; |
3445 | |
3446 | void target(Derived opt) { |
3447 | *opt; // [[unsafe]] |
3448 | if (opt.has_value()) |
3449 | *opt; |
3450 | |
3451 | // The same thing, but with a pointer receiver. |
3452 | Derived *popt = &opt; |
3453 | **popt; // [[unsafe]] |
3454 | if (popt->has_value()) |
3455 | **popt; |
3456 | } |
3457 | )" ); |
3458 | } |
3459 | |
3460 | TEST_P(UncheckedOptionalAccessTest, ClassTemplateDerivedFromOptional) { |
3461 | ExpectDiagnosticsFor(SourceCode: R"( |
3462 | #include "unchecked_optional_access_test.h" |
3463 | |
3464 | template <class T> |
3465 | struct Derived : public $ns::$optional<T> {}; |
3466 | |
3467 | void target(Derived<int> opt) { |
3468 | *opt; // [[unsafe]] |
3469 | if (opt.has_value()) |
3470 | *opt; |
3471 | |
3472 | // The same thing, but with a pointer receiver. |
3473 | Derived<int> *popt = &opt; |
3474 | **popt; // [[unsafe]] |
3475 | if (popt->has_value()) |
3476 | **popt; |
3477 | } |
3478 | )" ); |
3479 | } |
3480 | |
3481 | TEST_P(UncheckedOptionalAccessTest, ClassDerivedPrivatelyFromOptional) { |
3482 | // Classes that derive privately from optional can themselves still call |
3483 | // member functions of optional. Check that we model the optional correctly |
3484 | // in this situation. |
3485 | ExpectDiagnosticsFor(SourceCode: R"( |
3486 | #include "unchecked_optional_access_test.h" |
3487 | |
3488 | struct Derived : private $ns::$optional<int> { |
3489 | void Method() { |
3490 | **this; // [[unsafe]] |
3491 | if (this->has_value()) |
3492 | **this; |
3493 | } |
3494 | }; |
3495 | )" , |
3496 | FuncMatcher: ast_matchers::hasName(Name: "Method" )); |
3497 | } |
3498 | |
3499 | TEST_P(UncheckedOptionalAccessTest, ClassDerivedFromOptionalValueConstructor) { |
3500 | ExpectDiagnosticsFor(SourceCode: R"( |
3501 | #include "unchecked_optional_access_test.h" |
3502 | |
3503 | struct Derived : public $ns::$optional<int> { |
3504 | Derived(int); |
3505 | }; |
3506 | |
3507 | void target(Derived opt) { |
3508 | *opt; // [[unsafe]] |
3509 | opt = 1; |
3510 | *opt; |
3511 | } |
3512 | )" ); |
3513 | } |
3514 | |
3515 | // FIXME: Add support for: |
3516 | // - constructors (copy, move) |
3517 | // - assignment operators (default, copy, move) |
3518 | // - invalidation (passing optional by non-const reference/pointer) |
3519 | |