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_lvalue_reference(int) -> type_identity<T&>; 184 template <class T> 185 auto try_add_lvalue_reference(...) -> type_identity<T>; 186 187 template <class T> 188 auto try_add_rvalue_reference(int) -> type_identity<T&&>; 189 template <class T> 190 auto try_add_rvalue_reference(...) -> type_identity<T>; 191 192 } // namespace detail 193 194 template <class T> 195 struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) { 196 }; 197 198 template <class T> 199 struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) { 200 }; 201 202 template <class T> 203 typename add_rvalue_reference<T>::type declval() noexcept; 204 205 namespace detail { 206 207 template <class T> 208 auto test_returnable(int) 209 -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{}); 210 template <class> 211 auto test_returnable(...) -> false_type; 212 213 template <class From, class To> 214 auto test_implicitly_convertible(int) 215 -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{}); 216 template <class, class> 217 auto test_implicitly_convertible(...) -> false_type; 218 219 } // namespace detail 220 221 template <class From, class To> 222 struct is_convertible 223 : integral_constant<bool, 224 (decltype(detail::test_returnable<To>(0))::value && 225 decltype(detail::test_implicitly_convertible<From, To>( 226 0))::value) || 227 (is_void<From>::value && is_void<To>::value)> {}; 228 229 template <class From, class To> 230 inline constexpr bool is_convertible_v = is_convertible<From, To>::value; 231 232 template <class...> 233 using void_t = void; 234 235 template <class, class T, class... Args> 236 struct is_constructible_ : false_type {}; 237 238 template <class T, class... Args> 239 struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...> 240 : true_type {}; 241 242 template <class T, class... Args> 243 using is_constructible = is_constructible_<void_t<>, T, Args...>; 244 245 template <class T, class... Args> 246 inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value; 247 248 template <class _Tp> 249 struct __uncvref { 250 typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type; 251 }; 252 253 template <class _Tp> 254 using __uncvref_t = typename __uncvref<_Tp>::type; 255 256 template <bool _Val> 257 using _BoolConstant = integral_constant<bool, _Val>; 258 259 template <class _Tp, class _Up> 260 using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>; 261 262 template <class _Tp, class _Up> 263 using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>; 264 265 template <bool> 266 struct _MetaBase; 267 template <> 268 struct _MetaBase<true> { 269 template <class _Tp, class _Up> 270 using _SelectImpl = _Tp; 271 template <template <class...> class _FirstFn, template <class...> class, 272 class... _Args> 273 using _SelectApplyImpl = _FirstFn<_Args...>; 274 template <class _First, class...> 275 using _FirstImpl = _First; 276 template <class, class _Second, class...> 277 using _SecondImpl = _Second; 278 template <class _Result, class _First, class... _Rest> 279 using _OrImpl = 280 typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>:: 281 template _OrImpl<_First, _Rest...>; 282 }; 283 284 template <> 285 struct _MetaBase<false> { 286 template <class _Tp, class _Up> 287 using _SelectImpl = _Up; 288 template <template <class...> class, template <class...> class _SecondFn, 289 class... _Args> 290 using _SelectApplyImpl = _SecondFn<_Args...>; 291 template <class _Result, class...> 292 using _OrImpl = _Result; 293 }; 294 295 template <bool _Cond, class _IfRes, class _ElseRes> 296 using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>; 297 298 template <class... _Rest> 299 using _Or = typename _MetaBase<sizeof...(_Rest) != 300 0>::template _OrImpl<false_type, _Rest...>; 301 302 template <bool _Bp, class _Tp = void> 303 using __enable_if_t = typename enable_if<_Bp, _Tp>::type; 304 305 template <class...> 306 using __expand_to_true = true_type; 307 template <class... _Pred> 308 __expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int); 309 template <class...> 310 false_type __and_helper(...); 311 template <class... _Pred> 312 using _And = decltype(__and_helper<_Pred...>(0)); 313 314 template <class _Pred> 315 struct _Not : _BoolConstant<!_Pred::value> {}; 316 317 struct __check_tuple_constructor_fail { 318 static constexpr bool __enable_explicit_default() { return false; } 319 static constexpr bool __enable_implicit_default() { return false; } 320 template <class...> 321 static constexpr bool __enable_explicit() { 322 return false; 323 } 324 template <class...> 325 static constexpr bool __enable_implicit() { 326 return false; 327 } 328 }; 329 330 template <typename, typename _Tp> 331 struct __select_2nd { 332 typedef _Tp type; 333 }; 334 template <class _Tp, class _Arg> 335 typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())), 336 true_type>::type 337 __is_assignable_test(int); 338 template <class, class> 339 false_type __is_assignable_test(...); 340 template <class _Tp, class _Arg, 341 bool = is_void<_Tp>::value || is_void<_Arg>::value> 342 struct __is_assignable_imp 343 : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {}; 344 template <class _Tp, class _Arg> 345 struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {}; 346 template <class _Tp, class _Arg> 347 struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {}; 348 349 template <class _Tp> 350 struct __libcpp_is_integral : public false_type {}; 351 template <> 352 struct __libcpp_is_integral<bool> : public true_type {}; 353 template <> 354 struct __libcpp_is_integral<char> : public true_type {}; 355 template <> 356 struct __libcpp_is_integral<signed char> : public true_type {}; 357 template <> 358 struct __libcpp_is_integral<unsigned char> : public true_type {}; 359 template <> 360 struct __libcpp_is_integral<wchar_t> : public true_type {}; 361 template <> 362 struct __libcpp_is_integral<short> : public true_type {}; // NOLINT 363 template <> 364 struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT 365 template <> 366 struct __libcpp_is_integral<int> : public true_type {}; 367 template <> 368 struct __libcpp_is_integral<unsigned int> : public true_type {}; 369 template <> 370 struct __libcpp_is_integral<long> : public true_type {}; // NOLINT 371 template <> 372 struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT 373 template <> 374 struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT 375 template <> // NOLINTNEXTLINE 376 struct __libcpp_is_integral<unsigned long long> : public true_type {}; 377 template <class _Tp> 378 struct is_integral 379 : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {}; 380 381 template <class _Tp> 382 struct __libcpp_is_floating_point : public false_type {}; 383 template <> 384 struct __libcpp_is_floating_point<float> : public true_type {}; 385 template <> 386 struct __libcpp_is_floating_point<double> : public true_type {}; 387 template <> 388 struct __libcpp_is_floating_point<long double> : public true_type {}; 389 template <class _Tp> 390 struct is_floating_point 391 : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {}; 392 393 template <class _Tp> 394 struct is_arithmetic 395 : public integral_constant<bool, is_integral<_Tp>::value || 396 is_floating_point<_Tp>::value> {}; 397 398 template <class _Tp> 399 struct __libcpp_is_pointer : public false_type {}; 400 template <class _Tp> 401 struct __libcpp_is_pointer<_Tp*> : public true_type {}; 402 template <class _Tp> 403 struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> { 404 }; 405 406 template <class _Tp> 407 struct __libcpp_is_member_pointer : public false_type {}; 408 template <class _Tp, class _Up> 409 struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {}; 410 template <class _Tp> 411 struct is_member_pointer 412 : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {}; 413 414 template <class _Tp> 415 struct __libcpp_union : public false_type {}; 416 template <class _Tp> 417 struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {}; 418 419 template <class T> 420 struct is_reference : false_type {}; 421 template <class T> 422 struct is_reference<T&> : true_type {}; 423 template <class T> 424 struct is_reference<T&&> : true_type {}; 425 426 template <class T> 427 inline constexpr bool is_reference_v = is_reference<T>::value; 428 429 struct __two { 430 char __lx[2]; 431 }; 432 433 namespace __is_class_imp { 434 template <class _Tp> 435 char __test(int _Tp::*); 436 template <class _Tp> 437 __two __test(...); 438 } // namespace __is_class_imp 439 template <class _Tp> 440 struct is_class 441 : public integral_constant<bool, 442 sizeof(__is_class_imp::__test<_Tp>(0)) == 1 && 443 !is_union<_Tp>::value> {}; 444 445 template <class _Tp> 446 struct __is_nullptr_t_impl : public false_type {}; 447 template <> 448 struct __is_nullptr_t_impl<nullptr_t> : public true_type {}; 449 template <class _Tp> 450 struct __is_nullptr_t 451 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; 452 template <class _Tp> 453 struct is_null_pointer 454 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; 455 456 template <class _Tp> 457 struct is_enum 458 : public integral_constant< 459 bool, !is_void<_Tp>::value && !is_integral<_Tp>::value && 460 !is_floating_point<_Tp>::value && !is_array<_Tp>::value && 461 !is_pointer<_Tp>::value && !is_reference<_Tp>::value && 462 !is_member_pointer<_Tp>::value && !is_union<_Tp>::value && 463 !is_class<_Tp>::value && !is_function<_Tp>::value> {}; 464 465 template <class _Tp> 466 struct is_scalar 467 : public integral_constant< 468 bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value || 469 is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value || 470 is_enum<_Tp>::value> {}; 471 template <> 472 struct is_scalar<nullptr_t> : public true_type {}; 473 474 } // namespace std 475 476 #endif // STD_TYPE_TRAITS_H 477 )"; 478 479 static constexpr char AbslTypeTraitsHeader[] = R"( 480 #ifndef ABSL_TYPE_TRAITS_H 481 #define ABSL_TYPE_TRAITS_H 482 483 #include "std_type_traits.h" 484 485 namespace absl { 486 487 template <typename... Ts> 488 struct conjunction : std::true_type {}; 489 490 template <typename T, typename... Ts> 491 struct conjunction<T, Ts...> 492 : std::conditional<T::value, conjunction<Ts...>, T>::type {}; 493 494 template <typename T> 495 struct conjunction<T> : T {}; 496 497 template <typename T> 498 struct negation : std::integral_constant<bool, !T::value> {}; 499 500 template <bool B, typename T = void> 501 using enable_if_t = typename std::enable_if<B, T>::type; 502 503 } // namespace absl 504 505 #endif // ABSL_TYPE_TRAITS_H 506 )"; 507 508 static constexpr char StdStringHeader[] = R"( 509 #ifndef STRING_H 510 #define STRING_H 511 512 namespace std { 513 514 struct string { 515 string(const char*); 516 ~string(); 517 bool empty(); 518 }; 519 bool operator!=(const string &LHS, const char *RHS); 520 521 } // namespace std 522 523 #endif // STRING_H 524 )"; 525 526 static constexpr char StdUtilityHeader[] = R"( 527 #ifndef UTILITY_H 528 #define UTILITY_H 529 530 #include "std_type_traits.h" 531 532 namespace std { 533 534 template <typename T> 535 constexpr remove_reference_t<T>&& move(T&& x); 536 537 template <typename T> 538 void swap(T& a, T& b) noexcept; 539 540 } // namespace std 541 542 #endif // UTILITY_H 543 )"; 544 545 static constexpr char StdInitializerListHeader[] = R"( 546 #ifndef INITIALIZER_LIST_H 547 #define INITIALIZER_LIST_H 548 549 namespace std { 550 551 template <typename T> 552 class initializer_list { 553 public: 554 initializer_list() noexcept; 555 }; 556 557 } // namespace std 558 559 #endif // INITIALIZER_LIST_H 560 )"; 561 562 static constexpr char StdOptionalHeader[] = R"( 563 #include "std_initializer_list.h" 564 #include "std_type_traits.h" 565 #include "std_utility.h" 566 567 namespace std { 568 569 struct in_place_t {}; 570 constexpr in_place_t in_place; 571 572 struct nullopt_t { 573 constexpr explicit nullopt_t() {} 574 }; 575 constexpr nullopt_t nullopt; 576 577 template <class _Tp> 578 struct __optional_destruct_base { 579 constexpr void reset() noexcept; 580 }; 581 582 template <class _Tp> 583 struct __optional_storage_base : __optional_destruct_base<_Tp> { 584 constexpr bool has_value() const noexcept; 585 }; 586 587 template <typename _Tp> 588 class optional : private __optional_storage_base<_Tp> { 589 using __base = __optional_storage_base<_Tp>; 590 591 public: 592 using value_type = _Tp; 593 594 private: 595 struct _CheckOptionalArgsConstructor { 596 template <class _Up> 597 static constexpr bool __enable_implicit() { 598 return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>; 599 } 600 601 template <class _Up> 602 static constexpr bool __enable_explicit() { 603 return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>; 604 } 605 }; 606 template <class _Up> 607 using _CheckOptionalArgsCtor = 608 _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value && 609 _IsNotSame<__uncvref_t<_Up>, optional>::value, 610 _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>; 611 template <class _QualUp> 612 struct _CheckOptionalLikeConstructor { 613 template <class _Up, class _Opt = optional<_Up>> 614 using __check_constructible_from_opt = 615 _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>, 616 is_constructible<_Tp, _Opt&&>, is_constructible<_Tp, _Opt const&&>, 617 is_convertible<_Opt&, _Tp>, is_convertible<_Opt const&, _Tp>, 618 is_convertible<_Opt&&, _Tp>, is_convertible<_Opt const&&, _Tp>>; 619 template <class _Up, class _QUp = _QualUp> 620 static constexpr bool __enable_implicit() { 621 return is_convertible<_QUp, _Tp>::value && 622 !__check_constructible_from_opt<_Up>::value; 623 } 624 template <class _Up, class _QUp = _QualUp> 625 static constexpr bool __enable_explicit() { 626 return !is_convertible<_QUp, _Tp>::value && 627 !__check_constructible_from_opt<_Up>::value; 628 } 629 }; 630 631 template <class _Up, class _QualUp> 632 using _CheckOptionalLikeCtor = 633 _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value, 634 _CheckOptionalLikeConstructor<_QualUp>, 635 __check_tuple_constructor_fail>; 636 637 638 template <class _Up, class _QualUp> 639 using _CheckOptionalLikeAssign = _If< 640 _And< 641 _IsNotSame<_Up, _Tp>, 642 is_constructible<_Tp, _QualUp>, 643 is_assignable<_Tp&, _QualUp> 644 >::value, 645 _CheckOptionalLikeConstructor<_QualUp>, 646 __check_tuple_constructor_fail 647 >; 648 649 public: 650 constexpr optional() noexcept {} 651 constexpr optional(const optional&) = default; 652 constexpr optional(optional&&) = default; 653 constexpr optional(nullopt_t) noexcept {} 654 655 template < 656 class _InPlaceT, class... _Args, 657 class = enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, 658 is_constructible<value_type, _Args...>>::value>> 659 constexpr explicit optional(_InPlaceT, _Args&&... __args); 660 661 template <class _Up, class... _Args, 662 class = enable_if_t<is_constructible_v< 663 value_type, initializer_list<_Up>&, _Args...>>> 664 constexpr explicit optional(in_place_t, initializer_list<_Up> __il, 665 _Args&&... __args); 666 667 template < 668 class _Up = value_type, 669 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), 670 int> = 0> 671 constexpr optional(_Up&& __v); 672 673 template < 674 class _Up, 675 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), 676 int> = 0> 677 constexpr explicit optional(_Up&& __v); 678 679 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: 680 template __enable_implicit<_Up>(), 681 int> = 0> 682 constexpr optional(const optional<_Up>& __v); 683 684 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: 685 template __enable_explicit<_Up>(), 686 int> = 0> 687 constexpr explicit optional(const optional<_Up>& __v); 688 689 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 690 template __enable_implicit<_Up>(), 691 int> = 0> 692 constexpr optional(optional<_Up>&& __v); 693 694 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 695 template __enable_explicit<_Up>(), 696 int> = 0> 697 constexpr explicit optional(optional<_Up>&& __v); 698 699 constexpr optional& operator=(nullopt_t) noexcept; 700 701 optional& operator=(const optional&); 702 703 optional& operator=(optional&&); 704 705 template <class _Up = value_type, 706 class = enable_if_t<_And<_IsNotSame<__uncvref_t<_Up>, optional>, 707 _Or<_IsNotSame<__uncvref_t<_Up>, value_type>, 708 _Not<is_scalar<value_type>>>, 709 is_constructible<value_type, _Up>, 710 is_assignable<value_type&, _Up>>::value>> 711 constexpr optional& operator=(_Up&& __v); 712 713 template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>:: 714 template __enable_assign<_Up>(), 715 int> = 0> 716 constexpr optional& operator=(const optional<_Up>& __v); 717 718 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 719 template __enable_assign<_Up>(), 720 int> = 0> 721 constexpr optional& operator=(optional<_Up>&& __v); 722 723 const _Tp& operator*() const&; 724 _Tp& operator*() &; 725 const _Tp&& operator*() const&&; 726 _Tp&& operator*() &&; 727 728 const _Tp* operator->() const; 729 _Tp* operator->(); 730 731 const _Tp& value() const&; 732 _Tp& value() &; 733 const _Tp&& value() const&&; 734 _Tp&& value() &&; 735 736 template <typename U> 737 constexpr _Tp value_or(U&& v) const&; 738 template <typename U> 739 _Tp value_or(U&& v) &&; 740 741 template <typename... Args> 742 _Tp& emplace(Args&&... args); 743 744 template <typename U, typename... Args> 745 _Tp& emplace(std::initializer_list<U> ilist, Args&&... args); 746 747 using __base::reset; 748 749 constexpr explicit operator bool() const noexcept; 750 using __base::has_value; 751 752 constexpr void swap(optional& __opt) noexcept; 753 }; 754 755 template <typename T> 756 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 757 758 template <typename T, typename... Args> 759 constexpr optional<T> make_optional(Args&&... args); 760 761 template <typename T, typename U, typename... Args> 762 constexpr optional<T> make_optional(std::initializer_list<U> il, 763 Args&&... args); 764 765 } // namespace std 766 )"; 767 768 static constexpr char AbslOptionalHeader[] = R"( 769 #include "absl_type_traits.h" 770 #include "std_initializer_list.h" 771 #include "std_type_traits.h" 772 #include "std_utility.h" 773 774 namespace absl { 775 776 struct nullopt_t { 777 constexpr explicit nullopt_t() {} 778 }; 779 constexpr nullopt_t nullopt; 780 781 struct in_place_t {}; 782 constexpr in_place_t in_place; 783 784 template <typename T> 785 class optional; 786 787 namespace optional_internal { 788 789 template <typename T, typename U> 790 struct is_constructible_convertible_from_optional 791 : std::integral_constant< 792 bool, std::is_constructible<T, optional<U>&>::value || 793 std::is_constructible<T, optional<U>&&>::value || 794 std::is_constructible<T, const optional<U>&>::value || 795 std::is_constructible<T, const optional<U>&&>::value || 796 std::is_convertible<optional<U>&, T>::value || 797 std::is_convertible<optional<U>&&, T>::value || 798 std::is_convertible<const optional<U>&, T>::value || 799 std::is_convertible<const optional<U>&&, T>::value> {}; 800 801 template <typename T, typename U> 802 struct is_constructible_convertible_assignable_from_optional 803 : std::integral_constant< 804 bool, is_constructible_convertible_from_optional<T, U>::value || 805 std::is_assignable<T&, optional<U>&>::value || 806 std::is_assignable<T&, optional<U>&&>::value || 807 std::is_assignable<T&, const optional<U>&>::value || 808 std::is_assignable<T&, const optional<U>&&>::value> {}; 809 810 } // namespace optional_internal 811 812 template <typename T> 813 class optional { 814 public: 815 constexpr optional() noexcept; 816 817 constexpr optional(nullopt_t) noexcept; 818 819 optional(const optional&) = default; 820 821 optional(optional&&) = default; 822 823 template <typename InPlaceT, typename... Args, 824 absl::enable_if_t<absl::conjunction< 825 std::is_same<InPlaceT, in_place_t>, 826 std::is_constructible<T, Args&&...>>::value>* = nullptr> 827 constexpr explicit optional(InPlaceT, Args&&... args); 828 829 template <typename U, typename... Args, 830 typename = typename std::enable_if<std::is_constructible< 831 T, std::initializer_list<U>&, Args&&...>::value>::type> 832 constexpr explicit optional(in_place_t, std::initializer_list<U> il, 833 Args&&... args); 834 835 template < 836 typename U = T, 837 typename std::enable_if< 838 absl::conjunction<absl::negation<std::is_same< 839 in_place_t, typename std::decay<U>::type>>, 840 absl::negation<std::is_same< 841 optional<T>, typename std::decay<U>::type>>, 842 std::is_convertible<U&&, T>, 843 std::is_constructible<T, U&&>>::value, 844 bool>::type = false> 845 constexpr optional(U&& v); 846 847 template < 848 typename U = T, 849 typename std::enable_if< 850 absl::conjunction<absl::negation<std::is_same< 851 in_place_t, typename std::decay<U>::type>>, 852 absl::negation<std::is_same< 853 optional<T>, typename std::decay<U>::type>>, 854 absl::negation<std::is_convertible<U&&, T>>, 855 std::is_constructible<T, U&&>>::value, 856 bool>::type = false> 857 explicit constexpr optional(U&& v); 858 859 template <typename U, 860 typename std::enable_if< 861 absl::conjunction< 862 absl::negation<std::is_same<T, U>>, 863 std::is_constructible<T, const U&>, 864 absl::negation< 865 optional_internal:: 866 is_constructible_convertible_from_optional<T, U>>, 867 std::is_convertible<const U&, T>>::value, 868 bool>::type = false> 869 optional(const optional<U>& rhs); 870 871 template <typename U, 872 typename std::enable_if< 873 absl::conjunction< 874 absl::negation<std::is_same<T, U>>, 875 std::is_constructible<T, const U&>, 876 absl::negation< 877 optional_internal:: 878 is_constructible_convertible_from_optional<T, U>>, 879 absl::negation<std::is_convertible<const U&, T>>>::value, 880 bool>::type = false> 881 explicit optional(const optional<U>& rhs); 882 883 template < 884 typename U, 885 typename std::enable_if< 886 absl::conjunction< 887 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 888 absl::negation< 889 optional_internal::is_constructible_convertible_from_optional< 890 T, U>>, 891 std::is_convertible<U&&, T>>::value, 892 bool>::type = false> 893 optional(optional<U>&& rhs); 894 895 template < 896 typename U, 897 typename std::enable_if< 898 absl::conjunction< 899 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 900 absl::negation< 901 optional_internal::is_constructible_convertible_from_optional< 902 T, U>>, 903 absl::negation<std::is_convertible<U&&, T>>>::value, 904 bool>::type = false> 905 explicit optional(optional<U>&& rhs); 906 907 optional& operator=(nullopt_t) noexcept; 908 909 optional& operator=(const optional& src); 910 911 optional& operator=(optional&& src); 912 913 template < 914 typename U = T, 915 typename = typename std::enable_if<absl::conjunction< 916 absl::negation< 917 std::is_same<optional<T>, typename std::decay<U>::type>>, 918 absl::negation< 919 absl::conjunction<std::is_scalar<T>, 920 std::is_same<T, typename std::decay<U>::type>>>, 921 std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type> 922 optional& operator=(U&& v); 923 924 template < 925 typename U, 926 typename = typename std::enable_if<absl::conjunction< 927 absl::negation<std::is_same<T, U>>, 928 std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>, 929 absl::negation< 930 optional_internal:: 931 is_constructible_convertible_assignable_from_optional< 932 T, U>>>::value>::type> 933 optional& operator=(const optional<U>& rhs); 934 935 template <typename U, 936 typename = typename std::enable_if<absl::conjunction< 937 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>, 938 std::is_assignable<T&, U>, 939 absl::negation< 940 optional_internal:: 941 is_constructible_convertible_assignable_from_optional< 942 T, U>>>::value>::type> 943 optional& operator=(optional<U>&& rhs); 944 945 const T& operator*() const&; 946 T& operator*() &; 947 const T&& operator*() const&&; 948 T&& operator*() &&; 949 950 const T* operator->() const; 951 T* operator->(); 952 953 const T& value() const&; 954 T& value() &; 955 const T&& value() const&&; 956 T&& value() &&; 957 958 template <typename U> 959 constexpr T value_or(U&& v) const&; 960 template <typename U> 961 T value_or(U&& v) &&; 962 963 template <typename... Args> 964 T& emplace(Args&&... args); 965 966 template <typename U, typename... Args> 967 T& emplace(std::initializer_list<U> ilist, Args&&... args); 968 969 void reset() noexcept; 970 971 constexpr explicit operator bool() const noexcept; 972 constexpr bool has_value() const noexcept; 973 974 void swap(optional& rhs) noexcept; 975 }; 976 977 template <typename T> 978 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 979 980 template <typename T, typename... Args> 981 constexpr optional<T> make_optional(Args&&... args); 982 983 template <typename T, typename U, typename... Args> 984 constexpr optional<T> make_optional(std::initializer_list<U> il, 985 Args&&... args); 986 987 } // namespace absl 988 )"; 989 990 static constexpr char BaseOptionalHeader[] = R"( 991 #include "std_initializer_list.h" 992 #include "std_type_traits.h" 993 #include "std_utility.h" 994 995 namespace base { 996 997 struct in_place_t {}; 998 constexpr in_place_t in_place; 999 1000 struct nullopt_t { 1001 constexpr explicit nullopt_t() {} 1002 }; 1003 constexpr nullopt_t nullopt; 1004 1005 template <typename T> 1006 class Optional; 1007 1008 namespace internal { 1009 1010 template <typename T> 1011 using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>; 1012 1013 template <typename T, typename U> 1014 struct IsConvertibleFromOptional 1015 : std::integral_constant< 1016 bool, std::is_constructible<T, Optional<U>&>::value || 1017 std::is_constructible<T, const Optional<U>&>::value || 1018 std::is_constructible<T, Optional<U>&&>::value || 1019 std::is_constructible<T, const Optional<U>&&>::value || 1020 std::is_convertible<Optional<U>&, T>::value || 1021 std::is_convertible<const Optional<U>&, T>::value || 1022 std::is_convertible<Optional<U>&&, T>::value || 1023 std::is_convertible<const Optional<U>&&, T>::value> {}; 1024 1025 template <typename T, typename U> 1026 struct IsAssignableFromOptional 1027 : std::integral_constant< 1028 bool, IsConvertibleFromOptional<T, U>::value || 1029 std::is_assignable<T&, Optional<U>&>::value || 1030 std::is_assignable<T&, const Optional<U>&>::value || 1031 std::is_assignable<T&, Optional<U>&&>::value || 1032 std::is_assignable<T&, const Optional<U>&&>::value> {}; 1033 1034 } // namespace internal 1035 1036 template <typename T> 1037 class Optional { 1038 public: 1039 using value_type = T; 1040 1041 constexpr Optional() = default; 1042 constexpr Optional(const Optional& other) noexcept = default; 1043 constexpr Optional(Optional&& other) noexcept = default; 1044 1045 constexpr Optional(nullopt_t); 1046 1047 template <typename U, 1048 typename std::enable_if< 1049 std::is_constructible<T, const U&>::value && 1050 !internal::IsConvertibleFromOptional<T, U>::value && 1051 std::is_convertible<const U&, T>::value, 1052 bool>::type = false> 1053 Optional(const Optional<U>& other) noexcept; 1054 1055 template <typename U, 1056 typename std::enable_if< 1057 std::is_constructible<T, const U&>::value && 1058 !internal::IsConvertibleFromOptional<T, U>::value && 1059 !std::is_convertible<const U&, T>::value, 1060 bool>::type = false> 1061 explicit Optional(const Optional<U>& other) noexcept; 1062 1063 template <typename U, 1064 typename std::enable_if< 1065 std::is_constructible<T, U&&>::value && 1066 !internal::IsConvertibleFromOptional<T, U>::value && 1067 std::is_convertible<U&&, T>::value, 1068 bool>::type = false> 1069 Optional(Optional<U>&& other) noexcept; 1070 1071 template <typename U, 1072 typename std::enable_if< 1073 std::is_constructible<T, U&&>::value && 1074 !internal::IsConvertibleFromOptional<T, U>::value && 1075 !std::is_convertible<U&&, T>::value, 1076 bool>::type = false> 1077 explicit Optional(Optional<U>&& other) noexcept; 1078 1079 template <class... Args> 1080 constexpr explicit Optional(in_place_t, Args&&... args); 1081 1082 template <class U, class... Args, 1083 class = typename std::enable_if<std::is_constructible< 1084 value_type, std::initializer_list<U>&, Args...>::value>::type> 1085 constexpr explicit Optional(in_place_t, std::initializer_list<U> il, 1086 Args&&... args); 1087 1088 template < 1089 typename U = value_type, 1090 typename std::enable_if< 1091 std::is_constructible<T, U&&>::value && 1092 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1093 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1094 std::is_convertible<U&&, T>::value, 1095 bool>::type = false> 1096 constexpr Optional(U&& value); 1097 1098 template < 1099 typename U = value_type, 1100 typename std::enable_if< 1101 std::is_constructible<T, U&&>::value && 1102 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1103 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1104 !std::is_convertible<U&&, T>::value, 1105 bool>::type = false> 1106 constexpr explicit Optional(U&& value); 1107 1108 Optional& operator=(const Optional& other) noexcept; 1109 1110 Optional& operator=(Optional&& other) noexcept; 1111 1112 Optional& operator=(nullopt_t); 1113 1114 template <typename U> 1115 typename std::enable_if< 1116 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1117 std::is_constructible<T, U>::value && 1118 std::is_assignable<T&, U>::value && 1119 (!std::is_scalar<T>::value || 1120 !std::is_same<typename std::decay<U>::type, T>::value), 1121 Optional&>::type 1122 operator=(U&& value) noexcept; 1123 1124 template <typename U> 1125 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1126 std::is_constructible<T, const U&>::value && 1127 std::is_assignable<T&, const U&>::value, 1128 Optional&>::type 1129 operator=(const Optional<U>& other) noexcept; 1130 1131 template <typename U> 1132 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1133 std::is_constructible<T, U>::value && 1134 std::is_assignable<T&, U>::value, 1135 Optional&>::type 1136 operator=(Optional<U>&& other) noexcept; 1137 1138 const T& operator*() const&; 1139 T& operator*() &; 1140 const T&& operator*() const&&; 1141 T&& operator*() &&; 1142 1143 const T* operator->() const; 1144 T* operator->(); 1145 1146 const T& value() const&; 1147 T& value() &; 1148 const T&& value() const&&; 1149 T&& value() &&; 1150 1151 template <typename U> 1152 constexpr T value_or(U&& v) const&; 1153 template <typename U> 1154 T value_or(U&& v) &&; 1155 1156 template <typename... Args> 1157 T& emplace(Args&&... args); 1158 1159 template <typename U, typename... Args> 1160 T& emplace(std::initializer_list<U> ilist, Args&&... args); 1161 1162 void reset() noexcept; 1163 1164 constexpr explicit operator bool() const noexcept; 1165 constexpr bool has_value() const noexcept; 1166 1167 void swap(Optional& other); 1168 }; 1169 1170 template <typename T> 1171 constexpr Optional<typename std::decay<T>::type> make_optional(T&& v); 1172 1173 template <typename T, typename... Args> 1174 constexpr Optional<T> make_optional(Args&&... args); 1175 1176 template <typename T, typename U, typename... Args> 1177 constexpr Optional<T> make_optional(std::initializer_list<U> il, 1178 Args&&... args); 1179 1180 } // namespace base 1181 )"; 1182 1183 /// Converts `L` to string. 1184 static std::string ConvertToString(const SourceLocationsLattice &L, 1185 const ASTContext &Ctx) { 1186 return L.getSourceLocations().empty() ? "safe" 1187 : "unsafe: " + DebugString(L, Ctx); 1188 } 1189 1190 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. 1191 static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern, 1192 const std::string &Replacement) { 1193 size_t Pos = 0; 1194 while (true) { 1195 Pos = S.find(Pattern, Pos); 1196 if (Pos == std::string::npos) 1197 break; 1198 S.replace(Pos, Pattern.size(), Replacement); 1199 } 1200 } 1201 1202 struct OptionalTypeIdentifier { 1203 std::string NamespaceName; 1204 std::string TypeName; 1205 }; 1206 1207 class UncheckedOptionalAccessTest 1208 : public ::testing::TestWithParam<OptionalTypeIdentifier> { 1209 protected: 1210 template <typename LatticeChecksMatcher> 1211 void ExpectLatticeChecksFor(std::string SourceCode, 1212 LatticeChecksMatcher MatchesLatticeChecks) { 1213 ExpectLatticeChecksFor(SourceCode, ast_matchers::hasName("target"), 1214 MatchesLatticeChecks); 1215 } 1216 1217 private: 1218 template <typename FuncDeclMatcher, typename LatticeChecksMatcher> 1219 void ExpectLatticeChecksFor(std::string SourceCode, 1220 FuncDeclMatcher FuncMatcher, 1221 LatticeChecksMatcher MatchesLatticeChecks) { 1222 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName); 1223 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName); 1224 1225 std::vector<std::pair<std::string, std::string>> Headers; 1226 Headers.emplace_back("cstddef.h", CSDtdDefHeader); 1227 Headers.emplace_back("std_initializer_list.h", StdInitializerListHeader); 1228 Headers.emplace_back("std_string.h", StdStringHeader); 1229 Headers.emplace_back("std_type_traits.h", StdTypeTraitsHeader); 1230 Headers.emplace_back("std_utility.h", StdUtilityHeader); 1231 Headers.emplace_back("std_optional.h", StdOptionalHeader); 1232 Headers.emplace_back("absl_type_traits.h", AbslTypeTraitsHeader); 1233 Headers.emplace_back("absl_optional.h", AbslOptionalHeader); 1234 Headers.emplace_back("base_optional.h", BaseOptionalHeader); 1235 Headers.emplace_back("unchecked_optional_access_test.h", R"( 1236 #include "absl_optional.h" 1237 #include "base_optional.h" 1238 #include "std_initializer_list.h" 1239 #include "std_optional.h" 1240 #include "std_string.h" 1241 #include "std_utility.h" 1242 1243 template <typename T> 1244 T Make(); 1245 )"); 1246 const tooling::FileContentMappings FileContents(Headers.begin(), 1247 Headers.end()); 1248 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>( 1249 SourceCode, FuncMatcher, 1250 [](ASTContext &Ctx, Environment &) { 1251 return UncheckedOptionalAccessModel( 1252 Ctx, UncheckedOptionalAccessModelOptions{ 1253 /*IgnoreSmartPointerDereference=*/true}); 1254 }, 1255 [&MatchesLatticeChecks]( 1256 llvm::ArrayRef<std::pair< 1257 std::string, DataflowAnalysisState<SourceLocationsLattice>>> 1258 CheckToLatticeMap, 1259 ASTContext &Ctx) { 1260 // FIXME: Consider using a matcher instead of translating 1261 // `CheckToLatticeMap` to `CheckToStringifiedLatticeMap`. 1262 std::vector<std::pair<std::string, std::string>> 1263 CheckToStringifiedLatticeMap; 1264 for (const auto &E : CheckToLatticeMap) { 1265 CheckToStringifiedLatticeMap.emplace_back( 1266 E.first, ConvertToString(E.second.Lattice, Ctx)); 1267 } 1268 EXPECT_THAT(CheckToStringifiedLatticeMap, MatchesLatticeChecks); 1269 }, 1270 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents); 1271 if (Error) 1272 FAIL() << llvm::toString(std::move(Error)); 1273 } 1274 }; 1275 1276 INSTANTIATE_TEST_SUITE_P( 1277 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest, 1278 ::testing::Values(OptionalTypeIdentifier{"std", "optional"}, 1279 OptionalTypeIdentifier{"absl", "optional"}, 1280 OptionalTypeIdentifier{"base", "Optional"}), 1281 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) { 1282 return Info.param.NamespaceName; 1283 }); 1284 1285 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { 1286 ExpectLatticeChecksFor(R"( 1287 void target() { 1288 (void)0; 1289 /*[[check]]*/ 1290 } 1291 )", 1292 UnorderedElementsAre(Pair("check", "safe"))); 1293 } 1294 1295 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { 1296 ExpectLatticeChecksFor( 1297 R"( 1298 #include "unchecked_optional_access_test.h" 1299 1300 void target($ns::$optional<int> opt) { 1301 opt.value(); 1302 /*[[check]]*/ 1303 } 1304 )", 1305 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1306 1307 ExpectLatticeChecksFor( 1308 R"( 1309 #include "unchecked_optional_access_test.h" 1310 1311 void target($ns::$optional<int> opt) { 1312 std::move(opt).value(); 1313 /*[[check]]*/ 1314 } 1315 )", 1316 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1317 } 1318 1319 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { 1320 ExpectLatticeChecksFor( 1321 R"( 1322 #include "unchecked_optional_access_test.h" 1323 1324 void target($ns::$optional<int> opt) { 1325 *opt; 1326 /*[[check]]*/ 1327 } 1328 )", 1329 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); 1330 1331 ExpectLatticeChecksFor( 1332 R"( 1333 #include "unchecked_optional_access_test.h" 1334 1335 void target($ns::$optional<int> opt) { 1336 *std::move(opt); 1337 /*[[check]]*/ 1338 } 1339 )", 1340 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); 1341 } 1342 1343 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { 1344 ExpectLatticeChecksFor( 1345 R"( 1346 #include "unchecked_optional_access_test.h" 1347 1348 struct Foo { 1349 void foo(); 1350 }; 1351 1352 void target($ns::$optional<Foo> opt) { 1353 opt->foo(); 1354 /*[[check]]*/ 1355 } 1356 )", 1357 UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); 1358 1359 ExpectLatticeChecksFor( 1360 R"( 1361 #include "unchecked_optional_access_test.h" 1362 1363 struct Foo { 1364 void foo(); 1365 }; 1366 1367 void target($ns::$optional<Foo> opt) { 1368 std::move(opt)->foo(); 1369 /*[[check]]*/ 1370 } 1371 )", 1372 UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); 1373 } 1374 1375 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { 1376 ExpectLatticeChecksFor(R"( 1377 #include "unchecked_optional_access_test.h" 1378 1379 void target($ns::$optional<int> opt) { 1380 if (opt.has_value()) { 1381 opt.value(); 1382 /*[[check]]*/ 1383 } 1384 } 1385 )", 1386 UnorderedElementsAre(Pair("check", "safe"))); 1387 } 1388 1389 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { 1390 ExpectLatticeChecksFor(R"( 1391 #include "unchecked_optional_access_test.h" 1392 1393 void target($ns::$optional<int> opt) { 1394 if (opt) { 1395 opt.value(); 1396 /*[[check]]*/ 1397 } 1398 } 1399 )", 1400 UnorderedElementsAre(Pair("check", "safe"))); 1401 } 1402 1403 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { 1404 ExpectLatticeChecksFor( 1405 R"( 1406 #include "unchecked_optional_access_test.h" 1407 1408 void target() { 1409 Make<$ns::$optional<int>>().value(); 1410 (void)0; 1411 /*[[check]]*/ 1412 } 1413 )", 1414 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1415 1416 ExpectLatticeChecksFor( 1417 R"( 1418 #include "unchecked_optional_access_test.h" 1419 1420 void target($ns::$optional<int> opt) { 1421 std::move(opt).value(); 1422 /*[[check]]*/ 1423 } 1424 )", 1425 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1426 } 1427 1428 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { 1429 ExpectLatticeChecksFor( 1430 R"( 1431 #include "unchecked_optional_access_test.h" 1432 1433 void target() { 1434 $ns::$optional<int> opt; 1435 opt.value(); 1436 /*[[check]]*/ 1437 } 1438 )", 1439 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1440 } 1441 1442 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { 1443 ExpectLatticeChecksFor( 1444 R"( 1445 #include "unchecked_optional_access_test.h" 1446 1447 void target() { 1448 $ns::$optional<int> opt($ns::nullopt); 1449 opt.value(); 1450 /*[[check]]*/ 1451 } 1452 )", 1453 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1454 } 1455 1456 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { 1457 ExpectLatticeChecksFor(R"( 1458 #include "unchecked_optional_access_test.h" 1459 1460 void target() { 1461 $ns::$optional<int> opt($ns::in_place, 3); 1462 opt.value(); 1463 /*[[check]]*/ 1464 } 1465 )", 1466 UnorderedElementsAre(Pair("check", "safe"))); 1467 1468 ExpectLatticeChecksFor(R"( 1469 #include "unchecked_optional_access_test.h" 1470 1471 struct Foo {}; 1472 1473 void target() { 1474 $ns::$optional<Foo> opt($ns::in_place); 1475 opt.value(); 1476 /*[[check]]*/ 1477 } 1478 )", 1479 UnorderedElementsAre(Pair("check", "safe"))); 1480 1481 ExpectLatticeChecksFor(R"( 1482 #include "unchecked_optional_access_test.h" 1483 1484 struct Foo { 1485 explicit Foo(int, bool); 1486 }; 1487 1488 void target() { 1489 $ns::$optional<Foo> opt($ns::in_place, 3, false); 1490 opt.value(); 1491 /*[[check]]*/ 1492 } 1493 )", 1494 UnorderedElementsAre(Pair("check", "safe"))); 1495 1496 ExpectLatticeChecksFor(R"( 1497 #include "unchecked_optional_access_test.h" 1498 1499 struct Foo { 1500 explicit Foo(std::initializer_list<int>); 1501 }; 1502 1503 void target() { 1504 $ns::$optional<Foo> opt($ns::in_place, {3}); 1505 opt.value(); 1506 /*[[check]]*/ 1507 } 1508 )", 1509 UnorderedElementsAre(Pair("check", "safe"))); 1510 } 1511 1512 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { 1513 ExpectLatticeChecksFor(R"( 1514 #include "unchecked_optional_access_test.h" 1515 1516 void target() { 1517 $ns::$optional<int> opt(21); 1518 opt.value(); 1519 /*[[check]]*/ 1520 } 1521 )", 1522 UnorderedElementsAre(Pair("check", "safe"))); 1523 1524 ExpectLatticeChecksFor(R"( 1525 #include "unchecked_optional_access_test.h" 1526 1527 void target() { 1528 $ns::$optional<int> opt = $ns::$optional<int>(21); 1529 opt.value(); 1530 /*[[check]]*/ 1531 } 1532 )", 1533 UnorderedElementsAre(Pair("check", "safe"))); 1534 ExpectLatticeChecksFor(R"( 1535 #include "unchecked_optional_access_test.h" 1536 1537 void target() { 1538 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>()); 1539 opt.value(); 1540 /*[[check]]*/ 1541 } 1542 )", 1543 UnorderedElementsAre(Pair("check", "safe"))); 1544 1545 ExpectLatticeChecksFor(R"( 1546 #include "unchecked_optional_access_test.h" 1547 1548 struct MyString { 1549 MyString(const char*); 1550 }; 1551 1552 void target() { 1553 $ns::$optional<MyString> opt("foo"); 1554 opt.value(); 1555 /*[[check]]*/ 1556 } 1557 )", 1558 UnorderedElementsAre(Pair("check", "safe"))); 1559 1560 ExpectLatticeChecksFor(R"( 1561 #include "unchecked_optional_access_test.h" 1562 1563 struct Foo {}; 1564 1565 struct Bar { 1566 Bar(const Foo&); 1567 }; 1568 1569 void target() { 1570 $ns::$optional<Bar> opt(Make<Foo>()); 1571 opt.value(); 1572 /*[[check]]*/ 1573 } 1574 )", 1575 UnorderedElementsAre(Pair("check", "safe"))); 1576 1577 ExpectLatticeChecksFor(R"( 1578 #include "unchecked_optional_access_test.h" 1579 1580 struct Foo { 1581 explicit Foo(int); 1582 }; 1583 1584 void target() { 1585 $ns::$optional<Foo> opt(3); 1586 opt.value(); 1587 /*[[check]]*/ 1588 } 1589 )", 1590 UnorderedElementsAre(Pair("check", "safe"))); 1591 } 1592 1593 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { 1594 ExpectLatticeChecksFor( 1595 R"( 1596 #include "unchecked_optional_access_test.h" 1597 1598 struct Foo {}; 1599 1600 struct Bar { 1601 Bar(const Foo&); 1602 }; 1603 1604 void target() { 1605 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1606 opt.value(); 1607 /*[[check]]*/ 1608 } 1609 )", 1610 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 1611 1612 ExpectLatticeChecksFor( 1613 R"( 1614 #include "unchecked_optional_access_test.h" 1615 1616 struct Foo {}; 1617 1618 struct Bar { 1619 explicit Bar(const Foo&); 1620 }; 1621 1622 void target() { 1623 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1624 opt.value(); 1625 /*[[check]]*/ 1626 } 1627 )", 1628 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 1629 1630 ExpectLatticeChecksFor( 1631 R"( 1632 #include "unchecked_optional_access_test.h" 1633 1634 struct Foo {}; 1635 1636 struct Bar { 1637 Bar(const Foo&); 1638 }; 1639 1640 void target() { 1641 $ns::$optional<Foo> opt1 = $ns::nullopt; 1642 $ns::$optional<Bar> opt2(opt1); 1643 opt2.value(); 1644 /*[[check]]*/ 1645 } 1646 )", 1647 UnorderedElementsAre(Pair("check", "unsafe: input.cc:13:7"))); 1648 1649 ExpectLatticeChecksFor(R"( 1650 #include "unchecked_optional_access_test.h" 1651 1652 struct Foo {}; 1653 1654 struct Bar { 1655 Bar(const Foo&); 1656 }; 1657 1658 void target() { 1659 $ns::$optional<Foo> opt1(Make<Foo>()); 1660 $ns::$optional<Bar> opt2(opt1); 1661 opt2.value(); 1662 /*[[check]]*/ 1663 } 1664 )", 1665 UnorderedElementsAre(Pair("check", "safe"))); 1666 1667 ExpectLatticeChecksFor(R"( 1668 #include "unchecked_optional_access_test.h" 1669 1670 struct Foo {}; 1671 1672 struct Bar { 1673 explicit Bar(const Foo&); 1674 }; 1675 1676 void target() { 1677 $ns::$optional<Foo> opt1(Make<Foo>()); 1678 $ns::$optional<Bar> opt2(opt1); 1679 opt2.value(); 1680 /*[[check]]*/ 1681 } 1682 )", 1683 UnorderedElementsAre(Pair("check", "safe"))); 1684 } 1685 1686 TEST_P(UncheckedOptionalAccessTest, MakeOptional) { 1687 ExpectLatticeChecksFor(R"( 1688 #include "unchecked_optional_access_test.h" 1689 1690 void target() { 1691 $ns::$optional<int> opt = $ns::make_optional(0); 1692 opt.value(); 1693 /*[[check]]*/ 1694 } 1695 )", 1696 UnorderedElementsAre(Pair("check", "safe"))); 1697 1698 ExpectLatticeChecksFor(R"( 1699 #include "unchecked_optional_access_test.h" 1700 1701 struct Foo { 1702 Foo(int, int); 1703 }; 1704 1705 void target() { 1706 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22); 1707 opt.value(); 1708 /*[[check]]*/ 1709 } 1710 )", 1711 UnorderedElementsAre(Pair("check", "safe"))); 1712 1713 ExpectLatticeChecksFor(R"( 1714 #include "unchecked_optional_access_test.h" 1715 1716 struct Foo { 1717 constexpr Foo(std::initializer_list<char>); 1718 }; 1719 1720 void target() { 1721 char a = 'a'; 1722 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a}); 1723 opt.value(); 1724 /*[[check]]*/ 1725 } 1726 )", 1727 UnorderedElementsAre(Pair("check", "safe"))); 1728 } 1729 1730 TEST_P(UncheckedOptionalAccessTest, ValueOr) { 1731 ExpectLatticeChecksFor(R"( 1732 #include "unchecked_optional_access_test.h" 1733 1734 void target() { 1735 $ns::$optional<int> opt; 1736 opt.value_or(0); 1737 (void)0; 1738 /*[[check]]*/ 1739 } 1740 )", 1741 UnorderedElementsAre(Pair("check", "safe"))); 1742 } 1743 1744 TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) { 1745 // Pointers. 1746 ExpectLatticeChecksFor( 1747 R"code( 1748 #include "unchecked_optional_access_test.h" 1749 1750 void target($ns::$optional<int*> opt) { 1751 if (opt.value_or(nullptr) != nullptr) { 1752 opt.value(); 1753 /*[[check-ptrs-1]]*/ 1754 } else { 1755 opt.value(); 1756 /*[[check-ptrs-2]]*/ 1757 } 1758 } 1759 )code", 1760 UnorderedElementsAre(Pair("check-ptrs-1", "safe"), 1761 Pair("check-ptrs-2", "unsafe: input.cc:9:9"))); 1762 1763 // Integers. 1764 ExpectLatticeChecksFor( 1765 R"code( 1766 #include "unchecked_optional_access_test.h" 1767 1768 void target($ns::$optional<int> opt) { 1769 if (opt.value_or(0) != 0) { 1770 opt.value(); 1771 /*[[check-ints-1]]*/ 1772 } else { 1773 opt.value(); 1774 /*[[check-ints-2]]*/ 1775 } 1776 } 1777 )code", 1778 UnorderedElementsAre(Pair("check-ints-1", "safe"), 1779 Pair("check-ints-2", "unsafe: input.cc:9:9"))); 1780 1781 // Strings. 1782 ExpectLatticeChecksFor( 1783 R"code( 1784 #include "unchecked_optional_access_test.h" 1785 1786 void target($ns::$optional<std::string> opt) { 1787 if (!opt.value_or("").empty()) { 1788 opt.value(); 1789 /*[[check-strings-1]]*/ 1790 } else { 1791 opt.value(); 1792 /*[[check-strings-2]]*/ 1793 } 1794 } 1795 )code", 1796 UnorderedElementsAre(Pair("check-strings-1", "safe"), 1797 Pair("check-strings-2", "unsafe: input.cc:9:9"))); 1798 1799 ExpectLatticeChecksFor( 1800 R"code( 1801 #include "unchecked_optional_access_test.h" 1802 1803 void target($ns::$optional<std::string> opt) { 1804 if (opt.value_or("") != "") { 1805 opt.value(); 1806 /*[[check-strings-neq-1]]*/ 1807 } else { 1808 opt.value(); 1809 /*[[check-strings-neq-2]]*/ 1810 } 1811 } 1812 )code", 1813 UnorderedElementsAre( 1814 Pair("check-strings-neq-1", "safe"), 1815 Pair("check-strings-neq-2", "unsafe: input.cc:9:9"))); 1816 1817 // Pointer-to-optional. 1818 // 1819 // FIXME: make `opt` a parameter directly, once we ensure that all `optional` 1820 // values have a `has_value` property. 1821 ExpectLatticeChecksFor( 1822 R"code( 1823 #include "unchecked_optional_access_test.h" 1824 1825 void target($ns::$optional<int> p) { 1826 $ns::$optional<int> *opt = &p; 1827 if (opt->value_or(0) != 0) { 1828 opt->value(); 1829 /*[[check-pto-1]]*/ 1830 } else { 1831 opt->value(); 1832 /*[[check-pto-2]]*/ 1833 } 1834 } 1835 )code", 1836 UnorderedElementsAre(Pair("check-pto-1", "safe"), 1837 Pair("check-pto-2", "unsafe: input.cc:10:9"))); 1838 } 1839 1840 TEST_P(UncheckedOptionalAccessTest, Emplace) { 1841 ExpectLatticeChecksFor(R"( 1842 #include "unchecked_optional_access_test.h" 1843 1844 void target() { 1845 $ns::$optional<int> opt; 1846 opt.emplace(0); 1847 opt.value(); 1848 /*[[check]]*/ 1849 } 1850 )", 1851 UnorderedElementsAre(Pair("check", "safe"))); 1852 1853 ExpectLatticeChecksFor(R"( 1854 #include "unchecked_optional_access_test.h" 1855 1856 void target($ns::$optional<int> *opt) { 1857 opt->emplace(0); 1858 opt->value(); 1859 /*[[check]]*/ 1860 } 1861 )", 1862 UnorderedElementsAre(Pair("check", "safe"))); 1863 1864 // FIXME: Add tests that call `emplace` in conditional branches: 1865 // ExpectLatticeChecksFor( 1866 // R"( 1867 // #include "unchecked_optional_access_test.h" 1868 // 1869 // void target($ns::$optional<int> opt, bool b) { 1870 // if (b) { 1871 // opt.emplace(0); 1872 // } 1873 // if (b) { 1874 // opt.value(); 1875 // /*[[check-1]]*/ 1876 // } else { 1877 // opt.value(); 1878 // /*[[check-2]]*/ 1879 // } 1880 // } 1881 // )", 1882 // UnorderedElementsAre(Pair("check-1", "safe"), 1883 // Pair("check-2", "unsafe: input.cc:12:9"))); 1884 } 1885 1886 TEST_P(UncheckedOptionalAccessTest, Reset) { 1887 ExpectLatticeChecksFor( 1888 R"( 1889 #include "unchecked_optional_access_test.h" 1890 1891 void target() { 1892 $ns::$optional<int> opt = $ns::make_optional(0); 1893 opt.reset(); 1894 opt.value(); 1895 /*[[check]]*/ 1896 } 1897 )", 1898 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); 1899 1900 ExpectLatticeChecksFor( 1901 R"( 1902 #include "unchecked_optional_access_test.h" 1903 1904 void target($ns::$optional<int> &opt) { 1905 if (opt.has_value()) { 1906 opt.reset(); 1907 opt.value(); 1908 /*[[check]]*/ 1909 } 1910 } 1911 )", 1912 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:9"))); 1913 1914 // FIXME: Add tests that call `reset` in conditional branches: 1915 // ExpectLatticeChecksFor( 1916 // R"( 1917 // #include "unchecked_optional_access_test.h" 1918 // 1919 // void target(bool b) { 1920 // $ns::$optional<int> opt = $ns::make_optional(0); 1921 // if (b) { 1922 // opt.reset(); 1923 // } 1924 // if (b) { 1925 // opt.value(); 1926 // /*[[check-1]]*/ 1927 // } else { 1928 // opt.value(); 1929 // /*[[check-2]]*/ 1930 // } 1931 // } 1932 // )", 1933 // UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:10:9"), 1934 // Pair("check-2", "safe"))); 1935 } 1936 1937 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { 1938 ExpectLatticeChecksFor(R"( 1939 #include "unchecked_optional_access_test.h" 1940 1941 struct Foo {}; 1942 1943 void target() { 1944 $ns::$optional<Foo> opt; 1945 opt = Foo(); 1946 opt.value(); 1947 /*[[check]]*/ 1948 } 1949 )", 1950 UnorderedElementsAre(Pair("check", "safe"))); 1951 1952 ExpectLatticeChecksFor(R"( 1953 #include "unchecked_optional_access_test.h" 1954 1955 struct Foo {}; 1956 1957 void target() { 1958 $ns::$optional<Foo> opt; 1959 (opt = Foo()).value(); 1960 (void)0; 1961 /*[[check]]*/ 1962 } 1963 )", 1964 UnorderedElementsAre(Pair("check", "safe"))); 1965 1966 ExpectLatticeChecksFor(R"( 1967 #include "unchecked_optional_access_test.h" 1968 1969 struct MyString { 1970 MyString(const char*); 1971 }; 1972 1973 void target() { 1974 $ns::$optional<MyString> opt; 1975 opt = "foo"; 1976 opt.value(); 1977 /*[[check]]*/ 1978 } 1979 )", 1980 UnorderedElementsAre(Pair("check", "safe"))); 1981 1982 ExpectLatticeChecksFor(R"( 1983 #include "unchecked_optional_access_test.h" 1984 1985 struct MyString { 1986 MyString(const char*); 1987 }; 1988 1989 void target() { 1990 $ns::$optional<MyString> opt; 1991 (opt = "foo").value(); 1992 /*[[check]]*/ 1993 } 1994 )", 1995 UnorderedElementsAre(Pair("check", "safe"))); 1996 } 1997 1998 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { 1999 ExpectLatticeChecksFor( 2000 R"( 2001 #include "unchecked_optional_access_test.h" 2002 2003 struct Foo {}; 2004 2005 struct Bar { 2006 Bar(const Foo&); 2007 }; 2008 2009 void target() { 2010 $ns::$optional<Foo> opt1 = Foo(); 2011 $ns::$optional<Bar> opt2; 2012 opt2 = opt1; 2013 opt2.value(); 2014 /*[[check]]*/ 2015 } 2016 )", 2017 UnorderedElementsAre(Pair("check", "safe"))); 2018 2019 ExpectLatticeChecksFor( 2020 R"( 2021 #include "unchecked_optional_access_test.h" 2022 2023 struct Foo {}; 2024 2025 struct Bar { 2026 Bar(const Foo&); 2027 }; 2028 2029 void target() { 2030 $ns::$optional<Foo> opt1; 2031 $ns::$optional<Bar> opt2; 2032 if (opt2.has_value()) { 2033 opt2 = opt1; 2034 opt2.value(); 2035 /*[[check]]*/ 2036 } 2037 } 2038 )", 2039 UnorderedElementsAre(Pair("check", "unsafe: input.cc:15:9"))); 2040 2041 ExpectLatticeChecksFor( 2042 R"( 2043 #include "unchecked_optional_access_test.h" 2044 2045 struct Foo {}; 2046 2047 struct Bar { 2048 Bar(const Foo&); 2049 }; 2050 2051 void target() { 2052 $ns::$optional<Foo> opt1 = Foo(); 2053 $ns::$optional<Bar> opt2; 2054 (opt2 = opt1).value(); 2055 (void)0; 2056 /*[[check]]*/ 2057 } 2058 )", 2059 UnorderedElementsAre(Pair("check", "safe"))); 2060 } 2061 2062 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { 2063 ExpectLatticeChecksFor( 2064 R"( 2065 #include "unchecked_optional_access_test.h" 2066 2067 void target() { 2068 $ns::$optional<int> opt = 3; 2069 opt = $ns::nullopt; 2070 opt.value(); 2071 /*[[check]]*/ 2072 } 2073 )", 2074 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); 2075 2076 ExpectLatticeChecksFor( 2077 R"( 2078 #include "unchecked_optional_access_test.h" 2079 2080 void target() { 2081 $ns::$optional<int> opt = 3; 2082 (opt = $ns::nullopt).value(); 2083 /*[[check]]*/ 2084 } 2085 )", 2086 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 2087 } 2088 2089 TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { 2090 ExpectLatticeChecksFor( 2091 R"( 2092 #include "unchecked_optional_access_test.h" 2093 2094 void target() { 2095 $ns::$optional<int> opt1 = $ns::nullopt; 2096 $ns::$optional<int> opt2 = 3; 2097 2098 opt1.swap(opt2); 2099 2100 opt1.value(); 2101 /*[[check-1]]*/ 2102 2103 opt2.value(); 2104 /*[[check-2]]*/ 2105 } 2106 )", 2107 UnorderedElementsAre(Pair("check-1", "safe"), 2108 Pair("check-2", "unsafe: input.cc:13:7"))); 2109 2110 ExpectLatticeChecksFor( 2111 R"( 2112 #include "unchecked_optional_access_test.h" 2113 2114 void target() { 2115 $ns::$optional<int> opt1 = $ns::nullopt; 2116 $ns::$optional<int> opt2 = 3; 2117 2118 opt2.swap(opt1); 2119 2120 opt1.value(); 2121 /*[[check-3]]*/ 2122 2123 opt2.value(); 2124 /*[[check-4]]*/ 2125 } 2126 )", 2127 UnorderedElementsAre(Pair("check-3", "safe"), 2128 Pair("check-4", "unsafe: input.cc:13:7"))); 2129 } 2130 2131 TEST_P(UncheckedOptionalAccessTest, StdSwap) { 2132 ExpectLatticeChecksFor( 2133 R"( 2134 #include "unchecked_optional_access_test.h" 2135 2136 void target() { 2137 $ns::$optional<int> opt1 = $ns::nullopt; 2138 $ns::$optional<int> opt2 = 3; 2139 2140 std::swap(opt1, opt2); 2141 2142 opt1.value(); 2143 /*[[check-1]]*/ 2144 2145 opt2.value(); 2146 /*[[check-2]]*/ 2147 } 2148 )", 2149 UnorderedElementsAre(Pair("check-1", "safe"), 2150 Pair("check-2", "unsafe: input.cc:13:7"))); 2151 2152 ExpectLatticeChecksFor( 2153 R"( 2154 #include "unchecked_optional_access_test.h" 2155 2156 void target() { 2157 $ns::$optional<int> opt1 = $ns::nullopt; 2158 $ns::$optional<int> opt2 = 3; 2159 2160 std::swap(opt2, opt1); 2161 2162 opt1.value(); 2163 /*[[check-3]]*/ 2164 2165 opt2.value(); 2166 /*[[check-4]]*/ 2167 } 2168 )", 2169 UnorderedElementsAre(Pair("check-3", "safe"), 2170 Pair("check-4", "unsafe: input.cc:13:7"))); 2171 } 2172 2173 TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { 2174 // We suppress diagnostics for values reachable from smart pointers (other 2175 // than `optional` itself). 2176 ExpectLatticeChecksFor( 2177 R"( 2178 #include "unchecked_optional_access_test.h" 2179 2180 template <typename T> 2181 struct smart_ptr { 2182 T& operator*() &; 2183 T* operator->(); 2184 }; 2185 2186 struct Foo { 2187 $ns::$optional<int> opt; 2188 }; 2189 2190 void target() { 2191 smart_ptr<Foo> foo; 2192 *foo->opt; 2193 /*[[check-1]]*/ 2194 *(*foo).opt; 2195 /*[[check-2]]*/ 2196 } 2197 )", 2198 UnorderedElementsAre(Pair("check-1", "safe"), Pair("check-2", "safe"))); 2199 } 2200 2201 TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { 2202 ExpectLatticeChecksFor( 2203 R"( 2204 #include "unchecked_optional_access_test.h" 2205 2206 $ns::$optional<int> MakeOpt(); 2207 2208 void target() { 2209 $ns::$optional<int> opt = 0; 2210 opt = MakeOpt(); 2211 opt.value(); 2212 /*[[check-1]]*/ 2213 } 2214 )", 2215 UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:9:7"))); 2216 ExpectLatticeChecksFor( 2217 R"( 2218 #include "unchecked_optional_access_test.h" 2219 2220 const $ns::$optional<int>& MakeOpt(); 2221 2222 void target() { 2223 $ns::$optional<int> opt = 0; 2224 opt = MakeOpt(); 2225 opt.value(); 2226 /*[[check-2]]*/ 2227 } 2228 )", 2229 UnorderedElementsAre(Pair("check-2", "unsafe: input.cc:9:7"))); 2230 2231 ExpectLatticeChecksFor( 2232 R"( 2233 #include "unchecked_optional_access_test.h" 2234 2235 using IntOpt = $ns::$optional<int>; 2236 IntOpt MakeOpt(); 2237 2238 void target() { 2239 IntOpt opt = 0; 2240 opt = MakeOpt(); 2241 opt.value(); 2242 /*[[check-3]]*/ 2243 } 2244 )", 2245 UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:10:7"))); 2246 2247 ExpectLatticeChecksFor( 2248 R"( 2249 #include "unchecked_optional_access_test.h" 2250 2251 using IntOpt = $ns::$optional<int>; 2252 const IntOpt& MakeOpt(); 2253 2254 void target() { 2255 IntOpt opt = 0; 2256 opt = MakeOpt(); 2257 opt.value(); 2258 /*[[check-4]]*/ 2259 } 2260 )", 2261 UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:10:7"))); 2262 } 2263 2264 // Verifies that the model sees through aliases. 2265 TEST_P(UncheckedOptionalAccessTest, WithAlias) { 2266 ExpectLatticeChecksFor( 2267 R"( 2268 #include "unchecked_optional_access_test.h" 2269 2270 template <typename T> 2271 using MyOptional = $ns::$optional<T>; 2272 2273 void target(MyOptional<int> opt) { 2274 opt.value(); 2275 /*[[check]]*/ 2276 } 2277 )", 2278 UnorderedElementsAre(Pair("check", "unsafe: input.cc:8:7"))); 2279 } 2280 2281 TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) { 2282 // Basic test that nested values are populated. We nest an optional because 2283 // its easy to use in a test, but the type of the nested value shouldn't 2284 // matter. 2285 ExpectLatticeChecksFor( 2286 R"( 2287 #include "unchecked_optional_access_test.h" 2288 2289 using Foo = $ns::$optional<std::string>; 2290 2291 void target($ns::$optional<Foo> foo) { 2292 if (foo && *foo) { 2293 foo->value(); 2294 /*[[access]]*/ 2295 } 2296 } 2297 )", 2298 UnorderedElementsAre(Pair("access", "safe"))); 2299 2300 // Mutation is supported for nested values. 2301 ExpectLatticeChecksFor( 2302 R"( 2303 #include "unchecked_optional_access_test.h" 2304 2305 using Foo = $ns::$optional<std::string>; 2306 2307 void target($ns::$optional<Foo> foo) { 2308 if (foo && *foo) { 2309 foo->reset(); 2310 foo->value(); 2311 /*[[reset]]*/ 2312 } 2313 } 2314 )", 2315 UnorderedElementsAre(Pair("reset", "unsafe: input.cc:9:9"))); 2316 } 2317 2318 // Tests that structs can be nested. We use an optional field because its easy 2319 // to use in a test, but the type of the field shouldn't matter. 2320 TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) { 2321 ExpectLatticeChecksFor( 2322 R"( 2323 #include "unchecked_optional_access_test.h" 2324 2325 struct Foo { 2326 $ns::$optional<std::string> opt; 2327 }; 2328 2329 void target($ns::$optional<Foo> foo) { 2330 if (foo && foo->opt) { 2331 foo->opt.value(); 2332 /*[[access]]*/ 2333 } 2334 } 2335 )", 2336 UnorderedElementsAre(Pair("access", "safe"))); 2337 } 2338 2339 TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) { 2340 // FIXME: Fix when to initialize `value`. All unwrapping should be safe in 2341 // this example, but `value` initialization is done multiple times during the 2342 // fixpoint iterations and joining the environment won't correctly merge them. 2343 ExpectLatticeChecksFor( 2344 R"( 2345 #include "unchecked_optional_access_test.h" 2346 2347 using Foo = $ns::$optional<std::string>; 2348 2349 void target($ns::$optional<Foo> foo, bool b) { 2350 if (!foo.has_value()) return; 2351 if (b) { 2352 if (!foo->has_value()) return; 2353 // We have created `foo.value()`. 2354 foo->value(); 2355 } else { 2356 if (!foo->has_value()) return; 2357 // We have created `foo.value()` again, in a different environment. 2358 foo->value(); 2359 } 2360 // Now we merge the two values. UncheckedOptionalAccessModel::merge() will 2361 // throw away the "value" property. 2362 foo->value(); 2363 /*[[merge]]*/ 2364 } 2365 )", 2366 UnorderedElementsAre(Pair("merge", "unsafe: input.cc:19:7"))); 2367 } 2368 2369 TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) { 2370 ExpectLatticeChecksFor( 2371 R"( 2372 #include "unchecked_optional_access_test.h" 2373 2374 template <typename T> 2375 struct smart_ptr { 2376 typename std::add_lvalue_reference<T>::type operator*() &; 2377 }; 2378 2379 void target() { 2380 smart_ptr<$ns::$optional<float>> x; 2381 *x = $ns::nullopt; 2382 (*x).value(); 2383 /*[[check]]*/ 2384 } 2385 )", 2386 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 2387 } 2388 2389 TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) { 2390 ExpectLatticeChecksFor(R"code( 2391 #include "unchecked_optional_access_test.h" 2392 2393 void target(bool b, $ns::$optional<int> opt) { 2394 if (b || opt.has_value()) { 2395 if (!b) { 2396 opt.value(); 2397 /*[[check-1]]*/ 2398 } 2399 } 2400 } 2401 )code", 2402 UnorderedElementsAre(Pair("check-1", "safe"))); 2403 2404 ExpectLatticeChecksFor(R"code( 2405 #include "unchecked_optional_access_test.h" 2406 2407 void target(bool b, $ns::$optional<int> opt) { 2408 if (b && !opt.has_value()) return; 2409 if (b) { 2410 opt.value(); 2411 /*[[check-2]]*/ 2412 } 2413 } 2414 )code", 2415 UnorderedElementsAre(Pair("check-2", "safe"))); 2416 2417 ExpectLatticeChecksFor( 2418 R"code( 2419 #include "unchecked_optional_access_test.h" 2420 2421 void target(bool b, $ns::$optional<int> opt) { 2422 if (opt.has_value()) b = true; 2423 if (b) { 2424 opt.value(); 2425 /*[[check-3]]*/ 2426 } 2427 } 2428 )code", 2429 UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:7:9"))); 2430 2431 ExpectLatticeChecksFor(R"code( 2432 #include "unchecked_optional_access_test.h" 2433 2434 void target(bool b, $ns::$optional<int> opt) { 2435 if (b) return; 2436 if (opt.has_value()) b = true; 2437 if (b) { 2438 opt.value(); 2439 /*[[check-4]]*/ 2440 } 2441 } 2442 )code", 2443 UnorderedElementsAre(Pair("check-4", "safe"))); 2444 2445 ExpectLatticeChecksFor(R"( 2446 #include "unchecked_optional_access_test.h" 2447 2448 void target(bool b, $ns::$optional<int> opt) { 2449 if (opt.has_value() == b) { 2450 if (b) { 2451 opt.value(); 2452 /*[[check-5]]*/ 2453 } 2454 } 2455 } 2456 )", 2457 UnorderedElementsAre(Pair("check-5", "safe"))); 2458 2459 ExpectLatticeChecksFor(R"( 2460 #include "unchecked_optional_access_test.h" 2461 2462 void target(bool b, $ns::$optional<int> opt) { 2463 if (opt.has_value() != b) { 2464 if (!b) { 2465 opt.value(); 2466 /*[[check-6]]*/ 2467 } 2468 } 2469 } 2470 )", 2471 UnorderedElementsAre(Pair("check-6", "safe"))); 2472 2473 ExpectLatticeChecksFor(R"( 2474 #include "unchecked_optional_access_test.h" 2475 2476 void target(bool b) { 2477 $ns::$optional<int> opt1 = $ns::nullopt; 2478 $ns::$optional<int> opt2; 2479 if (b) { 2480 opt2 = $ns::nullopt; 2481 } else { 2482 opt2 = $ns::nullopt; 2483 } 2484 if (opt2.has_value()) { 2485 opt1.value(); 2486 /*[[check]]*/ 2487 } 2488 } 2489 )", 2490 UnorderedElementsAre(Pair("check", "safe"))); 2491 2492 // FIXME: Add support for operator==. 2493 // ExpectLatticeChecksFor(R"( 2494 // #include "unchecked_optional_access_test.h" 2495 // 2496 // void target($ns::$optional<int> opt1, $ns::$optional<int> opt2) { 2497 // if (opt1 == opt2) { 2498 // if (opt1.has_value()) { 2499 // opt2.value(); 2500 // /*[[check-7]]*/ 2501 // } 2502 // } 2503 // } 2504 // )", 2505 // UnorderedElementsAre(Pair("check-7", "safe"))); 2506 } 2507 2508 TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) { 2509 ExpectLatticeChecksFor( 2510 R"code( 2511 #include "unchecked_optional_access_test.h" 2512 2513 void target(bool b) { 2514 $ns::$optional<int> opt; 2515 if (b) { 2516 opt = Make<$ns::$optional<int>>(); 2517 } else { 2518 opt = Make<$ns::$optional<int>>(); 2519 } 2520 if (opt.has_value()) { 2521 opt.value(); 2522 /*[[check-1]]*/ 2523 } else { 2524 opt.value(); 2525 /*[[check-2]]*/ 2526 } 2527 } 2528 )code", 2529 UnorderedElementsAre(Pair("check-1", "safe"), 2530 Pair("check-2", "unsafe: input.cc:15:9"))); 2531 2532 ExpectLatticeChecksFor(R"code( 2533 #include "unchecked_optional_access_test.h" 2534 2535 void target(bool b) { 2536 $ns::$optional<int> opt; 2537 if (b) { 2538 opt = Make<$ns::$optional<int>>(); 2539 if (!opt.has_value()) return; 2540 } else { 2541 opt = Make<$ns::$optional<int>>(); 2542 if (!opt.has_value()) return; 2543 } 2544 opt.value(); 2545 /*[[check-3]]*/ 2546 } 2547 )code", 2548 UnorderedElementsAre(Pair("check-3", "safe"))); 2549 2550 ExpectLatticeChecksFor( 2551 R"code( 2552 #include "unchecked_optional_access_test.h" 2553 2554 void target(bool b) { 2555 $ns::$optional<int> opt; 2556 if (b) { 2557 opt = Make<$ns::$optional<int>>(); 2558 if (!opt.has_value()) return; 2559 } else { 2560 opt = Make<$ns::$optional<int>>(); 2561 } 2562 opt.value(); 2563 /*[[check-4]]*/ 2564 } 2565 )code", 2566 UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:12:7"))); 2567 2568 ExpectLatticeChecksFor( 2569 R"code( 2570 #include "unchecked_optional_access_test.h" 2571 2572 void target(bool b) { 2573 $ns::$optional<int> opt; 2574 if (b) { 2575 opt = 1; 2576 } else { 2577 opt = 2; 2578 } 2579 opt.value(); 2580 /*[[check-5]]*/ 2581 } 2582 )code", 2583 UnorderedElementsAre(Pair("check-5", "safe"))); 2584 2585 ExpectLatticeChecksFor( 2586 R"code( 2587 #include "unchecked_optional_access_test.h" 2588 2589 void target(bool b) { 2590 $ns::$optional<int> opt; 2591 if (b) { 2592 opt = 1; 2593 } else { 2594 opt = Make<$ns::$optional<int>>(); 2595 } 2596 opt.value(); 2597 /*[[check-6]]*/ 2598 } 2599 )code", 2600 UnorderedElementsAre(Pair("check-6", "unsafe: input.cc:11:7"))); 2601 } 2602 2603 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoop) { 2604 ExpectLatticeChecksFor(R"( 2605 #include "unchecked_optional_access_test.h" 2606 2607 void target() { 2608 $ns::$optional<int> opt = 3; 2609 while (Make<bool>()) { 2610 opt.value(); 2611 /*[[check-1]]*/ 2612 } 2613 } 2614 )", 2615 UnorderedElementsAre(Pair("check-1", "safe"))); 2616 2617 ExpectLatticeChecksFor(R"( 2618 #include "unchecked_optional_access_test.h" 2619 2620 void target() { 2621 $ns::$optional<int> opt = 3; 2622 while (Make<bool>()) { 2623 opt.value(); 2624 /*[[check-2]]*/ 2625 2626 opt = Make<$ns::$optional<int>>(); 2627 if (!opt.has_value()) return; 2628 } 2629 } 2630 )", 2631 UnorderedElementsAre(Pair("check-2", "safe"))); 2632 2633 ExpectLatticeChecksFor( 2634 R"( 2635 #include "unchecked_optional_access_test.h" 2636 2637 void target() { 2638 $ns::$optional<int> opt = 3; 2639 while (Make<bool>()) { 2640 opt.value(); 2641 /*[[check-3]]*/ 2642 2643 opt = Make<$ns::$optional<int>>(); 2644 } 2645 } 2646 )", 2647 UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:7:9"))); 2648 2649 ExpectLatticeChecksFor( 2650 R"( 2651 #include "unchecked_optional_access_test.h" 2652 2653 void target() { 2654 $ns::$optional<int> opt = 3; 2655 while (Make<bool>()) { 2656 opt.value(); 2657 /*[[check-4]]*/ 2658 2659 opt = Make<$ns::$optional<int>>(); 2660 if (!opt.has_value()) continue; 2661 } 2662 } 2663 )", 2664 UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:7:9"))); 2665 } 2666 2667 // FIXME: Add support for: 2668 // - constructors (copy, move) 2669 // - assignment operators (default, copy, move) 2670 // - invalidation (passing optional by non-const reference/pointer) 2671