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/Analysis/FlowSensitive/SourceLocationsLattice.h" 15 #include "clang/Tooling/Tooling.h" 16 #include "llvm/ADT/ArrayRef.h" 17 #include "llvm/ADT/StringExtras.h" 18 #include "llvm/Support/Error.h" 19 #include "gmock/gmock.h" 20 #include "gtest/gtest.h" 21 #include <string> 22 #include <utility> 23 #include <vector> 24 25 using namespace clang; 26 using namespace dataflow; 27 using namespace test; 28 29 using ::testing::Pair; 30 using ::testing::UnorderedElementsAre; 31 32 // FIXME: Move header definitions in separate file(s). 33 static constexpr char CSDtdDefHeader[] = R"( 34 #ifndef CSTDDEF_H 35 #define CSTDDEF_H 36 37 namespace std { 38 39 typedef decltype(sizeof(char)) size_t; 40 41 using nullptr_t = decltype(nullptr); 42 43 } // namespace std 44 45 #endif // CSTDDEF_H 46 )"; 47 48 static constexpr char StdTypeTraitsHeader[] = R"( 49 #ifndef STD_TYPE_TRAITS_H 50 #define STD_TYPE_TRAITS_H 51 52 #include "cstddef.h" 53 54 namespace std { 55 56 template <typename T, T V> 57 struct integral_constant { 58 static constexpr T value = V; 59 }; 60 61 using true_type = integral_constant<bool, true>; 62 using false_type = integral_constant<bool, false>; 63 64 template< class T > struct remove_reference {typedef T type;}; 65 template< class T > struct remove_reference<T&> {typedef T type;}; 66 template< class T > struct remove_reference<T&&> {typedef T type;}; 67 68 template <class T> 69 using remove_reference_t = typename remove_reference<T>::type; 70 71 template <class T> 72 struct remove_extent { 73 typedef T type; 74 }; 75 76 template <class T> 77 struct remove_extent<T[]> { 78 typedef T type; 79 }; 80 81 template <class T, size_t N> 82 struct remove_extent<T[N]> { 83 typedef T type; 84 }; 85 86 template <class T> 87 struct is_array : false_type {}; 88 89 template <class T> 90 struct is_array<T[]> : true_type {}; 91 92 template <class T, size_t N> 93 struct is_array<T[N]> : true_type {}; 94 95 template <class> 96 struct is_function : false_type {}; 97 98 template <class Ret, class... Args> 99 struct is_function<Ret(Args...)> : true_type {}; 100 101 namespace detail { 102 103 template <class T> 104 struct type_identity { 105 using type = T; 106 }; // or use type_identity (since C++20) 107 108 template <class T> 109 auto try_add_pointer(int) -> type_identity<typename remove_reference<T>::type*>; 110 template <class T> 111 auto try_add_pointer(...) -> type_identity<T>; 112 113 } // namespace detail 114 115 template <class T> 116 struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {}; 117 118 template <bool B, class T, class F> 119 struct conditional { 120 typedef T type; 121 }; 122 123 template <class T, class F> 124 struct conditional<false, T, F> { 125 typedef F type; 126 }; 127 128 template <class T> 129 struct remove_cv { 130 typedef T type; 131 }; 132 template <class T> 133 struct remove_cv<const T> { 134 typedef T type; 135 }; 136 template <class T> 137 struct remove_cv<volatile T> { 138 typedef T type; 139 }; 140 template <class T> 141 struct remove_cv<const volatile T> { 142 typedef T type; 143 }; 144 145 template <class T> 146 using remove_cv_t = typename remove_cv<T>::type; 147 148 template <class T> 149 struct decay { 150 private: 151 typedef typename remove_reference<T>::type U; 152 153 public: 154 typedef typename conditional< 155 is_array<U>::value, typename remove_extent<U>::type*, 156 typename conditional<is_function<U>::value, typename add_pointer<U>::type, 157 typename remove_cv<U>::type>::type>::type type; 158 }; 159 160 template <bool B, class T = void> 161 struct enable_if {}; 162 163 template <class T> 164 struct enable_if<true, T> { 165 typedef T type; 166 }; 167 168 template <bool B, class T = void> 169 using enable_if_t = typename enable_if<B, T>::type; 170 171 template <class T, class U> 172 struct is_same : false_type {}; 173 174 template <class T> 175 struct is_same<T, T> : true_type {}; 176 177 template <class T> 178 struct is_void : is_same<void, typename remove_cv<T>::type> {}; 179 180 namespace detail { 181 182 template <class T> 183 auto try_add_rvalue_reference(int) -> type_identity<T&&>; 184 template <class T> 185 auto try_add_rvalue_reference(...) -> type_identity<T>; 186 187 } // namespace detail 188 189 template <class T> 190 struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) { 191 }; 192 193 template <class T> 194 typename add_rvalue_reference<T>::type declval() noexcept; 195 196 namespace detail { 197 198 template <class T> 199 auto test_returnable(int) 200 -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{}); 201 template <class> 202 auto test_returnable(...) -> false_type; 203 204 template <class From, class To> 205 auto test_implicitly_convertible(int) 206 -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{}); 207 template <class, class> 208 auto test_implicitly_convertible(...) -> false_type; 209 210 } // namespace detail 211 212 template <class From, class To> 213 struct is_convertible 214 : integral_constant<bool, 215 (decltype(detail::test_returnable<To>(0))::value && 216 decltype(detail::test_implicitly_convertible<From, To>( 217 0))::value) || 218 (is_void<From>::value && is_void<To>::value)> {}; 219 220 template <class From, class To> 221 inline constexpr bool is_convertible_v = is_convertible<From, To>::value; 222 223 template <class...> 224 using void_t = void; 225 226 template <class, class T, class... Args> 227 struct is_constructible_ : false_type {}; 228 229 template <class T, class... Args> 230 struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...> 231 : true_type {}; 232 233 template <class T, class... Args> 234 using is_constructible = is_constructible_<void_t<>, T, Args...>; 235 236 template <class T, class... Args> 237 inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value; 238 239 template <class _Tp> 240 struct __uncvref { 241 typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type; 242 }; 243 244 template <class _Tp> 245 using __uncvref_t = typename __uncvref<_Tp>::type; 246 247 template <bool _Val> 248 using _BoolConstant = integral_constant<bool, _Val>; 249 250 template <class _Tp, class _Up> 251 using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>; 252 253 template <class _Tp, class _Up> 254 using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>; 255 256 template <bool> 257 struct _MetaBase; 258 template <> 259 struct _MetaBase<true> { 260 template <class _Tp, class _Up> 261 using _SelectImpl = _Tp; 262 template <template <class...> class _FirstFn, template <class...> class, 263 class... _Args> 264 using _SelectApplyImpl = _FirstFn<_Args...>; 265 template <class _First, class...> 266 using _FirstImpl = _First; 267 template <class, class _Second, class...> 268 using _SecondImpl = _Second; 269 template <class _Result, class _First, class... _Rest> 270 using _OrImpl = 271 typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>:: 272 template _OrImpl<_First, _Rest...>; 273 }; 274 275 template <> 276 struct _MetaBase<false> { 277 template <class _Tp, class _Up> 278 using _SelectImpl = _Up; 279 template <template <class...> class, template <class...> class _SecondFn, 280 class... _Args> 281 using _SelectApplyImpl = _SecondFn<_Args...>; 282 template <class _Result, class...> 283 using _OrImpl = _Result; 284 }; 285 286 template <bool _Cond, class _IfRes, class _ElseRes> 287 using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>; 288 289 template <class... _Rest> 290 using _Or = typename _MetaBase<sizeof...(_Rest) != 291 0>::template _OrImpl<false_type, _Rest...>; 292 293 template <bool _Bp, class _Tp = void> 294 using __enable_if_t = typename enable_if<_Bp, _Tp>::type; 295 296 template <class...> 297 using __expand_to_true = true_type; 298 template <class... _Pred> 299 __expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int); 300 template <class...> 301 false_type __and_helper(...); 302 template <class... _Pred> 303 using _And = decltype(__and_helper<_Pred...>(0)); 304 305 template <class _Pred> 306 struct _Not : _BoolConstant<!_Pred::value> {}; 307 308 struct __check_tuple_constructor_fail { 309 static constexpr bool __enable_explicit_default() { return false; } 310 static constexpr bool __enable_implicit_default() { return false; } 311 template <class...> 312 static constexpr bool __enable_explicit() { 313 return false; 314 } 315 template <class...> 316 static constexpr bool __enable_implicit() { 317 return false; 318 } 319 }; 320 321 template <typename, typename _Tp> 322 struct __select_2nd { 323 typedef _Tp type; 324 }; 325 template <class _Tp, class _Arg> 326 typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())), 327 true_type>::type 328 __is_assignable_test(int); 329 template <class, class> 330 false_type __is_assignable_test(...); 331 template <class _Tp, class _Arg, 332 bool = is_void<_Tp>::value || is_void<_Arg>::value> 333 struct __is_assignable_imp 334 : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {}; 335 template <class _Tp, class _Arg> 336 struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {}; 337 template <class _Tp, class _Arg> 338 struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {}; 339 340 template <class _Tp> 341 struct __libcpp_is_integral : public false_type {}; 342 template <> 343 struct __libcpp_is_integral<bool> : public true_type {}; 344 template <> 345 struct __libcpp_is_integral<char> : public true_type {}; 346 template <> 347 struct __libcpp_is_integral<signed char> : public true_type {}; 348 template <> 349 struct __libcpp_is_integral<unsigned char> : public true_type {}; 350 template <> 351 struct __libcpp_is_integral<wchar_t> : public true_type {}; 352 template <> 353 struct __libcpp_is_integral<short> : public true_type {}; // NOLINT 354 template <> 355 struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT 356 template <> 357 struct __libcpp_is_integral<int> : public true_type {}; 358 template <> 359 struct __libcpp_is_integral<unsigned int> : public true_type {}; 360 template <> 361 struct __libcpp_is_integral<long> : public true_type {}; // NOLINT 362 template <> 363 struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT 364 template <> 365 struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT 366 template <> // NOLINTNEXTLINE 367 struct __libcpp_is_integral<unsigned long long> : public true_type {}; 368 template <class _Tp> 369 struct is_integral 370 : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {}; 371 372 template <class _Tp> 373 struct __libcpp_is_floating_point : public false_type {}; 374 template <> 375 struct __libcpp_is_floating_point<float> : public true_type {}; 376 template <> 377 struct __libcpp_is_floating_point<double> : public true_type {}; 378 template <> 379 struct __libcpp_is_floating_point<long double> : public true_type {}; 380 template <class _Tp> 381 struct is_floating_point 382 : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {}; 383 384 template <class _Tp> 385 struct is_arithmetic 386 : public integral_constant<bool, is_integral<_Tp>::value || 387 is_floating_point<_Tp>::value> {}; 388 389 template <class _Tp> 390 struct __libcpp_is_pointer : public false_type {}; 391 template <class _Tp> 392 struct __libcpp_is_pointer<_Tp*> : public true_type {}; 393 template <class _Tp> 394 struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> { 395 }; 396 397 template <class _Tp> 398 struct __libcpp_is_member_pointer : public false_type {}; 399 template <class _Tp, class _Up> 400 struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {}; 401 template <class _Tp> 402 struct is_member_pointer 403 : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {}; 404 405 template <class _Tp> 406 struct __libcpp_union : public false_type {}; 407 template <class _Tp> 408 struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {}; 409 410 template <class T> 411 struct is_reference : false_type {}; 412 template <class T> 413 struct is_reference<T&> : true_type {}; 414 template <class T> 415 struct is_reference<T&&> : true_type {}; 416 417 template <class T> 418 inline constexpr bool is_reference_v = is_reference<T>::value; 419 420 struct __two { 421 char __lx[2]; 422 }; 423 424 namespace __is_class_imp { 425 template <class _Tp> 426 char __test(int _Tp::*); 427 template <class _Tp> 428 __two __test(...); 429 } // namespace __is_class_imp 430 template <class _Tp> 431 struct is_class 432 : public integral_constant<bool, 433 sizeof(__is_class_imp::__test<_Tp>(0)) == 1 && 434 !is_union<_Tp>::value> {}; 435 436 template <class _Tp> 437 struct __is_nullptr_t_impl : public false_type {}; 438 template <> 439 struct __is_nullptr_t_impl<nullptr_t> : public true_type {}; 440 template <class _Tp> 441 struct __is_nullptr_t 442 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; 443 template <class _Tp> 444 struct is_null_pointer 445 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; 446 447 template <class _Tp> 448 struct is_enum 449 : public integral_constant< 450 bool, !is_void<_Tp>::value && !is_integral<_Tp>::value && 451 !is_floating_point<_Tp>::value && !is_array<_Tp>::value && 452 !is_pointer<_Tp>::value && !is_reference<_Tp>::value && 453 !is_member_pointer<_Tp>::value && !is_union<_Tp>::value && 454 !is_class<_Tp>::value && !is_function<_Tp>::value> {}; 455 456 template <class _Tp> 457 struct is_scalar 458 : public integral_constant< 459 bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value || 460 is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value || 461 is_enum<_Tp>::value> {}; 462 template <> 463 struct is_scalar<nullptr_t> : public true_type {}; 464 465 } // namespace std 466 467 #endif // STD_TYPE_TRAITS_H 468 )"; 469 470 static constexpr char AbslTypeTraitsHeader[] = R"( 471 #ifndef ABSL_TYPE_TRAITS_H 472 #define ABSL_TYPE_TRAITS_H 473 474 #include "std_type_traits.h" 475 476 namespace absl { 477 478 template <typename... Ts> 479 struct conjunction : std::true_type {}; 480 481 template <typename T, typename... Ts> 482 struct conjunction<T, Ts...> 483 : std::conditional<T::value, conjunction<Ts...>, T>::type {}; 484 485 template <typename T> 486 struct conjunction<T> : T {}; 487 488 template <typename T> 489 struct negation : std::integral_constant<bool, !T::value> {}; 490 491 template <bool B, typename T = void> 492 using enable_if_t = typename std::enable_if<B, T>::type; 493 494 } // namespace absl 495 496 #endif // ABSL_TYPE_TRAITS_H 497 )"; 498 499 static constexpr char StdUtilityHeader[] = R"( 500 #ifndef UTILITY_H 501 #define UTILITY_H 502 503 #include "std_type_traits.h" 504 505 namespace std { 506 507 template <typename T> 508 constexpr remove_reference_t<T>&& move(T&& x); 509 510 } // namespace std 511 512 #endif // UTILITY_H 513 )"; 514 515 static constexpr char StdInitializerListHeader[] = R"( 516 #ifndef INITIALIZER_LIST_H 517 #define INITIALIZER_LIST_H 518 519 namespace std { 520 521 template <typename T> 522 class initializer_list { 523 public: 524 initializer_list() noexcept; 525 }; 526 527 } // namespace std 528 529 #endif // INITIALIZER_LIST_H 530 )"; 531 532 static constexpr char StdOptionalHeader[] = R"( 533 #include "std_initializer_list.h" 534 #include "std_type_traits.h" 535 #include "std_utility.h" 536 537 namespace std { 538 539 struct in_place_t {}; 540 constexpr in_place_t in_place; 541 542 struct nullopt_t { 543 constexpr explicit nullopt_t() {} 544 }; 545 constexpr nullopt_t nullopt; 546 547 template <class _Tp> 548 struct __optional_destruct_base { 549 constexpr void reset() noexcept; 550 }; 551 552 template <class _Tp> 553 struct __optional_storage_base : __optional_destruct_base<_Tp> { 554 constexpr bool has_value() const noexcept; 555 }; 556 557 template <typename _Tp> 558 class optional : private __optional_storage_base<_Tp> { 559 using __base = __optional_storage_base<_Tp>; 560 561 public: 562 using value_type = _Tp; 563 564 private: 565 struct _CheckOptionalArgsConstructor { 566 template <class _Up> 567 static constexpr bool __enable_implicit() { 568 return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>; 569 } 570 571 template <class _Up> 572 static constexpr bool __enable_explicit() { 573 return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>; 574 } 575 }; 576 template <class _Up> 577 using _CheckOptionalArgsCtor = 578 _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value && 579 _IsNotSame<__uncvref_t<_Up>, optional>::value, 580 _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>; 581 template <class _QualUp> 582 struct _CheckOptionalLikeConstructor { 583 template <class _Up, class _Opt = optional<_Up>> 584 using __check_constructible_from_opt = 585 _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>, 586 is_constructible<_Tp, _Opt&&>, is_constructible<_Tp, _Opt const&&>, 587 is_convertible<_Opt&, _Tp>, is_convertible<_Opt const&, _Tp>, 588 is_convertible<_Opt&&, _Tp>, is_convertible<_Opt const&&, _Tp>>; 589 template <class _Up, class _QUp = _QualUp> 590 static constexpr bool __enable_implicit() { 591 return is_convertible<_QUp, _Tp>::value && 592 !__check_constructible_from_opt<_Up>::value; 593 } 594 template <class _Up, class _QUp = _QualUp> 595 static constexpr bool __enable_explicit() { 596 return !is_convertible<_QUp, _Tp>::value && 597 !__check_constructible_from_opt<_Up>::value; 598 } 599 }; 600 601 template <class _Up, class _QualUp> 602 using _CheckOptionalLikeCtor = 603 _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value, 604 _CheckOptionalLikeConstructor<_QualUp>, 605 __check_tuple_constructor_fail>; 606 607 608 template <class _Up, class _QualUp> 609 using _CheckOptionalLikeAssign = _If< 610 _And< 611 _IsNotSame<_Up, _Tp>, 612 is_constructible<_Tp, _QualUp>, 613 is_assignable<_Tp&, _QualUp> 614 >::value, 615 _CheckOptionalLikeConstructor<_QualUp>, 616 __check_tuple_constructor_fail 617 >; 618 619 public: 620 constexpr optional() noexcept {} 621 constexpr optional(const optional&) = default; 622 constexpr optional(optional&&) = default; 623 constexpr optional(nullopt_t) noexcept {} 624 625 template < 626 class _InPlaceT, class... _Args, 627 class = enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, 628 is_constructible<value_type, _Args...>>::value>> 629 constexpr explicit optional(_InPlaceT, _Args&&... __args); 630 631 template <class _Up, class... _Args, 632 class = enable_if_t<is_constructible_v< 633 value_type, initializer_list<_Up>&, _Args...>>> 634 constexpr explicit optional(in_place_t, initializer_list<_Up> __il, 635 _Args&&... __args); 636 637 template < 638 class _Up = value_type, 639 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), 640 int> = 0> 641 constexpr optional(_Up&& __v); 642 643 template < 644 class _Up, 645 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), 646 int> = 0> 647 constexpr explicit optional(_Up&& __v); 648 649 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: 650 template __enable_implicit<_Up>(), 651 int> = 0> 652 constexpr optional(const optional<_Up>& __v); 653 654 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: 655 template __enable_explicit<_Up>(), 656 int> = 0> 657 constexpr explicit optional(const optional<_Up>& __v); 658 659 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 660 template __enable_implicit<_Up>(), 661 int> = 0> 662 constexpr optional(optional<_Up>&& __v); 663 664 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 665 template __enable_explicit<_Up>(), 666 int> = 0> 667 constexpr explicit optional(optional<_Up>&& __v); 668 669 constexpr optional& operator=(nullopt_t) noexcept; 670 671 optional& operator=(const optional&); 672 673 optional& operator=(optional&&); 674 675 template <class _Up = value_type, 676 class = enable_if_t<_And<_IsNotSame<__uncvref_t<_Up>, optional>, 677 _Or<_IsNotSame<__uncvref_t<_Up>, value_type>, 678 _Not<is_scalar<value_type>>>, 679 is_constructible<value_type, _Up>, 680 is_assignable<value_type&, _Up>>::value>> 681 constexpr optional& operator=(_Up&& __v); 682 683 template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>:: 684 template __enable_assign<_Up>(), 685 int> = 0> 686 constexpr optional& operator=(const optional<_Up>& __v); 687 688 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 689 template __enable_assign<_Up>(), 690 int> = 0> 691 constexpr optional& operator=(optional<_Up>&& __v); 692 693 const _Tp& operator*() const&; 694 _Tp& operator*() &; 695 const _Tp&& operator*() const&&; 696 _Tp&& operator*() &&; 697 698 const _Tp* operator->() const; 699 _Tp* operator->(); 700 701 const _Tp& value() const&; 702 _Tp& value() &; 703 const _Tp&& value() const&&; 704 _Tp&& value() &&; 705 706 template <typename U> 707 constexpr _Tp value_or(U&& v) const&; 708 template <typename U> 709 _Tp value_or(U&& v) &&; 710 711 template <typename... Args> 712 _Tp& emplace(Args&&... args); 713 714 template <typename U, typename... Args> 715 _Tp& emplace(std::initializer_list<U> ilist, Args&&... args); 716 717 using __base::reset; 718 719 constexpr explicit operator bool() const noexcept; 720 using __base::has_value; 721 }; 722 723 template <typename T> 724 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 725 726 template <typename T, typename... Args> 727 constexpr optional<T> make_optional(Args&&... args); 728 729 template <typename T, typename U, typename... Args> 730 constexpr optional<T> make_optional(std::initializer_list<U> il, 731 Args&&... args); 732 733 } // namespace std 734 )"; 735 736 static constexpr char AbslOptionalHeader[] = R"( 737 #include "absl_type_traits.h" 738 #include "std_initializer_list.h" 739 #include "std_type_traits.h" 740 #include "std_utility.h" 741 742 namespace absl { 743 744 struct nullopt_t { 745 constexpr explicit nullopt_t() {} 746 }; 747 constexpr nullopt_t nullopt; 748 749 struct in_place_t {}; 750 constexpr in_place_t in_place; 751 752 template <typename T> 753 class optional; 754 755 namespace optional_internal { 756 757 template <typename T, typename U> 758 struct is_constructible_convertible_from_optional 759 : std::integral_constant< 760 bool, std::is_constructible<T, optional<U>&>::value || 761 std::is_constructible<T, optional<U>&&>::value || 762 std::is_constructible<T, const optional<U>&>::value || 763 std::is_constructible<T, const optional<U>&&>::value || 764 std::is_convertible<optional<U>&, T>::value || 765 std::is_convertible<optional<U>&&, T>::value || 766 std::is_convertible<const optional<U>&, T>::value || 767 std::is_convertible<const optional<U>&&, T>::value> {}; 768 769 template <typename T, typename U> 770 struct is_constructible_convertible_assignable_from_optional 771 : std::integral_constant< 772 bool, is_constructible_convertible_from_optional<T, U>::value || 773 std::is_assignable<T&, optional<U>&>::value || 774 std::is_assignable<T&, optional<U>&&>::value || 775 std::is_assignable<T&, const optional<U>&>::value || 776 std::is_assignable<T&, const optional<U>&&>::value> {}; 777 778 } // namespace optional_internal 779 780 template <typename T> 781 class optional { 782 public: 783 constexpr optional() noexcept; 784 785 constexpr optional(nullopt_t) noexcept; 786 787 optional(const optional&) = default; 788 789 optional(optional&&) = default; 790 791 template <typename InPlaceT, typename... Args, 792 absl::enable_if_t<absl::conjunction< 793 std::is_same<InPlaceT, in_place_t>, 794 std::is_constructible<T, Args&&...>>::value>* = nullptr> 795 constexpr explicit optional(InPlaceT, Args&&... args); 796 797 template <typename U, typename... Args, 798 typename = typename std::enable_if<std::is_constructible< 799 T, std::initializer_list<U>&, Args&&...>::value>::type> 800 constexpr explicit optional(in_place_t, std::initializer_list<U> il, 801 Args&&... args); 802 803 template < 804 typename U = T, 805 typename std::enable_if< 806 absl::conjunction<absl::negation<std::is_same< 807 in_place_t, typename std::decay<U>::type>>, 808 absl::negation<std::is_same< 809 optional<T>, typename std::decay<U>::type>>, 810 std::is_convertible<U&&, T>, 811 std::is_constructible<T, U&&>>::value, 812 bool>::type = false> 813 constexpr optional(U&& v); 814 815 template < 816 typename U = T, 817 typename std::enable_if< 818 absl::conjunction<absl::negation<std::is_same< 819 in_place_t, typename std::decay<U>::type>>, 820 absl::negation<std::is_same< 821 optional<T>, typename std::decay<U>::type>>, 822 absl::negation<std::is_convertible<U&&, T>>, 823 std::is_constructible<T, U&&>>::value, 824 bool>::type = false> 825 explicit constexpr optional(U&& v); 826 827 template <typename U, 828 typename std::enable_if< 829 absl::conjunction< 830 absl::negation<std::is_same<T, U>>, 831 std::is_constructible<T, const U&>, 832 absl::negation< 833 optional_internal:: 834 is_constructible_convertible_from_optional<T, U>>, 835 std::is_convertible<const U&, T>>::value, 836 bool>::type = false> 837 optional(const optional<U>& rhs); 838 839 template <typename U, 840 typename std::enable_if< 841 absl::conjunction< 842 absl::negation<std::is_same<T, U>>, 843 std::is_constructible<T, const U&>, 844 absl::negation< 845 optional_internal:: 846 is_constructible_convertible_from_optional<T, U>>, 847 absl::negation<std::is_convertible<const U&, T>>>::value, 848 bool>::type = false> 849 explicit optional(const optional<U>& rhs); 850 851 template < 852 typename U, 853 typename std::enable_if< 854 absl::conjunction< 855 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 856 absl::negation< 857 optional_internal::is_constructible_convertible_from_optional< 858 T, U>>, 859 std::is_convertible<U&&, T>>::value, 860 bool>::type = false> 861 optional(optional<U>&& rhs); 862 863 template < 864 typename U, 865 typename std::enable_if< 866 absl::conjunction< 867 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 868 absl::negation< 869 optional_internal::is_constructible_convertible_from_optional< 870 T, U>>, 871 absl::negation<std::is_convertible<U&&, T>>>::value, 872 bool>::type = false> 873 explicit optional(optional<U>&& rhs); 874 875 optional& operator=(nullopt_t) noexcept; 876 877 optional& operator=(const optional& src); 878 879 optional& operator=(optional&& src); 880 881 template < 882 typename U = T, 883 typename = typename std::enable_if<absl::conjunction< 884 absl::negation< 885 std::is_same<optional<T>, typename std::decay<U>::type>>, 886 absl::negation< 887 absl::conjunction<std::is_scalar<T>, 888 std::is_same<T, typename std::decay<U>::type>>>, 889 std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type> 890 optional& operator=(U&& v); 891 892 template < 893 typename U, 894 typename = typename std::enable_if<absl::conjunction< 895 absl::negation<std::is_same<T, U>>, 896 std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>, 897 absl::negation< 898 optional_internal:: 899 is_constructible_convertible_assignable_from_optional< 900 T, U>>>::value>::type> 901 optional& operator=(const optional<U>& rhs); 902 903 template <typename U, 904 typename = typename std::enable_if<absl::conjunction< 905 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>, 906 std::is_assignable<T&, U>, 907 absl::negation< 908 optional_internal:: 909 is_constructible_convertible_assignable_from_optional< 910 T, U>>>::value>::type> 911 optional& operator=(optional<U>&& rhs); 912 913 const T& operator*() const&; 914 T& operator*() &; 915 const T&& operator*() const&&; 916 T&& operator*() &&; 917 918 const T* operator->() const; 919 T* operator->(); 920 921 const T& value() const&; 922 T& value() &; 923 const T&& value() const&&; 924 T&& value() &&; 925 926 template <typename U> 927 constexpr T value_or(U&& v) const&; 928 template <typename U> 929 T value_or(U&& v) &&; 930 931 template <typename... Args> 932 T& emplace(Args&&... args); 933 934 template <typename U, typename... Args> 935 T& emplace(std::initializer_list<U> ilist, Args&&... args); 936 937 void reset() noexcept; 938 939 constexpr explicit operator bool() const noexcept; 940 constexpr bool has_value() const noexcept; 941 }; 942 943 template <typename T> 944 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 945 946 template <typename T, typename... Args> 947 constexpr optional<T> make_optional(Args&&... args); 948 949 template <typename T, typename U, typename... Args> 950 constexpr optional<T> make_optional(std::initializer_list<U> il, 951 Args&&... args); 952 953 } // namespace absl 954 )"; 955 956 static constexpr char BaseOptionalHeader[] = R"( 957 #include "std_initializer_list.h" 958 #include "std_type_traits.h" 959 #include "std_utility.h" 960 961 namespace base { 962 963 struct in_place_t {}; 964 constexpr in_place_t in_place; 965 966 struct nullopt_t { 967 constexpr explicit nullopt_t() {} 968 }; 969 constexpr nullopt_t nullopt; 970 971 template <typename T> 972 class Optional; 973 974 namespace internal { 975 976 template <typename T> 977 using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>; 978 979 template <typename T, typename U> 980 struct IsConvertibleFromOptional 981 : std::integral_constant< 982 bool, std::is_constructible<T, Optional<U>&>::value || 983 std::is_constructible<T, const Optional<U>&>::value || 984 std::is_constructible<T, Optional<U>&&>::value || 985 std::is_constructible<T, const Optional<U>&&>::value || 986 std::is_convertible<Optional<U>&, T>::value || 987 std::is_convertible<const Optional<U>&, T>::value || 988 std::is_convertible<Optional<U>&&, T>::value || 989 std::is_convertible<const Optional<U>&&, T>::value> {}; 990 991 template <typename T, typename U> 992 struct IsAssignableFromOptional 993 : std::integral_constant< 994 bool, IsConvertibleFromOptional<T, U>::value || 995 std::is_assignable<T&, Optional<U>&>::value || 996 std::is_assignable<T&, const Optional<U>&>::value || 997 std::is_assignable<T&, Optional<U>&&>::value || 998 std::is_assignable<T&, const Optional<U>&&>::value> {}; 999 1000 } // namespace internal 1001 1002 template <typename T> 1003 class Optional { 1004 public: 1005 using value_type = T; 1006 1007 constexpr Optional() = default; 1008 constexpr Optional(const Optional& other) noexcept = default; 1009 constexpr Optional(Optional&& other) noexcept = default; 1010 1011 constexpr Optional(nullopt_t); 1012 1013 template <typename U, 1014 typename std::enable_if< 1015 std::is_constructible<T, const U&>::value && 1016 !internal::IsConvertibleFromOptional<T, U>::value && 1017 std::is_convertible<const U&, T>::value, 1018 bool>::type = false> 1019 Optional(const Optional<U>& other) noexcept; 1020 1021 template <typename U, 1022 typename std::enable_if< 1023 std::is_constructible<T, const U&>::value && 1024 !internal::IsConvertibleFromOptional<T, U>::value && 1025 !std::is_convertible<const U&, T>::value, 1026 bool>::type = false> 1027 explicit Optional(const Optional<U>& other) noexcept; 1028 1029 template <typename U, 1030 typename std::enable_if< 1031 std::is_constructible<T, U&&>::value && 1032 !internal::IsConvertibleFromOptional<T, U>::value && 1033 std::is_convertible<U&&, T>::value, 1034 bool>::type = false> 1035 Optional(Optional<U>&& other) noexcept; 1036 1037 template <typename U, 1038 typename std::enable_if< 1039 std::is_constructible<T, U&&>::value && 1040 !internal::IsConvertibleFromOptional<T, U>::value && 1041 !std::is_convertible<U&&, T>::value, 1042 bool>::type = false> 1043 explicit Optional(Optional<U>&& other) noexcept; 1044 1045 template <class... Args> 1046 constexpr explicit Optional(in_place_t, Args&&... args); 1047 1048 template <class U, class... Args, 1049 class = typename std::enable_if<std::is_constructible< 1050 value_type, std::initializer_list<U>&, Args...>::value>::type> 1051 constexpr explicit Optional(in_place_t, std::initializer_list<U> il, 1052 Args&&... args); 1053 1054 template < 1055 typename U = value_type, 1056 typename std::enable_if< 1057 std::is_constructible<T, U&&>::value && 1058 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1059 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1060 std::is_convertible<U&&, T>::value, 1061 bool>::type = false> 1062 constexpr Optional(U&& value); 1063 1064 template < 1065 typename U = value_type, 1066 typename std::enable_if< 1067 std::is_constructible<T, U&&>::value && 1068 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1069 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1070 !std::is_convertible<U&&, T>::value, 1071 bool>::type = false> 1072 constexpr explicit Optional(U&& value); 1073 1074 Optional& operator=(const Optional& other) noexcept; 1075 1076 Optional& operator=(Optional&& other) noexcept; 1077 1078 Optional& operator=(nullopt_t); 1079 1080 template <typename U> 1081 typename std::enable_if< 1082 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1083 std::is_constructible<T, U>::value && 1084 std::is_assignable<T&, U>::value && 1085 (!std::is_scalar<T>::value || 1086 !std::is_same<typename std::decay<U>::type, T>::value), 1087 Optional&>::type 1088 operator=(U&& value) noexcept; 1089 1090 template <typename U> 1091 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1092 std::is_constructible<T, const U&>::value && 1093 std::is_assignable<T&, const U&>::value, 1094 Optional&>::type 1095 operator=(const Optional<U>& other) noexcept; 1096 1097 template <typename U> 1098 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1099 std::is_constructible<T, U>::value && 1100 std::is_assignable<T&, U>::value, 1101 Optional&>::type 1102 operator=(Optional<U>&& other) noexcept; 1103 1104 const T& operator*() const&; 1105 T& operator*() &; 1106 const T&& operator*() const&&; 1107 T&& operator*() &&; 1108 1109 const T* operator->() const; 1110 T* operator->(); 1111 1112 const T& value() const&; 1113 T& value() &; 1114 const T&& value() const&&; 1115 T&& value() &&; 1116 1117 template <typename U> 1118 constexpr T value_or(U&& v) const&; 1119 template <typename U> 1120 T value_or(U&& v) &&; 1121 1122 template <typename... Args> 1123 T& emplace(Args&&... args); 1124 1125 template <typename U, typename... Args> 1126 T& emplace(std::initializer_list<U> ilist, Args&&... args); 1127 1128 void reset() noexcept; 1129 1130 constexpr explicit operator bool() const noexcept; 1131 constexpr bool has_value() const noexcept; 1132 }; 1133 1134 template <typename T> 1135 constexpr Optional<typename std::decay<T>::type> make_optional(T&& v); 1136 1137 template <typename T, typename... Args> 1138 constexpr Optional<T> make_optional(Args&&... args); 1139 1140 template <typename T, typename U, typename... Args> 1141 constexpr Optional<T> make_optional(std::initializer_list<U> il, 1142 Args&&... args); 1143 1144 } // namespace base 1145 )"; 1146 1147 /// Converts `L` to string. 1148 static std::string ConvertToString(const SourceLocationsLattice &L, 1149 const ASTContext &Ctx) { 1150 return L.getSourceLocations().empty() ? "safe" 1151 : "unsafe: " + DebugString(L, Ctx); 1152 } 1153 1154 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. 1155 static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern, 1156 const std::string &Replacement) { 1157 size_t Pos = 0; 1158 while (true) { 1159 Pos = S.find(Pattern, Pos); 1160 if (Pos == std::string::npos) 1161 break; 1162 S.replace(Pos, Pattern.size(), Replacement); 1163 } 1164 } 1165 1166 struct OptionalTypeIdentifier { 1167 std::string NamespaceName; 1168 std::string TypeName; 1169 }; 1170 1171 class UncheckedOptionalAccessTest 1172 : public ::testing::TestWithParam<OptionalTypeIdentifier> { 1173 protected: 1174 template <typename LatticeChecksMatcher> 1175 void ExpectLatticeChecksFor(std::string SourceCode, 1176 LatticeChecksMatcher MatchesLatticeChecks) { 1177 ExpectLatticeChecksFor(SourceCode, ast_matchers::hasName("target"), 1178 MatchesLatticeChecks); 1179 } 1180 1181 private: 1182 template <typename FuncDeclMatcher, typename LatticeChecksMatcher> 1183 void ExpectLatticeChecksFor(std::string SourceCode, 1184 FuncDeclMatcher FuncMatcher, 1185 LatticeChecksMatcher MatchesLatticeChecks) { 1186 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName); 1187 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName); 1188 1189 std::vector<std::pair<std::string, std::string>> Headers; 1190 Headers.emplace_back("cstddef.h", CSDtdDefHeader); 1191 Headers.emplace_back("std_initializer_list.h", StdInitializerListHeader); 1192 Headers.emplace_back("std_type_traits.h", StdTypeTraitsHeader); 1193 Headers.emplace_back("std_utility.h", StdUtilityHeader); 1194 Headers.emplace_back("std_optional.h", StdOptionalHeader); 1195 Headers.emplace_back("absl_type_traits.h", AbslTypeTraitsHeader); 1196 Headers.emplace_back("absl_optional.h", AbslOptionalHeader); 1197 Headers.emplace_back("base_optional.h", BaseOptionalHeader); 1198 Headers.emplace_back("unchecked_optional_access_test.h", R"( 1199 #include "absl_optional.h" 1200 #include "base_optional.h" 1201 #include "std_initializer_list.h" 1202 #include "std_optional.h" 1203 #include "std_utility.h" 1204 1205 template <typename T> 1206 T Make(); 1207 )"); 1208 const tooling::FileContentMappings FileContents(Headers.begin(), 1209 Headers.end()); 1210 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>( 1211 SourceCode, FuncMatcher, 1212 [](ASTContext &Ctx, Environment &) { 1213 return UncheckedOptionalAccessModel(Ctx); 1214 }, 1215 [&MatchesLatticeChecks]( 1216 llvm::ArrayRef<std::pair< 1217 std::string, DataflowAnalysisState<SourceLocationsLattice>>> 1218 CheckToLatticeMap, 1219 ASTContext &Ctx) { 1220 // FIXME: Consider using a matcher instead of translating 1221 // `CheckToLatticeMap` to `CheckToStringifiedLatticeMap`. 1222 std::vector<std::pair<std::string, std::string>> 1223 CheckToStringifiedLatticeMap; 1224 for (const auto &E : CheckToLatticeMap) { 1225 CheckToStringifiedLatticeMap.emplace_back( 1226 E.first, ConvertToString(E.second.Lattice, Ctx)); 1227 } 1228 EXPECT_THAT(CheckToStringifiedLatticeMap, MatchesLatticeChecks); 1229 }, 1230 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents); 1231 if (Error) 1232 FAIL() << llvm::toString(std::move(Error)); 1233 } 1234 }; 1235 1236 INSTANTIATE_TEST_SUITE_P( 1237 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest, 1238 ::testing::Values(OptionalTypeIdentifier{"std", "optional"}, 1239 OptionalTypeIdentifier{"absl", "optional"}, 1240 OptionalTypeIdentifier{"base", "Optional"}), 1241 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) { 1242 return Info.param.NamespaceName; 1243 }); 1244 1245 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { 1246 ExpectLatticeChecksFor(R"( 1247 void target() { 1248 (void)0; 1249 /*[[check]]*/ 1250 } 1251 )", 1252 UnorderedElementsAre(Pair("check", "safe"))); 1253 } 1254 1255 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { 1256 ExpectLatticeChecksFor( 1257 R"( 1258 #include "unchecked_optional_access_test.h" 1259 1260 void target($ns::$optional<int> opt) { 1261 opt.value(); 1262 /*[[check]]*/ 1263 } 1264 )", 1265 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1266 1267 ExpectLatticeChecksFor( 1268 R"( 1269 #include "unchecked_optional_access_test.h" 1270 1271 void target($ns::$optional<int> opt) { 1272 std::move(opt).value(); 1273 /*[[check]]*/ 1274 } 1275 )", 1276 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1277 } 1278 1279 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { 1280 ExpectLatticeChecksFor( 1281 R"( 1282 #include "unchecked_optional_access_test.h" 1283 1284 void target($ns::$optional<int> opt) { 1285 *opt; 1286 /*[[check]]*/ 1287 } 1288 )", 1289 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); 1290 1291 ExpectLatticeChecksFor( 1292 R"( 1293 #include "unchecked_optional_access_test.h" 1294 1295 void target($ns::$optional<int> opt) { 1296 *std::move(opt); 1297 /*[[check]]*/ 1298 } 1299 )", 1300 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); 1301 } 1302 1303 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { 1304 ExpectLatticeChecksFor( 1305 R"( 1306 #include "unchecked_optional_access_test.h" 1307 1308 struct Foo { 1309 void foo(); 1310 }; 1311 1312 void target($ns::$optional<Foo> opt) { 1313 opt->foo(); 1314 /*[[check]]*/ 1315 } 1316 )", 1317 UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); 1318 1319 ExpectLatticeChecksFor( 1320 R"( 1321 #include "unchecked_optional_access_test.h" 1322 1323 struct Foo { 1324 void foo(); 1325 }; 1326 1327 void target($ns::$optional<Foo> opt) { 1328 std::move(opt)->foo(); 1329 /*[[check]]*/ 1330 } 1331 )", 1332 UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); 1333 } 1334 1335 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { 1336 ExpectLatticeChecksFor(R"( 1337 #include "unchecked_optional_access_test.h" 1338 1339 void target($ns::$optional<int> opt) { 1340 if (opt.has_value()) { 1341 opt.value(); 1342 /*[[check]]*/ 1343 } 1344 } 1345 )", 1346 UnorderedElementsAre(Pair("check", "safe"))); 1347 } 1348 1349 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { 1350 ExpectLatticeChecksFor(R"( 1351 #include "unchecked_optional_access_test.h" 1352 1353 void target($ns::$optional<int> opt) { 1354 if (opt) { 1355 opt.value(); 1356 /*[[check]]*/ 1357 } 1358 } 1359 )", 1360 UnorderedElementsAre(Pair("check", "safe"))); 1361 } 1362 1363 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { 1364 ExpectLatticeChecksFor( 1365 R"( 1366 #include "unchecked_optional_access_test.h" 1367 1368 void target() { 1369 Make<$ns::$optional<int>>().value(); 1370 (void)0; 1371 /*[[check]]*/ 1372 } 1373 )", 1374 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1375 1376 ExpectLatticeChecksFor( 1377 R"( 1378 #include "unchecked_optional_access_test.h" 1379 1380 void target($ns::$optional<int> opt) { 1381 std::move(opt).value(); 1382 /*[[check]]*/ 1383 } 1384 )", 1385 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1386 } 1387 1388 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { 1389 ExpectLatticeChecksFor( 1390 R"( 1391 #include "unchecked_optional_access_test.h" 1392 1393 void target() { 1394 $ns::$optional<int> opt; 1395 opt.value(); 1396 /*[[check]]*/ 1397 } 1398 )", 1399 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1400 } 1401 1402 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { 1403 ExpectLatticeChecksFor( 1404 R"( 1405 #include "unchecked_optional_access_test.h" 1406 1407 void target() { 1408 $ns::$optional<int> opt($ns::nullopt); 1409 opt.value(); 1410 /*[[check]]*/ 1411 } 1412 )", 1413 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1414 } 1415 1416 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { 1417 ExpectLatticeChecksFor(R"( 1418 #include "unchecked_optional_access_test.h" 1419 1420 void target() { 1421 $ns::$optional<int> opt($ns::in_place, 3); 1422 opt.value(); 1423 /*[[check]]*/ 1424 } 1425 )", 1426 UnorderedElementsAre(Pair("check", "safe"))); 1427 1428 ExpectLatticeChecksFor(R"( 1429 #include "unchecked_optional_access_test.h" 1430 1431 struct Foo {}; 1432 1433 void target() { 1434 $ns::$optional<Foo> opt($ns::in_place); 1435 opt.value(); 1436 /*[[check]]*/ 1437 } 1438 )", 1439 UnorderedElementsAre(Pair("check", "safe"))); 1440 1441 ExpectLatticeChecksFor(R"( 1442 #include "unchecked_optional_access_test.h" 1443 1444 struct Foo { 1445 explicit Foo(int, bool); 1446 }; 1447 1448 void target() { 1449 $ns::$optional<Foo> opt($ns::in_place, 3, false); 1450 opt.value(); 1451 /*[[check]]*/ 1452 } 1453 )", 1454 UnorderedElementsAre(Pair("check", "safe"))); 1455 1456 ExpectLatticeChecksFor(R"( 1457 #include "unchecked_optional_access_test.h" 1458 1459 struct Foo { 1460 explicit Foo(std::initializer_list<int>); 1461 }; 1462 1463 void target() { 1464 $ns::$optional<Foo> opt($ns::in_place, {3}); 1465 opt.value(); 1466 /*[[check]]*/ 1467 } 1468 )", 1469 UnorderedElementsAre(Pair("check", "safe"))); 1470 } 1471 1472 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { 1473 ExpectLatticeChecksFor(R"( 1474 #include "unchecked_optional_access_test.h" 1475 1476 void target() { 1477 $ns::$optional<int> opt(21); 1478 opt.value(); 1479 /*[[check]]*/ 1480 } 1481 )", 1482 UnorderedElementsAre(Pair("check", "safe"))); 1483 1484 ExpectLatticeChecksFor(R"( 1485 #include "unchecked_optional_access_test.h" 1486 1487 void target() { 1488 $ns::$optional<int> opt = $ns::$optional<int>(21); 1489 opt.value(); 1490 /*[[check]]*/ 1491 } 1492 )", 1493 UnorderedElementsAre(Pair("check", "safe"))); 1494 ExpectLatticeChecksFor(R"( 1495 #include "unchecked_optional_access_test.h" 1496 1497 void target() { 1498 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>()); 1499 opt.value(); 1500 /*[[check]]*/ 1501 } 1502 )", 1503 UnorderedElementsAre(Pair("check", "safe"))); 1504 1505 ExpectLatticeChecksFor(R"( 1506 #include "unchecked_optional_access_test.h" 1507 1508 struct MyString { 1509 MyString(const char*); 1510 }; 1511 1512 void target() { 1513 $ns::$optional<MyString> opt("foo"); 1514 opt.value(); 1515 /*[[check]]*/ 1516 } 1517 )", 1518 UnorderedElementsAre(Pair("check", "safe"))); 1519 1520 ExpectLatticeChecksFor(R"( 1521 #include "unchecked_optional_access_test.h" 1522 1523 struct Foo {}; 1524 1525 struct Bar { 1526 Bar(const Foo&); 1527 }; 1528 1529 void target() { 1530 $ns::$optional<Bar> opt(Make<Foo>()); 1531 opt.value(); 1532 /*[[check]]*/ 1533 } 1534 )", 1535 UnorderedElementsAre(Pair("check", "safe"))); 1536 1537 ExpectLatticeChecksFor(R"( 1538 #include "unchecked_optional_access_test.h" 1539 1540 struct Foo { 1541 explicit Foo(int); 1542 }; 1543 1544 void target() { 1545 $ns::$optional<Foo> opt(3); 1546 opt.value(); 1547 /*[[check]]*/ 1548 } 1549 )", 1550 UnorderedElementsAre(Pair("check", "safe"))); 1551 } 1552 1553 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { 1554 ExpectLatticeChecksFor( 1555 R"( 1556 #include "unchecked_optional_access_test.h" 1557 1558 struct Foo {}; 1559 1560 struct Bar { 1561 Bar(const Foo&); 1562 }; 1563 1564 void target() { 1565 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1566 opt.value(); 1567 /*[[check]]*/ 1568 } 1569 )", 1570 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 1571 1572 ExpectLatticeChecksFor( 1573 R"( 1574 #include "unchecked_optional_access_test.h" 1575 1576 struct Foo {}; 1577 1578 struct Bar { 1579 explicit Bar(const Foo&); 1580 }; 1581 1582 void target() { 1583 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1584 opt.value(); 1585 /*[[check]]*/ 1586 } 1587 )", 1588 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 1589 1590 ExpectLatticeChecksFor( 1591 R"( 1592 #include "unchecked_optional_access_test.h" 1593 1594 struct Foo {}; 1595 1596 struct Bar { 1597 Bar(const Foo&); 1598 }; 1599 1600 void target() { 1601 $ns::$optional<Foo> opt1 = $ns::nullopt; 1602 $ns::$optional<Bar> opt2(opt1); 1603 opt2.value(); 1604 /*[[check]]*/ 1605 } 1606 )", 1607 UnorderedElementsAre(Pair("check", "unsafe: input.cc:13:7"))); 1608 1609 ExpectLatticeChecksFor(R"( 1610 #include "unchecked_optional_access_test.h" 1611 1612 struct Foo {}; 1613 1614 struct Bar { 1615 Bar(const Foo&); 1616 }; 1617 1618 void target() { 1619 $ns::$optional<Foo> opt1(Make<Foo>()); 1620 $ns::$optional<Bar> opt2(opt1); 1621 opt2.value(); 1622 /*[[check]]*/ 1623 } 1624 )", 1625 UnorderedElementsAre(Pair("check", "safe"))); 1626 1627 ExpectLatticeChecksFor(R"( 1628 #include "unchecked_optional_access_test.h" 1629 1630 struct Foo {}; 1631 1632 struct Bar { 1633 explicit Bar(const Foo&); 1634 }; 1635 1636 void target() { 1637 $ns::$optional<Foo> opt1(Make<Foo>()); 1638 $ns::$optional<Bar> opt2(opt1); 1639 opt2.value(); 1640 /*[[check]]*/ 1641 } 1642 )", 1643 UnorderedElementsAre(Pair("check", "safe"))); 1644 } 1645 1646 TEST_P(UncheckedOptionalAccessTest, MakeOptional) { 1647 ExpectLatticeChecksFor(R"( 1648 #include "unchecked_optional_access_test.h" 1649 1650 void target() { 1651 $ns::$optional<int> opt = $ns::make_optional(0); 1652 opt.value(); 1653 /*[[check]]*/ 1654 } 1655 )", 1656 UnorderedElementsAre(Pair("check", "safe"))); 1657 1658 ExpectLatticeChecksFor(R"( 1659 #include "unchecked_optional_access_test.h" 1660 1661 struct Foo { 1662 Foo(int, int); 1663 }; 1664 1665 void target() { 1666 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22); 1667 opt.value(); 1668 /*[[check]]*/ 1669 } 1670 )", 1671 UnorderedElementsAre(Pair("check", "safe"))); 1672 1673 ExpectLatticeChecksFor(R"( 1674 #include "unchecked_optional_access_test.h" 1675 1676 struct Foo { 1677 constexpr Foo(std::initializer_list<char>); 1678 }; 1679 1680 void target() { 1681 char a = 'a'; 1682 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a}); 1683 opt.value(); 1684 /*[[check]]*/ 1685 } 1686 )", 1687 UnorderedElementsAre(Pair("check", "safe"))); 1688 } 1689 1690 TEST_P(UncheckedOptionalAccessTest, ValueOr) { 1691 ExpectLatticeChecksFor(R"( 1692 #include "unchecked_optional_access_test.h" 1693 1694 void target() { 1695 $ns::$optional<int> opt; 1696 opt.value_or(0); 1697 (void)0; 1698 /*[[check]]*/ 1699 } 1700 )", 1701 UnorderedElementsAre(Pair("check", "safe"))); 1702 } 1703 1704 TEST_P(UncheckedOptionalAccessTest, Emplace) { 1705 ExpectLatticeChecksFor(R"( 1706 #include "unchecked_optional_access_test.h" 1707 1708 void target() { 1709 $ns::$optional<int> opt; 1710 opt.emplace(0); 1711 opt.value(); 1712 /*[[check]]*/ 1713 } 1714 )", 1715 UnorderedElementsAre(Pair("check", "safe"))); 1716 1717 ExpectLatticeChecksFor(R"( 1718 #include "unchecked_optional_access_test.h" 1719 1720 void target($ns::$optional<int> *opt) { 1721 opt->emplace(0); 1722 opt->value(); 1723 /*[[check]]*/ 1724 } 1725 )", 1726 UnorderedElementsAre(Pair("check", "safe"))); 1727 1728 // FIXME: Add tests that call `emplace` in conditional branches. 1729 } 1730 1731 TEST_P(UncheckedOptionalAccessTest, Reset) { 1732 ExpectLatticeChecksFor( 1733 R"( 1734 #include "unchecked_optional_access_test.h" 1735 1736 void target() { 1737 $ns::$optional<int> opt = $ns::make_optional(0); 1738 opt.reset(); 1739 opt.value(); 1740 /*[[check]]*/ 1741 } 1742 )", 1743 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); 1744 1745 ExpectLatticeChecksFor( 1746 R"( 1747 #include "unchecked_optional_access_test.h" 1748 1749 void target($ns::$optional<int> &opt) { 1750 if (opt.has_value()) { 1751 opt.reset(); 1752 opt.value(); 1753 /*[[check]]*/ 1754 } 1755 } 1756 )", 1757 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:9"))); 1758 1759 // FIXME: Add tests that call `reset` in conditional branches. 1760 } 1761 1762 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { 1763 ExpectLatticeChecksFor(R"( 1764 #include "unchecked_optional_access_test.h" 1765 1766 struct Foo {}; 1767 1768 void target() { 1769 $ns::$optional<Foo> opt; 1770 opt = Foo(); 1771 opt.value(); 1772 /*[[check]]*/ 1773 } 1774 )", 1775 UnorderedElementsAre(Pair("check", "safe"))); 1776 1777 ExpectLatticeChecksFor(R"( 1778 #include "unchecked_optional_access_test.h" 1779 1780 struct Foo {}; 1781 1782 void target() { 1783 $ns::$optional<Foo> opt; 1784 (opt = Foo()).value(); 1785 (void)0; 1786 /*[[check]]*/ 1787 } 1788 )", 1789 UnorderedElementsAre(Pair("check", "safe"))); 1790 1791 ExpectLatticeChecksFor(R"( 1792 #include "unchecked_optional_access_test.h" 1793 1794 struct MyString { 1795 MyString(const char*); 1796 }; 1797 1798 void target() { 1799 $ns::$optional<MyString> opt; 1800 opt = "foo"; 1801 opt.value(); 1802 /*[[check]]*/ 1803 } 1804 )", 1805 UnorderedElementsAre(Pair("check", "safe"))); 1806 1807 ExpectLatticeChecksFor(R"( 1808 #include "unchecked_optional_access_test.h" 1809 1810 struct MyString { 1811 MyString(const char*); 1812 }; 1813 1814 void target() { 1815 $ns::$optional<MyString> opt; 1816 (opt = "foo").value(); 1817 /*[[check]]*/ 1818 } 1819 )", 1820 UnorderedElementsAre(Pair("check", "safe"))); 1821 } 1822 1823 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { 1824 ExpectLatticeChecksFor( 1825 R"( 1826 #include "unchecked_optional_access_test.h" 1827 1828 struct Foo {}; 1829 1830 struct Bar { 1831 Bar(const Foo&); 1832 }; 1833 1834 void target() { 1835 $ns::$optional<Foo> opt1 = Foo(); 1836 $ns::$optional<Bar> opt2; 1837 opt2 = opt1; 1838 opt2.value(); 1839 /*[[check]]*/ 1840 } 1841 )", 1842 UnorderedElementsAre(Pair("check", "safe"))); 1843 1844 ExpectLatticeChecksFor( 1845 R"( 1846 #include "unchecked_optional_access_test.h" 1847 1848 struct Foo {}; 1849 1850 struct Bar { 1851 Bar(const Foo&); 1852 }; 1853 1854 void target() { 1855 $ns::$optional<Foo> opt1; 1856 $ns::$optional<Bar> opt2; 1857 if (opt2.has_value()) { 1858 opt2 = opt1; 1859 opt2.value(); 1860 /*[[check]]*/ 1861 } 1862 } 1863 )", 1864 UnorderedElementsAre(Pair("check", "unsafe: input.cc:15:9"))); 1865 1866 ExpectLatticeChecksFor( 1867 R"( 1868 #include "unchecked_optional_access_test.h" 1869 1870 struct Foo {}; 1871 1872 struct Bar { 1873 Bar(const Foo&); 1874 }; 1875 1876 void target() { 1877 $ns::$optional<Foo> opt1 = Foo(); 1878 $ns::$optional<Bar> opt2; 1879 (opt2 = opt1).value(); 1880 (void)0; 1881 /*[[check]]*/ 1882 } 1883 )", 1884 UnorderedElementsAre(Pair("check", "safe"))); 1885 } 1886 1887 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { 1888 ExpectLatticeChecksFor( 1889 R"( 1890 #include "unchecked_optional_access_test.h" 1891 1892 void target() { 1893 $ns::$optional<int> opt = 3; 1894 opt = $ns::nullopt; 1895 opt.value(); 1896 /*[[check]]*/ 1897 } 1898 )", 1899 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); 1900 1901 ExpectLatticeChecksFor( 1902 R"( 1903 #include "unchecked_optional_access_test.h" 1904 1905 void target() { 1906 $ns::$optional<int> opt = 3; 1907 (opt = $ns::nullopt).value(); 1908 /*[[check]]*/ 1909 } 1910 )", 1911 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1912 } 1913 1914 // FIXME: Add support for: 1915 // - constructors (copy, move) 1916 // - assignment operators (default, copy, move) 1917 // - swap 1918 // - invalidation (passing optional by non-const reference/pointer) 1919 // - `value_or(nullptr) != nullptr`, `value_or(0) != 0`, `value_or("").empty()` 1920 // - nested `optional` values 1921