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