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 const tooling::FileContentMappings FileContents(Headers.begin(), 1238 Headers.end()); 1239 UncheckedOptionalAccessModelOptions Options{ 1240 /*IgnoreSmartPointerDereference=*/true}; 1241 std::vector<SourceLocation> Diagnostics; 1242 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>( 1243 SourceCode, FuncMatcher, 1244 [Options](ASTContext &Ctx, Environment &) { 1245 return UncheckedOptionalAccessModel(Ctx, Options); 1246 }, 1247 [&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)]( 1248 ASTContext &Ctx, const CFGStmt &Stmt, 1249 const TypeErasedDataflowAnalysisState &State) mutable { 1250 auto StmtDiagnostics = 1251 Diagnoser.diagnose(Ctx, Stmt.getStmt(), State.Env); 1252 llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics)); 1253 }, 1254 [&Diagnostics](AnalysisData AnalysisData) { 1255 auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager(); 1256 1257 llvm::DenseSet<unsigned> AnnotationLines; 1258 for (const auto &Pair : AnalysisData.Annotations) { 1259 auto *Stmt = Pair.getFirst(); 1260 AnnotationLines.insert( 1261 SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc())); 1262 // We add both the begin and end locations, so that if the 1263 // statement spans multiple lines then the test will fail. 1264 // 1265 // FIXME: Going forward, we should change this to instead just 1266 // get the single line number from the annotation itself, rather 1267 // than looking at the statement it's attached to. 1268 AnnotationLines.insert( 1269 SrcMgr.getPresumedLineNumber(Stmt->getEndLoc())); 1270 } 1271 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 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents); 1280 if (Error) 1281 FAIL() << llvm::toString(std::move(Error)); 1282 } 1283 }; 1284 1285 INSTANTIATE_TEST_SUITE_P( 1286 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest, 1287 ::testing::Values(OptionalTypeIdentifier{"std", "optional"}, 1288 OptionalTypeIdentifier{"absl", "optional"}, 1289 OptionalTypeIdentifier{"base", "Optional"}), 1290 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) { 1291 return Info.param.NamespaceName; 1292 }); 1293 1294 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) { 1295 ExpectDiagnosticsFor(R"( 1296 void target() { 1297 (void)0; 1298 } 1299 )"); 1300 } 1301 1302 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) { 1303 ExpectDiagnosticsFor( 1304 R"( 1305 #include "unchecked_optional_access_test.h" 1306 1307 void target($ns::$optional<int> opt) { 1308 opt.value(); // [[unsafe]] 1309 } 1310 )"); 1311 1312 ExpectDiagnosticsFor( 1313 R"( 1314 #include "unchecked_optional_access_test.h" 1315 1316 void target($ns::$optional<int> opt) { 1317 std::move(opt).value(); // [[unsafe]] 1318 } 1319 )"); 1320 } 1321 1322 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) { 1323 ExpectDiagnosticsFor( 1324 R"( 1325 #include "unchecked_optional_access_test.h" 1326 1327 void target($ns::$optional<int> opt) { 1328 *opt; // [[unsafe]] 1329 } 1330 )"); 1331 1332 ExpectDiagnosticsFor( 1333 R"( 1334 #include "unchecked_optional_access_test.h" 1335 1336 void target($ns::$optional<int> opt) { 1337 *std::move(opt); // [[unsafe]] 1338 } 1339 )"); 1340 } 1341 1342 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) { 1343 ExpectDiagnosticsFor( 1344 R"( 1345 #include "unchecked_optional_access_test.h" 1346 1347 struct Foo { 1348 void foo(); 1349 }; 1350 1351 void target($ns::$optional<Foo> opt) { 1352 opt->foo(); // [[unsafe]] 1353 } 1354 )"); 1355 1356 ExpectDiagnosticsFor( 1357 R"( 1358 #include "unchecked_optional_access_test.h" 1359 1360 struct Foo { 1361 void foo(); 1362 }; 1363 1364 void target($ns::$optional<Foo> opt) { 1365 std::move(opt)->foo(); // [[unsafe]] 1366 } 1367 )"); 1368 } 1369 1370 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) { 1371 ExpectDiagnosticsFor(R"( 1372 #include "unchecked_optional_access_test.h" 1373 1374 void target($ns::$optional<int> opt) { 1375 if (opt.has_value()) { 1376 opt.value(); 1377 } 1378 } 1379 )"); 1380 } 1381 1382 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) { 1383 ExpectDiagnosticsFor(R"( 1384 #include "unchecked_optional_access_test.h" 1385 1386 void target($ns::$optional<int> opt) { 1387 if (opt) { 1388 opt.value(); 1389 } 1390 } 1391 )"); 1392 } 1393 1394 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) { 1395 ExpectDiagnosticsFor( 1396 R"( 1397 #include "unchecked_optional_access_test.h" 1398 1399 void target() { 1400 Make<$ns::$optional<int>>().value(); // [[unsafe]] 1401 (void)0; 1402 } 1403 )"); 1404 1405 ExpectDiagnosticsFor( 1406 R"( 1407 #include "unchecked_optional_access_test.h" 1408 1409 void target($ns::$optional<int> opt) { 1410 std::move(opt).value(); // [[unsafe]] 1411 } 1412 )"); 1413 } 1414 1415 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) { 1416 ExpectDiagnosticsFor( 1417 R"( 1418 #include "unchecked_optional_access_test.h" 1419 1420 void target() { 1421 $ns::$optional<int> opt; 1422 opt.value(); // [[unsafe]] 1423 } 1424 )"); 1425 } 1426 1427 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) { 1428 ExpectDiagnosticsFor( 1429 R"( 1430 #include "unchecked_optional_access_test.h" 1431 1432 void target() { 1433 $ns::$optional<int> opt($ns::nullopt); 1434 opt.value(); // [[unsafe]] 1435 } 1436 )"); 1437 } 1438 1439 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) { 1440 ExpectDiagnosticsFor(R"( 1441 #include "unchecked_optional_access_test.h" 1442 1443 void target() { 1444 $ns::$optional<int> opt($ns::in_place, 3); 1445 opt.value(); 1446 } 1447 )"); 1448 1449 ExpectDiagnosticsFor(R"( 1450 #include "unchecked_optional_access_test.h" 1451 1452 struct Foo {}; 1453 1454 void target() { 1455 $ns::$optional<Foo> opt($ns::in_place); 1456 opt.value(); 1457 } 1458 )"); 1459 1460 ExpectDiagnosticsFor(R"( 1461 #include "unchecked_optional_access_test.h" 1462 1463 struct Foo { 1464 explicit Foo(int, bool); 1465 }; 1466 1467 void target() { 1468 $ns::$optional<Foo> opt($ns::in_place, 3, false); 1469 opt.value(); 1470 } 1471 )"); 1472 1473 ExpectDiagnosticsFor(R"( 1474 #include "unchecked_optional_access_test.h" 1475 1476 struct Foo { 1477 explicit Foo(std::initializer_list<int>); 1478 }; 1479 1480 void target() { 1481 $ns::$optional<Foo> opt($ns::in_place, {3}); 1482 opt.value(); 1483 } 1484 )"); 1485 } 1486 1487 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) { 1488 ExpectDiagnosticsFor(R"( 1489 #include "unchecked_optional_access_test.h" 1490 1491 void target() { 1492 $ns::$optional<int> opt(21); 1493 opt.value(); 1494 } 1495 )"); 1496 1497 ExpectDiagnosticsFor(R"( 1498 #include "unchecked_optional_access_test.h" 1499 1500 void target() { 1501 $ns::$optional<int> opt = $ns::$optional<int>(21); 1502 opt.value(); 1503 } 1504 )"); 1505 ExpectDiagnosticsFor(R"( 1506 #include "unchecked_optional_access_test.h" 1507 1508 void target() { 1509 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>()); 1510 opt.value(); 1511 } 1512 )"); 1513 1514 ExpectDiagnosticsFor(R"( 1515 #include "unchecked_optional_access_test.h" 1516 1517 struct MyString { 1518 MyString(const char*); 1519 }; 1520 1521 void target() { 1522 $ns::$optional<MyString> opt("foo"); 1523 opt.value(); 1524 } 1525 )"); 1526 1527 ExpectDiagnosticsFor(R"( 1528 #include "unchecked_optional_access_test.h" 1529 1530 struct Foo {}; 1531 1532 struct Bar { 1533 Bar(const Foo&); 1534 }; 1535 1536 void target() { 1537 $ns::$optional<Bar> opt(Make<Foo>()); 1538 opt.value(); 1539 } 1540 )"); 1541 1542 ExpectDiagnosticsFor(R"( 1543 #include "unchecked_optional_access_test.h" 1544 1545 struct Foo { 1546 explicit Foo(int); 1547 }; 1548 1549 void target() { 1550 $ns::$optional<Foo> opt(3); 1551 opt.value(); 1552 } 1553 )"); 1554 } 1555 1556 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) { 1557 ExpectDiagnosticsFor( 1558 R"( 1559 #include "unchecked_optional_access_test.h" 1560 1561 struct Foo {}; 1562 1563 struct Bar { 1564 Bar(const Foo&); 1565 }; 1566 1567 void target() { 1568 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1569 opt.value(); // [[unsafe]] 1570 } 1571 )"); 1572 1573 ExpectDiagnosticsFor( 1574 R"( 1575 #include "unchecked_optional_access_test.h" 1576 1577 struct Foo {}; 1578 1579 struct Bar { 1580 explicit Bar(const Foo&); 1581 }; 1582 1583 void target() { 1584 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>()); 1585 opt.value(); // [[unsafe]] 1586 } 1587 )"); 1588 1589 ExpectDiagnosticsFor( 1590 R"( 1591 #include "unchecked_optional_access_test.h" 1592 1593 struct Foo {}; 1594 1595 struct Bar { 1596 Bar(const Foo&); 1597 }; 1598 1599 void target() { 1600 $ns::$optional<Foo> opt1 = $ns::nullopt; 1601 $ns::$optional<Bar> opt2(opt1); 1602 opt2.value(); // [[unsafe]] 1603 } 1604 )"); 1605 1606 ExpectDiagnosticsFor(R"( 1607 #include "unchecked_optional_access_test.h" 1608 1609 struct Foo {}; 1610 1611 struct Bar { 1612 Bar(const Foo&); 1613 }; 1614 1615 void target() { 1616 $ns::$optional<Foo> opt1(Make<Foo>()); 1617 $ns::$optional<Bar> opt2(opt1); 1618 opt2.value(); 1619 } 1620 )"); 1621 1622 ExpectDiagnosticsFor(R"( 1623 #include "unchecked_optional_access_test.h" 1624 1625 struct Foo {}; 1626 1627 struct Bar { 1628 explicit Bar(const Foo&); 1629 }; 1630 1631 void target() { 1632 $ns::$optional<Foo> opt1(Make<Foo>()); 1633 $ns::$optional<Bar> opt2(opt1); 1634 opt2.value(); 1635 } 1636 )"); 1637 } 1638 1639 TEST_P(UncheckedOptionalAccessTest, MakeOptional) { 1640 ExpectDiagnosticsFor(R"( 1641 #include "unchecked_optional_access_test.h" 1642 1643 void target() { 1644 $ns::$optional<int> opt = $ns::make_optional(0); 1645 opt.value(); 1646 } 1647 )"); 1648 1649 ExpectDiagnosticsFor(R"( 1650 #include "unchecked_optional_access_test.h" 1651 1652 struct Foo { 1653 Foo(int, int); 1654 }; 1655 1656 void target() { 1657 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22); 1658 opt.value(); 1659 } 1660 )"); 1661 1662 ExpectDiagnosticsFor(R"( 1663 #include "unchecked_optional_access_test.h" 1664 1665 struct Foo { 1666 constexpr Foo(std::initializer_list<char>); 1667 }; 1668 1669 void target() { 1670 char a = 'a'; 1671 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a}); 1672 opt.value(); 1673 } 1674 )"); 1675 } 1676 1677 TEST_P(UncheckedOptionalAccessTest, ValueOr) { 1678 ExpectDiagnosticsFor(R"( 1679 #include "unchecked_optional_access_test.h" 1680 1681 void target() { 1682 $ns::$optional<int> opt; 1683 opt.value_or(0); 1684 (void)0; 1685 } 1686 )"); 1687 } 1688 1689 TEST_P(UncheckedOptionalAccessTest, ValueOrComparison) { 1690 // Pointers. 1691 ExpectDiagnosticsFor( 1692 R"code( 1693 #include "unchecked_optional_access_test.h" 1694 1695 void target($ns::$optional<int*> opt) { 1696 if (opt.value_or(nullptr) != nullptr) { 1697 opt.value(); 1698 } else { 1699 opt.value(); // [[unsafe]] 1700 } 1701 } 1702 )code"); 1703 1704 // Integers. 1705 ExpectDiagnosticsFor( 1706 R"code( 1707 #include "unchecked_optional_access_test.h" 1708 1709 void target($ns::$optional<int> opt) { 1710 if (opt.value_or(0) != 0) { 1711 opt.value(); 1712 } else { 1713 opt.value(); // [[unsafe]] 1714 } 1715 } 1716 )code"); 1717 1718 // Strings. 1719 ExpectDiagnosticsFor( 1720 R"code( 1721 #include "unchecked_optional_access_test.h" 1722 1723 void target($ns::$optional<std::string> opt) { 1724 if (!opt.value_or("").empty()) { 1725 opt.value(); 1726 } else { 1727 opt.value(); // [[unsafe]] 1728 } 1729 } 1730 )code"); 1731 1732 ExpectDiagnosticsFor( 1733 R"code( 1734 #include "unchecked_optional_access_test.h" 1735 1736 void target($ns::$optional<std::string> opt) { 1737 if (opt.value_or("") != "") { 1738 opt.value(); 1739 } else { 1740 opt.value(); // [[unsafe]] 1741 } 1742 } 1743 )code"); 1744 1745 // Pointer-to-optional. 1746 // 1747 // FIXME: make `opt` a parameter directly, once we ensure that all `optional` 1748 // values have a `has_value` property. 1749 ExpectDiagnosticsFor( 1750 R"code( 1751 #include "unchecked_optional_access_test.h" 1752 1753 void target($ns::$optional<int> p) { 1754 $ns::$optional<int> *opt = &p; 1755 if (opt->value_or(0) != 0) { 1756 opt->value(); 1757 } else { 1758 opt->value(); // [[unsafe]] 1759 } 1760 } 1761 )code"); 1762 } 1763 1764 TEST_P(UncheckedOptionalAccessTest, Emplace) { 1765 ExpectDiagnosticsFor(R"( 1766 #include "unchecked_optional_access_test.h" 1767 1768 void target() { 1769 $ns::$optional<int> opt; 1770 opt.emplace(0); 1771 opt.value(); 1772 } 1773 )"); 1774 1775 ExpectDiagnosticsFor(R"( 1776 #include "unchecked_optional_access_test.h" 1777 1778 void target($ns::$optional<int> *opt) { 1779 opt->emplace(0); 1780 opt->value(); 1781 } 1782 )"); 1783 1784 // FIXME: Add tests that call `emplace` in conditional branches: 1785 // ExpectDiagnosticsFor( 1786 // R"( 1787 // #include "unchecked_optional_access_test.h" 1788 // 1789 // void target($ns::$optional<int> opt, bool b) { 1790 // if (b) { 1791 // opt.emplace(0); 1792 // } 1793 // if (b) { 1794 // opt.value(); 1795 // } else { 1796 // opt.value(); // [[unsafe]] 1797 // } 1798 // } 1799 // )"); 1800 } 1801 1802 TEST_P(UncheckedOptionalAccessTest, Reset) { 1803 ExpectDiagnosticsFor( 1804 R"( 1805 #include "unchecked_optional_access_test.h" 1806 1807 void target() { 1808 $ns::$optional<int> opt = $ns::make_optional(0); 1809 opt.reset(); 1810 opt.value(); // [[unsafe]] 1811 } 1812 )"); 1813 1814 ExpectDiagnosticsFor( 1815 R"( 1816 #include "unchecked_optional_access_test.h" 1817 1818 void target($ns::$optional<int> &opt) { 1819 if (opt.has_value()) { 1820 opt.reset(); 1821 opt.value(); // [[unsafe]] 1822 } 1823 } 1824 )"); 1825 1826 // FIXME: Add tests that call `reset` in conditional branches: 1827 // ExpectDiagnosticsFor( 1828 // R"( 1829 // #include "unchecked_optional_access_test.h" 1830 // 1831 // void target(bool b) { 1832 // $ns::$optional<int> opt = $ns::make_optional(0); 1833 // if (b) { 1834 // opt.reset(); 1835 // } 1836 // if (b) { 1837 // opt.value(); // [[unsafe]] 1838 // } else { 1839 // opt.value(); 1840 // } 1841 // } 1842 // )"); 1843 } 1844 1845 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) { 1846 ExpectDiagnosticsFor(R"( 1847 #include "unchecked_optional_access_test.h" 1848 1849 struct Foo {}; 1850 1851 void target() { 1852 $ns::$optional<Foo> opt; 1853 opt = Foo(); 1854 opt.value(); 1855 } 1856 )"); 1857 1858 ExpectDiagnosticsFor(R"( 1859 #include "unchecked_optional_access_test.h" 1860 1861 struct Foo {}; 1862 1863 void target() { 1864 $ns::$optional<Foo> opt; 1865 (opt = Foo()).value(); 1866 (void)0; 1867 } 1868 )"); 1869 1870 ExpectDiagnosticsFor(R"( 1871 #include "unchecked_optional_access_test.h" 1872 1873 struct MyString { 1874 MyString(const char*); 1875 }; 1876 1877 void target() { 1878 $ns::$optional<MyString> opt; 1879 opt = "foo"; 1880 opt.value(); 1881 } 1882 )"); 1883 1884 ExpectDiagnosticsFor(R"( 1885 #include "unchecked_optional_access_test.h" 1886 1887 struct MyString { 1888 MyString(const char*); 1889 }; 1890 1891 void target() { 1892 $ns::$optional<MyString> opt; 1893 (opt = "foo").value(); 1894 } 1895 )"); 1896 } 1897 1898 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) { 1899 ExpectDiagnosticsFor( 1900 R"( 1901 #include "unchecked_optional_access_test.h" 1902 1903 struct Foo {}; 1904 1905 struct Bar { 1906 Bar(const Foo&); 1907 }; 1908 1909 void target() { 1910 $ns::$optional<Foo> opt1 = Foo(); 1911 $ns::$optional<Bar> opt2; 1912 opt2 = opt1; 1913 opt2.value(); 1914 } 1915 )"); 1916 1917 ExpectDiagnosticsFor( 1918 R"( 1919 #include "unchecked_optional_access_test.h" 1920 1921 struct Foo {}; 1922 1923 struct Bar { 1924 Bar(const Foo&); 1925 }; 1926 1927 void target() { 1928 $ns::$optional<Foo> opt1; 1929 $ns::$optional<Bar> opt2; 1930 if (opt2.has_value()) { 1931 opt2 = opt1; 1932 opt2.value(); // [[unsafe]] 1933 } 1934 } 1935 )"); 1936 1937 ExpectDiagnosticsFor( 1938 R"( 1939 #include "unchecked_optional_access_test.h" 1940 1941 struct Foo {}; 1942 1943 struct Bar { 1944 Bar(const Foo&); 1945 }; 1946 1947 void target() { 1948 $ns::$optional<Foo> opt1 = Foo(); 1949 $ns::$optional<Bar> opt2; 1950 (opt2 = opt1).value(); 1951 (void)0; 1952 } 1953 )"); 1954 } 1955 1956 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) { 1957 ExpectDiagnosticsFor( 1958 R"( 1959 #include "unchecked_optional_access_test.h" 1960 1961 void target() { 1962 $ns::$optional<int> opt = 3; 1963 opt = $ns::nullopt; 1964 opt.value(); // [[unsafe]] 1965 } 1966 )"); 1967 1968 ExpectDiagnosticsFor( 1969 R"( 1970 #include "unchecked_optional_access_test.h" 1971 1972 void target() { 1973 $ns::$optional<int> opt = 3; 1974 (opt = $ns::nullopt).value(); // [[unsafe]] 1975 } 1976 )"); 1977 } 1978 1979 TEST_P(UncheckedOptionalAccessTest, OptionalSwap) { 1980 ExpectDiagnosticsFor( 1981 R"( 1982 #include "unchecked_optional_access_test.h" 1983 1984 void target() { 1985 $ns::$optional<int> opt1 = $ns::nullopt; 1986 $ns::$optional<int> opt2 = 3; 1987 1988 opt1.swap(opt2); 1989 1990 opt1.value(); 1991 1992 opt2.value(); // [[unsafe]] 1993 } 1994 )"); 1995 1996 ExpectDiagnosticsFor( 1997 R"( 1998 #include "unchecked_optional_access_test.h" 1999 2000 void target() { 2001 $ns::$optional<int> opt1 = $ns::nullopt; 2002 $ns::$optional<int> opt2 = 3; 2003 2004 opt2.swap(opt1); 2005 2006 opt1.value(); 2007 2008 opt2.value(); // [[unsafe]] 2009 } 2010 )"); 2011 } 2012 2013 TEST_P(UncheckedOptionalAccessTest, StdSwap) { 2014 ExpectDiagnosticsFor( 2015 R"( 2016 #include "unchecked_optional_access_test.h" 2017 2018 void target() { 2019 $ns::$optional<int> opt1 = $ns::nullopt; 2020 $ns::$optional<int> opt2 = 3; 2021 2022 std::swap(opt1, opt2); 2023 2024 opt1.value(); 2025 2026 opt2.value(); // [[unsafe]] 2027 } 2028 )"); 2029 2030 ExpectDiagnosticsFor( 2031 R"( 2032 #include "unchecked_optional_access_test.h" 2033 2034 void target() { 2035 $ns::$optional<int> opt1 = $ns::nullopt; 2036 $ns::$optional<int> opt2 = 3; 2037 2038 std::swap(opt2, opt1); 2039 2040 opt1.value(); 2041 2042 opt2.value(); // [[unsafe]] 2043 } 2044 )"); 2045 } 2046 2047 TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) { 2048 // We suppress diagnostics for values reachable from smart pointers (other 2049 // than `optional` itself). 2050 ExpectDiagnosticsFor( 2051 R"( 2052 #include "unchecked_optional_access_test.h" 2053 2054 template <typename T> 2055 struct smart_ptr { 2056 T& operator*() &; 2057 T* operator->(); 2058 }; 2059 2060 struct Foo { 2061 $ns::$optional<int> opt; 2062 }; 2063 2064 void target() { 2065 smart_ptr<Foo> foo; 2066 *foo->opt; 2067 *(*foo).opt; 2068 } 2069 )"); 2070 } 2071 2072 TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) { 2073 ExpectDiagnosticsFor( 2074 R"( 2075 #include "unchecked_optional_access_test.h" 2076 2077 $ns::$optional<int> MakeOpt(); 2078 2079 void target() { 2080 $ns::$optional<int> opt = 0; 2081 opt = MakeOpt(); 2082 opt.value(); // [[unsafe]] 2083 } 2084 )"); 2085 ExpectDiagnosticsFor( 2086 R"( 2087 #include "unchecked_optional_access_test.h" 2088 2089 const $ns::$optional<int>& MakeOpt(); 2090 2091 void target() { 2092 $ns::$optional<int> opt = 0; 2093 opt = MakeOpt(); 2094 opt.value(); // [[unsafe]] 2095 } 2096 )"); 2097 2098 ExpectDiagnosticsFor( 2099 R"( 2100 #include "unchecked_optional_access_test.h" 2101 2102 using IntOpt = $ns::$optional<int>; 2103 IntOpt MakeOpt(); 2104 2105 void target() { 2106 IntOpt opt = 0; 2107 opt = MakeOpt(); 2108 opt.value(); // [[unsafe]] 2109 } 2110 )"); 2111 2112 ExpectDiagnosticsFor( 2113 R"( 2114 #include "unchecked_optional_access_test.h" 2115 2116 using IntOpt = $ns::$optional<int>; 2117 const IntOpt& MakeOpt(); 2118 2119 void target() { 2120 IntOpt opt = 0; 2121 opt = MakeOpt(); 2122 opt.value(); // [[unsafe]] 2123 } 2124 )"); 2125 } 2126 2127 // Verifies that the model sees through aliases. 2128 TEST_P(UncheckedOptionalAccessTest, WithAlias) { 2129 ExpectDiagnosticsFor( 2130 R"( 2131 #include "unchecked_optional_access_test.h" 2132 2133 template <typename T> 2134 using MyOptional = $ns::$optional<T>; 2135 2136 void target(MyOptional<int> opt) { 2137 opt.value(); // [[unsafe]] 2138 } 2139 )"); 2140 } 2141 2142 TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) { 2143 // Basic test that nested values are populated. We nest an optional because 2144 // its easy to use in a test, but the type of the nested value shouldn't 2145 // matter. 2146 ExpectDiagnosticsFor( 2147 R"( 2148 #include "unchecked_optional_access_test.h" 2149 2150 using Foo = $ns::$optional<std::string>; 2151 2152 void target($ns::$optional<Foo> foo) { 2153 if (foo && *foo) { 2154 foo->value(); 2155 } 2156 } 2157 )"); 2158 2159 // Mutation is supported for nested values. 2160 ExpectDiagnosticsFor( 2161 R"( 2162 #include "unchecked_optional_access_test.h" 2163 2164 using Foo = $ns::$optional<std::string>; 2165 2166 void target($ns::$optional<Foo> foo) { 2167 if (foo && *foo) { 2168 foo->reset(); 2169 foo->value(); // [[unsafe]] 2170 } 2171 } 2172 )"); 2173 } 2174 2175 // Tests that structs can be nested. We use an optional field because its easy 2176 // to use in a test, but the type of the field shouldn't matter. 2177 TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) { 2178 ExpectDiagnosticsFor( 2179 R"( 2180 #include "unchecked_optional_access_test.h" 2181 2182 struct Foo { 2183 $ns::$optional<std::string> opt; 2184 }; 2185 2186 void target($ns::$optional<Foo> foo) { 2187 if (foo && foo->opt) { 2188 foo->opt.value(); 2189 } 2190 } 2191 )"); 2192 } 2193 2194 TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) { 2195 // FIXME: Fix when to initialize `value`. All unwrapping should be safe in 2196 // this example, but `value` initialization is done multiple times during the 2197 // fixpoint iterations and joining the environment won't correctly merge them. 2198 ExpectDiagnosticsFor( 2199 R"( 2200 #include "unchecked_optional_access_test.h" 2201 2202 using Foo = $ns::$optional<std::string>; 2203 2204 void target($ns::$optional<Foo> foo, bool b) { 2205 if (!foo.has_value()) return; 2206 if (b) { 2207 if (!foo->has_value()) return; 2208 // We have created `foo.value()`. 2209 foo->value(); 2210 } else { 2211 if (!foo->has_value()) return; 2212 // We have created `foo.value()` again, in a different environment. 2213 foo->value(); 2214 } 2215 // Now we merge the two values. UncheckedOptionalAccessModel::merge() will 2216 // throw away the "value" property. 2217 foo->value(); // [[unsafe]] 2218 } 2219 )"); 2220 } 2221 2222 TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) { 2223 ExpectDiagnosticsFor( 2224 R"( 2225 #include "unchecked_optional_access_test.h" 2226 2227 template <typename T> 2228 struct smart_ptr { 2229 typename std::add_lvalue_reference<T>::type operator*() &; 2230 }; 2231 2232 void target() { 2233 smart_ptr<$ns::$optional<float>> x; 2234 *x = $ns::nullopt; 2235 (*x).value(); // [[unsafe]] 2236 } 2237 )"); 2238 } 2239 2240 TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) { 2241 ExpectDiagnosticsFor(R"code( 2242 #include "unchecked_optional_access_test.h" 2243 2244 void target(bool b, $ns::$optional<int> opt) { 2245 if (b || opt.has_value()) { 2246 if (!b) { 2247 opt.value(); 2248 } 2249 } 2250 } 2251 )code"); 2252 2253 ExpectDiagnosticsFor(R"code( 2254 #include "unchecked_optional_access_test.h" 2255 2256 void target(bool b, $ns::$optional<int> opt) { 2257 if (b && !opt.has_value()) return; 2258 if (b) { 2259 opt.value(); 2260 } 2261 } 2262 )code"); 2263 2264 ExpectDiagnosticsFor( 2265 R"code( 2266 #include "unchecked_optional_access_test.h" 2267 2268 void target(bool b, $ns::$optional<int> opt) { 2269 if (opt.has_value()) b = true; 2270 if (b) { 2271 opt.value(); // [[unsafe]] 2272 } 2273 } 2274 )code"); 2275 2276 ExpectDiagnosticsFor(R"code( 2277 #include "unchecked_optional_access_test.h" 2278 2279 void target(bool b, $ns::$optional<int> opt) { 2280 if (b) return; 2281 if (opt.has_value()) b = true; 2282 if (b) { 2283 opt.value(); 2284 } 2285 } 2286 )code"); 2287 2288 ExpectDiagnosticsFor(R"( 2289 #include "unchecked_optional_access_test.h" 2290 2291 void target(bool b, $ns::$optional<int> opt) { 2292 if (opt.has_value() == b) { 2293 if (b) { 2294 opt.value(); 2295 } 2296 } 2297 } 2298 )"); 2299 2300 ExpectDiagnosticsFor(R"( 2301 #include "unchecked_optional_access_test.h" 2302 2303 void target(bool b, $ns::$optional<int> opt) { 2304 if (opt.has_value() != b) { 2305 if (!b) { 2306 opt.value(); 2307 } 2308 } 2309 } 2310 )"); 2311 2312 ExpectDiagnosticsFor(R"( 2313 #include "unchecked_optional_access_test.h" 2314 2315 void target(bool b) { 2316 $ns::$optional<int> opt1 = $ns::nullopt; 2317 $ns::$optional<int> opt2; 2318 if (b) { 2319 opt2 = $ns::nullopt; 2320 } else { 2321 opt2 = $ns::nullopt; 2322 } 2323 if (opt2.has_value()) { 2324 opt1.value(); 2325 } 2326 } 2327 )"); 2328 2329 // FIXME: Add support for operator==. 2330 // ExpectDiagnosticsFor(R"( 2331 // #include "unchecked_optional_access_test.h" 2332 // 2333 // void target($ns::$optional<int> opt1, $ns::$optional<int> opt2) { 2334 // if (opt1 == opt2) { 2335 // if (opt1.has_value()) { 2336 // opt2.value(); 2337 // } 2338 // } 2339 // } 2340 // )"); 2341 } 2342 2343 TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) { 2344 ExpectDiagnosticsFor( 2345 R"code( 2346 #include "unchecked_optional_access_test.h" 2347 2348 void target(bool b) { 2349 $ns::$optional<int> opt; 2350 if (b) { 2351 opt = Make<$ns::$optional<int>>(); 2352 } else { 2353 opt = Make<$ns::$optional<int>>(); 2354 } 2355 if (opt.has_value()) { 2356 opt.value(); 2357 } else { 2358 opt.value(); // [[unsafe]] 2359 } 2360 } 2361 )code"); 2362 2363 ExpectDiagnosticsFor(R"code( 2364 #include "unchecked_optional_access_test.h" 2365 2366 void target(bool b) { 2367 $ns::$optional<int> opt; 2368 if (b) { 2369 opt = Make<$ns::$optional<int>>(); 2370 if (!opt.has_value()) return; 2371 } else { 2372 opt = Make<$ns::$optional<int>>(); 2373 if (!opt.has_value()) return; 2374 } 2375 opt.value(); 2376 } 2377 )code"); 2378 2379 ExpectDiagnosticsFor( 2380 R"code( 2381 #include "unchecked_optional_access_test.h" 2382 2383 void target(bool b) { 2384 $ns::$optional<int> opt; 2385 if (b) { 2386 opt = Make<$ns::$optional<int>>(); 2387 if (!opt.has_value()) return; 2388 } else { 2389 opt = Make<$ns::$optional<int>>(); 2390 } 2391 opt.value(); // [[unsafe]] 2392 } 2393 )code"); 2394 2395 ExpectDiagnosticsFor( 2396 R"code( 2397 #include "unchecked_optional_access_test.h" 2398 2399 void target(bool b) { 2400 $ns::$optional<int> opt; 2401 if (b) { 2402 opt = 1; 2403 } else { 2404 opt = 2; 2405 } 2406 opt.value(); 2407 } 2408 )code"); 2409 2410 ExpectDiagnosticsFor( 2411 R"code( 2412 #include "unchecked_optional_access_test.h" 2413 2414 void target(bool b) { 2415 $ns::$optional<int> opt; 2416 if (b) { 2417 opt = 1; 2418 } else { 2419 opt = Make<$ns::$optional<int>>(); 2420 } 2421 opt.value(); // [[unsafe]] 2422 } 2423 )code"); 2424 } 2425 2426 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoop) { 2427 ExpectDiagnosticsFor(R"( 2428 #include "unchecked_optional_access_test.h" 2429 2430 void target() { 2431 $ns::$optional<int> opt = 3; 2432 while (Make<bool>()) { 2433 opt.value(); 2434 } 2435 } 2436 )"); 2437 2438 ExpectDiagnosticsFor(R"( 2439 #include "unchecked_optional_access_test.h" 2440 2441 void target() { 2442 $ns::$optional<int> opt = 3; 2443 while (Make<bool>()) { 2444 opt.value(); 2445 2446 opt = Make<$ns::$optional<int>>(); 2447 if (!opt.has_value()) return; 2448 } 2449 } 2450 )"); 2451 2452 ExpectDiagnosticsFor( 2453 R"( 2454 #include "unchecked_optional_access_test.h" 2455 2456 void target() { 2457 $ns::$optional<int> opt = 3; 2458 while (Make<bool>()) { 2459 opt.value(); // [[unsafe]] 2460 2461 opt = Make<$ns::$optional<int>>(); 2462 } 2463 } 2464 )"); 2465 2466 ExpectDiagnosticsFor( 2467 R"( 2468 #include "unchecked_optional_access_test.h" 2469 2470 void target() { 2471 $ns::$optional<int> opt = 3; 2472 while (Make<bool>()) { 2473 opt.value(); // [[unsafe]] 2474 2475 opt = Make<$ns::$optional<int>>(); 2476 if (!opt.has_value()) continue; 2477 } 2478 } 2479 )"); 2480 } 2481 2482 // FIXME: Add support for: 2483 // - constructors (copy, move) 2484 // - assignment operators (default, copy, move) 2485 // - invalidation (passing optional by non-const reference/pointer) 2486