Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / UncheckedOptionalAccessModelTest.cpp
blob76af8baba85183980337b3aba44bd34b1a495795
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.
10 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
11 #include "TestingSupport.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchers.h"
14 #include "clang/Basic/SourceLocation.h"
15 #include "clang/Frontend/TextDiagnostic.h"
16 #include "clang/Tooling/Tooling.h"
17 #include "llvm/ADT/DenseSet.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/Support/Error.h"
20 #include "gmock/gmock.h"
21 #include "gtest/gtest.h"
22 #include <optional>
23 #include <string>
24 #include <utility>
25 #include <vector>
27 using namespace clang;
28 using namespace dataflow;
29 using namespace test;
31 using ::testing::ContainerEq;
33 // FIXME: Move header definitions in separate file(s).
34 static constexpr char CSDtdDefHeader[] = R"(
35 #ifndef CSTDDEF_H
36 #define CSTDDEF_H
38 namespace std {
40 typedef decltype(sizeof(char)) size_t;
42 using nullptr_t = decltype(nullptr);
44 } // namespace std
46 #endif // CSTDDEF_H
47 )";
49 static constexpr char StdTypeTraitsHeader[] = R"(
50 #ifndef STD_TYPE_TRAITS_H
51 #define STD_TYPE_TRAITS_H
53 #include "cstddef.h"
55 namespace std {
57 template <typename T, T V>
58 struct integral_constant {
59 static constexpr T value = V;
62 using true_type = integral_constant<bool, true>;
63 using false_type = integral_constant<bool, false>;
65 template< class T > struct remove_reference {typedef T type;};
66 template< class T > struct remove_reference<T&> {typedef T type;};
67 template< class T > struct remove_reference<T&&> {typedef T type;};
69 template <class T>
70 using remove_reference_t = typename remove_reference<T>::type;
72 template <class T>
73 struct remove_extent {
74 typedef T type;
77 template <class T>
78 struct remove_extent<T[]> {
79 typedef T type;
82 template <class T, size_t N>
83 struct remove_extent<T[N]> {
84 typedef T type;
87 template <class T>
88 struct is_array : false_type {};
90 template <class T>
91 struct is_array<T[]> : true_type {};
93 template <class T, size_t N>
94 struct is_array<T[N]> : true_type {};
96 template <class>
97 struct is_function : false_type {};
99 template <class Ret, class... Args>
100 struct is_function<Ret(Args...)> : true_type {};
102 namespace detail {
104 template <class T>
105 struct type_identity {
106 using type = T;
107 }; // or use type_identity (since C++20)
109 template <class T>
110 auto try_add_pointer(int) -> type_identity<typename remove_reference<T>::type*>;
111 template <class T>
112 auto try_add_pointer(...) -> type_identity<T>;
114 } // namespace detail
116 template <class T>
117 struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {};
119 template <bool B, class T, class F>
120 struct conditional {
121 typedef T type;
124 template <class T, class F>
125 struct conditional<false, T, F> {
126 typedef F type;
129 template <class T>
130 struct remove_cv {
131 typedef T type;
133 template <class T>
134 struct remove_cv<const T> {
135 typedef T type;
137 template <class T>
138 struct remove_cv<volatile T> {
139 typedef T type;
141 template <class T>
142 struct remove_cv<const volatile T> {
143 typedef T type;
146 template <class T>
147 using remove_cv_t = typename remove_cv<T>::type;
149 template <class T>
150 struct decay {
151 private:
152 typedef typename remove_reference<T>::type U;
154 public:
155 typedef typename conditional<
156 is_array<U>::value, typename remove_extent<U>::type*,
157 typename conditional<is_function<U>::value, typename add_pointer<U>::type,
158 typename remove_cv<U>::type>::type>::type type;
161 template <bool B, class T = void>
162 struct enable_if {};
164 template <class T>
165 struct enable_if<true, T> {
166 typedef T type;
169 template <bool B, class T = void>
170 using enable_if_t = typename enable_if<B, T>::type;
172 template <class T, class U>
173 struct is_same : false_type {};
175 template <class T>
176 struct is_same<T, T> : true_type {};
178 template <class T>
179 struct is_void : is_same<void, typename remove_cv<T>::type> {};
181 namespace detail {
183 template <class T>
184 auto try_add_lvalue_reference(int) -> type_identity<T&>;
185 template <class T>
186 auto try_add_lvalue_reference(...) -> type_identity<T>;
188 template <class T>
189 auto try_add_rvalue_reference(int) -> type_identity<T&&>;
190 template <class T>
191 auto try_add_rvalue_reference(...) -> type_identity<T>;
193 } // namespace detail
195 template <class T>
196 struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {
199 template <class T>
200 struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {
203 template <class T>
204 typename add_rvalue_reference<T>::type declval() noexcept;
206 namespace detail {
208 template <class T>
209 auto test_returnable(int)
210 -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{});
211 template <class>
212 auto test_returnable(...) -> false_type;
214 template <class From, class To>
215 auto test_implicitly_convertible(int)
216 -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{});
217 template <class, class>
218 auto test_implicitly_convertible(...) -> false_type;
220 } // namespace detail
222 template <class From, class To>
223 struct is_convertible
224 : integral_constant<bool,
225 (decltype(detail::test_returnable<To>(0))::value &&
226 decltype(detail::test_implicitly_convertible<From, To>(
227 0))::value) ||
228 (is_void<From>::value && is_void<To>::value)> {};
230 template <class From, class To>
231 inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
233 template <class...>
234 using void_t = void;
236 template <class, class T, class... Args>
237 struct is_constructible_ : false_type {};
239 template <class T, class... Args>
240 struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...>
241 : true_type {};
243 template <class T, class... Args>
244 using is_constructible = is_constructible_<void_t<>, T, Args...>;
246 template <class T, class... Args>
247 inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
249 template <class _Tp>
250 struct __uncvref {
251 typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type;
254 template <class _Tp>
255 using __uncvref_t = typename __uncvref<_Tp>::type;
257 template <bool _Val>
258 using _BoolConstant = integral_constant<bool, _Val>;
260 template <class _Tp, class _Up>
261 using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>;
263 template <class _Tp, class _Up>
264 using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>;
266 template <bool>
267 struct _MetaBase;
268 template <>
269 struct _MetaBase<true> {
270 template <class _Tp, class _Up>
271 using _SelectImpl = _Tp;
272 template <template <class...> class _FirstFn, template <class...> class,
273 class... _Args>
274 using _SelectApplyImpl = _FirstFn<_Args...>;
275 template <class _First, class...>
276 using _FirstImpl = _First;
277 template <class, class _Second, class...>
278 using _SecondImpl = _Second;
279 template <class _Result, class _First, class... _Rest>
280 using _OrImpl =
281 typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>::
282 template _OrImpl<_First, _Rest...>;
285 template <>
286 struct _MetaBase<false> {
287 template <class _Tp, class _Up>
288 using _SelectImpl = _Up;
289 template <template <class...> class, template <class...> class _SecondFn,
290 class... _Args>
291 using _SelectApplyImpl = _SecondFn<_Args...>;
292 template <class _Result, class...>
293 using _OrImpl = _Result;
296 template <bool _Cond, class _IfRes, class _ElseRes>
297 using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
299 template <class... _Rest>
300 using _Or = typename _MetaBase<sizeof...(_Rest) !=
301 0>::template _OrImpl<false_type, _Rest...>;
303 template <bool _Bp, class _Tp = void>
304 using __enable_if_t = typename enable_if<_Bp, _Tp>::type;
306 template <class...>
307 using __expand_to_true = true_type;
308 template <class... _Pred>
309 __expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int);
310 template <class...>
311 false_type __and_helper(...);
312 template <class... _Pred>
313 using _And = decltype(__and_helper<_Pred...>(0));
315 template <class _Pred>
316 struct _Not : _BoolConstant<!_Pred::value> {};
318 struct __check_tuple_constructor_fail {
319 static constexpr bool __enable_explicit_default() { return false; }
320 static constexpr bool __enable_implicit_default() { return false; }
321 template <class...>
322 static constexpr bool __enable_explicit() {
323 return false;
325 template <class...>
326 static constexpr bool __enable_implicit() {
327 return false;
331 template <typename, typename _Tp>
332 struct __select_2nd {
333 typedef _Tp type;
335 template <class _Tp, class _Arg>
336 typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())),
337 true_type>::type
338 __is_assignable_test(int);
339 template <class, class>
340 false_type __is_assignable_test(...);
341 template <class _Tp, class _Arg,
342 bool = is_void<_Tp>::value || is_void<_Arg>::value>
343 struct __is_assignable_imp
344 : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {};
345 template <class _Tp, class _Arg>
346 struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {};
347 template <class _Tp, class _Arg>
348 struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {};
350 template <class _Tp>
351 struct __libcpp_is_integral : public false_type {};
352 template <>
353 struct __libcpp_is_integral<bool> : public true_type {};
354 template <>
355 struct __libcpp_is_integral<char> : public true_type {};
356 template <>
357 struct __libcpp_is_integral<signed char> : public true_type {};
358 template <>
359 struct __libcpp_is_integral<unsigned char> : public true_type {};
360 template <>
361 struct __libcpp_is_integral<wchar_t> : public true_type {};
362 template <>
363 struct __libcpp_is_integral<short> : public true_type {}; // NOLINT
364 template <>
365 struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT
366 template <>
367 struct __libcpp_is_integral<int> : public true_type {};
368 template <>
369 struct __libcpp_is_integral<unsigned int> : public true_type {};
370 template <>
371 struct __libcpp_is_integral<long> : public true_type {}; // NOLINT
372 template <>
373 struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT
374 template <>
375 struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT
376 template <> // NOLINTNEXTLINE
377 struct __libcpp_is_integral<unsigned long long> : public true_type {};
378 template <class _Tp>
379 struct is_integral
380 : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {};
382 template <class _Tp>
383 struct __libcpp_is_floating_point : public false_type {};
384 template <>
385 struct __libcpp_is_floating_point<float> : public true_type {};
386 template <>
387 struct __libcpp_is_floating_point<double> : public true_type {};
388 template <>
389 struct __libcpp_is_floating_point<long double> : public true_type {};
390 template <class _Tp>
391 struct is_floating_point
392 : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {};
394 template <class _Tp>
395 struct is_arithmetic
396 : public integral_constant<bool, is_integral<_Tp>::value ||
397 is_floating_point<_Tp>::value> {};
399 template <class _Tp>
400 struct __libcpp_is_pointer : public false_type {};
401 template <class _Tp>
402 struct __libcpp_is_pointer<_Tp*> : public true_type {};
403 template <class _Tp>
404 struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> {
407 template <class _Tp>
408 struct __libcpp_is_member_pointer : public false_type {};
409 template <class _Tp, class _Up>
410 struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {};
411 template <class _Tp>
412 struct is_member_pointer
413 : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {};
415 template <class _Tp>
416 struct __libcpp_union : public false_type {};
417 template <class _Tp>
418 struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {};
420 template <class T>
421 struct is_reference : false_type {};
422 template <class T>
423 struct is_reference<T&> : true_type {};
424 template <class T>
425 struct is_reference<T&&> : true_type {};
427 template <class T>
428 inline constexpr bool is_reference_v = is_reference<T>::value;
430 struct __two {
431 char __lx[2];
434 namespace __is_class_imp {
435 template <class _Tp>
436 char __test(int _Tp::*);
437 template <class _Tp>
438 __two __test(...);
439 } // namespace __is_class_imp
440 template <class _Tp>
441 struct is_class
442 : public integral_constant<bool,
443 sizeof(__is_class_imp::__test<_Tp>(0)) == 1 &&
444 !is_union<_Tp>::value> {};
446 template <class _Tp>
447 struct __is_nullptr_t_impl : public false_type {};
448 template <>
449 struct __is_nullptr_t_impl<nullptr_t> : public true_type {};
450 template <class _Tp>
451 struct __is_nullptr_t
452 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
453 template <class _Tp>
454 struct is_null_pointer
455 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
457 template <class _Tp>
458 struct is_enum
459 : public integral_constant<
460 bool, !is_void<_Tp>::value && !is_integral<_Tp>::value &&
461 !is_floating_point<_Tp>::value && !is_array<_Tp>::value &&
462 !is_pointer<_Tp>::value && !is_reference<_Tp>::value &&
463 !is_member_pointer<_Tp>::value && !is_union<_Tp>::value &&
464 !is_class<_Tp>::value && !is_function<_Tp>::value> {};
466 template <class _Tp>
467 struct is_scalar
468 : public integral_constant<
469 bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value ||
470 is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value ||
471 is_enum<_Tp>::value> {};
472 template <>
473 struct is_scalar<nullptr_t> : public true_type {};
475 } // namespace std
477 #endif // STD_TYPE_TRAITS_H
480 static constexpr char AbslTypeTraitsHeader[] = R"(
481 #ifndef ABSL_TYPE_TRAITS_H
482 #define ABSL_TYPE_TRAITS_H
484 #include "std_type_traits.h"
486 namespace absl {
488 template <typename... Ts>
489 struct conjunction : std::true_type {};
491 template <typename T, typename... Ts>
492 struct conjunction<T, Ts...>
493 : std::conditional<T::value, conjunction<Ts...>, T>::type {};
495 template <typename T>
496 struct conjunction<T> : T {};
498 template <typename T>
499 struct negation : std::integral_constant<bool, !T::value> {};
501 template <bool B, typename T = void>
502 using enable_if_t = typename std::enable_if<B, T>::type;
504 } // namespace absl
506 #endif // ABSL_TYPE_TRAITS_H
509 static constexpr char StdStringHeader[] = R"(
510 #ifndef STRING_H
511 #define STRING_H
513 namespace std {
515 struct string {
516 string(const char*);
517 ~string();
518 bool empty();
520 bool operator!=(const string &LHS, const char *RHS);
522 } // namespace std
524 #endif // STRING_H
527 static constexpr char StdUtilityHeader[] = R"(
528 #ifndef UTILITY_H
529 #define UTILITY_H
531 #include "std_type_traits.h"
533 namespace std {
535 template <typename T>
536 constexpr remove_reference_t<T>&& move(T&& x);
538 template <typename T>
539 void swap(T& a, T& b) noexcept;
541 } // namespace std
543 #endif // UTILITY_H
546 static constexpr char StdInitializerListHeader[] = R"(
547 #ifndef INITIALIZER_LIST_H
548 #define INITIALIZER_LIST_H
550 namespace std {
552 template <typename T>
553 class initializer_list {
554 public:
555 initializer_list() noexcept;
558 } // namespace std
560 #endif // INITIALIZER_LIST_H
563 static constexpr char StdOptionalHeader[] = R"(
564 #include "std_initializer_list.h"
565 #include "std_type_traits.h"
566 #include "std_utility.h"
568 namespace std {
570 struct in_place_t {};
571 constexpr in_place_t in_place;
573 struct nullopt_t {
574 constexpr explicit nullopt_t() {}
576 constexpr nullopt_t nullopt;
578 template <class _Tp>
579 struct __optional_destruct_base {
580 constexpr void reset() noexcept;
583 template <class _Tp>
584 struct __optional_storage_base : __optional_destruct_base<_Tp> {
585 constexpr bool has_value() const noexcept;
588 template <typename _Tp>
589 class optional : private __optional_storage_base<_Tp> {
590 using __base = __optional_storage_base<_Tp>;
592 public:
593 using value_type = _Tp;
595 private:
596 struct _CheckOptionalArgsConstructor {
597 template <class _Up>
598 static constexpr bool __enable_implicit() {
599 return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
602 template <class _Up>
603 static constexpr bool __enable_explicit() {
604 return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
607 template <class _Up>
608 using _CheckOptionalArgsCtor =
609 _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value &&
610 _IsNotSame<__uncvref_t<_Up>, optional>::value,
611 _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>;
612 template <class _QualUp>
613 struct _CheckOptionalLikeConstructor {
614 template <class _Up, class _Opt = optional<_Up>>
615 using __check_constructible_from_opt =
616 _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>,
617 is_constructible<_Tp, _Opt&&>, is_constructible<_Tp, _Opt const&&>,
618 is_convertible<_Opt&, _Tp>, is_convertible<_Opt const&, _Tp>,
619 is_convertible<_Opt&&, _Tp>, is_convertible<_Opt const&&, _Tp>>;
620 template <class _Up, class _QUp = _QualUp>
621 static constexpr bool __enable_implicit() {
622 return is_convertible<_QUp, _Tp>::value &&
623 !__check_constructible_from_opt<_Up>::value;
625 template <class _Up, class _QUp = _QualUp>
626 static constexpr bool __enable_explicit() {
627 return !is_convertible<_QUp, _Tp>::value &&
628 !__check_constructible_from_opt<_Up>::value;
632 template <class _Up, class _QualUp>
633 using _CheckOptionalLikeCtor =
634 _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value,
635 _CheckOptionalLikeConstructor<_QualUp>,
636 __check_tuple_constructor_fail>;
639 template <class _Up, class _QualUp>
640 using _CheckOptionalLikeAssign = _If<
641 _And<
642 _IsNotSame<_Up, _Tp>,
643 is_constructible<_Tp, _QualUp>,
644 is_assignable<_Tp&, _QualUp>
645 >::value,
646 _CheckOptionalLikeConstructor<_QualUp>,
647 __check_tuple_constructor_fail
650 public:
651 constexpr optional() noexcept {}
652 constexpr optional(const optional&) = default;
653 constexpr optional(optional&&) = default;
654 constexpr optional(nullopt_t) noexcept {}
656 template <
657 class _InPlaceT, class... _Args,
658 class = enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>,
659 is_constructible<value_type, _Args...>>::value>>
660 constexpr explicit optional(_InPlaceT, _Args&&... __args);
662 template <class _Up, class... _Args,
663 class = enable_if_t<is_constructible_v<
664 value_type, initializer_list<_Up>&, _Args...>>>
665 constexpr explicit optional(in_place_t, initializer_list<_Up> __il,
666 _Args&&... __args);
668 template <
669 class _Up = value_type,
670 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(),
671 int> = 0>
672 constexpr optional(_Up&& __v);
674 template <
675 class _Up,
676 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(),
677 int> = 0>
678 constexpr explicit optional(_Up&& __v);
680 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::
681 template __enable_implicit<_Up>(),
682 int> = 0>
683 constexpr optional(const optional<_Up>& __v);
685 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::
686 template __enable_explicit<_Up>(),
687 int> = 0>
688 constexpr explicit optional(const optional<_Up>& __v);
690 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
691 template __enable_implicit<_Up>(),
692 int> = 0>
693 constexpr optional(optional<_Up>&& __v);
695 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
696 template __enable_explicit<_Up>(),
697 int> = 0>
698 constexpr explicit optional(optional<_Up>&& __v);
700 constexpr optional& operator=(nullopt_t) noexcept;
702 optional& operator=(const optional&);
704 optional& operator=(optional&&);
706 template <class _Up = value_type,
707 class = enable_if_t<_And<_IsNotSame<__uncvref_t<_Up>, optional>,
708 _Or<_IsNotSame<__uncvref_t<_Up>, value_type>,
709 _Not<is_scalar<value_type>>>,
710 is_constructible<value_type, _Up>,
711 is_assignable<value_type&, _Up>>::value>>
712 constexpr optional& operator=(_Up&& __v);
714 template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::
715 template __enable_assign<_Up>(),
716 int> = 0>
717 constexpr optional& operator=(const optional<_Up>& __v);
719 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
720 template __enable_assign<_Up>(),
721 int> = 0>
722 constexpr optional& operator=(optional<_Up>&& __v);
724 const _Tp& operator*() const&;
725 _Tp& operator*() &;
726 const _Tp&& operator*() const&&;
727 _Tp&& operator*() &&;
729 const _Tp* operator->() const;
730 _Tp* operator->();
732 const _Tp& value() const&;
733 _Tp& value() &;
734 const _Tp&& value() const&&;
735 _Tp&& value() &&;
737 template <typename U>
738 constexpr _Tp value_or(U&& v) const&;
739 template <typename U>
740 _Tp value_or(U&& v) &&;
742 template <typename... Args>
743 _Tp& emplace(Args&&... args);
745 template <typename U, typename... Args>
746 _Tp& emplace(std::initializer_list<U> ilist, Args&&... args);
748 using __base::reset;
750 constexpr explicit operator bool() const noexcept;
751 using __base::has_value;
753 constexpr void swap(optional& __opt) noexcept;
756 template <typename T>
757 constexpr optional<typename std::decay<T>::type> make_optional(T&& v);
759 template <typename T, typename... Args>
760 constexpr optional<T> make_optional(Args&&... args);
762 template <typename T, typename U, typename... Args>
763 constexpr optional<T> make_optional(std::initializer_list<U> il,
764 Args&&... args);
766 template <typename T, typename U>
767 constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs);
768 template <typename T, typename U>
769 constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs);
771 template <typename T>
772 constexpr bool operator==(const optional<T> &opt, nullopt_t);
773 template <typename T>
774 constexpr bool operator==(nullopt_t, const optional<T> &opt);
775 template <typename T>
776 constexpr bool operator!=(const optional<T> &opt, nullopt_t);
777 template <typename T>
778 constexpr bool operator!=(nullopt_t, const optional<T> &opt);
780 template <typename T, typename U>
781 constexpr bool operator==(const optional<T> &opt, const U &value);
782 template <typename T, typename U>
783 constexpr bool operator==(const T &value, const optional<U> &opt);
784 template <typename T, typename U>
785 constexpr bool operator!=(const optional<T> &opt, const U &value);
786 template <typename T, typename U>
787 constexpr bool operator!=(const T &value, const optional<U> &opt);
789 } // namespace std
792 static constexpr char AbslOptionalHeader[] = R"(
793 #include "absl_type_traits.h"
794 #include "std_initializer_list.h"
795 #include "std_type_traits.h"
796 #include "std_utility.h"
798 namespace absl {
800 struct nullopt_t {
801 constexpr explicit nullopt_t() {}
803 constexpr nullopt_t nullopt;
805 struct in_place_t {};
806 constexpr in_place_t in_place;
808 template <typename T>
809 class optional;
811 namespace optional_internal {
813 template <typename T, typename U>
814 struct is_constructible_convertible_from_optional
815 : std::integral_constant<
816 bool, std::is_constructible<T, optional<U>&>::value ||
817 std::is_constructible<T, optional<U>&&>::value ||
818 std::is_constructible<T, const optional<U>&>::value ||
819 std::is_constructible<T, const optional<U>&&>::value ||
820 std::is_convertible<optional<U>&, T>::value ||
821 std::is_convertible<optional<U>&&, T>::value ||
822 std::is_convertible<const optional<U>&, T>::value ||
823 std::is_convertible<const optional<U>&&, T>::value> {};
825 template <typename T, typename U>
826 struct is_constructible_convertible_assignable_from_optional
827 : std::integral_constant<
828 bool, is_constructible_convertible_from_optional<T, U>::value ||
829 std::is_assignable<T&, optional<U>&>::value ||
830 std::is_assignable<T&, optional<U>&&>::value ||
831 std::is_assignable<T&, const optional<U>&>::value ||
832 std::is_assignable<T&, const optional<U>&&>::value> {};
834 } // namespace optional_internal
836 template <typename T>
837 class optional {
838 public:
839 constexpr optional() noexcept;
841 constexpr optional(nullopt_t) noexcept;
843 optional(const optional&) = default;
845 optional(optional&&) = default;
847 template <typename InPlaceT, typename... Args,
848 absl::enable_if_t<absl::conjunction<
849 std::is_same<InPlaceT, in_place_t>,
850 std::is_constructible<T, Args&&...>>::value>* = nullptr>
851 constexpr explicit optional(InPlaceT, Args&&... args);
853 template <typename U, typename... Args,
854 typename = typename std::enable_if<std::is_constructible<
855 T, std::initializer_list<U>&, Args&&...>::value>::type>
856 constexpr explicit optional(in_place_t, std::initializer_list<U> il,
857 Args&&... args);
859 template <
860 typename U = T,
861 typename std::enable_if<
862 absl::conjunction<absl::negation<std::is_same<
863 in_place_t, typename std::decay<U>::type>>,
864 absl::negation<std::is_same<
865 optional<T>, typename std::decay<U>::type>>,
866 std::is_convertible<U&&, T>,
867 std::is_constructible<T, U&&>>::value,
868 bool>::type = false>
869 constexpr optional(U&& v);
871 template <
872 typename U = T,
873 typename std::enable_if<
874 absl::conjunction<absl::negation<std::is_same<
875 in_place_t, typename std::decay<U>::type>>,
876 absl::negation<std::is_same<
877 optional<T>, typename std::decay<U>::type>>,
878 absl::negation<std::is_convertible<U&&, T>>,
879 std::is_constructible<T, U&&>>::value,
880 bool>::type = false>
881 explicit constexpr optional(U&& v);
883 template <typename U,
884 typename std::enable_if<
885 absl::conjunction<
886 absl::negation<std::is_same<T, U>>,
887 std::is_constructible<T, const U&>,
888 absl::negation<
889 optional_internal::
890 is_constructible_convertible_from_optional<T, U>>,
891 std::is_convertible<const U&, T>>::value,
892 bool>::type = false>
893 optional(const optional<U>& rhs);
895 template <typename U,
896 typename std::enable_if<
897 absl::conjunction<
898 absl::negation<std::is_same<T, U>>,
899 std::is_constructible<T, const U&>,
900 absl::negation<
901 optional_internal::
902 is_constructible_convertible_from_optional<T, U>>,
903 absl::negation<std::is_convertible<const U&, T>>>::value,
904 bool>::type = false>
905 explicit optional(const optional<U>& rhs);
907 template <
908 typename U,
909 typename std::enable_if<
910 absl::conjunction<
911 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
912 absl::negation<
913 optional_internal::is_constructible_convertible_from_optional<
914 T, U>>,
915 std::is_convertible<U&&, T>>::value,
916 bool>::type = false>
917 optional(optional<U>&& rhs);
919 template <
920 typename U,
921 typename std::enable_if<
922 absl::conjunction<
923 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
924 absl::negation<
925 optional_internal::is_constructible_convertible_from_optional<
926 T, U>>,
927 absl::negation<std::is_convertible<U&&, T>>>::value,
928 bool>::type = false>
929 explicit optional(optional<U>&& rhs);
931 optional& operator=(nullopt_t) noexcept;
933 optional& operator=(const optional& src);
935 optional& operator=(optional&& src);
937 template <
938 typename U = T,
939 typename = typename std::enable_if<absl::conjunction<
940 absl::negation<
941 std::is_same<optional<T>, typename std::decay<U>::type>>,
942 absl::negation<
943 absl::conjunction<std::is_scalar<T>,
944 std::is_same<T, typename std::decay<U>::type>>>,
945 std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type>
946 optional& operator=(U&& v);
948 template <
949 typename U,
950 typename = typename std::enable_if<absl::conjunction<
951 absl::negation<std::is_same<T, U>>,
952 std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>,
953 absl::negation<
954 optional_internal::
955 is_constructible_convertible_assignable_from_optional<
956 T, U>>>::value>::type>
957 optional& operator=(const optional<U>& rhs);
959 template <typename U,
960 typename = typename std::enable_if<absl::conjunction<
961 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>,
962 std::is_assignable<T&, U>,
963 absl::negation<
964 optional_internal::
965 is_constructible_convertible_assignable_from_optional<
966 T, U>>>::value>::type>
967 optional& operator=(optional<U>&& rhs);
969 const T& operator*() const&;
970 T& operator*() &;
971 const T&& operator*() const&&;
972 T&& operator*() &&;
974 const T* operator->() const;
975 T* operator->();
977 const T& value() const&;
978 T& value() &;
979 const T&& value() const&&;
980 T&& value() &&;
982 template <typename U>
983 constexpr T value_or(U&& v) const&;
984 template <typename U>
985 T value_or(U&& v) &&;
987 template <typename... Args>
988 T& emplace(Args&&... args);
990 template <typename U, typename... Args>
991 T& emplace(std::initializer_list<U> ilist, Args&&... args);
993 void reset() noexcept;
995 constexpr explicit operator bool() const noexcept;
996 constexpr bool has_value() const noexcept;
998 void swap(optional& rhs) noexcept;
1001 template <typename T>
1002 constexpr optional<typename std::decay<T>::type> make_optional(T&& v);
1004 template <typename T, typename... Args>
1005 constexpr optional<T> make_optional(Args&&... args);
1007 template <typename T, typename U, typename... Args>
1008 constexpr optional<T> make_optional(std::initializer_list<U> il,
1009 Args&&... args);
1011 template <typename T, typename U>
1012 constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs);
1013 template <typename T, typename U>
1014 constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs);
1016 template <typename T>
1017 constexpr bool operator==(const optional<T> &opt, nullopt_t);
1018 template <typename T>
1019 constexpr bool operator==(nullopt_t, const optional<T> &opt);
1020 template <typename T>
1021 constexpr bool operator!=(const optional<T> &opt, nullopt_t);
1022 template <typename T>
1023 constexpr bool operator!=(nullopt_t, const optional<T> &opt);
1025 template <typename T, typename U>
1026 constexpr bool operator==(const optional<T> &opt, const U &value);
1027 template <typename T, typename U>
1028 constexpr bool operator==(const T &value, const optional<U> &opt);
1029 template <typename T, typename U>
1030 constexpr bool operator!=(const optional<T> &opt, const U &value);
1031 template <typename T, typename U>
1032 constexpr bool operator!=(const T &value, const optional<U> &opt);
1034 } // namespace absl
1037 static constexpr char BaseOptionalHeader[] = R"(
1038 #include "std_initializer_list.h"
1039 #include "std_type_traits.h"
1040 #include "std_utility.h"
1042 namespace base {
1044 struct in_place_t {};
1045 constexpr in_place_t in_place;
1047 struct nullopt_t {
1048 constexpr explicit nullopt_t() {}
1050 constexpr nullopt_t nullopt;
1052 template <typename T>
1053 class Optional;
1055 namespace internal {
1057 template <typename T>
1058 using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>;
1060 template <typename T, typename U>
1061 struct IsConvertibleFromOptional
1062 : std::integral_constant<
1063 bool, std::is_constructible<T, Optional<U>&>::value ||
1064 std::is_constructible<T, const Optional<U>&>::value ||
1065 std::is_constructible<T, Optional<U>&&>::value ||
1066 std::is_constructible<T, const Optional<U>&&>::value ||
1067 std::is_convertible<Optional<U>&, T>::value ||
1068 std::is_convertible<const Optional<U>&, T>::value ||
1069 std::is_convertible<Optional<U>&&, T>::value ||
1070 std::is_convertible<const Optional<U>&&, T>::value> {};
1072 template <typename T, typename U>
1073 struct IsAssignableFromOptional
1074 : std::integral_constant<
1075 bool, IsConvertibleFromOptional<T, U>::value ||
1076 std::is_assignable<T&, Optional<U>&>::value ||
1077 std::is_assignable<T&, const Optional<U>&>::value ||
1078 std::is_assignable<T&, Optional<U>&&>::value ||
1079 std::is_assignable<T&, const Optional<U>&&>::value> {};
1081 } // namespace internal
1083 template <typename T>
1084 class Optional {
1085 public:
1086 using value_type = T;
1088 constexpr Optional() = default;
1089 constexpr Optional(const Optional& other) noexcept = default;
1090 constexpr Optional(Optional&& other) noexcept = default;
1092 constexpr Optional(nullopt_t);
1094 template <typename U,
1095 typename std::enable_if<
1096 std::is_constructible<T, const U&>::value &&
1097 !internal::IsConvertibleFromOptional<T, U>::value &&
1098 std::is_convertible<const U&, T>::value,
1099 bool>::type = false>
1100 Optional(const Optional<U>& other) noexcept;
1102 template <typename U,
1103 typename std::enable_if<
1104 std::is_constructible<T, const U&>::value &&
1105 !internal::IsConvertibleFromOptional<T, U>::value &&
1106 !std::is_convertible<const U&, T>::value,
1107 bool>::type = false>
1108 explicit Optional(const Optional<U>& other) noexcept;
1110 template <typename U,
1111 typename std::enable_if<
1112 std::is_constructible<T, U&&>::value &&
1113 !internal::IsConvertibleFromOptional<T, U>::value &&
1114 std::is_convertible<U&&, T>::value,
1115 bool>::type = false>
1116 Optional(Optional<U>&& other) noexcept;
1118 template <typename U,
1119 typename std::enable_if<
1120 std::is_constructible<T, U&&>::value &&
1121 !internal::IsConvertibleFromOptional<T, U>::value &&
1122 !std::is_convertible<U&&, T>::value,
1123 bool>::type = false>
1124 explicit Optional(Optional<U>&& other) noexcept;
1126 template <class... Args>
1127 constexpr explicit Optional(in_place_t, Args&&... args);
1129 template <class U, class... Args,
1130 class = typename std::enable_if<std::is_constructible<
1131 value_type, std::initializer_list<U>&, Args...>::value>::type>
1132 constexpr explicit Optional(in_place_t, std::initializer_list<U> il,
1133 Args&&... args);
1135 template <
1136 typename U = value_type,
1137 typename std::enable_if<
1138 std::is_constructible<T, U&&>::value &&
1139 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
1140 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
1141 std::is_convertible<U&&, T>::value,
1142 bool>::type = false>
1143 constexpr Optional(U&& value);
1145 template <
1146 typename U = value_type,
1147 typename std::enable_if<
1148 std::is_constructible<T, U&&>::value &&
1149 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
1150 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
1151 !std::is_convertible<U&&, T>::value,
1152 bool>::type = false>
1153 constexpr explicit Optional(U&& value);
1155 Optional& operator=(const Optional& other) noexcept;
1157 Optional& operator=(Optional&& other) noexcept;
1159 Optional& operator=(nullopt_t);
1161 template <typename U>
1162 typename std::enable_if<
1163 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
1164 std::is_constructible<T, U>::value &&
1165 std::is_assignable<T&, U>::value &&
1166 (!std::is_scalar<T>::value ||
1167 !std::is_same<typename std::decay<U>::type, T>::value),
1168 Optional&>::type
1169 operator=(U&& value) noexcept;
1171 template <typename U>
1172 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value &&
1173 std::is_constructible<T, const U&>::value &&
1174 std::is_assignable<T&, const U&>::value,
1175 Optional&>::type
1176 operator=(const Optional<U>& other) noexcept;
1178 template <typename U>
1179 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value &&
1180 std::is_constructible<T, U>::value &&
1181 std::is_assignable<T&, U>::value,
1182 Optional&>::type
1183 operator=(Optional<U>&& other) noexcept;
1185 const T& operator*() const&;
1186 T& operator*() &;
1187 const T&& operator*() const&&;
1188 T&& operator*() &&;
1190 const T* operator->() const;
1191 T* operator->();
1193 const T& value() const&;
1194 T& value() &;
1195 const T&& value() const&&;
1196 T&& value() &&;
1198 template <typename U>
1199 constexpr T value_or(U&& v) const&;
1200 template <typename U>
1201 T value_or(U&& v) &&;
1203 template <typename... Args>
1204 T& emplace(Args&&... args);
1206 template <typename U, typename... Args>
1207 T& emplace(std::initializer_list<U> ilist, Args&&... args);
1209 void reset() noexcept;
1211 constexpr explicit operator bool() const noexcept;
1212 constexpr bool has_value() const noexcept;
1214 void swap(Optional& other);
1217 template <typename T>
1218 constexpr Optional<typename std::decay<T>::type> make_optional(T&& v);
1220 template <typename T, typename... Args>
1221 constexpr Optional<T> make_optional(Args&&... args);
1223 template <typename T, typename U, typename... Args>
1224 constexpr Optional<T> make_optional(std::initializer_list<U> il,
1225 Args&&... args);
1227 template <typename T, typename U>
1228 constexpr bool operator==(const Optional<T> &lhs, const Optional<U> &rhs);
1229 template <typename T, typename U>
1230 constexpr bool operator!=(const Optional<T> &lhs, const Optional<U> &rhs);
1232 template <typename T>
1233 constexpr bool operator==(const Optional<T> &opt, nullopt_t);
1234 template <typename T>
1235 constexpr bool operator==(nullopt_t, const Optional<T> &opt);
1236 template <typename T>
1237 constexpr bool operator!=(const Optional<T> &opt, nullopt_t);
1238 template <typename T>
1239 constexpr bool operator!=(nullopt_t, const Optional<T> &opt);
1241 template <typename T, typename U>
1242 constexpr bool operator==(const Optional<T> &opt, const U &value);
1243 template <typename T, typename U>
1244 constexpr bool operator==(const T &value, const Optional<U> &opt);
1245 template <typename T, typename U>
1246 constexpr bool operator!=(const Optional<T> &opt, const U &value);
1247 template <typename T, typename U>
1248 constexpr bool operator!=(const T &value, const Optional<U> &opt);
1250 } // namespace base
1253 /// Replaces all occurrences of `Pattern` in `S` with `Replacement`.
1254 static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern,
1255 const std::string &Replacement) {
1256 size_t Pos = 0;
1257 while (true) {
1258 Pos = S.find(Pattern, Pos);
1259 if (Pos == std::string::npos)
1260 break;
1261 S.replace(Pos, Pattern.size(), Replacement);
1265 struct OptionalTypeIdentifier {
1266 std::string NamespaceName;
1267 std::string TypeName;
1270 static raw_ostream &operator<<(raw_ostream &OS,
1271 const OptionalTypeIdentifier &TypeId) {
1272 OS << TypeId.NamespaceName << "::" << TypeId.TypeName;
1273 return OS;
1276 class UncheckedOptionalAccessTest
1277 : public ::testing::TestWithParam<OptionalTypeIdentifier> {
1278 protected:
1279 void ExpectDiagnosticsFor(std::string SourceCode) {
1280 ExpectDiagnosticsFor(SourceCode, ast_matchers::hasName("target"));
1283 void ExpectDiagnosticsForLambda(std::string SourceCode) {
1284 ExpectDiagnosticsFor(
1285 SourceCode, ast_matchers::hasDeclContext(
1286 ast_matchers::cxxRecordDecl(ast_matchers::isLambda())));
1289 template <typename FuncDeclMatcher>
1290 void ExpectDiagnosticsFor(std::string SourceCode,
1291 FuncDeclMatcher FuncMatcher) {
1292 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName);
1293 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName);
1295 std::vector<std::pair<std::string, std::string>> Headers;
1296 Headers.emplace_back("cstddef.h", CSDtdDefHeader);
1297 Headers.emplace_back("std_initializer_list.h", StdInitializerListHeader);
1298 Headers.emplace_back("std_string.h", StdStringHeader);
1299 Headers.emplace_back("std_type_traits.h", StdTypeTraitsHeader);
1300 Headers.emplace_back("std_utility.h", StdUtilityHeader);
1301 Headers.emplace_back("std_optional.h", StdOptionalHeader);
1302 Headers.emplace_back("absl_type_traits.h", AbslTypeTraitsHeader);
1303 Headers.emplace_back("absl_optional.h", AbslOptionalHeader);
1304 Headers.emplace_back("base_optional.h", BaseOptionalHeader);
1305 Headers.emplace_back("unchecked_optional_access_test.h", R"(
1306 #include "absl_optional.h"
1307 #include "base_optional.h"
1308 #include "std_initializer_list.h"
1309 #include "std_optional.h"
1310 #include "std_string.h"
1311 #include "std_utility.h"
1313 template <typename T>
1314 T Make();
1315 )");
1316 UncheckedOptionalAccessModelOptions Options{
1317 /*IgnoreSmartPointerDereference=*/true};
1318 std::vector<SourceLocation> Diagnostics;
1319 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>(
1320 AnalysisInputs<UncheckedOptionalAccessModel>(
1321 SourceCode, std::move(FuncMatcher),
1322 [](ASTContext &Ctx, Environment &) {
1323 return UncheckedOptionalAccessModel(Ctx);
1325 .withPostVisitCFG(
1326 [&Diagnostics,
1327 Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
1328 ASTContext &Ctx, const CFGElement &Elt,
1329 const TransferStateForDiagnostics<NoopLattice>
1330 &State) mutable {
1331 auto EltDiagnostics = Diagnoser(Elt, Ctx, State);
1332 llvm::move(EltDiagnostics, std::back_inserter(Diagnostics));
1334 .withASTBuildArgs(
1335 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"})
1336 .withASTBuildVirtualMappedFiles(
1337 tooling::FileContentMappings(Headers.begin(), Headers.end())),
1338 /*VerifyResults=*/[&Diagnostics](
1339 const llvm::DenseMap<unsigned, std::string>
1340 &Annotations,
1341 const AnalysisOutputs &AO) {
1342 llvm::DenseSet<unsigned> AnnotationLines;
1343 for (const auto &[Line, _] : Annotations) {
1344 AnnotationLines.insert(Line);
1346 auto &SrcMgr = AO.ASTCtx.getSourceManager();
1347 llvm::DenseSet<unsigned> DiagnosticLines;
1348 for (SourceLocation &Loc : Diagnostics) {
1349 unsigned Line = SrcMgr.getPresumedLineNumber(Loc);
1350 DiagnosticLines.insert(Line);
1351 if (!AnnotationLines.contains(Line)) {
1352 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(
1353 new DiagnosticOptions());
1354 TextDiagnostic TD(llvm::errs(), AO.ASTCtx.getLangOpts(),
1355 DiagOpts.get());
1356 TD.emitDiagnostic(
1357 FullSourceLoc(Loc, SrcMgr), DiagnosticsEngine::Error,
1358 "unexpected diagnostic", std::nullopt, std::nullopt);
1362 EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
1364 if (Error)
1365 FAIL() << llvm::toString(std::move(Error));
1369 INSTANTIATE_TEST_SUITE_P(
1370 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest,
1371 ::testing::Values(OptionalTypeIdentifier{"std", "optional"},
1372 OptionalTypeIdentifier{"absl", "optional"},
1373 OptionalTypeIdentifier{"base", "Optional"}),
1374 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) {
1375 return Info.param.NamespaceName;
1378 // Verifies that similarly-named types are ignored.
1379 TEST_P(UncheckedOptionalAccessTest, NonTrackedOptionalType) {
1380 ExpectDiagnosticsFor(
1382 namespace other {
1383 namespace $ns {
1384 template <typename T>
1385 struct $optional {
1386 T value();
1390 void target($ns::$optional<int> opt) {
1391 opt.value();
1394 )");
1397 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) {
1398 ExpectDiagnosticsFor(R"(
1399 void target() {
1400 (void)0;
1402 )");
1405 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) {
1406 ExpectDiagnosticsFor(
1408 #include "unchecked_optional_access_test.h"
1410 void target($ns::$optional<int> opt) {
1411 opt.value(); // [[unsafe]]
1413 )");
1415 ExpectDiagnosticsFor(
1417 #include "unchecked_optional_access_test.h"
1419 void target($ns::$optional<int> opt) {
1420 std::move(opt).value(); // [[unsafe]]
1422 )");
1425 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) {
1426 ExpectDiagnosticsFor(
1428 #include "unchecked_optional_access_test.h"
1430 void target($ns::$optional<int> opt) {
1431 *opt; // [[unsafe]]
1433 )");
1435 ExpectDiagnosticsFor(
1437 #include "unchecked_optional_access_test.h"
1439 void target($ns::$optional<int> opt) {
1440 *std::move(opt); // [[unsafe]]
1442 )");
1445 TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) {
1446 ExpectDiagnosticsFor(
1448 #include "unchecked_optional_access_test.h"
1450 struct Foo {
1451 void foo();
1454 void target($ns::$optional<Foo> opt) {
1455 opt->foo(); // [[unsafe]]
1457 )");
1459 ExpectDiagnosticsFor(
1461 #include "unchecked_optional_access_test.h"
1463 struct Foo {
1464 void foo();
1467 void target($ns::$optional<Foo> opt) {
1468 std::move(opt)->foo(); // [[unsafe]]
1470 )");
1473 TEST_P(UncheckedOptionalAccessTest, HasValueCheck) {
1474 ExpectDiagnosticsFor(R"(
1475 #include "unchecked_optional_access_test.h"
1477 void target($ns::$optional<int> opt) {
1478 if (opt.has_value()) {
1479 opt.value();
1482 )");
1485 TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) {
1486 ExpectDiagnosticsFor(R"(
1487 #include "unchecked_optional_access_test.h"
1489 void target($ns::$optional<int> opt) {
1490 if (opt) {
1491 opt.value();
1494 )");
1497 TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) {
1498 ExpectDiagnosticsFor(
1500 #include "unchecked_optional_access_test.h"
1502 void target() {
1503 Make<$ns::$optional<int>>().value(); // [[unsafe]]
1504 (void)0;
1506 )");
1508 ExpectDiagnosticsFor(
1510 #include "unchecked_optional_access_test.h"
1512 void target($ns::$optional<int> opt) {
1513 std::move(opt).value(); // [[unsafe]]
1515 )");
1518 TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) {
1519 ExpectDiagnosticsFor(
1521 #include "unchecked_optional_access_test.h"
1523 void target() {
1524 $ns::$optional<int> opt;
1525 opt.value(); // [[unsafe]]
1527 )");
1530 TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) {
1531 ExpectDiagnosticsFor(
1533 #include "unchecked_optional_access_test.h"
1535 void target() {
1536 $ns::$optional<int> opt($ns::nullopt);
1537 opt.value(); // [[unsafe]]
1539 )");
1542 TEST_P(UncheckedOptionalAccessTest, NulloptConstructorWithSugaredType) {
1543 ExpectDiagnosticsFor(
1545 #include "unchecked_optional_access_test.h"
1546 template <typename T>
1547 using wrapper = T;
1549 template <typename T>
1550 wrapper<T> wrap(T);
1552 void target() {
1553 $ns::$optional<int> opt(wrap($ns::nullopt));
1554 opt.value(); // [[unsafe]]
1556 )");
1559 TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) {
1560 ExpectDiagnosticsFor(R"(
1561 #include "unchecked_optional_access_test.h"
1563 void target() {
1564 $ns::$optional<int> opt($ns::in_place, 3);
1565 opt.value();
1567 )");
1569 ExpectDiagnosticsFor(R"(
1570 #include "unchecked_optional_access_test.h"
1572 struct Foo {};
1574 void target() {
1575 $ns::$optional<Foo> opt($ns::in_place);
1576 opt.value();
1578 )");
1580 ExpectDiagnosticsFor(R"(
1581 #include "unchecked_optional_access_test.h"
1583 struct Foo {
1584 explicit Foo(int, bool);
1587 void target() {
1588 $ns::$optional<Foo> opt($ns::in_place, 3, false);
1589 opt.value();
1591 )");
1593 ExpectDiagnosticsFor(R"(
1594 #include "unchecked_optional_access_test.h"
1596 struct Foo {
1597 explicit Foo(std::initializer_list<int>);
1600 void target() {
1601 $ns::$optional<Foo> opt($ns::in_place, {3});
1602 opt.value();
1604 )");
1607 TEST_P(UncheckedOptionalAccessTest, ValueConstructor) {
1608 ExpectDiagnosticsFor(R"(
1609 #include "unchecked_optional_access_test.h"
1611 void target() {
1612 $ns::$optional<int> opt(21);
1613 opt.value();
1615 )");
1617 ExpectDiagnosticsFor(R"(
1618 #include "unchecked_optional_access_test.h"
1620 void target() {
1621 $ns::$optional<int> opt = $ns::$optional<int>(21);
1622 opt.value();
1624 )");
1625 ExpectDiagnosticsFor(R"(
1626 #include "unchecked_optional_access_test.h"
1628 void target() {
1629 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>());
1630 opt.value();
1632 )");
1634 ExpectDiagnosticsFor(R"(
1635 #include "unchecked_optional_access_test.h"
1637 struct MyString {
1638 MyString(const char*);
1641 void target() {
1642 $ns::$optional<MyString> opt("foo");
1643 opt.value();
1645 )");
1647 ExpectDiagnosticsFor(R"(
1648 #include "unchecked_optional_access_test.h"
1650 struct Foo {};
1652 struct Bar {
1653 Bar(const Foo&);
1656 void target() {
1657 $ns::$optional<Bar> opt(Make<Foo>());
1658 opt.value();
1660 )");
1662 ExpectDiagnosticsFor(R"(
1663 #include "unchecked_optional_access_test.h"
1665 struct Foo {
1666 explicit Foo(int);
1669 void target() {
1670 $ns::$optional<Foo> opt(3);
1671 opt.value();
1673 )");
1676 TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) {
1677 ExpectDiagnosticsFor(
1679 #include "unchecked_optional_access_test.h"
1681 struct Foo {};
1683 struct Bar {
1684 Bar(const Foo&);
1687 void target() {
1688 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>());
1689 opt.value(); // [[unsafe]]
1691 )");
1693 ExpectDiagnosticsFor(
1695 #include "unchecked_optional_access_test.h"
1697 struct Foo {};
1699 struct Bar {
1700 explicit Bar(const Foo&);
1703 void target() {
1704 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>());
1705 opt.value(); // [[unsafe]]
1707 )");
1709 ExpectDiagnosticsFor(
1711 #include "unchecked_optional_access_test.h"
1713 struct Foo {};
1715 struct Bar {
1716 Bar(const Foo&);
1719 void target() {
1720 $ns::$optional<Foo> opt1 = $ns::nullopt;
1721 $ns::$optional<Bar> opt2(opt1);
1722 opt2.value(); // [[unsafe]]
1724 )");
1726 ExpectDiagnosticsFor(R"(
1727 #include "unchecked_optional_access_test.h"
1729 struct Foo {};
1731 struct Bar {
1732 Bar(const Foo&);
1735 void target() {
1736 $ns::$optional<Foo> opt1(Make<Foo>());
1737 $ns::$optional<Bar> opt2(opt1);
1738 opt2.value();
1740 )");
1742 ExpectDiagnosticsFor(R"(
1743 #include "unchecked_optional_access_test.h"
1745 struct Foo {};
1747 struct Bar {
1748 explicit Bar(const Foo&);
1751 void target() {
1752 $ns::$optional<Foo> opt1(Make<Foo>());
1753 $ns::$optional<Bar> opt2(opt1);
1754 opt2.value();
1756 )");
1759 TEST_P(UncheckedOptionalAccessTest, MakeOptional) {
1760 ExpectDiagnosticsFor(R"(
1761 #include "unchecked_optional_access_test.h"
1763 void target() {
1764 $ns::$optional<int> opt = $ns::make_optional(0);
1765 opt.value();
1767 )");
1769 ExpectDiagnosticsFor(R"(
1770 #include "unchecked_optional_access_test.h"
1772 struct Foo {
1773 Foo(int, int);
1776 void target() {
1777 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22);
1778 opt.value();
1780 )");
1782 ExpectDiagnosticsFor(R"(
1783 #include "unchecked_optional_access_test.h"
1785 struct Foo {
1786 constexpr Foo(std::initializer_list<char>);
1789 void target() {
1790 char a = 'a';
1791 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a});
1792 opt.value();
1794 )");
1797 TEST_P(UncheckedOptionalAccessTest, ValueOr) {
1798 ExpectDiagnosticsFor(R"(
1799 #include "unchecked_optional_access_test.h"
1801 void target() {
1802 $ns::$optional<int> opt;
1803 opt.value_or(0);
1804 (void)0;
1806 )");
1809 TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointers) {
1810 ExpectDiagnosticsFor(
1811 R"code(
1812 #include "unchecked_optional_access_test.h"
1814 void target($ns::$optional<int*> opt) {
1815 if (opt.value_or(nullptr) != nullptr) {
1816 opt.value();
1817 } else {
1818 opt.value(); // [[unsafe]]
1821 )code");
1824 TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonIntegers) {
1825 ExpectDiagnosticsFor(
1826 R"code(
1827 #include "unchecked_optional_access_test.h"
1829 void target($ns::$optional<int> opt) {
1830 if (opt.value_or(0) != 0) {
1831 opt.value();
1832 } else {
1833 opt.value(); // [[unsafe]]
1836 )code");
1839 TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonStrings) {
1840 ExpectDiagnosticsFor(
1841 R"code(
1842 #include "unchecked_optional_access_test.h"
1844 void target($ns::$optional<std::string> opt) {
1845 if (!opt.value_or("").empty()) {
1846 opt.value();
1847 } else {
1848 opt.value(); // [[unsafe]]
1851 )code");
1853 ExpectDiagnosticsFor(
1854 R"code(
1855 #include "unchecked_optional_access_test.h"
1857 void target($ns::$optional<std::string> opt) {
1858 if (opt.value_or("") != "") {
1859 opt.value();
1860 } else {
1861 opt.value(); // [[unsafe]]
1864 )code");
1867 TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointerToOptional) {
1868 // FIXME: make `opt` a parameter directly, once we ensure that all `optional`
1869 // values have a `has_value` property.
1870 ExpectDiagnosticsFor(
1871 R"code(
1872 #include "unchecked_optional_access_test.h"
1874 void target($ns::$optional<int> p) {
1875 $ns::$optional<int> *opt = &p;
1876 if (opt->value_or(0) != 0) {
1877 opt->value();
1878 } else {
1879 opt->value(); // [[unsafe]]
1882 )code");
1885 TEST_P(UncheckedOptionalAccessTest, Emplace) {
1886 ExpectDiagnosticsFor(R"(
1887 #include "unchecked_optional_access_test.h"
1889 void target() {
1890 $ns::$optional<int> opt;
1891 opt.emplace(0);
1892 opt.value();
1894 )");
1896 ExpectDiagnosticsFor(R"(
1897 #include "unchecked_optional_access_test.h"
1899 void target($ns::$optional<int> *opt) {
1900 opt->emplace(0);
1901 opt->value();
1903 )");
1905 // FIXME: Add tests that call `emplace` in conditional branches:
1906 // ExpectDiagnosticsFor(
1907 // R"(
1908 // #include "unchecked_optional_access_test.h"
1910 // void target($ns::$optional<int> opt, bool b) {
1911 // if (b) {
1912 // opt.emplace(0);
1913 // }
1914 // if (b) {
1915 // opt.value();
1916 // } else {
1917 // opt.value(); // [[unsafe]]
1918 // }
1919 // }
1920 // )");
1923 TEST_P(UncheckedOptionalAccessTest, Reset) {
1924 ExpectDiagnosticsFor(
1926 #include "unchecked_optional_access_test.h"
1928 void target() {
1929 $ns::$optional<int> opt = $ns::make_optional(0);
1930 opt.reset();
1931 opt.value(); // [[unsafe]]
1933 )");
1935 ExpectDiagnosticsFor(
1937 #include "unchecked_optional_access_test.h"
1939 void target($ns::$optional<int> &opt) {
1940 if (opt.has_value()) {
1941 opt.reset();
1942 opt.value(); // [[unsafe]]
1945 )");
1947 // FIXME: Add tests that call `reset` in conditional branches:
1948 // ExpectDiagnosticsFor(
1949 // R"(
1950 // #include "unchecked_optional_access_test.h"
1952 // void target(bool b) {
1953 // $ns::$optional<int> opt = $ns::make_optional(0);
1954 // if (b) {
1955 // opt.reset();
1956 // }
1957 // if (b) {
1958 // opt.value(); // [[unsafe]]
1959 // } else {
1960 // opt.value();
1961 // }
1962 // }
1963 // )");
1966 TEST_P(UncheckedOptionalAccessTest, ValueAssignment) {
1967 ExpectDiagnosticsFor(R"(
1968 #include "unchecked_optional_access_test.h"
1970 struct Foo {};
1972 void target() {
1973 $ns::$optional<Foo> opt;
1974 opt = Foo();
1975 opt.value();
1977 )");
1979 ExpectDiagnosticsFor(R"(
1980 #include "unchecked_optional_access_test.h"
1982 struct Foo {};
1984 void target() {
1985 $ns::$optional<Foo> opt;
1986 (opt = Foo()).value();
1987 (void)0;
1989 )");
1991 ExpectDiagnosticsFor(R"(
1992 #include "unchecked_optional_access_test.h"
1994 struct MyString {
1995 MyString(const char*);
1998 void target() {
1999 $ns::$optional<MyString> opt;
2000 opt = "foo";
2001 opt.value();
2003 )");
2005 ExpectDiagnosticsFor(R"(
2006 #include "unchecked_optional_access_test.h"
2008 struct MyString {
2009 MyString(const char*);
2012 void target() {
2013 $ns::$optional<MyString> opt;
2014 (opt = "foo").value();
2016 )");
2019 TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) {
2020 ExpectDiagnosticsFor(
2022 #include "unchecked_optional_access_test.h"
2024 struct Foo {};
2026 struct Bar {
2027 Bar(const Foo&);
2030 void target() {
2031 $ns::$optional<Foo> opt1 = Foo();
2032 $ns::$optional<Bar> opt2;
2033 opt2 = opt1;
2034 opt2.value();
2036 )");
2038 ExpectDiagnosticsFor(
2040 #include "unchecked_optional_access_test.h"
2042 struct Foo {};
2044 struct Bar {
2045 Bar(const Foo&);
2048 void target() {
2049 $ns::$optional<Foo> opt1;
2050 $ns::$optional<Bar> opt2;
2051 if (opt2.has_value()) {
2052 opt2 = opt1;
2053 opt2.value(); // [[unsafe]]
2056 )");
2058 ExpectDiagnosticsFor(
2060 #include "unchecked_optional_access_test.h"
2062 struct Foo {};
2064 struct Bar {
2065 Bar(const Foo&);
2068 void target() {
2069 $ns::$optional<Foo> opt1 = Foo();
2070 $ns::$optional<Bar> opt2;
2071 (opt2 = opt1).value();
2072 (void)0;
2074 )");
2077 TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) {
2078 ExpectDiagnosticsFor(
2080 #include "unchecked_optional_access_test.h"
2082 void target() {
2083 $ns::$optional<int> opt = 3;
2084 opt = $ns::nullopt;
2085 opt.value(); // [[unsafe]]
2087 )");
2089 ExpectDiagnosticsFor(
2091 #include "unchecked_optional_access_test.h"
2093 void target() {
2094 $ns::$optional<int> opt = 3;
2095 (opt = $ns::nullopt).value(); // [[unsafe]]
2097 )");
2100 TEST_P(UncheckedOptionalAccessTest, OptionalSwap) {
2101 ExpectDiagnosticsFor(
2103 #include "unchecked_optional_access_test.h"
2105 void target() {
2106 $ns::$optional<int> opt1 = $ns::nullopt;
2107 $ns::$optional<int> opt2 = 3;
2109 opt1.swap(opt2);
2111 opt1.value();
2113 opt2.value(); // [[unsafe]]
2115 )");
2117 ExpectDiagnosticsFor(
2119 #include "unchecked_optional_access_test.h"
2121 void target() {
2122 $ns::$optional<int> opt1 = $ns::nullopt;
2123 $ns::$optional<int> opt2 = 3;
2125 opt2.swap(opt1);
2127 opt1.value();
2129 opt2.value(); // [[unsafe]]
2131 )");
2134 TEST_P(UncheckedOptionalAccessTest, OptionalReturnedFromFuntionCall) {
2135 ExpectDiagnosticsFor(
2137 #include "unchecked_optional_access_test.h"
2139 struct S {
2140 $ns::$optional<float> x;
2141 } s;
2142 S getOptional() {
2143 return s;
2146 void target() {
2147 getOptional().x = 0;
2149 )");
2152 TEST_P(UncheckedOptionalAccessTest, StdSwap) {
2153 ExpectDiagnosticsFor(
2155 #include "unchecked_optional_access_test.h"
2157 void target() {
2158 $ns::$optional<int> opt1 = $ns::nullopt;
2159 $ns::$optional<int> opt2 = 3;
2161 std::swap(opt1, opt2);
2163 opt1.value();
2165 opt2.value(); // [[unsafe]]
2167 )");
2169 ExpectDiagnosticsFor(
2171 #include "unchecked_optional_access_test.h"
2173 void target() {
2174 $ns::$optional<int> opt1 = $ns::nullopt;
2175 $ns::$optional<int> opt2 = 3;
2177 std::swap(opt2, opt1);
2179 opt1.value();
2181 opt2.value(); // [[unsafe]]
2183 )");
2186 TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocLeft) {
2187 ExpectDiagnosticsFor(
2189 #include "unchecked_optional_access_test.h"
2191 struct L { $ns::$optional<int> hd; L* tl; };
2193 void target() {
2194 $ns::$optional<int> foo = 3;
2195 L bar;
2197 // Any `tl` beyond the first is not modeled.
2198 bar.tl->tl->hd.swap(foo);
2200 bar.tl->tl->hd.value(); // [[unsafe]]
2201 foo.value(); // [[unsafe]]
2203 )");
2206 TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocRight) {
2207 ExpectDiagnosticsFor(
2209 #include "unchecked_optional_access_test.h"
2211 struct L { $ns::$optional<int> hd; L* tl; };
2213 void target() {
2214 $ns::$optional<int> foo = 3;
2215 L bar;
2217 // Any `tl` beyond the first is not modeled.
2218 foo.swap(bar.tl->tl->hd);
2220 bar.tl->tl->hd.value(); // [[unsafe]]
2221 foo.value(); // [[unsafe]]
2223 )");
2226 TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftSet) {
2227 ExpectDiagnosticsFor(
2229 #include "unchecked_optional_access_test.h"
2231 struct S { int x; };
2232 struct A { $ns::$optional<S> late; };
2233 struct B { A f3; };
2234 struct C { B f2; };
2235 struct D { C f1; };
2237 void target() {
2238 $ns::$optional<S> foo = S{3};
2239 D bar;
2241 bar.f1.f2.f3.late.swap(foo);
2243 bar.f1.f2.f3.late.value();
2244 foo.value(); // [[unsafe]]
2246 )");
2249 TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftUnset) {
2250 ExpectDiagnosticsFor(
2252 #include "unchecked_optional_access_test.h"
2254 struct S { int x; };
2255 struct A { $ns::$optional<S> late; };
2256 struct B { A f3; };
2257 struct C { B f2; };
2258 struct D { C f1; };
2260 void target() {
2261 $ns::$optional<S> foo;
2262 D bar;
2264 bar.f1.f2.f3.late.swap(foo);
2266 bar.f1.f2.f3.late.value(); // [[unsafe]]
2267 foo.value(); // [[unsafe]]
2269 )");
2272 // fixme: use recursion instead of depth.
2273 TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightSet) {
2274 ExpectDiagnosticsFor(
2276 #include "unchecked_optional_access_test.h"
2278 struct S { int x; };
2279 struct A { $ns::$optional<S> late; };
2280 struct B { A f3; };
2281 struct C { B f2; };
2282 struct D { C f1; };
2284 void target() {
2285 $ns::$optional<S> foo = S{3};
2286 D bar;
2288 foo.swap(bar.f1.f2.f3.late);
2290 bar.f1.f2.f3.late.value();
2291 foo.value(); // [[unsafe]]
2293 )");
2296 TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightUnset) {
2297 ExpectDiagnosticsFor(
2299 #include "unchecked_optional_access_test.h"
2301 struct S { int x; };
2302 struct A { $ns::$optional<S> late; };
2303 struct B { A f3; };
2304 struct C { B f2; };
2305 struct D { C f1; };
2307 void target() {
2308 $ns::$optional<S> foo;
2309 D bar;
2311 foo.swap(bar.f1.f2.f3.late);
2313 bar.f1.f2.f3.late.value(); // [[unsafe]]
2314 foo.value(); // [[unsafe]]
2316 )");
2319 TEST_P(UncheckedOptionalAccessTest, UniquePtrToOptional) {
2320 // We suppress diagnostics for optionals in smart pointers (other than
2321 // `optional` itself).
2322 ExpectDiagnosticsFor(
2324 #include "unchecked_optional_access_test.h"
2326 template <typename T>
2327 struct smart_ptr {
2328 T& operator*() &;
2329 T* operator->();
2332 void target() {
2333 smart_ptr<$ns::$optional<bool>> foo;
2334 foo->value();
2335 (*foo).value();
2337 )");
2340 TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) {
2341 // We suppress diagnostics for optional fields reachable from smart pointers
2342 // (other than `optional` itself) through (exactly) one member access.
2343 ExpectDiagnosticsFor(
2345 #include "unchecked_optional_access_test.h"
2347 template <typename T>
2348 struct smart_ptr {
2349 T& operator*() &;
2350 T* operator->();
2353 struct Foo {
2354 $ns::$optional<int> opt;
2357 void target() {
2358 smart_ptr<Foo> foo;
2359 *foo->opt;
2360 *(*foo).opt;
2362 )");
2365 TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) {
2366 ExpectDiagnosticsFor(
2368 #include "unchecked_optional_access_test.h"
2370 $ns::$optional<int> MakeOpt();
2372 void target() {
2373 $ns::$optional<int> opt = 0;
2374 opt = MakeOpt();
2375 opt.value(); // [[unsafe]]
2377 )");
2378 ExpectDiagnosticsFor(
2380 #include "unchecked_optional_access_test.h"
2382 const $ns::$optional<int>& MakeOpt();
2384 void target() {
2385 $ns::$optional<int> opt = 0;
2386 opt = MakeOpt();
2387 opt.value(); // [[unsafe]]
2389 )");
2391 ExpectDiagnosticsFor(
2393 #include "unchecked_optional_access_test.h"
2395 using IntOpt = $ns::$optional<int>;
2396 IntOpt MakeOpt();
2398 void target() {
2399 IntOpt opt = 0;
2400 opt = MakeOpt();
2401 opt.value(); // [[unsafe]]
2403 )");
2405 ExpectDiagnosticsFor(
2407 #include "unchecked_optional_access_test.h"
2409 using IntOpt = $ns::$optional<int>;
2410 const IntOpt& MakeOpt();
2412 void target() {
2413 IntOpt opt = 0;
2414 opt = MakeOpt();
2415 opt.value(); // [[unsafe]]
2417 )");
2421 TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftSet) {
2422 ExpectDiagnosticsFor(
2424 #include "unchecked_optional_access_test.h"
2426 void target() {
2427 $ns::$optional<int> opt1 = 3;
2428 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2430 if (opt1 == opt2) {
2431 opt2.value();
2432 } else {
2433 opt2.value(); // [[unsafe]]
2436 )");
2439 TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightSet) {
2440 ExpectDiagnosticsFor(
2442 #include "unchecked_optional_access_test.h"
2444 void target() {
2445 $ns::$optional<int> opt1 = 3;
2446 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2448 if (opt2 == opt1) {
2449 opt2.value();
2450 } else {
2451 opt2.value(); // [[unsafe]]
2454 )");
2457 TEST_P(UncheckedOptionalAccessTest, EqualityCheckVerifySetAfterEq) {
2458 ExpectDiagnosticsFor(
2460 #include "unchecked_optional_access_test.h"
2462 void target() {
2463 $ns::$optional<int> opt1 = Make<$ns::$optional<int>>();
2464 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2466 if (opt1 == opt2) {
2467 if (opt1.has_value())
2468 opt2.value();
2469 if (opt2.has_value())
2470 opt1.value();
2473 )");
2476 TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftUnset) {
2477 ExpectDiagnosticsFor(
2479 #include "unchecked_optional_access_test.h"
2481 void target() {
2482 $ns::$optional<int> opt1 = $ns::nullopt;
2483 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2485 if (opt1 == opt2) {
2486 opt2.value(); // [[unsafe]]
2487 } else {
2488 opt2.value();
2491 )");
2494 TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightUnset) {
2495 ExpectDiagnosticsFor(
2497 #include "unchecked_optional_access_test.h"
2499 void target() {
2500 $ns::$optional<int> opt1 = $ns::nullopt;
2501 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2503 if (opt2 == opt1) {
2504 opt2.value(); // [[unsafe]]
2505 } else {
2506 opt2.value();
2509 )");
2512 TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightNullopt) {
2513 ExpectDiagnosticsFor(
2515 #include "unchecked_optional_access_test.h"
2517 void target() {
2518 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2520 if (opt == $ns::nullopt) {
2521 opt.value(); // [[unsafe]]
2522 } else {
2523 opt.value();
2526 )");
2529 TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftNullopt) {
2530 ExpectDiagnosticsFor(
2532 #include "unchecked_optional_access_test.h"
2534 void target() {
2535 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2537 if ($ns::nullopt == opt) {
2538 opt.value(); // [[unsafe]]
2539 } else {
2540 opt.value();
2543 )");
2546 TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightValue) {
2547 ExpectDiagnosticsFor(
2549 #include "unchecked_optional_access_test.h"
2551 void target() {
2552 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2554 if (opt == 3) {
2555 opt.value();
2556 } else {
2557 opt.value(); // [[unsafe]]
2560 )");
2563 TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftValue) {
2564 ExpectDiagnosticsFor(
2566 #include "unchecked_optional_access_test.h"
2568 void target() {
2569 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2571 if (3 == opt) {
2572 opt.value();
2573 } else {
2574 opt.value(); // [[unsafe]]
2577 )");
2580 TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftSet) {
2581 ExpectDiagnosticsFor(
2583 #include "unchecked_optional_access_test.h"
2585 void target() {
2586 $ns::$optional<int> opt1 = 3;
2587 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2589 if (opt1 != opt2) {
2590 opt2.value(); // [[unsafe]]
2591 } else {
2592 opt2.value();
2595 )");
2598 TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightSet) {
2599 ExpectDiagnosticsFor(
2601 #include "unchecked_optional_access_test.h"
2603 void target() {
2604 $ns::$optional<int> opt1 = 3;
2605 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2607 if (opt2 != opt1) {
2608 opt2.value(); // [[unsafe]]
2609 } else {
2610 opt2.value();
2613 )");
2616 TEST_P(UncheckedOptionalAccessTest, InequalityCheckVerifySetAfterEq) {
2617 ExpectDiagnosticsFor(
2619 #include "unchecked_optional_access_test.h"
2621 void target() {
2622 $ns::$optional<int> opt1 = Make<$ns::$optional<int>>();
2623 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2625 if (opt1 != opt2) {
2626 if (opt1.has_value())
2627 opt2.value(); // [[unsafe]]
2628 if (opt2.has_value())
2629 opt1.value(); // [[unsafe]]
2632 )");
2635 TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftUnset) {
2636 ExpectDiagnosticsFor(
2638 #include "unchecked_optional_access_test.h"
2640 void target() {
2641 $ns::$optional<int> opt1 = $ns::nullopt;
2642 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2644 if (opt1 != opt2) {
2645 opt2.value();
2646 } else {
2647 opt2.value(); // [[unsafe]]
2650 )");
2653 TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightUnset) {
2654 ExpectDiagnosticsFor(
2656 #include "unchecked_optional_access_test.h"
2658 void target() {
2659 $ns::$optional<int> opt1 = $ns::nullopt;
2660 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2662 if (opt2 != opt1) {
2663 opt2.value();
2664 } else {
2665 opt2.value(); // [[unsafe]]
2668 )");
2671 TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightNullopt) {
2672 ExpectDiagnosticsFor(
2674 #include "unchecked_optional_access_test.h"
2676 void target() {
2677 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2679 if (opt != $ns::nullopt) {
2680 opt.value();
2681 } else {
2682 opt.value(); // [[unsafe]]
2685 )");
2688 TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftNullopt) {
2689 ExpectDiagnosticsFor(
2691 #include "unchecked_optional_access_test.h"
2693 void target() {
2694 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2696 if ($ns::nullopt != opt) {
2697 opt.value();
2698 } else {
2699 opt.value(); // [[unsafe]]
2702 )");
2705 TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightValue) {
2706 ExpectDiagnosticsFor(
2708 #include "unchecked_optional_access_test.h"
2710 void target() {
2711 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2713 if (opt != 3) {
2714 opt.value(); // [[unsafe]]
2715 } else {
2716 opt.value();
2719 )");
2722 TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftValue) {
2723 ExpectDiagnosticsFor(
2725 #include "unchecked_optional_access_test.h"
2727 void target() {
2728 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2730 if (3 != opt) {
2731 opt.value(); // [[unsafe]]
2732 } else {
2733 opt.value();
2736 )");
2739 // Verifies that the model sees through aliases.
2740 TEST_P(UncheckedOptionalAccessTest, WithAlias) {
2741 ExpectDiagnosticsFor(
2743 #include "unchecked_optional_access_test.h"
2745 template <typename T>
2746 using MyOptional = $ns::$optional<T>;
2748 void target(MyOptional<int> opt) {
2749 opt.value(); // [[unsafe]]
2751 )");
2754 TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) {
2755 // Basic test that nested values are populated. We nest an optional because
2756 // its easy to use in a test, but the type of the nested value shouldn't
2757 // matter.
2758 ExpectDiagnosticsFor(
2760 #include "unchecked_optional_access_test.h"
2762 using Foo = $ns::$optional<std::string>;
2764 void target($ns::$optional<Foo> foo) {
2765 if (foo && *foo) {
2766 foo->value();
2769 )");
2771 // Mutation is supported for nested values.
2772 ExpectDiagnosticsFor(
2774 #include "unchecked_optional_access_test.h"
2776 using Foo = $ns::$optional<std::string>;
2778 void target($ns::$optional<Foo> foo) {
2779 if (foo && *foo) {
2780 foo->reset();
2781 foo->value(); // [[unsafe]]
2784 )");
2787 // Tests that structs can be nested. We use an optional field because its easy
2788 // to use in a test, but the type of the field shouldn't matter.
2789 TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) {
2790 ExpectDiagnosticsFor(
2792 #include "unchecked_optional_access_test.h"
2794 struct Foo {
2795 $ns::$optional<std::string> opt;
2798 void target($ns::$optional<Foo> foo) {
2799 if (foo && foo->opt) {
2800 foo->opt.value();
2803 )");
2806 TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) {
2807 ExpectDiagnosticsFor(
2809 #include "unchecked_optional_access_test.h"
2811 using Foo = $ns::$optional<std::string>;
2813 void target($ns::$optional<Foo> foo, bool b) {
2814 if (!foo.has_value()) return;
2815 if (b) {
2816 if (!foo->has_value()) return;
2817 // We have created `foo.value()`.
2818 foo->value();
2819 } else {
2820 if (!foo->has_value()) return;
2821 // We have created `foo.value()` again, in a different environment.
2822 foo->value();
2824 // Now we merge the two values. UncheckedOptionalAccessModel::merge() will
2825 // throw away the "value" property.
2826 foo->value();
2828 )");
2831 // This test is aimed at the core model, not the diagnostic. It is a regression
2832 // test against a crash when using non-trivial smart pointers, like
2833 // `std::unique_ptr`. As such, it doesn't test the access itself, which would be
2834 // ignored regardless because of `IgnoreSmartPointerDereference = true`, above.
2835 TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) {
2836 ExpectDiagnosticsFor(
2838 #include "unchecked_optional_access_test.h"
2840 template <typename T>
2841 struct smart_ptr {
2842 typename std::add_lvalue_reference<T>::type operator*() &;
2845 void target() {
2846 smart_ptr<$ns::$optional<int>> x;
2847 // Verify that this assignment does not crash.
2848 *x = 3;
2850 )");
2853 TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) {
2854 ExpectDiagnosticsFor(R"code(
2855 #include "unchecked_optional_access_test.h"
2857 void target(bool b, $ns::$optional<int> opt) {
2858 if (b || opt.has_value()) {
2859 if (!b) {
2860 opt.value();
2864 )code");
2866 ExpectDiagnosticsFor(R"code(
2867 #include "unchecked_optional_access_test.h"
2869 void target(bool b, $ns::$optional<int> opt) {
2870 if (b && !opt.has_value()) return;
2871 if (b) {
2872 opt.value();
2875 )code");
2877 ExpectDiagnosticsFor(
2878 R"code(
2879 #include "unchecked_optional_access_test.h"
2881 void target(bool b, $ns::$optional<int> opt) {
2882 if (opt.has_value()) b = true;
2883 if (b) {
2884 opt.value(); // [[unsafe]]
2887 )code");
2889 ExpectDiagnosticsFor(R"code(
2890 #include "unchecked_optional_access_test.h"
2892 void target(bool b, $ns::$optional<int> opt) {
2893 if (b) return;
2894 if (opt.has_value()) b = true;
2895 if (b) {
2896 opt.value();
2899 )code");
2901 ExpectDiagnosticsFor(R"(
2902 #include "unchecked_optional_access_test.h"
2904 void target(bool b, $ns::$optional<int> opt) {
2905 if (opt.has_value() == b) {
2906 if (b) {
2907 opt.value();
2911 )");
2913 ExpectDiagnosticsFor(R"(
2914 #include "unchecked_optional_access_test.h"
2916 void target(bool b, $ns::$optional<int> opt) {
2917 if (opt.has_value() != b) {
2918 if (!b) {
2919 opt.value();
2923 )");
2925 ExpectDiagnosticsFor(R"(
2926 #include "unchecked_optional_access_test.h"
2928 void target(bool b) {
2929 $ns::$optional<int> opt1 = $ns::nullopt;
2930 $ns::$optional<int> opt2;
2931 if (b) {
2932 opt2 = $ns::nullopt;
2933 } else {
2934 opt2 = $ns::nullopt;
2936 if (opt2.has_value()) {
2937 opt1.value();
2940 )");
2943 TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) {
2944 ExpectDiagnosticsFor(
2945 R"code(
2946 #include "unchecked_optional_access_test.h"
2948 void target(bool b) {
2949 $ns::$optional<int> opt;
2950 if (b) {
2951 opt = Make<$ns::$optional<int>>();
2952 } else {
2953 opt = Make<$ns::$optional<int>>();
2955 if (opt.has_value()) {
2956 opt.value();
2957 } else {
2958 opt.value(); // [[unsafe]]
2961 )code");
2963 ExpectDiagnosticsFor(R"code(
2964 #include "unchecked_optional_access_test.h"
2966 void target(bool b) {
2967 $ns::$optional<int> opt;
2968 if (b) {
2969 opt = Make<$ns::$optional<int>>();
2970 if (!opt.has_value()) return;
2971 } else {
2972 opt = Make<$ns::$optional<int>>();
2973 if (!opt.has_value()) return;
2975 opt.value();
2977 )code");
2979 ExpectDiagnosticsFor(
2980 R"code(
2981 #include "unchecked_optional_access_test.h"
2983 void target(bool b) {
2984 $ns::$optional<int> opt;
2985 if (b) {
2986 opt = Make<$ns::$optional<int>>();
2987 if (!opt.has_value()) return;
2988 } else {
2989 opt = Make<$ns::$optional<int>>();
2991 opt.value(); // [[unsafe]]
2993 )code");
2995 ExpectDiagnosticsFor(
2996 R"code(
2997 #include "unchecked_optional_access_test.h"
2999 void target(bool b) {
3000 $ns::$optional<int> opt;
3001 if (b) {
3002 opt = 1;
3003 } else {
3004 opt = 2;
3006 opt.value();
3008 )code");
3010 ExpectDiagnosticsFor(
3011 R"code(
3012 #include "unchecked_optional_access_test.h"
3014 void target(bool b) {
3015 $ns::$optional<int> opt;
3016 if (b) {
3017 opt = 1;
3018 } else {
3019 opt = Make<$ns::$optional<int>>();
3021 opt.value(); // [[unsafe]]
3023 )code");
3026 TEST_P(UncheckedOptionalAccessTest, AccessValueInLoop) {
3027 ExpectDiagnosticsFor(R"(
3028 #include "unchecked_optional_access_test.h"
3030 void target() {
3031 $ns::$optional<int> opt = 3;
3032 while (Make<bool>()) {
3033 opt.value();
3036 )");
3039 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopWithCheckSafe) {
3040 ExpectDiagnosticsFor(R"(
3041 #include "unchecked_optional_access_test.h"
3043 void target() {
3044 $ns::$optional<int> opt = 3;
3045 while (Make<bool>()) {
3046 opt.value();
3048 opt = Make<$ns::$optional<int>>();
3049 if (!opt.has_value()) return;
3052 )");
3055 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopNoCheckUnsafe) {
3056 ExpectDiagnosticsFor(
3058 #include "unchecked_optional_access_test.h"
3060 void target() {
3061 $ns::$optional<int> opt = 3;
3062 while (Make<bool>()) {
3063 opt.value(); // [[unsafe]]
3065 opt = Make<$ns::$optional<int>>();
3068 )");
3071 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnsetUnsafe) {
3072 ExpectDiagnosticsFor(
3074 #include "unchecked_optional_access_test.h"
3076 void target() {
3077 $ns::$optional<int> opt = 3;
3078 while (Make<bool>())
3079 opt = $ns::nullopt;
3080 $ns::$optional<int> opt2 = $ns::nullopt;
3081 if (opt.has_value())
3082 opt2 = $ns::$optional<int>(3);
3083 opt2.value(); // [[unsafe]]
3085 )");
3088 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToSetUnsafe) {
3089 ExpectDiagnosticsFor(
3091 #include "unchecked_optional_access_test.h"
3093 void target() {
3094 $ns::$optional<int> opt = $ns::nullopt;
3095 while (Make<bool>())
3096 opt = $ns::$optional<int>(3);
3097 $ns::$optional<int> opt2 = $ns::nullopt;
3098 if (!opt.has_value())
3099 opt2 = $ns::$optional<int>(3);
3100 opt2.value(); // [[unsafe]]
3102 )");
3105 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnknownUnsafe) {
3106 ExpectDiagnosticsFor(
3108 #include "unchecked_optional_access_test.h"
3110 void target() {
3111 $ns::$optional<int> opt = $ns::nullopt;
3112 while (Make<bool>())
3113 opt = Make<$ns::$optional<int>>();
3114 $ns::$optional<int> opt2 = $ns::nullopt;
3115 if (!opt.has_value())
3116 opt2 = $ns::$optional<int>(3);
3117 opt2.value(); // [[unsafe]]
3119 )");
3122 TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopBadConditionUnsafe) {
3123 ExpectDiagnosticsFor(
3125 #include "unchecked_optional_access_test.h"
3127 void target() {
3128 $ns::$optional<int> opt = 3;
3129 while (Make<bool>()) {
3130 opt.value(); // [[unsafe]]
3132 opt = Make<$ns::$optional<int>>();
3133 if (!opt.has_value()) continue;
3136 )");
3139 TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromStruct) {
3140 ExpectDiagnosticsFor(R"(
3141 #include "unchecked_optional_access_test.h"
3143 struct kv { $ns::$optional<int> opt; int x; };
3144 int target() {
3145 auto [contents, x] = Make<kv>();
3146 return contents ? *contents : x;
3148 )");
3150 ExpectDiagnosticsFor(R"(
3151 #include "unchecked_optional_access_test.h"
3153 template <typename T1, typename T2>
3154 struct pair { T1 fst; T2 snd; };
3155 int target() {
3156 auto [contents, x] = Make<pair<$ns::$optional<int>, int>>();
3157 return contents ? *contents : x;
3159 )");
3162 TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromTupleLikeType) {
3163 ExpectDiagnosticsFor(R"(
3164 #include "unchecked_optional_access_test.h"
3166 namespace std {
3167 template <class> struct tuple_size;
3168 template <size_t, class> struct tuple_element;
3169 template <class...> class tuple;
3171 template <class... T>
3172 struct tuple_size<tuple<T...>> : integral_constant<size_t, sizeof...(T)> {};
3174 template <size_t I, class... T>
3175 struct tuple_element<I, tuple<T...>> {
3176 using type = __type_pack_element<I, T...>;
3179 template <class...> class tuple {};
3180 template <size_t I, class... T>
3181 typename tuple_element<I, tuple<T...>>::type get(tuple<T...>);
3182 } // namespace std
3184 std::tuple<$ns::$optional<const char *>, int> get_opt();
3185 void target() {
3186 auto [content, ck] = get_opt();
3187 content ? *content : "";
3189 )");
3192 TEST_P(UncheckedOptionalAccessTest, CtorInitializerNullopt) {
3193 using namespace ast_matchers;
3194 ExpectDiagnosticsFor(
3196 #include "unchecked_optional_access_test.h"
3198 struct Target {
3199 Target(): opt($ns::nullopt) {
3200 opt.value(); // [[unsafe]]
3202 $ns::$optional<int> opt;
3205 cxxConstructorDecl(ofClass(hasName("Target"))));
3208 TEST_P(UncheckedOptionalAccessTest, CtorInitializerValue) {
3209 using namespace ast_matchers;
3210 ExpectDiagnosticsFor(
3212 #include "unchecked_optional_access_test.h"
3214 struct Target {
3215 Target(): opt(3) {
3216 opt.value();
3218 $ns::$optional<int> opt;
3221 cxxConstructorDecl(ofClass(hasName("Target"))));
3224 // This is regression test, it shouldn't crash.
3225 TEST_P(UncheckedOptionalAccessTest, Bitfield) {
3226 using namespace ast_matchers;
3227 ExpectDiagnosticsFor(
3229 #include "unchecked_optional_access_test.h"
3230 struct Dst {
3231 unsigned int n : 1;
3233 void target() {
3234 $ns::$optional<bool> v;
3235 Dst d;
3236 if (v.has_value())
3237 d.n = v.value();
3239 )");
3242 TEST_P(UncheckedOptionalAccessTest, LambdaParam) {
3243 ExpectDiagnosticsForLambda(R"(
3244 #include "unchecked_optional_access_test.h"
3246 void target() {
3247 []($ns::$optional<int> opt) {
3248 if (opt.has_value()) {
3249 opt.value();
3250 } else {
3251 opt.value(); // [[unsafe]]
3253 }(Make<$ns::$optional<int>>());
3255 )");
3258 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopy) {
3259 ExpectDiagnosticsForLambda(R"(
3260 #include "unchecked_optional_access_test.h"
3262 void target($ns::$optional<int> opt) {
3263 [opt]() {
3264 if (opt.has_value()) {
3265 opt.value();
3266 } else {
3267 opt.value(); // [[unsafe]]
3269 }();
3271 )");
3274 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReference) {
3275 ExpectDiagnosticsForLambda(R"(
3276 #include "unchecked_optional_access_test.h"
3278 void target($ns::$optional<int> opt) {
3279 [&opt]() {
3280 if (opt.has_value()) {
3281 opt.value();
3282 } else {
3283 opt.value(); // [[unsafe]]
3285 }();
3287 )");
3290 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureWithInitializer) {
3291 ExpectDiagnosticsForLambda(R"(
3292 #include "unchecked_optional_access_test.h"
3294 void target($ns::$optional<int> opt) {
3295 [opt2=opt]() {
3296 if (opt2.has_value()) {
3297 opt2.value();
3298 } else {
3299 opt2.value(); // [[unsafe]]
3301 }();
3303 )");
3306 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopyImplicit) {
3307 ExpectDiagnosticsForLambda(R"(
3308 #include "unchecked_optional_access_test.h"
3310 void target($ns::$optional<int> opt) {
3311 [=]() {
3312 if (opt.has_value()) {
3313 opt.value();
3314 } else {
3315 opt.value(); // [[unsafe]]
3317 }();
3319 )");
3322 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReferenceImplicit) {
3323 ExpectDiagnosticsForLambda(R"(
3324 #include "unchecked_optional_access_test.h"
3326 void target($ns::$optional<int> opt) {
3327 [&]() {
3328 if (opt.has_value()) {
3329 opt.value();
3330 } else {
3331 opt.value(); // [[unsafe]]
3333 }();
3335 )");
3338 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureThis) {
3339 ExpectDiagnosticsForLambda(R"(
3340 #include "unchecked_optional_access_test.h"
3342 struct Foo {
3343 $ns::$optional<int> opt;
3345 void target() {
3346 [this]() {
3347 if (opt.has_value()) {
3348 opt.value();
3349 } else {
3350 opt.value(); // [[unsafe]]
3352 }();
3355 )");
3358 TEST_P(UncheckedOptionalAccessTest, LambdaCaptureStateNotPropagated) {
3359 // We can't propagate information from the surrounding context.
3360 ExpectDiagnosticsForLambda(R"(
3361 #include "unchecked_optional_access_test.h"
3363 void target($ns::$optional<int> opt) {
3364 if (opt.has_value()) {
3365 [&opt]() {
3366 opt.value(); // [[unsafe]]
3367 }();
3370 )");
3372 // FIXME: Add support for:
3373 // - constructors (copy, move)
3374 // - assignment operators (default, copy, move)
3375 // - invalidation (passing optional by non-const reference/pointer)