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 StdStringHeader[] = R"( 500 #ifndef STRING_H 501 #define STRING_H 502 503 namespace std { 504 505 struct string { 506 string(const char*); 507 ~string(); 508 bool empty(); 509 }; 510 bool operator!=(const string &LHS, const char *RHS); 511 512 } // namespace std 513 514 #endif // STRING_H 515 )"; 516 517 static constexpr char StdUtilityHeader[] = R"( 518 #ifndef UTILITY_H 519 #define UTILITY_H 520 521 #include "std_type_traits.h" 522 523 namespace std { 524 525 template <typename T> 526 constexpr remove_reference_t<T>&& move(T&& x); 527 528 template <typename T> 529 void swap(T& a, T& b) noexcept; 530 531 } // namespace std 532 533 #endif // UTILITY_H 534 )"; 535 536 static constexpr char StdInitializerListHeader[] = R"( 537 #ifndef INITIALIZER_LIST_H 538 #define INITIALIZER_LIST_H 539 540 namespace std { 541 542 template <typename T> 543 class initializer_list { 544 public: 545 initializer_list() noexcept; 546 }; 547 548 } // namespace std 549 550 #endif // INITIALIZER_LIST_H 551 )"; 552 553 static constexpr char StdOptionalHeader[] = R"( 554 #include "std_initializer_list.h" 555 #include "std_type_traits.h" 556 #include "std_utility.h" 557 558 namespace std { 559 560 struct in_place_t {}; 561 constexpr in_place_t in_place; 562 563 struct nullopt_t { 564 constexpr explicit nullopt_t() {} 565 }; 566 constexpr nullopt_t nullopt; 567 568 template <class _Tp> 569 struct __optional_destruct_base { 570 constexpr void reset() noexcept; 571 }; 572 573 template <class _Tp> 574 struct __optional_storage_base : __optional_destruct_base<_Tp> { 575 constexpr bool has_value() const noexcept; 576 }; 577 578 template <typename _Tp> 579 class optional : private __optional_storage_base<_Tp> { 580 using __base = __optional_storage_base<_Tp>; 581 582 public: 583 using value_type = _Tp; 584 585 private: 586 struct _CheckOptionalArgsConstructor { 587 template <class _Up> 588 static constexpr bool __enable_implicit() { 589 return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>; 590 } 591 592 template <class _Up> 593 static constexpr bool __enable_explicit() { 594 return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>; 595 } 596 }; 597 template <class _Up> 598 using _CheckOptionalArgsCtor = 599 _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value && 600 _IsNotSame<__uncvref_t<_Up>, optional>::value, 601 _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>; 602 template <class _QualUp> 603 struct _CheckOptionalLikeConstructor { 604 template <class _Up, class _Opt = optional<_Up>> 605 using __check_constructible_from_opt = 606 _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>, 607 is_constructible<_Tp, _Opt&&>, is_constructible<_Tp, _Opt const&&>, 608 is_convertible<_Opt&, _Tp>, is_convertible<_Opt const&, _Tp>, 609 is_convertible<_Opt&&, _Tp>, is_convertible<_Opt const&&, _Tp>>; 610 template <class _Up, class _QUp = _QualUp> 611 static constexpr bool __enable_implicit() { 612 return is_convertible<_QUp, _Tp>::value && 613 !__check_constructible_from_opt<_Up>::value; 614 } 615 template <class _Up, class _QUp = _QualUp> 616 static constexpr bool __enable_explicit() { 617 return !is_convertible<_QUp, _Tp>::value && 618 !__check_constructible_from_opt<_Up>::value; 619 } 620 }; 621 622 template <class _Up, class _QualUp> 623 using _CheckOptionalLikeCtor = 624 _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value, 625 _CheckOptionalLikeConstructor<_QualUp>, 626 __check_tuple_constructor_fail>; 627 628 629 template <class _Up, class _QualUp> 630 using _CheckOptionalLikeAssign = _If< 631 _And< 632 _IsNotSame<_Up, _Tp>, 633 is_constructible<_Tp, _QualUp>, 634 is_assignable<_Tp&, _QualUp> 635 >::value, 636 _CheckOptionalLikeConstructor<_QualUp>, 637 __check_tuple_constructor_fail 638 >; 639 640 public: 641 constexpr optional() noexcept {} 642 constexpr optional(const optional&) = default; 643 constexpr optional(optional&&) = default; 644 constexpr optional(nullopt_t) noexcept {} 645 646 template < 647 class _InPlaceT, class... _Args, 648 class = enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>, 649 is_constructible<value_type, _Args...>>::value>> 650 constexpr explicit optional(_InPlaceT, _Args&&... __args); 651 652 template <class _Up, class... _Args, 653 class = enable_if_t<is_constructible_v< 654 value_type, initializer_list<_Up>&, _Args...>>> 655 constexpr explicit optional(in_place_t, initializer_list<_Up> __il, 656 _Args&&... __args); 657 658 template < 659 class _Up = value_type, 660 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(), 661 int> = 0> 662 constexpr optional(_Up&& __v); 663 664 template < 665 class _Up, 666 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(), 667 int> = 0> 668 constexpr explicit optional(_Up&& __v); 669 670 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: 671 template __enable_implicit<_Up>(), 672 int> = 0> 673 constexpr optional(const optional<_Up>& __v); 674 675 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>:: 676 template __enable_explicit<_Up>(), 677 int> = 0> 678 constexpr explicit optional(const optional<_Up>& __v); 679 680 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 681 template __enable_implicit<_Up>(), 682 int> = 0> 683 constexpr optional(optional<_Up>&& __v); 684 685 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 686 template __enable_explicit<_Up>(), 687 int> = 0> 688 constexpr explicit optional(optional<_Up>&& __v); 689 690 constexpr optional& operator=(nullopt_t) noexcept; 691 692 optional& operator=(const optional&); 693 694 optional& operator=(optional&&); 695 696 template <class _Up = value_type, 697 class = enable_if_t<_And<_IsNotSame<__uncvref_t<_Up>, optional>, 698 _Or<_IsNotSame<__uncvref_t<_Up>, value_type>, 699 _Not<is_scalar<value_type>>>, 700 is_constructible<value_type, _Up>, 701 is_assignable<value_type&, _Up>>::value>> 702 constexpr optional& operator=(_Up&& __v); 703 704 template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>:: 705 template __enable_assign<_Up>(), 706 int> = 0> 707 constexpr optional& operator=(const optional<_Up>& __v); 708 709 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>:: 710 template __enable_assign<_Up>(), 711 int> = 0> 712 constexpr optional& operator=(optional<_Up>&& __v); 713 714 const _Tp& operator*() const&; 715 _Tp& operator*() &; 716 const _Tp&& operator*() const&&; 717 _Tp&& operator*() &&; 718 719 const _Tp* operator->() const; 720 _Tp* operator->(); 721 722 const _Tp& value() const&; 723 _Tp& value() &; 724 const _Tp&& value() const&&; 725 _Tp&& value() &&; 726 727 template <typename U> 728 constexpr _Tp value_or(U&& v) const&; 729 template <typename U> 730 _Tp value_or(U&& v) &&; 731 732 template <typename... Args> 733 _Tp& emplace(Args&&... args); 734 735 template <typename U, typename... Args> 736 _Tp& emplace(std::initializer_list<U> ilist, Args&&... args); 737 738 using __base::reset; 739 740 constexpr explicit operator bool() const noexcept; 741 using __base::has_value; 742 743 constexpr void swap(optional& __opt) noexcept; 744 }; 745 746 template <typename T> 747 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 748 749 template <typename T, typename... Args> 750 constexpr optional<T> make_optional(Args&&... args); 751 752 template <typename T, typename U, typename... Args> 753 constexpr optional<T> make_optional(std::initializer_list<U> il, 754 Args&&... args); 755 756 } // namespace std 757 )"; 758 759 static constexpr char AbslOptionalHeader[] = R"( 760 #include "absl_type_traits.h" 761 #include "std_initializer_list.h" 762 #include "std_type_traits.h" 763 #include "std_utility.h" 764 765 namespace absl { 766 767 struct nullopt_t { 768 constexpr explicit nullopt_t() {} 769 }; 770 constexpr nullopt_t nullopt; 771 772 struct in_place_t {}; 773 constexpr in_place_t in_place; 774 775 template <typename T> 776 class optional; 777 778 namespace optional_internal { 779 780 template <typename T, typename U> 781 struct is_constructible_convertible_from_optional 782 : std::integral_constant< 783 bool, std::is_constructible<T, optional<U>&>::value || 784 std::is_constructible<T, optional<U>&&>::value || 785 std::is_constructible<T, const optional<U>&>::value || 786 std::is_constructible<T, const optional<U>&&>::value || 787 std::is_convertible<optional<U>&, T>::value || 788 std::is_convertible<optional<U>&&, T>::value || 789 std::is_convertible<const optional<U>&, T>::value || 790 std::is_convertible<const optional<U>&&, T>::value> {}; 791 792 template <typename T, typename U> 793 struct is_constructible_convertible_assignable_from_optional 794 : std::integral_constant< 795 bool, is_constructible_convertible_from_optional<T, U>::value || 796 std::is_assignable<T&, optional<U>&>::value || 797 std::is_assignable<T&, optional<U>&&>::value || 798 std::is_assignable<T&, const optional<U>&>::value || 799 std::is_assignable<T&, const optional<U>&&>::value> {}; 800 801 } // namespace optional_internal 802 803 template <typename T> 804 class optional { 805 public: 806 constexpr optional() noexcept; 807 808 constexpr optional(nullopt_t) noexcept; 809 810 optional(const optional&) = default; 811 812 optional(optional&&) = default; 813 814 template <typename InPlaceT, typename... Args, 815 absl::enable_if_t<absl::conjunction< 816 std::is_same<InPlaceT, in_place_t>, 817 std::is_constructible<T, Args&&...>>::value>* = nullptr> 818 constexpr explicit optional(InPlaceT, Args&&... args); 819 820 template <typename U, typename... Args, 821 typename = typename std::enable_if<std::is_constructible< 822 T, std::initializer_list<U>&, Args&&...>::value>::type> 823 constexpr explicit optional(in_place_t, std::initializer_list<U> il, 824 Args&&... args); 825 826 template < 827 typename U = T, 828 typename std::enable_if< 829 absl::conjunction<absl::negation<std::is_same< 830 in_place_t, typename std::decay<U>::type>>, 831 absl::negation<std::is_same< 832 optional<T>, typename std::decay<U>::type>>, 833 std::is_convertible<U&&, T>, 834 std::is_constructible<T, U&&>>::value, 835 bool>::type = false> 836 constexpr optional(U&& v); 837 838 template < 839 typename U = T, 840 typename std::enable_if< 841 absl::conjunction<absl::negation<std::is_same< 842 in_place_t, typename std::decay<U>::type>>, 843 absl::negation<std::is_same< 844 optional<T>, typename std::decay<U>::type>>, 845 absl::negation<std::is_convertible<U&&, T>>, 846 std::is_constructible<T, U&&>>::value, 847 bool>::type = false> 848 explicit constexpr optional(U&& v); 849 850 template <typename U, 851 typename std::enable_if< 852 absl::conjunction< 853 absl::negation<std::is_same<T, U>>, 854 std::is_constructible<T, const U&>, 855 absl::negation< 856 optional_internal:: 857 is_constructible_convertible_from_optional<T, U>>, 858 std::is_convertible<const U&, T>>::value, 859 bool>::type = false> 860 optional(const optional<U>& rhs); 861 862 template <typename U, 863 typename std::enable_if< 864 absl::conjunction< 865 absl::negation<std::is_same<T, U>>, 866 std::is_constructible<T, const U&>, 867 absl::negation< 868 optional_internal:: 869 is_constructible_convertible_from_optional<T, U>>, 870 absl::negation<std::is_convertible<const U&, T>>>::value, 871 bool>::type = false> 872 explicit optional(const optional<U>& rhs); 873 874 template < 875 typename U, 876 typename std::enable_if< 877 absl::conjunction< 878 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 879 absl::negation< 880 optional_internal::is_constructible_convertible_from_optional< 881 T, U>>, 882 std::is_convertible<U&&, T>>::value, 883 bool>::type = false> 884 optional(optional<U>&& rhs); 885 886 template < 887 typename U, 888 typename std::enable_if< 889 absl::conjunction< 890 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 891 absl::negation< 892 optional_internal::is_constructible_convertible_from_optional< 893 T, U>>, 894 absl::negation<std::is_convertible<U&&, T>>>::value, 895 bool>::type = false> 896 explicit optional(optional<U>&& rhs); 897 898 optional& operator=(nullopt_t) noexcept; 899 900 optional& operator=(const optional& src); 901 902 optional& operator=(optional&& src); 903 904 template < 905 typename U = T, 906 typename = typename std::enable_if<absl::conjunction< 907 absl::negation< 908 std::is_same<optional<T>, typename std::decay<U>::type>>, 909 absl::negation< 910 absl::conjunction<std::is_scalar<T>, 911 std::is_same<T, typename std::decay<U>::type>>>, 912 std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type> 913 optional& operator=(U&& v); 914 915 template < 916 typename U, 917 typename = typename std::enable_if<absl::conjunction< 918 absl::negation<std::is_same<T, U>>, 919 std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>, 920 absl::negation< 921 optional_internal:: 922 is_constructible_convertible_assignable_from_optional< 923 T, U>>>::value>::type> 924 optional& operator=(const optional<U>& rhs); 925 926 template <typename U, 927 typename = typename std::enable_if<absl::conjunction< 928 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>, 929 std::is_assignable<T&, U>, 930 absl::negation< 931 optional_internal:: 932 is_constructible_convertible_assignable_from_optional< 933 T, U>>>::value>::type> 934 optional& operator=(optional<U>&& rhs); 935 936 const T& operator*() const&; 937 T& operator*() &; 938 const T&& operator*() const&&; 939 T&& operator*() &&; 940 941 const T* operator->() const; 942 T* operator->(); 943 944 const T& value() const&; 945 T& value() &; 946 const T&& value() const&&; 947 T&& value() &&; 948 949 template <typename U> 950 constexpr T value_or(U&& v) const&; 951 template <typename U> 952 T value_or(U&& v) &&; 953 954 template <typename... Args> 955 T& emplace(Args&&... args); 956 957 template <typename U, typename... Args> 958 T& emplace(std::initializer_list<U> ilist, Args&&... args); 959 960 void reset() noexcept; 961 962 constexpr explicit operator bool() const noexcept; 963 constexpr bool has_value() const noexcept; 964 965 void swap(optional& rhs) noexcept; 966 }; 967 968 template <typename T> 969 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 970 971 template <typename T, typename... Args> 972 constexpr optional<T> make_optional(Args&&... args); 973 974 template <typename T, typename U, typename... Args> 975 constexpr optional<T> make_optional(std::initializer_list<U> il, 976 Args&&... args); 977 978 } // namespace absl 979 )"; 980 981 static constexpr char BaseOptionalHeader[] = R"( 982 #include "std_initializer_list.h" 983 #include "std_type_traits.h" 984 #include "std_utility.h" 985 986 namespace base { 987 988 struct in_place_t {}; 989 constexpr in_place_t in_place; 990 991 struct nullopt_t { 992 constexpr explicit nullopt_t() {} 993 }; 994 constexpr nullopt_t nullopt; 995 996 template <typename T> 997 class Optional; 998 999 namespace internal { 1000 1001 template <typename T> 1002 using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>; 1003 1004 template <typename T, typename U> 1005 struct IsConvertibleFromOptional 1006 : std::integral_constant< 1007 bool, std::is_constructible<T, Optional<U>&>::value || 1008 std::is_constructible<T, const Optional<U>&>::value || 1009 std::is_constructible<T, Optional<U>&&>::value || 1010 std::is_constructible<T, const Optional<U>&&>::value || 1011 std::is_convertible<Optional<U>&, T>::value || 1012 std::is_convertible<const Optional<U>&, T>::value || 1013 std::is_convertible<Optional<U>&&, T>::value || 1014 std::is_convertible<const Optional<U>&&, T>::value> {}; 1015 1016 template <typename T, typename U> 1017 struct IsAssignableFromOptional 1018 : std::integral_constant< 1019 bool, IsConvertibleFromOptional<T, U>::value || 1020 std::is_assignable<T&, Optional<U>&>::value || 1021 std::is_assignable<T&, const Optional<U>&>::value || 1022 std::is_assignable<T&, Optional<U>&&>::value || 1023 std::is_assignable<T&, const Optional<U>&&>::value> {}; 1024 1025 } // namespace internal 1026 1027 template <typename T> 1028 class Optional { 1029 public: 1030 using value_type = T; 1031 1032 constexpr Optional() = default; 1033 constexpr Optional(const Optional& other) noexcept = default; 1034 constexpr Optional(Optional&& other) noexcept = default; 1035 1036 constexpr Optional(nullopt_t); 1037 1038 template <typename U, 1039 typename std::enable_if< 1040 std::is_constructible<T, const U&>::value && 1041 !internal::IsConvertibleFromOptional<T, U>::value && 1042 std::is_convertible<const U&, T>::value, 1043 bool>::type = false> 1044 Optional(const Optional<U>& other) noexcept; 1045 1046 template <typename U, 1047 typename std::enable_if< 1048 std::is_constructible<T, const U&>::value && 1049 !internal::IsConvertibleFromOptional<T, U>::value && 1050 !std::is_convertible<const U&, T>::value, 1051 bool>::type = false> 1052 explicit Optional(const Optional<U>& other) noexcept; 1053 1054 template <typename U, 1055 typename std::enable_if< 1056 std::is_constructible<T, U&&>::value && 1057 !internal::IsConvertibleFromOptional<T, U>::value && 1058 std::is_convertible<U&&, T>::value, 1059 bool>::type = false> 1060 Optional(Optional<U>&& other) noexcept; 1061 1062 template <typename U, 1063 typename std::enable_if< 1064 std::is_constructible<T, U&&>::value && 1065 !internal::IsConvertibleFromOptional<T, U>::value && 1066 !std::is_convertible<U&&, T>::value, 1067 bool>::type = false> 1068 explicit Optional(Optional<U>&& other) noexcept; 1069 1070 template <class... Args> 1071 constexpr explicit Optional(in_place_t, Args&&... args); 1072 1073 template <class U, class... Args, 1074 class = typename std::enable_if<std::is_constructible< 1075 value_type, std::initializer_list<U>&, Args...>::value>::type> 1076 constexpr explicit Optional(in_place_t, std::initializer_list<U> il, 1077 Args&&... args); 1078 1079 template < 1080 typename U = value_type, 1081 typename std::enable_if< 1082 std::is_constructible<T, U&&>::value && 1083 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1084 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1085 std::is_convertible<U&&, T>::value, 1086 bool>::type = false> 1087 constexpr Optional(U&& value); 1088 1089 template < 1090 typename U = value_type, 1091 typename std::enable_if< 1092 std::is_constructible<T, U&&>::value && 1093 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1094 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1095 !std::is_convertible<U&&, T>::value, 1096 bool>::type = false> 1097 constexpr explicit Optional(U&& value); 1098 1099 Optional& operator=(const Optional& other) noexcept; 1100 1101 Optional& operator=(Optional&& other) noexcept; 1102 1103 Optional& operator=(nullopt_t); 1104 1105 template <typename U> 1106 typename std::enable_if< 1107 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1108 std::is_constructible<T, U>::value && 1109 std::is_assignable<T&, U>::value && 1110 (!std::is_scalar<T>::value || 1111 !std::is_same<typename std::decay<U>::type, T>::value), 1112 Optional&>::type 1113 operator=(U&& value) noexcept; 1114 1115 template <typename U> 1116 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1117 std::is_constructible<T, const U&>::value && 1118 std::is_assignable<T&, const U&>::value, 1119 Optional&>::type 1120 operator=(const Optional<U>& other) noexcept; 1121 1122 template <typename U> 1123 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1124 std::is_constructible<T, U>::value && 1125 std::is_assignable<T&, U>::value, 1126 Optional&>::type 1127 operator=(Optional<U>&& other) noexcept; 1128 1129 const T& operator*() const&; 1130 T& operator*() &; 1131 const T&& operator*() const&&; 1132 T&& operator*() &&; 1133 1134 const T* operator->() const; 1135 T* operator->(); 1136 1137 const T& value() const&; 1138 T& value() &; 1139 const T&& value() const&&; 1140 T&& value() &&; 1141 1142 template <typename U> 1143 constexpr T value_or(U&& v) const&; 1144 template <typename U> 1145 T value_or(U&& v) &&; 1146 1147 template <typename... Args> 1148 T& emplace(Args&&... args); 1149 1150 template <typename U, typename... Args> 1151 T& emplace(std::initializer_list<U> ilist, Args&&... args); 1152 1153 void reset() noexcept; 1154 1155 constexpr explicit operator bool() const noexcept; 1156 constexpr bool has_value() const noexcept; 1157 1158 void swap(Optional& other); 1159 }; 1160 1161 template <typename T> 1162 constexpr Optional<typename std::decay<T>::type> make_optional(T&& v); 1163 1164 template <typename T, typename... Args> 1165 constexpr Optional<T> make_optional(Args&&... args); 1166 1167 template <typename T, typename U, typename... Args> 1168 constexpr Optional<T> make_optional(std::initializer_list<U> il, 1169 Args&&... args); 1170 1171 } // namespace base 1172 )"; 1173 1174 /// Converts `L` to string. 1175 static std::string ConvertToString(const SourceLocationsLattice &L, 1176 const ASTContext &Ctx) { 1177 return L.getSourceLocations().empty() ? "safe" 1178 : "unsafe: " + DebugString(L, Ctx); 1179 } 1180 1181 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. 1182 static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern, 1183 const std::string &Replacement) { 1184 size_t Pos = 0; 1185 while (true) { 1186 Pos = S.find(Pattern, Pos); 1187 if (Pos == std::string::npos) 1188 break; 1189 S.replace(Pos, Pattern.size(), Replacement); 1190 } 1191 } 1192 1193 struct OptionalTypeIdentifier { 1194 std::string NamespaceName; 1195 std::string TypeName; 1196 }; 1197 1198 class UncheckedOptionalAccessTest 1199 : public ::testing::TestWithParam<OptionalTypeIdentifier> { 1200 protected: 1201 template <typename LatticeChecksMatcher> 1202 void ExpectLatticeChecksFor(std::string SourceCode, 1203 LatticeChecksMatcher MatchesLatticeChecks) { 1204 ExpectLatticeChecksFor(SourceCode, ast_matchers::hasName("target"), 1205 MatchesLatticeChecks); 1206 } 1207 1208 private: 1209 template <typename FuncDeclMatcher, typename LatticeChecksMatcher> 1210 void ExpectLatticeChecksFor(std::string SourceCode, 1211 FuncDeclMatcher FuncMatcher, 1212 LatticeChecksMatcher MatchesLatticeChecks) { 1213 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName); 1214 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName); 1215 1216 std::vector<std::pair<std::string, std::string>> Headers; 1217 Headers.emplace_back("cstddef.h", CSDtdDefHeader); 1218 Headers.emplace_back("std_initializer_list.h", StdInitializerListHeader); 1219 Headers.emplace_back("std_string.h", StdStringHeader); 1220 Headers.emplace_back("std_type_traits.h", StdTypeTraitsHeader); 1221 Headers.emplace_back("std_utility.h", StdUtilityHeader); 1222 Headers.emplace_back("std_optional.h", StdOptionalHeader); 1223 Headers.emplace_back("absl_type_traits.h", AbslTypeTraitsHeader); 1224 Headers.emplace_back("absl_optional.h", AbslOptionalHeader); 1225 Headers.emplace_back("base_optional.h", BaseOptionalHeader); 1226 Headers.emplace_back("unchecked_optional_access_test.h", R"( 1227 #include "absl_optional.h" 1228 #include "base_optional.h" 1229 #include "std_initializer_list.h" 1230 #include "std_optional.h" 1231 #include "std_string.h" 1232 #include "std_utility.h" 1233 1234 template <typename T> 1235 T Make(); 1236 )"); 1237 const tooling::FileContentMappings FileContents(Headers.begin(), 1238 Headers.end()); 1239 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>( 1240 SourceCode, FuncMatcher, 1241 [](ASTContext &Ctx, Environment &) { 1242 return UncheckedOptionalAccessModel( 1243 Ctx, UncheckedOptionalAccessModelOptions{ 1244 /*IgnoreSmartPointerDereference=*/true}); 1245 }, 1246 [&MatchesLatticeChecks]( 1247 llvm::ArrayRef<std::pair< 1248 std::string, DataflowAnalysisState<SourceLocationsLattice>>> 1249 CheckToLatticeMap, 1250 ASTContext &Ctx) { 1251 // FIXME: Consider using a matcher instead of translating 1252 // `CheckToLatticeMap` to `CheckToStringifiedLatticeMap`. 1253 std::vector<std::pair<std::string, std::string>> 1254 CheckToStringifiedLatticeMap; 1255 for (const auto &E : CheckToLatticeMap) { 1256 CheckToStringifiedLatticeMap.emplace_back( 1257 E.first, ConvertToString(E.second.Lattice, Ctx)); 1258 } 1259 EXPECT_THAT(CheckToStringifiedLatticeMap, MatchesLatticeChecks); 1260 }, 1261 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents); 1262 if (Error) 1263 FAIL() << llvm::toString(std::move(Error)); 1264 } 1265 }; 1266 1267 INSTANTIATE_TEST_SUITE_P( 1268 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest, 1269 ::testing::Values(OptionalTypeIdentifier{"std", "optional"}, 1270 OptionalTypeIdentifier{"absl", "optional"}, 1271 OptionalTypeIdentifier{"base", "Optional"}), 1272 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) { 1273 return Info.param.NamespaceName; 1274 }); 1275 1276 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { 1277 ExpectLatticeChecksFor(R"( 1278 void target() { 1279 (void)0; 1280 /*[[check]]*/ 1281 } 1282 )", 1283 UnorderedElementsAre(Pair("check", "safe"))); 1284 } 1285 1286 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { 1287 ExpectLatticeChecksFor( 1288 R"( 1289 #include "unchecked_optional_access_test.h" 1290 1291 void target($ns::$optional<int> opt) { 1292 opt.value(); 1293 /*[[check]]*/ 1294 } 1295 )", 1296 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1297 1298 ExpectLatticeChecksFor( 1299 R"( 1300 #include "unchecked_optional_access_test.h" 1301 1302 void target($ns::$optional<int> opt) { 1303 std::move(opt).value(); 1304 /*[[check]]*/ 1305 } 1306 )", 1307 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1308 } 1309 1310 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { 1311 ExpectLatticeChecksFor( 1312 R"( 1313 #include "unchecked_optional_access_test.h" 1314 1315 void target($ns::$optional<int> opt) { 1316 *opt; 1317 /*[[check]]*/ 1318 } 1319 )", 1320 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); 1321 1322 ExpectLatticeChecksFor( 1323 R"( 1324 #include "unchecked_optional_access_test.h" 1325 1326 void target($ns::$optional<int> opt) { 1327 *std::move(opt); 1328 /*[[check]]*/ 1329 } 1330 )", 1331 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:8"))); 1332 } 1333 1334 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { 1335 ExpectLatticeChecksFor( 1336 R"( 1337 #include "unchecked_optional_access_test.h" 1338 1339 struct Foo { 1340 void foo(); 1341 }; 1342 1343 void target($ns::$optional<Foo> opt) { 1344 opt->foo(); 1345 /*[[check]]*/ 1346 } 1347 )", 1348 UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); 1349 1350 ExpectLatticeChecksFor( 1351 R"( 1352 #include "unchecked_optional_access_test.h" 1353 1354 struct Foo { 1355 void foo(); 1356 }; 1357 1358 void target($ns::$optional<Foo> opt) { 1359 std::move(opt)->foo(); 1360 /*[[check]]*/ 1361 } 1362 )", 1363 UnorderedElementsAre(Pair("check", "unsafe: input.cc:9:7"))); 1364 } 1365 1366 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { 1367 ExpectLatticeChecksFor(R"( 1368 #include "unchecked_optional_access_test.h" 1369 1370 void target($ns::$optional<int> opt) { 1371 if (opt.has_value()) { 1372 opt.value(); 1373 /*[[check]]*/ 1374 } 1375 } 1376 )", 1377 UnorderedElementsAre(Pair("check", "safe"))); 1378 } 1379 1380 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { 1381 ExpectLatticeChecksFor(R"( 1382 #include "unchecked_optional_access_test.h" 1383 1384 void target($ns::$optional<int> opt) { 1385 if (opt) { 1386 opt.value(); 1387 /*[[check]]*/ 1388 } 1389 } 1390 )", 1391 UnorderedElementsAre(Pair("check", "safe"))); 1392 } 1393 1394 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { 1395 ExpectLatticeChecksFor( 1396 R"( 1397 #include "unchecked_optional_access_test.h" 1398 1399 void target() { 1400 Make<$ns::$optional<int>>().value(); 1401 (void)0; 1402 /*[[check]]*/ 1403 } 1404 )", 1405 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1406 1407 ExpectLatticeChecksFor( 1408 R"( 1409 #include "unchecked_optional_access_test.h" 1410 1411 void target($ns::$optional<int> opt) { 1412 std::move(opt).value(); 1413 /*[[check]]*/ 1414 } 1415 )", 1416 UnorderedElementsAre(Pair("check", "unsafe: input.cc:5:7"))); 1417 } 1418 1419 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { 1420 ExpectLatticeChecksFor( 1421 R"( 1422 #include "unchecked_optional_access_test.h" 1423 1424 void target() { 1425 $ns::$optional<int> opt; 1426 opt.value(); 1427 /*[[check]]*/ 1428 } 1429 )", 1430 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1431 } 1432 1433 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { 1434 ExpectLatticeChecksFor( 1435 R"( 1436 #include "unchecked_optional_access_test.h" 1437 1438 void target() { 1439 $ns::$optional<int> opt($ns::nullopt); 1440 opt.value(); 1441 /*[[check]]*/ 1442 } 1443 )", 1444 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 1445 } 1446 1447 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { 1448 ExpectLatticeChecksFor(R"( 1449 #include "unchecked_optional_access_test.h" 1450 1451 void target() { 1452 $ns::$optional<int> opt($ns::in_place, 3); 1453 opt.value(); 1454 /*[[check]]*/ 1455 } 1456 )", 1457 UnorderedElementsAre(Pair("check", "safe"))); 1458 1459 ExpectLatticeChecksFor(R"( 1460 #include "unchecked_optional_access_test.h" 1461 1462 struct Foo {}; 1463 1464 void target() { 1465 $ns::$optional<Foo> opt($ns::in_place); 1466 opt.value(); 1467 /*[[check]]*/ 1468 } 1469 )", 1470 UnorderedElementsAre(Pair("check", "safe"))); 1471 1472 ExpectLatticeChecksFor(R"( 1473 #include "unchecked_optional_access_test.h" 1474 1475 struct Foo { 1476 explicit Foo(int, bool); 1477 }; 1478 1479 void target() { 1480 $ns::$optional<Foo> opt($ns::in_place, 3, false); 1481 opt.value(); 1482 /*[[check]]*/ 1483 } 1484 )", 1485 UnorderedElementsAre(Pair("check", "safe"))); 1486 1487 ExpectLatticeChecksFor(R"( 1488 #include "unchecked_optional_access_test.h" 1489 1490 struct Foo { 1491 explicit Foo(std::initializer_list<int>); 1492 }; 1493 1494 void target() { 1495 $ns::$optional<Foo> opt($ns::in_place, {3}); 1496 opt.value(); 1497 /*[[check]]*/ 1498 } 1499 )", 1500 UnorderedElementsAre(Pair("check", "safe"))); 1501 } 1502 1503 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { 1504 ExpectLatticeChecksFor(R"( 1505 #include "unchecked_optional_access_test.h" 1506 1507 void target() { 1508 $ns::$optional<int> opt(21); 1509 opt.value(); 1510 /*[[check]]*/ 1511 } 1512 )", 1513 UnorderedElementsAre(Pair("check", "safe"))); 1514 1515 ExpectLatticeChecksFor(R"( 1516 #include "unchecked_optional_access_test.h" 1517 1518 void target() { 1519 $ns::$optional<int> opt = $ns::$optional<int>(21); 1520 opt.value(); 1521 /*[[check]]*/ 1522 } 1523 )", 1524 UnorderedElementsAre(Pair("check", "safe"))); 1525 ExpectLatticeChecksFor(R"( 1526 #include "unchecked_optional_access_test.h" 1527 1528 void target() { 1529 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>()); 1530 opt.value(); 1531 /*[[check]]*/ 1532 } 1533 )", 1534 UnorderedElementsAre(Pair("check", "safe"))); 1535 1536 ExpectLatticeChecksFor(R"( 1537 #include "unchecked_optional_access_test.h" 1538 1539 struct MyString { 1540 MyString(const char*); 1541 }; 1542 1543 void target() { 1544 $ns::$optional<MyString> opt("foo"); 1545 opt.value(); 1546 /*[[check]]*/ 1547 } 1548 )", 1549 UnorderedElementsAre(Pair("check", "safe"))); 1550 1551 ExpectLatticeChecksFor(R"( 1552 #include "unchecked_optional_access_test.h" 1553 1554 struct Foo {}; 1555 1556 struct Bar { 1557 Bar(const Foo&); 1558 }; 1559 1560 void target() { 1561 $ns::$optional<Bar> opt(Make<Foo>()); 1562 opt.value(); 1563 /*[[check]]*/ 1564 } 1565 )", 1566 UnorderedElementsAre(Pair("check", "safe"))); 1567 1568 ExpectLatticeChecksFor(R"( 1569 #include "unchecked_optional_access_test.h" 1570 1571 struct Foo { 1572 explicit Foo(int); 1573 }; 1574 1575 void target() { 1576 $ns::$optional<Foo> opt(3); 1577 opt.value(); 1578 /*[[check]]*/ 1579 } 1580 )", 1581 UnorderedElementsAre(Pair("check", "safe"))); 1582 } 1583 1584 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { 1585 ExpectLatticeChecksFor( 1586 R"( 1587 #include "unchecked_optional_access_test.h" 1588 1589 struct Foo {}; 1590 1591 struct Bar { 1592 Bar(const Foo&); 1593 }; 1594 1595 void target() { 1596 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1597 opt.value(); 1598 /*[[check]]*/ 1599 } 1600 )", 1601 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 1602 1603 ExpectLatticeChecksFor( 1604 R"( 1605 #include "unchecked_optional_access_test.h" 1606 1607 struct Foo {}; 1608 1609 struct Bar { 1610 explicit Bar(const Foo&); 1611 }; 1612 1613 void target() { 1614 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1615 opt.value(); 1616 /*[[check]]*/ 1617 } 1618 )", 1619 UnorderedElementsAre(Pair("check", "unsafe: input.cc:12:7"))); 1620 1621 ExpectLatticeChecksFor( 1622 R"( 1623 #include "unchecked_optional_access_test.h" 1624 1625 struct Foo {}; 1626 1627 struct Bar { 1628 Bar(const Foo&); 1629 }; 1630 1631 void target() { 1632 $ns::$optional<Foo> opt1 = $ns::nullopt; 1633 $ns::$optional<Bar> opt2(opt1); 1634 opt2.value(); 1635 /*[[check]]*/ 1636 } 1637 )", 1638 UnorderedElementsAre(Pair("check", "unsafe: input.cc:13:7"))); 1639 1640 ExpectLatticeChecksFor(R"( 1641 #include "unchecked_optional_access_test.h" 1642 1643 struct Foo {}; 1644 1645 struct Bar { 1646 Bar(const Foo&); 1647 }; 1648 1649 void target() { 1650 $ns::$optional<Foo> opt1(Make<Foo>()); 1651 $ns::$optional<Bar> opt2(opt1); 1652 opt2.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 1663 struct Bar { 1664 explicit Bar(const Foo&); 1665 }; 1666 1667 void target() { 1668 $ns::$optional<Foo> opt1(Make<Foo>()); 1669 $ns::$optional<Bar> opt2(opt1); 1670 opt2.value(); 1671 /*[[check]]*/ 1672 } 1673 )", 1674 UnorderedElementsAre(Pair("check", "safe"))); 1675 } 1676 1677 TEST_P(UncheckedOptionalAccessTest, MakeOptional) { 1678 ExpectLatticeChecksFor(R"( 1679 #include "unchecked_optional_access_test.h" 1680 1681 void target() { 1682 $ns::$optional<int> opt = $ns::make_optional(0); 1683 opt.value(); 1684 /*[[check]]*/ 1685 } 1686 )", 1687 UnorderedElementsAre(Pair("check", "safe"))); 1688 1689 ExpectLatticeChecksFor(R"( 1690 #include "unchecked_optional_access_test.h" 1691 1692 struct Foo { 1693 Foo(int, int); 1694 }; 1695 1696 void target() { 1697 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22); 1698 opt.value(); 1699 /*[[check]]*/ 1700 } 1701 )", 1702 UnorderedElementsAre(Pair("check", "safe"))); 1703 1704 ExpectLatticeChecksFor(R"( 1705 #include "unchecked_optional_access_test.h" 1706 1707 struct Foo { 1708 constexpr Foo(std::initializer_list<char>); 1709 }; 1710 1711 void target() { 1712 char a = 'a'; 1713 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a}); 1714 opt.value(); 1715 /*[[check]]*/ 1716 } 1717 )", 1718 UnorderedElementsAre(Pair("check", "safe"))); 1719 } 1720 1721 TEST_P(UncheckedOptionalAccessTest, ValueOr) { 1722 ExpectLatticeChecksFor(R"( 1723 #include "unchecked_optional_access_test.h" 1724 1725 void target() { 1726 $ns::$optional<int> opt; 1727 opt.value_or(0); 1728 (void)0; 1729 /*[[check]]*/ 1730 } 1731 )", 1732 UnorderedElementsAre(Pair("check", "safe"))); 1733 } 1734 1735 TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) { 1736 // Pointers. 1737 ExpectLatticeChecksFor( 1738 R"code( 1739 #include "unchecked_optional_access_test.h" 1740 1741 void target($ns::$optional<int*> opt) { 1742 if (opt.value_or(nullptr) != nullptr) { 1743 opt.value(); 1744 /*[[check-ptrs-1]]*/ 1745 } else { 1746 opt.value(); 1747 /*[[check-ptrs-2]]*/ 1748 } 1749 } 1750 )code", 1751 UnorderedElementsAre(Pair("check-ptrs-1", "safe"), 1752 Pair("check-ptrs-2", "unsafe: input.cc:9:9"))); 1753 1754 // Integers. 1755 ExpectLatticeChecksFor( 1756 R"code( 1757 #include "unchecked_optional_access_test.h" 1758 1759 void target($ns::$optional<int> opt) { 1760 if (opt.value_or(0) != 0) { 1761 opt.value(); 1762 /*[[check-ints-1]]*/ 1763 } else { 1764 opt.value(); 1765 /*[[check-ints-2]]*/ 1766 } 1767 } 1768 )code", 1769 UnorderedElementsAre(Pair("check-ints-1", "safe"), 1770 Pair("check-ints-2", "unsafe: input.cc:9:9"))); 1771 1772 // Strings. 1773 ExpectLatticeChecksFor( 1774 R"code( 1775 #include "unchecked_optional_access_test.h" 1776 1777 void target($ns::$optional<std::string> opt) { 1778 if (!opt.value_or("").empty()) { 1779 opt.value(); 1780 /*[[check-strings-1]]*/ 1781 } else { 1782 opt.value(); 1783 /*[[check-strings-2]]*/ 1784 } 1785 } 1786 )code", 1787 UnorderedElementsAre(Pair("check-strings-1", "safe"), 1788 Pair("check-strings-2", "unsafe: input.cc:9:9"))); 1789 1790 ExpectLatticeChecksFor( 1791 R"code( 1792 #include "unchecked_optional_access_test.h" 1793 1794 void target($ns::$optional<std::string> opt) { 1795 if (opt.value_or("") != "") { 1796 opt.value(); 1797 /*[[check-strings-neq-1]]*/ 1798 } else { 1799 opt.value(); 1800 /*[[check-strings-neq-2]]*/ 1801 } 1802 } 1803 )code", 1804 UnorderedElementsAre( 1805 Pair("check-strings-neq-1", "safe"), 1806 Pair("check-strings-neq-2", "unsafe: input.cc:9:9"))); 1807 1808 // Pointer-to-optional. 1809 // 1810 // FIXME: make `opt` a parameter directly, once we ensure that all `optional` 1811 // values have a `has_value` property. 1812 ExpectLatticeChecksFor( 1813 R"code( 1814 #include "unchecked_optional_access_test.h" 1815 1816 void target($ns::$optional<int> p) { 1817 $ns::$optional<int> *opt = &p; 1818 if (opt->value_or(0) != 0) { 1819 opt->value(); 1820 /*[[check-pto-1]]*/ 1821 } else { 1822 opt->value(); 1823 /*[[check-pto-2]]*/ 1824 } 1825 } 1826 )code", 1827 UnorderedElementsAre(Pair("check-pto-1", "safe"), 1828 Pair("check-pto-2", "unsafe: input.cc:10:9"))); 1829 } 1830 1831 TEST_P(UncheckedOptionalAccessTest, Emplace) { 1832 ExpectLatticeChecksFor(R"( 1833 #include "unchecked_optional_access_test.h" 1834 1835 void target() { 1836 $ns::$optional<int> opt; 1837 opt.emplace(0); 1838 opt.value(); 1839 /*[[check]]*/ 1840 } 1841 )", 1842 UnorderedElementsAre(Pair("check", "safe"))); 1843 1844 ExpectLatticeChecksFor(R"( 1845 #include "unchecked_optional_access_test.h" 1846 1847 void target($ns::$optional<int> *opt) { 1848 opt->emplace(0); 1849 opt->value(); 1850 /*[[check]]*/ 1851 } 1852 )", 1853 UnorderedElementsAre(Pair("check", "safe"))); 1854 1855 // FIXME: Add tests that call `emplace` in conditional branches. 1856 } 1857 1858 TEST_P(UncheckedOptionalAccessTest, Reset) { 1859 ExpectLatticeChecksFor( 1860 R"( 1861 #include "unchecked_optional_access_test.h" 1862 1863 void target() { 1864 $ns::$optional<int> opt = $ns::make_optional(0); 1865 opt.reset(); 1866 opt.value(); 1867 /*[[check]]*/ 1868 } 1869 )", 1870 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); 1871 1872 ExpectLatticeChecksFor( 1873 R"( 1874 #include "unchecked_optional_access_test.h" 1875 1876 void target($ns::$optional<int> &opt) { 1877 if (opt.has_value()) { 1878 opt.reset(); 1879 opt.value(); 1880 /*[[check]]*/ 1881 } 1882 } 1883 )", 1884 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:9"))); 1885 1886 // FIXME: Add tests that call `reset` in conditional branches. 1887 } 1888 1889 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { 1890 ExpectLatticeChecksFor(R"( 1891 #include "unchecked_optional_access_test.h" 1892 1893 struct Foo {}; 1894 1895 void target() { 1896 $ns::$optional<Foo> opt; 1897 opt = Foo(); 1898 opt.value(); 1899 /*[[check]]*/ 1900 } 1901 )", 1902 UnorderedElementsAre(Pair("check", "safe"))); 1903 1904 ExpectLatticeChecksFor(R"( 1905 #include "unchecked_optional_access_test.h" 1906 1907 struct Foo {}; 1908 1909 void target() { 1910 $ns::$optional<Foo> opt; 1911 (opt = Foo()).value(); 1912 (void)0; 1913 /*[[check]]*/ 1914 } 1915 )", 1916 UnorderedElementsAre(Pair("check", "safe"))); 1917 1918 ExpectLatticeChecksFor(R"( 1919 #include "unchecked_optional_access_test.h" 1920 1921 struct MyString { 1922 MyString(const char*); 1923 }; 1924 1925 void target() { 1926 $ns::$optional<MyString> opt; 1927 opt = "foo"; 1928 opt.value(); 1929 /*[[check]]*/ 1930 } 1931 )", 1932 UnorderedElementsAre(Pair("check", "safe"))); 1933 1934 ExpectLatticeChecksFor(R"( 1935 #include "unchecked_optional_access_test.h" 1936 1937 struct MyString { 1938 MyString(const char*); 1939 }; 1940 1941 void target() { 1942 $ns::$optional<MyString> opt; 1943 (opt = "foo").value(); 1944 /*[[check]]*/ 1945 } 1946 )", 1947 UnorderedElementsAre(Pair("check", "safe"))); 1948 } 1949 1950 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { 1951 ExpectLatticeChecksFor( 1952 R"( 1953 #include "unchecked_optional_access_test.h" 1954 1955 struct Foo {}; 1956 1957 struct Bar { 1958 Bar(const Foo&); 1959 }; 1960 1961 void target() { 1962 $ns::$optional<Foo> opt1 = Foo(); 1963 $ns::$optional<Bar> opt2; 1964 opt2 = opt1; 1965 opt2.value(); 1966 /*[[check]]*/ 1967 } 1968 )", 1969 UnorderedElementsAre(Pair("check", "safe"))); 1970 1971 ExpectLatticeChecksFor( 1972 R"( 1973 #include "unchecked_optional_access_test.h" 1974 1975 struct Foo {}; 1976 1977 struct Bar { 1978 Bar(const Foo&); 1979 }; 1980 1981 void target() { 1982 $ns::$optional<Foo> opt1; 1983 $ns::$optional<Bar> opt2; 1984 if (opt2.has_value()) { 1985 opt2 = opt1; 1986 opt2.value(); 1987 /*[[check]]*/ 1988 } 1989 } 1990 )", 1991 UnorderedElementsAre(Pair("check", "unsafe: input.cc:15:9"))); 1992 1993 ExpectLatticeChecksFor( 1994 R"( 1995 #include "unchecked_optional_access_test.h" 1996 1997 struct Foo {}; 1998 1999 struct Bar { 2000 Bar(const Foo&); 2001 }; 2002 2003 void target() { 2004 $ns::$optional<Foo> opt1 = Foo(); 2005 $ns::$optional<Bar> opt2; 2006 (opt2 = opt1).value(); 2007 (void)0; 2008 /*[[check]]*/ 2009 } 2010 )", 2011 UnorderedElementsAre(Pair("check", "safe"))); 2012 } 2013 2014 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { 2015 ExpectLatticeChecksFor( 2016 R"( 2017 #include "unchecked_optional_access_test.h" 2018 2019 void target() { 2020 $ns::$optional<int> opt = 3; 2021 opt = $ns::nullopt; 2022 opt.value(); 2023 /*[[check]]*/ 2024 } 2025 )", 2026 UnorderedElementsAre(Pair("check", "unsafe: input.cc:7:7"))); 2027 2028 ExpectLatticeChecksFor( 2029 R"( 2030 #include "unchecked_optional_access_test.h" 2031 2032 void target() { 2033 $ns::$optional<int> opt = 3; 2034 (opt = $ns::nullopt).value(); 2035 /*[[check]]*/ 2036 } 2037 )", 2038 UnorderedElementsAre(Pair("check", "unsafe: input.cc:6:7"))); 2039 } 2040 2041 TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { 2042 ExpectLatticeChecksFor( 2043 R"( 2044 #include "unchecked_optional_access_test.h" 2045 2046 void target() { 2047 $ns::$optional<int> opt1 = $ns::nullopt; 2048 $ns::$optional<int> opt2 = 3; 2049 2050 opt1.swap(opt2); 2051 2052 opt1.value(); 2053 /*[[check-1]]*/ 2054 2055 opt2.value(); 2056 /*[[check-2]]*/ 2057 } 2058 )", 2059 UnorderedElementsAre(Pair("check-1", "safe"), 2060 Pair("check-2", "unsafe: input.cc:13:7"))); 2061 2062 ExpectLatticeChecksFor( 2063 R"( 2064 #include "unchecked_optional_access_test.h" 2065 2066 void target() { 2067 $ns::$optional<int> opt1 = $ns::nullopt; 2068 $ns::$optional<int> opt2 = 3; 2069 2070 opt2.swap(opt1); 2071 2072 opt1.value(); 2073 /*[[check-3]]*/ 2074 2075 opt2.value(); 2076 /*[[check-4]]*/ 2077 } 2078 )", 2079 UnorderedElementsAre(Pair("check-3", "safe"), 2080 Pair("check-4", "unsafe: input.cc:13:7"))); 2081 } 2082 2083 TEST_P(UncheckedOptionalAccessTest, StdSwap) { 2084 ExpectLatticeChecksFor( 2085 R"( 2086 #include "unchecked_optional_access_test.h" 2087 2088 void target() { 2089 $ns::$optional<int> opt1 = $ns::nullopt; 2090 $ns::$optional<int> opt2 = 3; 2091 2092 std::swap(opt1, opt2); 2093 2094 opt1.value(); 2095 /*[[check-1]]*/ 2096 2097 opt2.value(); 2098 /*[[check-2]]*/ 2099 } 2100 )", 2101 UnorderedElementsAre(Pair("check-1", "safe"), 2102 Pair("check-2", "unsafe: input.cc:13:7"))); 2103 2104 ExpectLatticeChecksFor( 2105 R"( 2106 #include "unchecked_optional_access_test.h" 2107 2108 void target() { 2109 $ns::$optional<int> opt1 = $ns::nullopt; 2110 $ns::$optional<int> opt2 = 3; 2111 2112 std::swap(opt2, opt1); 2113 2114 opt1.value(); 2115 /*[[check-3]]*/ 2116 2117 opt2.value(); 2118 /*[[check-4]]*/ 2119 } 2120 )", 2121 UnorderedElementsAre(Pair("check-3", "safe"), 2122 Pair("check-4", "unsafe: input.cc:13:7"))); 2123 } 2124 2125 TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { 2126 // We suppress diagnostics for values reachable from smart pointers (other 2127 // than `optional` itself). 2128 ExpectLatticeChecksFor( 2129 R"( 2130 #include "unchecked_optional_access_test.h" 2131 2132 template <typename T> 2133 struct smart_ptr { 2134 T& operator*() &; 2135 T* operator->(); 2136 }; 2137 2138 struct Foo { 2139 $ns::$optional<int> opt; 2140 }; 2141 2142 void target() { 2143 smart_ptr<Foo> foo; 2144 *foo->opt; 2145 /*[[check-1]]*/ 2146 *(*foo).opt; 2147 /*[[check-2]]*/ 2148 } 2149 )", 2150 UnorderedElementsAre(Pair("check-1", "safe"), Pair("check-2", "safe"))); 2151 } 2152 2153 TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { 2154 ExpectLatticeChecksFor( 2155 R"( 2156 #include "unchecked_optional_access_test.h" 2157 2158 $ns::$optional<int> MakeOpt(); 2159 2160 void target() { 2161 $ns::$optional<int> opt = 0; 2162 opt = MakeOpt(); 2163 opt.value(); 2164 /*[[check-1]]*/ 2165 } 2166 )", 2167 UnorderedElementsAre(Pair("check-1", "unsafe: input.cc:9:7"))); 2168 2169 ExpectLatticeChecksFor( 2170 R"( 2171 #include "unchecked_optional_access_test.h" 2172 2173 const $ns::$optional<int>& MakeOpt(); 2174 2175 void target() { 2176 $ns::$optional<int> opt = 0; 2177 opt = MakeOpt(); 2178 opt.value(); 2179 /*[[check-2]]*/ 2180 } 2181 )", 2182 UnorderedElementsAre(Pair("check-2", "unsafe: input.cc:9:7"))); 2183 2184 ExpectLatticeChecksFor( 2185 R"( 2186 #include "unchecked_optional_access_test.h" 2187 2188 using IntOpt = $ns::$optional<int>; 2189 IntOpt MakeOpt(); 2190 2191 void target() { 2192 IntOpt opt = 0; 2193 opt = MakeOpt(); 2194 opt.value(); 2195 /*[[check-3]]*/ 2196 } 2197 )", 2198 UnorderedElementsAre(Pair("check-3", "unsafe: input.cc:10:7"))); 2199 2200 ExpectLatticeChecksFor( 2201 R"( 2202 #include "unchecked_optional_access_test.h" 2203 2204 using IntOpt = $ns::$optional<int>; 2205 const IntOpt& MakeOpt(); 2206 2207 void target() { 2208 IntOpt opt = 0; 2209 opt = MakeOpt(); 2210 opt.value(); 2211 /*[[check-4]]*/ 2212 } 2213 )", 2214 UnorderedElementsAre(Pair("check-4", "unsafe: input.cc:10:7"))); 2215 } 2216 2217 // FIXME: Add support for: 2218 // - constructors (copy, move) 2219 // - assignment operators (default, copy, move) 2220 // - invalidation (passing optional by non-const reference/pointer) 2221 // - nested `optional` values 2222