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