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`.
ReplaceAllOccurrences(std::string & S,const std::string & Pattern,const std::string & 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:
ExpectDiagnosticsFor(std::string SourceCode)1205 void ExpectDiagnosticsFor(std::string SourceCode) {
1206 ExpectDiagnosticsFor(SourceCode, ast_matchers::hasName("target"));
1207 }
1208
1209 private:
1210 template <typename FuncDeclMatcher>
ExpectDiagnosticsFor(std::string SourceCode,FuncDeclMatcher FuncMatcher)1211 void ExpectDiagnosticsFor(std::string SourceCode,
1212 FuncDeclMatcher FuncMatcher) {
1213 ReplaceAllOccurrences(SourceCode, "$ns", GetParam().NamespaceName);
1214 ReplaceAllOccurrences(SourceCode, "$optional", GetParam().TypeName);
1215
1216 std::vector<std::pair<std::string, std::string>> Headers;
1217 Headers.emplace_back("cstddef.h", CSDtdDefHeader);
1218 Headers.emplace_back("std_initializer_list.h", StdInitializerListHeader);
1219 Headers.emplace_back("std_string.h", StdStringHeader);
1220 Headers.emplace_back("std_type_traits.h", StdTypeTraitsHeader);
1221 Headers.emplace_back("std_utility.h", StdUtilityHeader);
1222 Headers.emplace_back("std_optional.h", StdOptionalHeader);
1223 Headers.emplace_back("absl_type_traits.h", AbslTypeTraitsHeader);
1224 Headers.emplace_back("absl_optional.h", AbslOptionalHeader);
1225 Headers.emplace_back("base_optional.h", BaseOptionalHeader);
1226 Headers.emplace_back("unchecked_optional_access_test.h", R"(
1227 #include "absl_optional.h"
1228 #include "base_optional.h"
1229 #include "std_initializer_list.h"
1230 #include "std_optional.h"
1231 #include "std_string.h"
1232 #include "std_utility.h"
1233
1234 template <typename T>
1235 T Make();
1236 )");
1237 const tooling::FileContentMappings FileContents(Headers.begin(),
1238 Headers.end());
1239 UncheckedOptionalAccessModelOptions Options{
1240 /*IgnoreSmartPointerDereference=*/true};
1241 std::vector<SourceLocation> Diagnostics;
1242 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>(
1243 SourceCode, FuncMatcher,
1244 [Options](ASTContext &Ctx, Environment &) {
1245 return UncheckedOptionalAccessModel(Ctx, Options);
1246 },
1247 [&Diagnostics, Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
1248 ASTContext &Ctx, const Stmt *Stmt,
1249 const TypeErasedDataflowAnalysisState &State) mutable {
1250 auto StmtDiagnostics = Diagnoser.diagnose(Ctx, Stmt, State.Env);
1251 llvm::move(StmtDiagnostics, std::back_inserter(Diagnostics));
1252 },
1253 [&Diagnostics](AnalysisData AnalysisData) {
1254 auto &SrcMgr = AnalysisData.ASTCtx.getSourceManager();
1255
1256 llvm::DenseSet<unsigned> AnnotationLines;
1257 for (const auto &Pair : AnalysisData.Annotations) {
1258 auto *Stmt = Pair.getFirst();
1259 AnnotationLines.insert(
1260 SrcMgr.getPresumedLineNumber(Stmt->getBeginLoc()));
1261 // We add both the begin and end locations, so that if the
1262 // statement spans multiple lines then the test will fail.
1263 //
1264 // FIXME: Going forward, we should change this to instead just
1265 // get the single line number from the annotation itself, rather
1266 // than looking at the statement it's attached to.
1267 AnnotationLines.insert(
1268 SrcMgr.getPresumedLineNumber(Stmt->getEndLoc()));
1269 }
1270
1271 llvm::DenseSet<unsigned> DiagnosticLines;
1272 for (SourceLocation &Loc : Diagnostics) {
1273 DiagnosticLines.insert(SrcMgr.getPresumedLineNumber(Loc));
1274 }
1275
1276 EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
1277 },
1278 {"-fsyntax-only", "-std=c++17", "-Wno-undefined-inline"}, FileContents);
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"}),
__anonc523bd4d0502(const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) 1289 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) {
1290 return Info.param.NamespaceName;
1291 });
1292
TEST_P(UncheckedOptionalAccessTest,EmptyFunctionBody)1293 TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) {
1294 ExpectDiagnosticsFor(R"(
1295 void target() {
1296 (void)0;
1297 }
1298 )");
1299 }
1300
TEST_P(UncheckedOptionalAccessTest,UnwrapUsingValueNoCheck)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
TEST_P(UncheckedOptionalAccessTest,UnwrapUsingOperatorStarNoCheck)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
TEST_P(UncheckedOptionalAccessTest,UnwrapUsingOperatorArrowNoCheck)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
TEST_P(UncheckedOptionalAccessTest,HasValueCheck)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
TEST_P(UncheckedOptionalAccessTest,OperatorBoolCheck)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
TEST_P(UncheckedOptionalAccessTest,UnwrapFunctionCallResultNoCheck)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
TEST_P(UncheckedOptionalAccessTest,DefaultConstructor)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
TEST_P(UncheckedOptionalAccessTest,NulloptConstructor)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
TEST_P(UncheckedOptionalAccessTest,InPlaceConstructor)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
TEST_P(UncheckedOptionalAccessTest,ValueConstructor)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
TEST_P(UncheckedOptionalAccessTest,ConvertibleOptionalConstructor)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
TEST_P(UncheckedOptionalAccessTest,MakeOptional)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
TEST_P(UncheckedOptionalAccessTest,ValueOr)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
TEST_P(UncheckedOptionalAccessTest,ValueOrComparison)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
TEST_P(UncheckedOptionalAccessTest,Emplace)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
TEST_P(UncheckedOptionalAccessTest,Reset)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
TEST_P(UncheckedOptionalAccessTest,ValueAssignment)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
TEST_P(UncheckedOptionalAccessTest,OptionalConversionAssignment)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
TEST_P(UncheckedOptionalAccessTest,NulloptAssignment)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
TEST_P(UncheckedOptionalAccessTest,OptionalSwap)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
TEST_P(UncheckedOptionalAccessTest,StdSwap)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
TEST_P(UncheckedOptionalAccessTest,UniquePtrToStructWithOptionalField)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
TEST_P(UncheckedOptionalAccessTest,CallReturningOptional)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.
TEST_P(UncheckedOptionalAccessTest,WithAlias)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
TEST_P(UncheckedOptionalAccessTest,OptionalValueOptional)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.
TEST_P(UncheckedOptionalAccessTest,OptionalValueStruct)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
TEST_P(UncheckedOptionalAccessTest,OptionalValueInitialization)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
TEST_P(UncheckedOptionalAccessTest,AssignThroughLvalueReferencePtr)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
TEST_P(UncheckedOptionalAccessTest,CorrelatedBranches)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
TEST_P(UncheckedOptionalAccessTest,JoinDistinctValues)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
TEST_P(UncheckedOptionalAccessTest,ReassignValueInLoop)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