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