1 //===- UncheckedOptionalAccessModelTest.cpp -------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // FIXME: Move this to clang/unittests/Analysis/FlowSensitive/Models. 9 10 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h" 11 #include "TestingSupport.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/ASTMatchers/ASTMatchers.h" 14 #include "clang/Analysis/FlowSensitive/TypeErasedDataflowAnalysis.h" 15 #include "clang/Basic/SourceLocation.h" 16 #include "clang/Tooling/Tooling.h" 17 #include "llvm/ADT/ArrayRef.h" 18 #include "llvm/ADT/DenseSet.h" 19 #include "llvm/ADT/STLExtras.h" 20 #include "llvm/ADT/StringExtras.h" 21 #include "llvm/Support/Error.h" 22 #include "gmock/gmock.h" 23 #include "gtest/gtest.h" 24 #include <string> 25 #include <utility> 26 #include <vector> 27 28 using namespace clang; 29 using namespace dataflow; 30 using namespace test; 31 32 using ::testing::ContainerEq; 33 34 // FIXME: Move header definitions in separate file(s). 35 static constexpr char CSDtdDefHeader[] = R"( 36 #ifndef CSTDDEF_H 37 #define CSTDDEF_H 38 39 namespace std { 40 41 typedef decltype(sizeof(char)) size_t; 42 43 using nullptr_t = decltype(nullptr); 44 45 } // namespace std 46 47 #endif // CSTDDEF_H 48 )"; 49 50 static constexpr char StdTypeTraitsHeader[] = R"( 51 #ifndef STD_TYPE_TRAITS_H 52 #define STD_TYPE_TRAITS_H 53 54 #include "cstddef.h" 55 56 namespace std { 57 58 template <typename T, T V> 59 struct integral_constant { 60 static constexpr T value = V; 61 }; 62 63 using true_type = integral_constant<bool, true>; 64 using false_type = integral_constant<bool, false>; 65 66 template< class T > struct remove_reference {typedef T type;}; 67 template< class T > struct remove_reference<T&> {typedef T type;}; 68 template< class T > struct remove_reference<T&&> {typedef T type;}; 69 70 template <class T> 71 using remove_reference_t = typename remove_reference<T>::type; 72 73 template <class T> 74 struct remove_extent { 75 typedef T type; 76 }; 77 78 template <class T> 79 struct remove_extent<T[]> { 80 typedef T type; 81 }; 82 83 template <class T, size_t N> 84 struct remove_extent<T[N]> { 85 typedef T type; 86 }; 87 88 template <class T> 89 struct is_array : false_type {}; 90 91 template <class T> 92 struct is_array<T[]> : true_type {}; 93 94 template <class T, size_t N> 95 struct is_array<T[N]> : true_type {}; 96 97 template <class> 98 struct is_function : false_type {}; 99 100 template <class Ret, class... Args> 101 struct is_function<Ret(Args...)> : true_type {}; 102 103 namespace detail { 104 105 template <class T> 106 struct type_identity { 107 using type = T; 108 }; // or use type_identity (since C++20) 109 110 template <class T> 111 auto try_add_pointer(int) -> type_identity<typename remove_reference<T>::type*>; 112 template <class T> 113 auto try_add_pointer(...) -> type_identity<T>; 114 115 } // namespace detail 116 117 template <class T> 118 struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {}; 119 120 template <bool B, class T, class F> 121 struct conditional { 122 typedef T type; 123 }; 124 125 template <class T, class F> 126 struct conditional<false, T, F> { 127 typedef F type; 128 }; 129 130 template <class T> 131 struct remove_cv { 132 typedef T type; 133 }; 134 template <class T> 135 struct remove_cv<const T> { 136 typedef T type; 137 }; 138 template <class T> 139 struct remove_cv<volatile T> { 140 typedef T type; 141 }; 142 template <class T> 143 struct remove_cv<const volatile T> { 144 typedef T type; 145 }; 146 147 template <class T> 148 using remove_cv_t = typename remove_cv<T>::type; 149 150 template <class T> 151 struct decay { 152 private: 153 typedef typename remove_reference<T>::type U; 154 155 public: 156 typedef typename conditional< 157 is_array<U>::value, typename remove_extent<U>::type*, 158 typename conditional<is_function<U>::value, typename add_pointer<U>::type, 159 typename remove_cv<U>::type>::type>::type type; 160 }; 161 162 template <bool B, class T = void> 163 struct enable_if {}; 164 165 template <class T> 166 struct enable_if<true, T> { 167 typedef T type; 168 }; 169 170 template <bool B, class T = void> 171 using enable_if_t = typename enable_if<B, T>::type; 172 173 template <class T, class U> 174 struct is_same : false_type {}; 175 176 template <class T> 177 struct is_same<T, T> : true_type {}; 178 179 template <class T> 180 struct is_void : is_same<void, typename remove_cv<T>::type> {}; 181 182 namespace detail { 183 184 template <class T> 185 auto try_add_lvalue_reference(int) -> type_identity<T&>; 186 template <class T> 187 auto try_add_lvalue_reference(...) -> type_identity<T>; 188 189 template <class T> 190 auto try_add_rvalue_reference(int) -> type_identity<T&&>; 191 template <class T> 192 auto try_add_rvalue_reference(...) -> type_identity<T>; 193 194 } // namespace detail 195 196 template <class T> 197 struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) { 198 }; 199 200 template <class T> 201 struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) { 202 }; 203 204 template <class T> 205 typename add_rvalue_reference<T>::type declval() noexcept; 206 207 namespace detail { 208 209 template <class T> 210 auto test_returnable(int) 211 -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{}); 212 template <class> 213 auto test_returnable(...) -> false_type; 214 215 template <class From, class To> 216 auto test_implicitly_convertible(int) 217 -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{}); 218 template <class, class> 219 auto test_implicitly_convertible(...) -> false_type; 220 221 } // namespace detail 222 223 template <class From, class To> 224 struct is_convertible 225 : integral_constant<bool, 226 (decltype(detail::test_returnable<To>(0))::value && 227 decltype(detail::test_implicitly_convertible<From, To>( 228 0))::value) || 229 (is_void<From>::value && is_void<To>::value)> {}; 230 231 template <class From, class To> 232 inline constexpr bool is_convertible_v = is_convertible<From, To>::value; 233 234 template <class...> 235 using void_t = void; 236 237 template <class, class T, class... Args> 238 struct is_constructible_ : false_type {}; 239 240 template <class T, class... Args> 241 struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...> 242 : true_type {}; 243 244 template <class T, class... Args> 245 using is_constructible = is_constructible_<void_t<>, T, Args...>; 246 247 template <class T, class... Args> 248 inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value; 249 250 template <class _Tp> 251 struct __uncvref { 252 typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type; 253 }; 254 255 template <class _Tp> 256 using __uncvref_t = typename __uncvref<_Tp>::type; 257 258 template <bool _Val> 259 using _BoolConstant = integral_constant<bool, _Val>; 260 261 template <class _Tp, class _Up> 262 using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>; 263 264 template <class _Tp, class _Up> 265 using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>; 266 267 template <bool> 268 struct _MetaBase; 269 template <> 270 struct _MetaBase<true> { 271 template <class _Tp, class _Up> 272 using _SelectImpl = _Tp; 273 template <template <class...> class _FirstFn, template <class...> class, 274 class... _Args> 275 using _SelectApplyImpl = _FirstFn<_Args...>; 276 template <class _First, class...> 277 using _FirstImpl = _First; 278 template <class, class _Second, class...> 279 using _SecondImpl = _Second; 280 template <class _Result, class _First, class... _Rest> 281 using _OrImpl = 282 typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>:: 283 template _OrImpl<_First, _Rest...>; 284 }; 285 286 template <> 287 struct _MetaBase<false> { 288 template <class _Tp, class _Up> 289 using _SelectImpl = _Up; 290 template <template <class...> class, template <class...> class _SecondFn, 291 class... _Args> 292 using _SelectApplyImpl = _SecondFn<_Args...>; 293 template <class _Result, class...> 294 using _OrImpl = _Result; 295 }; 296 297 template <bool _Cond, class _IfRes, class _ElseRes> 298 using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>; 299 300 template <class... _Rest> 301 using _Or = typename _MetaBase<sizeof...(_Rest) != 302 0>::template _OrImpl<false_type, _Rest...>; 303 304 template <bool _Bp, class _Tp = void> 305 using __enable_if_t = typename enable_if<_Bp, _Tp>::type; 306 307 template <class...> 308 using __expand_to_true = true_type; 309 template <class... _Pred> 310 __expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int); 311 template <class...> 312 false_type __and_helper(...); 313 template <class... _Pred> 314 using _And = decltype(__and_helper<_Pred...>(0)); 315 316 template <class _Pred> 317 struct _Not : _BoolConstant<!_Pred::value> {}; 318 319 struct __check_tuple_constructor_fail { 320 static constexpr bool __enable_explicit_default() { return false; } 321 static constexpr bool __enable_implicit_default() { return false; } 322 template <class...> 323 static constexpr bool __enable_explicit() { 324 return false; 325 } 326 template <class...> 327 static constexpr bool __enable_implicit() { 328 return false; 329 } 330 }; 331 332 template <typename, typename _Tp> 333 struct __select_2nd { 334 typedef _Tp type; 335 }; 336 template <class _Tp, class _Arg> 337 typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())), 338 true_type>::type 339 __is_assignable_test(int); 340 template <class, class> 341 false_type __is_assignable_test(...); 342 template <class _Tp, class _Arg, 343 bool = is_void<_Tp>::value || is_void<_Arg>::value> 344 struct __is_assignable_imp 345 : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {}; 346 template <class _Tp, class _Arg> 347 struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {}; 348 template <class _Tp, class _Arg> 349 struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {}; 350 351 template <class _Tp> 352 struct __libcpp_is_integral : public false_type {}; 353 template <> 354 struct __libcpp_is_integral<bool> : public true_type {}; 355 template <> 356 struct __libcpp_is_integral<char> : public true_type {}; 357 template <> 358 struct __libcpp_is_integral<signed char> : public true_type {}; 359 template <> 360 struct __libcpp_is_integral<unsigned char> : public true_type {}; 361 template <> 362 struct __libcpp_is_integral<wchar_t> : public true_type {}; 363 template <> 364 struct __libcpp_is_integral<short> : public true_type {}; // NOLINT 365 template <> 366 struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT 367 template <> 368 struct __libcpp_is_integral<int> : public true_type {}; 369 template <> 370 struct __libcpp_is_integral<unsigned int> : public true_type {}; 371 template <> 372 struct __libcpp_is_integral<long> : public true_type {}; // NOLINT 373 template <> 374 struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT 375 template <> 376 struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT 377 template <> // NOLINTNEXTLINE 378 struct __libcpp_is_integral<unsigned long long> : public true_type {}; 379 template <class _Tp> 380 struct is_integral 381 : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {}; 382 383 template <class _Tp> 384 struct __libcpp_is_floating_point : public false_type {}; 385 template <> 386 struct __libcpp_is_floating_point<float> : public true_type {}; 387 template <> 388 struct __libcpp_is_floating_point<double> : public true_type {}; 389 template <> 390 struct __libcpp_is_floating_point<long double> : public true_type {}; 391 template <class _Tp> 392 struct is_floating_point 393 : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {}; 394 395 template <class _Tp> 396 struct is_arithmetic 397 : public integral_constant<bool, is_integral<_Tp>::value || 398 is_floating_point<_Tp>::value> {}; 399 400 template <class _Tp> 401 struct __libcpp_is_pointer : public false_type {}; 402 template <class _Tp> 403 struct __libcpp_is_pointer<_Tp*> : public true_type {}; 404 template <class _Tp> 405 struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> { 406 }; 407 408 template <class _Tp> 409 struct __libcpp_is_member_pointer : public false_type {}; 410 template <class _Tp, class _Up> 411 struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {}; 412 template <class _Tp> 413 struct is_member_pointer 414 : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {}; 415 416 template <class _Tp> 417 struct __libcpp_union : public false_type {}; 418 template <class _Tp> 419 struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {}; 420 421 template <class T> 422 struct is_reference : false_type {}; 423 template <class T> 424 struct is_reference<T&> : true_type {}; 425 template <class T> 426 struct is_reference<T&&> : true_type {}; 427 428 template <class T> 429 inline constexpr bool is_reference_v = is_reference<T>::value; 430 431 struct __two { 432 char __lx[2]; 433 }; 434 435 namespace __is_class_imp { 436 template <class _Tp> 437 char __test(int _Tp::*); 438 template <class _Tp> 439 __two __test(...); 440 } // namespace __is_class_imp 441 template <class _Tp> 442 struct is_class 443 : public integral_constant<bool, 444 sizeof(__is_class_imp::__test<_Tp>(0)) == 1 && 445 !is_union<_Tp>::value> {}; 446 447 template <class _Tp> 448 struct __is_nullptr_t_impl : public false_type {}; 449 template <> 450 struct __is_nullptr_t_impl<nullptr_t> : public true_type {}; 451 template <class _Tp> 452 struct __is_nullptr_t 453 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; 454 template <class _Tp> 455 struct is_null_pointer 456 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {}; 457 458 template <class _Tp> 459 struct is_enum 460 : public integral_constant< 461 bool, !is_void<_Tp>::value && !is_integral<_Tp>::value && 462 !is_floating_point<_Tp>::value && !is_array<_Tp>::value && 463 !is_pointer<_Tp>::value && !is_reference<_Tp>::value && 464 !is_member_pointer<_Tp>::value && !is_union<_Tp>::value && 465 !is_class<_Tp>::value && !is_function<_Tp>::value> {}; 466 467 template <class _Tp> 468 struct is_scalar 469 : public integral_constant< 470 bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value || 471 is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value || 472 is_enum<_Tp>::value> {}; 473 template <> 474 struct is_scalar<nullptr_t> : public true_type {}; 475 476 } // namespace std 477 478 #endif // STD_TYPE_TRAITS_H 479 )"; 480 481 static constexpr char AbslTypeTraitsHeader[] = R"( 482 #ifndef ABSL_TYPE_TRAITS_H 483 #define ABSL_TYPE_TRAITS_H 484 485 #include "std_type_traits.h" 486 487 namespace absl { 488 489 template <typename... Ts> 490 struct conjunction : std::true_type {}; 491 492 template <typename T, typename... Ts> 493 struct conjunction<T, Ts...> 494 : std::conditional<T::value, conjunction<Ts...>, T>::type {}; 495 496 template <typename T> 497 struct conjunction<T> : T {}; 498 499 template <typename T> 500 struct negation : std::integral_constant<bool, !T::value> {}; 501 502 template <bool B, typename T = void> 503 using enable_if_t = typename std::enable_if<B, T>::type; 504 505 } // namespace absl 506 507 #endif // ABSL_TYPE_TRAITS_H 508 )"; 509 510 static constexpr char StdStringHeader[] = R"( 511 #ifndef STRING_H 512 #define STRING_H 513 514 namespace std { 515 516 struct string { 517 string(const char*); 518 ~string(); 519 bool empty(); 520 }; 521 bool operator!=(const string &LHS, const char *RHS); 522 523 } // namespace std 524 525 #endif // STRING_H 526 )"; 527 528 static constexpr char StdUtilityHeader[] = R"( 529 #ifndef UTILITY_H 530 #define UTILITY_H 531 532 #include "std_type_traits.h" 533 534 namespace std { 535 536 template <typename T> 537 constexpr remove_reference_t<T>&& move(T&& x); 538 539 template <typename T> 540 void swap(T& a, T& b) noexcept; 541 542 } // namespace std 543 544 #endif // UTILITY_H 545 )"; 546 547 static constexpr char StdInitializerListHeader[] = R"( 548 #ifndef INITIALIZER_LIST_H 549 #define INITIALIZER_LIST_H 550 551 namespace std { 552 553 template <typename T> 554 class initializer_list { 555 public: 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 } // namespace std 768 )"; 769 770 static constexpr char AbslOptionalHeader[] = R"( 771 #include "absl_type_traits.h" 772 #include "std_initializer_list.h" 773 #include "std_type_traits.h" 774 #include "std_utility.h" 775 776 namespace absl { 777 778 struct nullopt_t { 779 constexpr explicit nullopt_t() {} 780 }; 781 constexpr nullopt_t nullopt; 782 783 struct in_place_t {}; 784 constexpr in_place_t in_place; 785 786 template <typename T> 787 class optional; 788 789 namespace optional_internal { 790 791 template <typename T, typename U> 792 struct is_constructible_convertible_from_optional 793 : std::integral_constant< 794 bool, std::is_constructible<T, optional<U>&>::value || 795 std::is_constructible<T, optional<U>&&>::value || 796 std::is_constructible<T, const optional<U>&>::value || 797 std::is_constructible<T, const optional<U>&&>::value || 798 std::is_convertible<optional<U>&, T>::value || 799 std::is_convertible<optional<U>&&, T>::value || 800 std::is_convertible<const optional<U>&, T>::value || 801 std::is_convertible<const optional<U>&&, T>::value> {}; 802 803 template <typename T, typename U> 804 struct is_constructible_convertible_assignable_from_optional 805 : std::integral_constant< 806 bool, is_constructible_convertible_from_optional<T, U>::value || 807 std::is_assignable<T&, optional<U>&>::value || 808 std::is_assignable<T&, optional<U>&&>::value || 809 std::is_assignable<T&, const optional<U>&>::value || 810 std::is_assignable<T&, const optional<U>&&>::value> {}; 811 812 } // namespace optional_internal 813 814 template <typename T> 815 class optional { 816 public: 817 constexpr optional() noexcept; 818 819 constexpr optional(nullopt_t) noexcept; 820 821 optional(const optional&) = default; 822 823 optional(optional&&) = default; 824 825 template <typename InPlaceT, typename... Args, 826 absl::enable_if_t<absl::conjunction< 827 std::is_same<InPlaceT, in_place_t>, 828 std::is_constructible<T, Args&&...>>::value>* = nullptr> 829 constexpr explicit optional(InPlaceT, Args&&... args); 830 831 template <typename U, typename... Args, 832 typename = typename std::enable_if<std::is_constructible< 833 T, std::initializer_list<U>&, Args&&...>::value>::type> 834 constexpr explicit optional(in_place_t, std::initializer_list<U> il, 835 Args&&... args); 836 837 template < 838 typename U = T, 839 typename std::enable_if< 840 absl::conjunction<absl::negation<std::is_same< 841 in_place_t, typename std::decay<U>::type>>, 842 absl::negation<std::is_same< 843 optional<T>, typename std::decay<U>::type>>, 844 std::is_convertible<U&&, T>, 845 std::is_constructible<T, U&&>>::value, 846 bool>::type = false> 847 constexpr optional(U&& v); 848 849 template < 850 typename U = T, 851 typename std::enable_if< 852 absl::conjunction<absl::negation<std::is_same< 853 in_place_t, typename std::decay<U>::type>>, 854 absl::negation<std::is_same< 855 optional<T>, typename std::decay<U>::type>>, 856 absl::negation<std::is_convertible<U&&, T>>, 857 std::is_constructible<T, U&&>>::value, 858 bool>::type = false> 859 explicit constexpr optional(U&& v); 860 861 template <typename U, 862 typename std::enable_if< 863 absl::conjunction< 864 absl::negation<std::is_same<T, U>>, 865 std::is_constructible<T, const U&>, 866 absl::negation< 867 optional_internal:: 868 is_constructible_convertible_from_optional<T, U>>, 869 std::is_convertible<const U&, T>>::value, 870 bool>::type = false> 871 optional(const optional<U>& rhs); 872 873 template <typename U, 874 typename std::enable_if< 875 absl::conjunction< 876 absl::negation<std::is_same<T, U>>, 877 std::is_constructible<T, const U&>, 878 absl::negation< 879 optional_internal:: 880 is_constructible_convertible_from_optional<T, U>>, 881 absl::negation<std::is_convertible<const U&, T>>>::value, 882 bool>::type = false> 883 explicit optional(const optional<U>& rhs); 884 885 template < 886 typename U, 887 typename std::enable_if< 888 absl::conjunction< 889 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 890 absl::negation< 891 optional_internal::is_constructible_convertible_from_optional< 892 T, U>>, 893 std::is_convertible<U&&, T>>::value, 894 bool>::type = false> 895 optional(optional<U>&& rhs); 896 897 template < 898 typename U, 899 typename std::enable_if< 900 absl::conjunction< 901 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>, 902 absl::negation< 903 optional_internal::is_constructible_convertible_from_optional< 904 T, U>>, 905 absl::negation<std::is_convertible<U&&, T>>>::value, 906 bool>::type = false> 907 explicit optional(optional<U>&& rhs); 908 909 optional& operator=(nullopt_t) noexcept; 910 911 optional& operator=(const optional& src); 912 913 optional& operator=(optional&& src); 914 915 template < 916 typename U = T, 917 typename = typename std::enable_if<absl::conjunction< 918 absl::negation< 919 std::is_same<optional<T>, typename std::decay<U>::type>>, 920 absl::negation< 921 absl::conjunction<std::is_scalar<T>, 922 std::is_same<T, typename std::decay<U>::type>>>, 923 std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type> 924 optional& operator=(U&& v); 925 926 template < 927 typename U, 928 typename = typename std::enable_if<absl::conjunction< 929 absl::negation<std::is_same<T, U>>, 930 std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>, 931 absl::negation< 932 optional_internal:: 933 is_constructible_convertible_assignable_from_optional< 934 T, U>>>::value>::type> 935 optional& operator=(const optional<U>& rhs); 936 937 template <typename U, 938 typename = typename std::enable_if<absl::conjunction< 939 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>, 940 std::is_assignable<T&, U>, 941 absl::negation< 942 optional_internal:: 943 is_constructible_convertible_assignable_from_optional< 944 T, U>>>::value>::type> 945 optional& operator=(optional<U>&& rhs); 946 947 const T& operator*() const&; 948 T& operator*() &; 949 const T&& operator*() const&&; 950 T&& operator*() &&; 951 952 const T* operator->() const; 953 T* operator->(); 954 955 const T& value() const&; 956 T& value() &; 957 const T&& value() const&&; 958 T&& value() &&; 959 960 template <typename U> 961 constexpr T value_or(U&& v) const&; 962 template <typename U> 963 T value_or(U&& v) &&; 964 965 template <typename... Args> 966 T& emplace(Args&&... args); 967 968 template <typename U, typename... Args> 969 T& emplace(std::initializer_list<U> ilist, Args&&... args); 970 971 void reset() noexcept; 972 973 constexpr explicit operator bool() const noexcept; 974 constexpr bool has_value() const noexcept; 975 976 void swap(optional& rhs) noexcept; 977 }; 978 979 template <typename T> 980 constexpr optional<typename std::decay<T>::type> make_optional(T&& v); 981 982 template <typename T, typename... Args> 983 constexpr optional<T> make_optional(Args&&... args); 984 985 template <typename T, typename U, typename... Args> 986 constexpr optional<T> make_optional(std::initializer_list<U> il, 987 Args&&... args); 988 989 } // namespace absl 990 )"; 991 992 static constexpr char BaseOptionalHeader[] = R"( 993 #include "std_initializer_list.h" 994 #include "std_type_traits.h" 995 #include "std_utility.h" 996 997 namespace base { 998 999 struct in_place_t {}; 1000 constexpr in_place_t in_place; 1001 1002 struct nullopt_t { 1003 constexpr explicit nullopt_t() {} 1004 }; 1005 constexpr nullopt_t nullopt; 1006 1007 template <typename T> 1008 class Optional; 1009 1010 namespace internal { 1011 1012 template <typename T> 1013 using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>; 1014 1015 template <typename T, typename U> 1016 struct IsConvertibleFromOptional 1017 : std::integral_constant< 1018 bool, std::is_constructible<T, Optional<U>&>::value || 1019 std::is_constructible<T, const Optional<U>&>::value || 1020 std::is_constructible<T, Optional<U>&&>::value || 1021 std::is_constructible<T, const Optional<U>&&>::value || 1022 std::is_convertible<Optional<U>&, T>::value || 1023 std::is_convertible<const Optional<U>&, T>::value || 1024 std::is_convertible<Optional<U>&&, T>::value || 1025 std::is_convertible<const Optional<U>&&, T>::value> {}; 1026 1027 template <typename T, typename U> 1028 struct IsAssignableFromOptional 1029 : std::integral_constant< 1030 bool, IsConvertibleFromOptional<T, U>::value || 1031 std::is_assignable<T&, Optional<U>&>::value || 1032 std::is_assignable<T&, const Optional<U>&>::value || 1033 std::is_assignable<T&, Optional<U>&&>::value || 1034 std::is_assignable<T&, const Optional<U>&&>::value> {}; 1035 1036 } // namespace internal 1037 1038 template <typename T> 1039 class Optional { 1040 public: 1041 using value_type = T; 1042 1043 constexpr Optional() = default; 1044 constexpr Optional(const Optional& other) noexcept = default; 1045 constexpr Optional(Optional&& other) noexcept = default; 1046 1047 constexpr Optional(nullopt_t); 1048 1049 template <typename U, 1050 typename std::enable_if< 1051 std::is_constructible<T, const U&>::value && 1052 !internal::IsConvertibleFromOptional<T, U>::value && 1053 std::is_convertible<const U&, T>::value, 1054 bool>::type = false> 1055 Optional(const Optional<U>& other) noexcept; 1056 1057 template <typename U, 1058 typename std::enable_if< 1059 std::is_constructible<T, const U&>::value && 1060 !internal::IsConvertibleFromOptional<T, U>::value && 1061 !std::is_convertible<const U&, T>::value, 1062 bool>::type = false> 1063 explicit Optional(const Optional<U>& other) noexcept; 1064 1065 template <typename U, 1066 typename std::enable_if< 1067 std::is_constructible<T, U&&>::value && 1068 !internal::IsConvertibleFromOptional<T, U>::value && 1069 std::is_convertible<U&&, T>::value, 1070 bool>::type = false> 1071 Optional(Optional<U>&& other) noexcept; 1072 1073 template <typename U, 1074 typename std::enable_if< 1075 std::is_constructible<T, U&&>::value && 1076 !internal::IsConvertibleFromOptional<T, U>::value && 1077 !std::is_convertible<U&&, T>::value, 1078 bool>::type = false> 1079 explicit Optional(Optional<U>&& other) noexcept; 1080 1081 template <class... Args> 1082 constexpr explicit Optional(in_place_t, Args&&... args); 1083 1084 template <class U, class... Args, 1085 class = typename std::enable_if<std::is_constructible< 1086 value_type, std::initializer_list<U>&, Args...>::value>::type> 1087 constexpr explicit Optional(in_place_t, std::initializer_list<U> il, 1088 Args&&... args); 1089 1090 template < 1091 typename U = value_type, 1092 typename std::enable_if< 1093 std::is_constructible<T, U&&>::value && 1094 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1095 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1096 std::is_convertible<U&&, T>::value, 1097 bool>::type = false> 1098 constexpr Optional(U&& value); 1099 1100 template < 1101 typename U = value_type, 1102 typename std::enable_if< 1103 std::is_constructible<T, U&&>::value && 1104 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value && 1105 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1106 !std::is_convertible<U&&, T>::value, 1107 bool>::type = false> 1108 constexpr explicit Optional(U&& value); 1109 1110 Optional& operator=(const Optional& other) noexcept; 1111 1112 Optional& operator=(Optional&& other) noexcept; 1113 1114 Optional& operator=(nullopt_t); 1115 1116 template <typename U> 1117 typename std::enable_if< 1118 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value && 1119 std::is_constructible<T, U>::value && 1120 std::is_assignable<T&, U>::value && 1121 (!std::is_scalar<T>::value || 1122 !std::is_same<typename std::decay<U>::type, T>::value), 1123 Optional&>::type 1124 operator=(U&& value) noexcept; 1125 1126 template <typename U> 1127 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1128 std::is_constructible<T, const U&>::value && 1129 std::is_assignable<T&, const U&>::value, 1130 Optional&>::type 1131 operator=(const Optional<U>& other) noexcept; 1132 1133 template <typename U> 1134 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value && 1135 std::is_constructible<T, U>::value && 1136 std::is_assignable<T&, U>::value, 1137 Optional&>::type 1138 operator=(Optional<U>&& other) noexcept; 1139 1140 const T& operator*() const&; 1141 T& operator*() &; 1142 const T&& operator*() const&&; 1143 T&& operator*() &&; 1144 1145 const T* operator->() const; 1146 T* operator->(); 1147 1148 const T& value() const&; 1149 T& value() &; 1150 const T&& value() const&&; 1151 T&& value() &&; 1152 1153 template <typename U> 1154 constexpr T value_or(U&& v) const&; 1155 template <typename U> 1156 T value_or(U&& v) &&; 1157 1158 template <typename... Args> 1159 T& emplace(Args&&... args); 1160 1161 template <typename U, typename... Args> 1162 T& emplace(std::initializer_list<U> ilist, Args&&... args); 1163 1164 void reset() noexcept; 1165 1166 constexpr explicit operator bool() const noexcept; 1167 constexpr bool has_value() const noexcept; 1168 1169 void swap(Optional& other); 1170 }; 1171 1172 template <typename T> 1173 constexpr Optional<typename std::decay<T>::type> make_optional(T&& v); 1174 1175 template <typename T, typename... Args> 1176 constexpr Optional<T> make_optional(Args&&... args); 1177 1178 template <typename T, typename U, typename... Args> 1179 constexpr Optional<T> make_optional(std::initializer_list<U> il, 1180 Args&&... args); 1181 1182 } // namespace base 1183 )"; 1184 1185 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`. 1186 static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern, 1187 const std::string &Replacement) { 1188 size_t Pos = 0; 1189 while (true) { 1190 Pos = S.find(Pattern, Pos); 1191 if (Pos == std::string::npos) 1192 break; 1193 S.replace(Pos, Pattern.size(), Replacement); 1194 } 1195 } 1196 1197 struct OptionalTypeIdentifier { 1198 std::string NamespaceName; 1199 std::string TypeName; 1200 }; 1201 1202 class UncheckedOptionalAccessTest 1203 : public ::testing::TestWithParam<OptionalTypeIdentifier> { 1204 protected: 1205 void ExpectDiagnosticsFor(std::string SourceCode) { 1206 ExpectDiagnosticsFor(SourceCode, ast_matchers::hasName("target")); 1207 } 1208 1209 private: 1210 template <typename FuncDeclMatcher> 1211 void ExpectDiagnosticsFor(std::string SourceCode, 1212 FuncDeclMatcher FuncMatcher) { 1213 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName); 1214 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName); 1215 1216 std::vector<std::pair<std::string, std::string>> Headers; 1217 Headers.emplace_back("cstddef.h", CSDtdDefHeader); 1218 Headers.emplace_back("std_initializer_list.h", StdInitializerListHeader); 1219 Headers.emplace_back("std_string.h", StdStringHeader); 1220 Headers.emplace_back("std_type_traits.h", StdTypeTraitsHeader); 1221 Headers.emplace_back("std_utility.h", StdUtilityHeader); 1222 Headers.emplace_back("std_optional.h", StdOptionalHeader); 1223 Headers.emplace_back("absl_type_traits.h", AbslTypeTraitsHeader); 1224 Headers.emplace_back("absl_optional.h", AbslOptionalHeader); 1225 Headers.emplace_back("base_optional.h", BaseOptionalHeader); 1226 Headers.emplace_back("unchecked_optional_access_test.h", R"( 1227 #include "absl_optional.h" 1228 #include "base_optional.h" 1229 #include "std_initializer_list.h" 1230 #include "std_optional.h" 1231 #include "std_string.h" 1232 #include "std_utility.h" 1233 1234 template <typename T> 1235 T Make(); 1236 )"); 1237 UncheckedOptionalAccessModelOptions Options{ 1238 /*IgnoreSmartPointerDereference=*/true}; 1239 std::vector<SourceLocation> Diagnostics; 1240 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>( 1241 AnalysisInputs<UncheckedOptionalAccessModel>( 1242 SourceCode, std::move(FuncMatcher), 1243 [Options](ASTContext &Ctx, Environment &) { 1244 return UncheckedOptionalAccessModel(Ctx, Options); 1245 }) 1246 .withPostVisitCFG( 1247 [&Diagnostics, 1248 Diagnoser = UncheckedOptionalAccessDiagnoser(Options)]( 1249 ASTContext &Ctx, const CFGElement &Elt, 1250 const TypeErasedDataflowAnalysisState &State) mutable { 1251 auto Stmt = Elt.getAs<CFGStmt>(); 1252 if (!Stmt) { 1253 return; 1254 } 1255 auto StmtDiagnostics = 1256 Diagnoser.diagnose(Ctx, Stmt->getStmt(), State.Env); 1257 llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics)); 1258 }) 1259 .withASTBuildArgs( 1260 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}) 1261 .withASTBuildVirtualMappedFiles( 1262 tooling::FileContentMappings(Headers.begin(), Headers.end())), 1263 /*VerifyResults=*/[&Diagnostics]( 1264 const llvm::DenseMap<unsigned, std::string> 1265 &Annotations, 1266 const AnalysisOutputs &AO) { 1267 llvm::DenseSet<unsigned> AnnotationLines; 1268 for (const auto &[Line, _] : Annotations) { 1269 AnnotationLines.insert(Line); 1270 } 1271 auto &SrcMgr = AO.ASTCtx.getSourceManager(); 1272 llvm::DenseSet<unsigned> DiagnosticLines; 1273 for (SourceLocation &Loc : Diagnostics) { 1274 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc)); 1275 } 1276 1277 EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines)); 1278 }); 1279 if (Error) 1280 FAIL() << llvm::toString(std::move(Error)); 1281 } 1282 }; 1283 1284 INSTANTIATE_TEST_SUITE_P( 1285 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest, 1286 ::testing::Values(OptionalTypeIdentifier{"std", "optional"}, 1287 OptionalTypeIdentifier{"absl", "optional"}, 1288 OptionalTypeIdentifier{"base", "Optional"}), 1289 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) { 1290 return Info.param.NamespaceName; 1291 }); 1292 1293 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { 1294 ExpectDiagnosticsFor(R"( 1295 void target() { 1296 (void)0; 1297 } 1298 )"); 1299 } 1300 1301 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { 1302 ExpectDiagnosticsFor( 1303 R"( 1304 #include "unchecked_optional_access_test.h" 1305 1306 void target($ns::$optional<int> opt) { 1307 opt.value(); // [[unsafe]] 1308 } 1309 )"); 1310 1311 ExpectDiagnosticsFor( 1312 R"( 1313 #include "unchecked_optional_access_test.h" 1314 1315 void target($ns::$optional<int> opt) { 1316 std::move(opt).value(); // [[unsafe]] 1317 } 1318 )"); 1319 } 1320 1321 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { 1322 ExpectDiagnosticsFor( 1323 R"( 1324 #include "unchecked_optional_access_test.h" 1325 1326 void target($ns::$optional<int> opt) { 1327 *opt; // [[unsafe]] 1328 } 1329 )"); 1330 1331 ExpectDiagnosticsFor( 1332 R"( 1333 #include "unchecked_optional_access_test.h" 1334 1335 void target($ns::$optional<int> opt) { 1336 *std::move(opt); // [[unsafe]] 1337 } 1338 )"); 1339 } 1340 1341 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { 1342 ExpectDiagnosticsFor( 1343 R"( 1344 #include "unchecked_optional_access_test.h" 1345 1346 struct Foo { 1347 void foo(); 1348 }; 1349 1350 void target($ns::$optional<Foo> opt) { 1351 opt->foo(); // [[unsafe]] 1352 } 1353 )"); 1354 1355 ExpectDiagnosticsFor( 1356 R"( 1357 #include "unchecked_optional_access_test.h" 1358 1359 struct Foo { 1360 void foo(); 1361 }; 1362 1363 void target($ns::$optional<Foo> opt) { 1364 std::move(opt)->foo(); // [[unsafe]] 1365 } 1366 )"); 1367 } 1368 1369 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { 1370 ExpectDiagnosticsFor(R"( 1371 #include "unchecked_optional_access_test.h" 1372 1373 void target($ns::$optional<int> opt) { 1374 if (opt.has_value()) { 1375 opt.value(); 1376 } 1377 } 1378 )"); 1379 } 1380 1381 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { 1382 ExpectDiagnosticsFor(R"( 1383 #include "unchecked_optional_access_test.h" 1384 1385 void target($ns::$optional<int> opt) { 1386 if (opt) { 1387 opt.value(); 1388 } 1389 } 1390 )"); 1391 } 1392 1393 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { 1394 ExpectDiagnosticsFor( 1395 R"( 1396 #include "unchecked_optional_access_test.h" 1397 1398 void target() { 1399 Make<$ns::$optional<int>>().value(); // [[unsafe]] 1400 (void)0; 1401 } 1402 )"); 1403 1404 ExpectDiagnosticsFor( 1405 R"( 1406 #include "unchecked_optional_access_test.h" 1407 1408 void target($ns::$optional<int> opt) { 1409 std::move(opt).value(); // [[unsafe]] 1410 } 1411 )"); 1412 } 1413 1414 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { 1415 ExpectDiagnosticsFor( 1416 R"( 1417 #include "unchecked_optional_access_test.h" 1418 1419 void target() { 1420 $ns::$optional<int> opt; 1421 opt.value(); // [[unsafe]] 1422 } 1423 )"); 1424 } 1425 1426 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { 1427 ExpectDiagnosticsFor( 1428 R"( 1429 #include "unchecked_optional_access_test.h" 1430 1431 void target() { 1432 $ns::$optional<int> opt($ns::nullopt); 1433 opt.value(); // [[unsafe]] 1434 } 1435 )"); 1436 } 1437 1438 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { 1439 ExpectDiagnosticsFor(R"( 1440 #include "unchecked_optional_access_test.h" 1441 1442 void target() { 1443 $ns::$optional<int> opt($ns::in_place, 3); 1444 opt.value(); 1445 } 1446 )"); 1447 1448 ExpectDiagnosticsFor(R"( 1449 #include "unchecked_optional_access_test.h" 1450 1451 struct Foo {}; 1452 1453 void target() { 1454 $ns::$optional<Foo> opt($ns::in_place); 1455 opt.value(); 1456 } 1457 )"); 1458 1459 ExpectDiagnosticsFor(R"( 1460 #include "unchecked_optional_access_test.h" 1461 1462 struct Foo { 1463 explicit Foo(int, bool); 1464 }; 1465 1466 void target() { 1467 $ns::$optional<Foo> opt($ns::in_place, 3, false); 1468 opt.value(); 1469 } 1470 )"); 1471 1472 ExpectDiagnosticsFor(R"( 1473 #include "unchecked_optional_access_test.h" 1474 1475 struct Foo { 1476 explicit Foo(std::initializer_list<int>); 1477 }; 1478 1479 void target() { 1480 $ns::$optional<Foo> opt($ns::in_place, {3}); 1481 opt.value(); 1482 } 1483 )"); 1484 } 1485 1486 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { 1487 ExpectDiagnosticsFor(R"( 1488 #include "unchecked_optional_access_test.h" 1489 1490 void target() { 1491 $ns::$optional<int> opt(21); 1492 opt.value(); 1493 } 1494 )"); 1495 1496 ExpectDiagnosticsFor(R"( 1497 #include "unchecked_optional_access_test.h" 1498 1499 void target() { 1500 $ns::$optional<int> opt = $ns::$optional<int>(21); 1501 opt.value(); 1502 } 1503 )"); 1504 ExpectDiagnosticsFor(R"( 1505 #include "unchecked_optional_access_test.h" 1506 1507 void target() { 1508 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>()); 1509 opt.value(); 1510 } 1511 )"); 1512 1513 ExpectDiagnosticsFor(R"( 1514 #include "unchecked_optional_access_test.h" 1515 1516 struct MyString { 1517 MyString(const char*); 1518 }; 1519 1520 void target() { 1521 $ns::$optional<MyString> opt("foo"); 1522 opt.value(); 1523 } 1524 )"); 1525 1526 ExpectDiagnosticsFor(R"( 1527 #include "unchecked_optional_access_test.h" 1528 1529 struct Foo {}; 1530 1531 struct Bar { 1532 Bar(const Foo&); 1533 }; 1534 1535 void target() { 1536 $ns::$optional<Bar> opt(Make<Foo>()); 1537 opt.value(); 1538 } 1539 )"); 1540 1541 ExpectDiagnosticsFor(R"( 1542 #include "unchecked_optional_access_test.h" 1543 1544 struct Foo { 1545 explicit Foo(int); 1546 }; 1547 1548 void target() { 1549 $ns::$optional<Foo> opt(3); 1550 opt.value(); 1551 } 1552 )"); 1553 } 1554 1555 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { 1556 ExpectDiagnosticsFor( 1557 R"( 1558 #include "unchecked_optional_access_test.h" 1559 1560 struct Foo {}; 1561 1562 struct Bar { 1563 Bar(const Foo&); 1564 }; 1565 1566 void target() { 1567 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1568 opt.value(); // [[unsafe]] 1569 } 1570 )"); 1571 1572 ExpectDiagnosticsFor( 1573 R"( 1574 #include "unchecked_optional_access_test.h" 1575 1576 struct Foo {}; 1577 1578 struct Bar { 1579 explicit Bar(const Foo&); 1580 }; 1581 1582 void target() { 1583 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1584 opt.value(); // [[unsafe]] 1585 } 1586 )"); 1587 1588 ExpectDiagnosticsFor( 1589 R"( 1590 #include "unchecked_optional_access_test.h" 1591 1592 struct Foo {}; 1593 1594 struct Bar { 1595 Bar(const Foo&); 1596 }; 1597 1598 void target() { 1599 $ns::$optional<Foo> opt1 = $ns::nullopt; 1600 $ns::$optional<Bar> opt2(opt1); 1601 opt2.value(); // [[unsafe]] 1602 } 1603 )"); 1604 1605 ExpectDiagnosticsFor(R"( 1606 #include "unchecked_optional_access_test.h" 1607 1608 struct Foo {}; 1609 1610 struct Bar { 1611 Bar(const Foo&); 1612 }; 1613 1614 void target() { 1615 $ns::$optional<Foo> opt1(Make<Foo>()); 1616 $ns::$optional<Bar> opt2(opt1); 1617 opt2.value(); 1618 } 1619 )"); 1620 1621 ExpectDiagnosticsFor(R"( 1622 #include "unchecked_optional_access_test.h" 1623 1624 struct Foo {}; 1625 1626 struct Bar { 1627 explicit Bar(const Foo&); 1628 }; 1629 1630 void target() { 1631 $ns::$optional<Foo> opt1(Make<Foo>()); 1632 $ns::$optional<Bar> opt2(opt1); 1633 opt2.value(); 1634 } 1635 )"); 1636 } 1637 1638 TEST_P(UncheckedOptionalAccessTest, MakeOptional) { 1639 ExpectDiagnosticsFor(R"( 1640 #include "unchecked_optional_access_test.h" 1641 1642 void target() { 1643 $ns::$optional<int> opt = $ns::make_optional(0); 1644 opt.value(); 1645 } 1646 )"); 1647 1648 ExpectDiagnosticsFor(R"( 1649 #include "unchecked_optional_access_test.h" 1650 1651 struct Foo { 1652 Foo(int, int); 1653 }; 1654 1655 void target() { 1656 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22); 1657 opt.value(); 1658 } 1659 )"); 1660 1661 ExpectDiagnosticsFor(R"( 1662 #include "unchecked_optional_access_test.h" 1663 1664 struct Foo { 1665 constexpr Foo(std::initializer_list<char>); 1666 }; 1667 1668 void target() { 1669 char a = 'a'; 1670 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a}); 1671 opt.value(); 1672 } 1673 )"); 1674 } 1675 1676 TEST_P(UncheckedOptionalAccessTest, ValueOr) { 1677 ExpectDiagnosticsFor(R"( 1678 #include "unchecked_optional_access_test.h" 1679 1680 void target() { 1681 $ns::$optional<int> opt; 1682 opt.value_or(0); 1683 (void)0; 1684 } 1685 )"); 1686 } 1687 1688 TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) { 1689 // Pointers. 1690 ExpectDiagnosticsFor( 1691 R"code( 1692 #include "unchecked_optional_access_test.h" 1693 1694 void target($ns::$optional<int*> opt) { 1695 if (opt.value_or(nullptr) != nullptr) { 1696 opt.value(); 1697 } else { 1698 opt.value(); // [[unsafe]] 1699 } 1700 } 1701 )code"); 1702 1703 // Integers. 1704 ExpectDiagnosticsFor( 1705 R"code( 1706 #include "unchecked_optional_access_test.h" 1707 1708 void target($ns::$optional<int> opt) { 1709 if (opt.value_or(0) != 0) { 1710 opt.value(); 1711 } else { 1712 opt.value(); // [[unsafe]] 1713 } 1714 } 1715 )code"); 1716 1717 // Strings. 1718 ExpectDiagnosticsFor( 1719 R"code( 1720 #include "unchecked_optional_access_test.h" 1721 1722 void target($ns::$optional<std::string> opt) { 1723 if (!opt.value_or("").empty()) { 1724 opt.value(); 1725 } else { 1726 opt.value(); // [[unsafe]] 1727 } 1728 } 1729 )code"); 1730 1731 ExpectDiagnosticsFor( 1732 R"code( 1733 #include "unchecked_optional_access_test.h" 1734 1735 void target($ns::$optional<std::string> opt) { 1736 if (opt.value_or("") != "") { 1737 opt.value(); 1738 } else { 1739 opt.value(); // [[unsafe]] 1740 } 1741 } 1742 )code"); 1743 1744 // Pointer-to-optional. 1745 // 1746 // FIXME: make `opt` a parameter directly, once we ensure that all `optional` 1747 // values have a `has_value` property. 1748 ExpectDiagnosticsFor( 1749 R"code( 1750 #include "unchecked_optional_access_test.h" 1751 1752 void target($ns::$optional<int> p) { 1753 $ns::$optional<int> *opt = &p; 1754 if (opt->value_or(0) != 0) { 1755 opt->value(); 1756 } else { 1757 opt->value(); // [[unsafe]] 1758 } 1759 } 1760 )code"); 1761 } 1762 1763 TEST_P(UncheckedOptionalAccessTest, Emplace) { 1764 ExpectDiagnosticsFor(R"( 1765 #include "unchecked_optional_access_test.h" 1766 1767 void target() { 1768 $ns::$optional<int> opt; 1769 opt.emplace(0); 1770 opt.value(); 1771 } 1772 )"); 1773 1774 ExpectDiagnosticsFor(R"( 1775 #include "unchecked_optional_access_test.h" 1776 1777 void target($ns::$optional<int> *opt) { 1778 opt->emplace(0); 1779 opt->value(); 1780 } 1781 )"); 1782 1783 // FIXME: Add tests that call `emplace` in conditional branches: 1784 // ExpectDiagnosticsFor( 1785 // R"( 1786 // #include "unchecked_optional_access_test.h" 1787 // 1788 // void target($ns::$optional<int> opt, bool b) { 1789 // if (b) { 1790 // opt.emplace(0); 1791 // } 1792 // if (b) { 1793 // opt.value(); 1794 // } else { 1795 // opt.value(); // [[unsafe]] 1796 // } 1797 // } 1798 // )"); 1799 } 1800 1801 TEST_P(UncheckedOptionalAccessTest, Reset) { 1802 ExpectDiagnosticsFor( 1803 R"( 1804 #include "unchecked_optional_access_test.h" 1805 1806 void target() { 1807 $ns::$optional<int> opt = $ns::make_optional(0); 1808 opt.reset(); 1809 opt.value(); // [[unsafe]] 1810 } 1811 )"); 1812 1813 ExpectDiagnosticsFor( 1814 R"( 1815 #include "unchecked_optional_access_test.h" 1816 1817 void target($ns::$optional<int> &opt) { 1818 if (opt.has_value()) { 1819 opt.reset(); 1820 opt.value(); // [[unsafe]] 1821 } 1822 } 1823 )"); 1824 1825 // FIXME: Add tests that call `reset` in conditional branches: 1826 // ExpectDiagnosticsFor( 1827 // R"( 1828 // #include "unchecked_optional_access_test.h" 1829 // 1830 // void target(bool b) { 1831 // $ns::$optional<int> opt = $ns::make_optional(0); 1832 // if (b) { 1833 // opt.reset(); 1834 // } 1835 // if (b) { 1836 // opt.value(); // [[unsafe]] 1837 // } else { 1838 // opt.value(); 1839 // } 1840 // } 1841 // )"); 1842 } 1843 1844 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { 1845 ExpectDiagnosticsFor(R"( 1846 #include "unchecked_optional_access_test.h" 1847 1848 struct Foo {}; 1849 1850 void target() { 1851 $ns::$optional<Foo> opt; 1852 opt = Foo(); 1853 opt.value(); 1854 } 1855 )"); 1856 1857 ExpectDiagnosticsFor(R"( 1858 #include "unchecked_optional_access_test.h" 1859 1860 struct Foo {}; 1861 1862 void target() { 1863 $ns::$optional<Foo> opt; 1864 (opt = Foo()).value(); 1865 (void)0; 1866 } 1867 )"); 1868 1869 ExpectDiagnosticsFor(R"( 1870 #include "unchecked_optional_access_test.h" 1871 1872 struct MyString { 1873 MyString(const char*); 1874 }; 1875 1876 void target() { 1877 $ns::$optional<MyString> opt; 1878 opt = "foo"; 1879 opt.value(); 1880 } 1881 )"); 1882 1883 ExpectDiagnosticsFor(R"( 1884 #include "unchecked_optional_access_test.h" 1885 1886 struct MyString { 1887 MyString(const char*); 1888 }; 1889 1890 void target() { 1891 $ns::$optional<MyString> opt; 1892 (opt = "foo").value(); 1893 } 1894 )"); 1895 } 1896 1897 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { 1898 ExpectDiagnosticsFor( 1899 R"( 1900 #include "unchecked_optional_access_test.h" 1901 1902 struct Foo {}; 1903 1904 struct Bar { 1905 Bar(const Foo&); 1906 }; 1907 1908 void target() { 1909 $ns::$optional<Foo> opt1 = Foo(); 1910 $ns::$optional<Bar> opt2; 1911 opt2 = opt1; 1912 opt2.value(); 1913 } 1914 )"); 1915 1916 ExpectDiagnosticsFor( 1917 R"( 1918 #include "unchecked_optional_access_test.h" 1919 1920 struct Foo {}; 1921 1922 struct Bar { 1923 Bar(const Foo&); 1924 }; 1925 1926 void target() { 1927 $ns::$optional<Foo> opt1; 1928 $ns::$optional<Bar> opt2; 1929 if (opt2.has_value()) { 1930 opt2 = opt1; 1931 opt2.value(); // [[unsafe]] 1932 } 1933 } 1934 )"); 1935 1936 ExpectDiagnosticsFor( 1937 R"( 1938 #include "unchecked_optional_access_test.h" 1939 1940 struct Foo {}; 1941 1942 struct Bar { 1943 Bar(const Foo&); 1944 }; 1945 1946 void target() { 1947 $ns::$optional<Foo> opt1 = Foo(); 1948 $ns::$optional<Bar> opt2; 1949 (opt2 = opt1).value(); 1950 (void)0; 1951 } 1952 )"); 1953 } 1954 1955 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { 1956 ExpectDiagnosticsFor( 1957 R"( 1958 #include "unchecked_optional_access_test.h" 1959 1960 void target() { 1961 $ns::$optional<int> opt = 3; 1962 opt = $ns::nullopt; 1963 opt.value(); // [[unsafe]] 1964 } 1965 )"); 1966 1967 ExpectDiagnosticsFor( 1968 R"( 1969 #include "unchecked_optional_access_test.h" 1970 1971 void target() { 1972 $ns::$optional<int> opt = 3; 1973 (opt = $ns::nullopt).value(); // [[unsafe]] 1974 } 1975 )"); 1976 } 1977 1978 TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { 1979 ExpectDiagnosticsFor( 1980 R"( 1981 #include "unchecked_optional_access_test.h" 1982 1983 void target() { 1984 $ns::$optional<int> opt1 = $ns::nullopt; 1985 $ns::$optional<int> opt2 = 3; 1986 1987 opt1.swap(opt2); 1988 1989 opt1.value(); 1990 1991 opt2.value(); // [[unsafe]] 1992 } 1993 )"); 1994 1995 ExpectDiagnosticsFor( 1996 R"( 1997 #include "unchecked_optional_access_test.h" 1998 1999 void target() { 2000 $ns::$optional<int> opt1 = $ns::nullopt; 2001 $ns::$optional<int> opt2 = 3; 2002 2003 opt2.swap(opt1); 2004 2005 opt1.value(); 2006 2007 opt2.value(); // [[unsafe]] 2008 } 2009 )"); 2010 } 2011 2012 TEST_P(UncheckedOptionalAccessTest, StdSwap) { 2013 ExpectDiagnosticsFor( 2014 R"( 2015 #include "unchecked_optional_access_test.h" 2016 2017 void target() { 2018 $ns::$optional<int> opt1 = $ns::nullopt; 2019 $ns::$optional<int> opt2 = 3; 2020 2021 std::swap(opt1, opt2); 2022 2023 opt1.value(); 2024 2025 opt2.value(); // [[unsafe]] 2026 } 2027 )"); 2028 2029 ExpectDiagnosticsFor( 2030 R"( 2031 #include "unchecked_optional_access_test.h" 2032 2033 void target() { 2034 $ns::$optional<int> opt1 = $ns::nullopt; 2035 $ns::$optional<int> opt2 = 3; 2036 2037 std::swap(opt2, opt1); 2038 2039 opt1.value(); 2040 2041 opt2.value(); // [[unsafe]] 2042 } 2043 )"); 2044 } 2045 2046 TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { 2047 // We suppress diagnostics for values reachable from smart pointers (other 2048 // than `optional` itself). 2049 ExpectDiagnosticsFor( 2050 R"( 2051 #include "unchecked_optional_access_test.h" 2052 2053 template <typename T> 2054 struct smart_ptr { 2055 T& operator*() &; 2056 T* operator->(); 2057 }; 2058 2059 struct Foo { 2060 $ns::$optional<int> opt; 2061 }; 2062 2063 void target() { 2064 smart_ptr<Foo> foo; 2065 *foo->opt; 2066 *(*foo).opt; 2067 } 2068 )"); 2069 } 2070 2071 TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { 2072 ExpectDiagnosticsFor( 2073 R"( 2074 #include "unchecked_optional_access_test.h" 2075 2076 $ns::$optional<int> MakeOpt(); 2077 2078 void target() { 2079 $ns::$optional<int> opt = 0; 2080 opt = MakeOpt(); 2081 opt.value(); // [[unsafe]] 2082 } 2083 )"); 2084 ExpectDiagnosticsFor( 2085 R"( 2086 #include "unchecked_optional_access_test.h" 2087 2088 const $ns::$optional<int>& MakeOpt(); 2089 2090 void target() { 2091 $ns::$optional<int> opt = 0; 2092 opt = MakeOpt(); 2093 opt.value(); // [[unsafe]] 2094 } 2095 )"); 2096 2097 ExpectDiagnosticsFor( 2098 R"( 2099 #include "unchecked_optional_access_test.h" 2100 2101 using IntOpt = $ns::$optional<int>; 2102 IntOpt MakeOpt(); 2103 2104 void target() { 2105 IntOpt opt = 0; 2106 opt = MakeOpt(); 2107 opt.value(); // [[unsafe]] 2108 } 2109 )"); 2110 2111 ExpectDiagnosticsFor( 2112 R"( 2113 #include "unchecked_optional_access_test.h" 2114 2115 using IntOpt = $ns::$optional<int>; 2116 const IntOpt& MakeOpt(); 2117 2118 void target() { 2119 IntOpt opt = 0; 2120 opt = MakeOpt(); 2121 opt.value(); // [[unsafe]] 2122 } 2123 )"); 2124 } 2125 2126 // Verifies that the model sees through aliases. 2127 TEST_P(UncheckedOptionalAccessTest, WithAlias) { 2128 ExpectDiagnosticsFor( 2129 R"( 2130 #include "unchecked_optional_access_test.h" 2131 2132 template <typename T> 2133 using MyOptional = $ns::$optional<T>; 2134 2135 void target(MyOptional<int> opt) { 2136 opt.value(); // [[unsafe]] 2137 } 2138 )"); 2139 } 2140 2141 TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) { 2142 // Basic test that nested values are populated. We nest an optional because 2143 // its easy to use in a test, but the type of the nested value shouldn't 2144 // matter. 2145 ExpectDiagnosticsFor( 2146 R"( 2147 #include "unchecked_optional_access_test.h" 2148 2149 using Foo = $ns::$optional<std::string>; 2150 2151 void target($ns::$optional<Foo> foo) { 2152 if (foo && *foo) { 2153 foo->value(); 2154 } 2155 } 2156 )"); 2157 2158 // Mutation is supported for nested values. 2159 ExpectDiagnosticsFor( 2160 R"( 2161 #include "unchecked_optional_access_test.h" 2162 2163 using Foo = $ns::$optional<std::string>; 2164 2165 void target($ns::$optional<Foo> foo) { 2166 if (foo && *foo) { 2167 foo->reset(); 2168 foo->value(); // [[unsafe]] 2169 } 2170 } 2171 )"); 2172 } 2173 2174 // Tests that structs can be nested. We use an optional field because its easy 2175 // to use in a test, but the type of the field shouldn't matter. 2176 TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) { 2177 ExpectDiagnosticsFor( 2178 R"( 2179 #include "unchecked_optional_access_test.h" 2180 2181 struct Foo { 2182 $ns::$optional<std::string> opt; 2183 }; 2184 2185 void target($ns::$optional<Foo> foo) { 2186 if (foo && foo->opt) { 2187 foo->opt.value(); 2188 } 2189 } 2190 )"); 2191 } 2192 2193 TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) { 2194 // FIXME: Fix when to initialize `value`. All unwrapping should be safe in 2195 // this example, but `value` initialization is done multiple times during the 2196 // fixpoint iterations and joining the environment won't correctly merge them. 2197 ExpectDiagnosticsFor( 2198 R"( 2199 #include "unchecked_optional_access_test.h" 2200 2201 using Foo = $ns::$optional<std::string>; 2202 2203 void target($ns::$optional<Foo> foo, bool b) { 2204 if (!foo.has_value()) return; 2205 if (b) { 2206 if (!foo->has_value()) return; 2207 // We have created `foo.value()`. 2208 foo->value(); 2209 } else { 2210 if (!foo->has_value()) return; 2211 // We have created `foo.value()` again, in a different environment. 2212 foo->value(); 2213 } 2214 // Now we merge the two values. UncheckedOptionalAccessModel::merge() will 2215 // throw away the "value" property. 2216 foo->value(); // [[unsafe]] 2217 } 2218 )"); 2219 } 2220 2221 TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) { 2222 ExpectDiagnosticsFor( 2223 R"( 2224 #include "unchecked_optional_access_test.h" 2225 2226 template <typename T> 2227 struct smart_ptr { 2228 typename std::add_lvalue_reference<T>::type operator*() &; 2229 }; 2230 2231 void target() { 2232 smart_ptr<$ns::$optional<float>> x; 2233 *x = $ns::nullopt; 2234 (*x).value(); // [[unsafe]] 2235 } 2236 )"); 2237 } 2238 2239 TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) { 2240 ExpectDiagnosticsFor(R"code( 2241 #include "unchecked_optional_access_test.h" 2242 2243 void target(bool b, $ns::$optional<int> opt) { 2244 if (b || opt.has_value()) { 2245 if (!b) { 2246 opt.value(); 2247 } 2248 } 2249 } 2250 )code"); 2251 2252 ExpectDiagnosticsFor(R"code( 2253 #include "unchecked_optional_access_test.h" 2254 2255 void target(bool b, $ns::$optional<int> opt) { 2256 if (b && !opt.has_value()) return; 2257 if (b) { 2258 opt.value(); 2259 } 2260 } 2261 )code"); 2262 2263 ExpectDiagnosticsFor( 2264 R"code( 2265 #include "unchecked_optional_access_test.h" 2266 2267 void target(bool b, $ns::$optional<int> opt) { 2268 if (opt.has_value()) b = true; 2269 if (b) { 2270 opt.value(); // [[unsafe]] 2271 } 2272 } 2273 )code"); 2274 2275 ExpectDiagnosticsFor(R"code( 2276 #include "unchecked_optional_access_test.h" 2277 2278 void target(bool b, $ns::$optional<int> opt) { 2279 if (b) return; 2280 if (opt.has_value()) b = true; 2281 if (b) { 2282 opt.value(); 2283 } 2284 } 2285 )code"); 2286 2287 ExpectDiagnosticsFor(R"( 2288 #include "unchecked_optional_access_test.h" 2289 2290 void target(bool b, $ns::$optional<int> opt) { 2291 if (opt.has_value() == b) { 2292 if (b) { 2293 opt.value(); 2294 } 2295 } 2296 } 2297 )"); 2298 2299 ExpectDiagnosticsFor(R"( 2300 #include "unchecked_optional_access_test.h" 2301 2302 void target(bool b, $ns::$optional<int> opt) { 2303 if (opt.has_value() != b) { 2304 if (!b) { 2305 opt.value(); 2306 } 2307 } 2308 } 2309 )"); 2310 2311 ExpectDiagnosticsFor(R"( 2312 #include "unchecked_optional_access_test.h" 2313 2314 void target(bool b) { 2315 $ns::$optional<int> opt1 = $ns::nullopt; 2316 $ns::$optional<int> opt2; 2317 if (b) { 2318 opt2 = $ns::nullopt; 2319 } else { 2320 opt2 = $ns::nullopt; 2321 } 2322 if (opt2.has_value()) { 2323 opt1.value(); 2324 } 2325 } 2326 )"); 2327 2328 // FIXME: Add support for operator==. 2329 // ExpectDiagnosticsFor(R"( 2330 // #include "unchecked_optional_access_test.h" 2331 // 2332 // void target($ns::$optional<int> opt1, $ns::$optional<int> opt2) { 2333 // if (opt1 == opt2) { 2334 // if (opt1.has_value()) { 2335 // opt2.value(); 2336 // } 2337 // } 2338 // } 2339 // )"); 2340 } 2341 2342 TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) { 2343 ExpectDiagnosticsFor( 2344 R"code( 2345 #include "unchecked_optional_access_test.h" 2346 2347 void target(bool b) { 2348 $ns::$optional<int> opt; 2349 if (b) { 2350 opt = Make<$ns::$optional<int>>(); 2351 } else { 2352 opt = Make<$ns::$optional<int>>(); 2353 } 2354 if (opt.has_value()) { 2355 opt.value(); 2356 } else { 2357 opt.value(); // [[unsafe]] 2358 } 2359 } 2360 )code"); 2361 2362 ExpectDiagnosticsFor(R"code( 2363 #include "unchecked_optional_access_test.h" 2364 2365 void target(bool b) { 2366 $ns::$optional<int> opt; 2367 if (b) { 2368 opt = Make<$ns::$optional<int>>(); 2369 if (!opt.has_value()) return; 2370 } else { 2371 opt = Make<$ns::$optional<int>>(); 2372 if (!opt.has_value()) return; 2373 } 2374 opt.value(); 2375 } 2376 )code"); 2377 2378 ExpectDiagnosticsFor( 2379 R"code( 2380 #include "unchecked_optional_access_test.h" 2381 2382 void target(bool b) { 2383 $ns::$optional<int> opt; 2384 if (b) { 2385 opt = Make<$ns::$optional<int>>(); 2386 if (!opt.has_value()) return; 2387 } else { 2388 opt = Make<$ns::$optional<int>>(); 2389 } 2390 opt.value(); // [[unsafe]] 2391 } 2392 )code"); 2393 2394 ExpectDiagnosticsFor( 2395 R"code( 2396 #include "unchecked_optional_access_test.h" 2397 2398 void target(bool b) { 2399 $ns::$optional<int> opt; 2400 if (b) { 2401 opt = 1; 2402 } else { 2403 opt = 2; 2404 } 2405 opt.value(); 2406 } 2407 )code"); 2408 2409 ExpectDiagnosticsFor( 2410 R"code( 2411 #include "unchecked_optional_access_test.h" 2412 2413 void target(bool b) { 2414 $ns::$optional<int> opt; 2415 if (b) { 2416 opt = 1; 2417 } else { 2418 opt = Make<$ns::$optional<int>>(); 2419 } 2420 opt.value(); // [[unsafe]] 2421 } 2422 )code"); 2423 } 2424 2425 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoop) { 2426 ExpectDiagnosticsFor(R"( 2427 #include "unchecked_optional_access_test.h" 2428 2429 void target() { 2430 $ns::$optional<int> opt = 3; 2431 while (Make<bool>()) { 2432 opt.value(); 2433 } 2434 } 2435 )"); 2436 2437 ExpectDiagnosticsFor(R"( 2438 #include "unchecked_optional_access_test.h" 2439 2440 void target() { 2441 $ns::$optional<int> opt = 3; 2442 while (Make<bool>()) { 2443 opt.value(); 2444 2445 opt = Make<$ns::$optional<int>>(); 2446 if (!opt.has_value()) return; 2447 } 2448 } 2449 )"); 2450 2451 ExpectDiagnosticsFor( 2452 R"( 2453 #include "unchecked_optional_access_test.h" 2454 2455 void target() { 2456 $ns::$optional<int> opt = 3; 2457 while (Make<bool>()) { 2458 opt.value(); // [[unsafe]] 2459 2460 opt = Make<$ns::$optional<int>>(); 2461 } 2462 } 2463 )"); 2464 2465 ExpectDiagnosticsFor( 2466 R"( 2467 #include "unchecked_optional_access_test.h" 2468 2469 void target() { 2470 $ns::$optional<int> opt = 3; 2471 while (Make<bool>()) { 2472 opt.value(); // [[unsafe]] 2473 2474 opt = Make<$ns::$optional<int>>(); 2475 if (!opt.has_value()) continue; 2476 } 2477 } 2478 )"); 2479 } 2480 2481 // FIXME: Add support for: 2482 // - constructors (copy, move) 2483 // - assignment operators (default, copy, move) 2484 // - invalidation (passing optional by non-const reference/pointer) 2485