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