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/Basic/SourceLocation.h"
15#include "clang/Frontend/TextDiagnostic.h"
16#include "clang/Tooling/Tooling.h"
17#include "llvm/ADT/DenseSet.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/Error.h"
20#include "gmock/gmock.h"
21#include "gtest/gtest.h"
22#include <optional>
23#include <string>
24#include <utility>
25#include <vector>
26
27using namespace clang;
28using namespace dataflow;
29using namespace test;
30
31using ::testing::ContainerEq;
32
33// FIXME: Move header definitions in separate file(s).
34static constexpr char CSDtdDefHeader[] = R"(
35#ifndef CSTDDEF_H
36#define CSTDDEF_H
37
38namespace std {
39
40typedef decltype(sizeof(char)) size_t;
41
42using nullptr_t = decltype(nullptr);
43
44} // namespace std
45
46#endif // CSTDDEF_H
47)";
48
49static constexpr char StdTypeTraitsHeader[] = R"(
50#ifndef STD_TYPE_TRAITS_H
51#define STD_TYPE_TRAITS_H
52
53#include "cstddef.h"
54
55namespace std {
56
57template <typename T, T V>
58struct integral_constant {
59 static constexpr T value = V;
60};
61
62using true_type = integral_constant<bool, true>;
63using false_type = integral_constant<bool, false>;
64
65template< class T > struct remove_reference {typedef T type;};
66template< class T > struct remove_reference<T&> {typedef T type;};
67template< class T > struct remove_reference<T&&> {typedef T type;};
68
69template <class T>
70 using remove_reference_t = typename remove_reference<T>::type;
71
72template <class T>
73struct remove_extent {
74 typedef T type;
75};
76
77template <class T>
78struct remove_extent<T[]> {
79 typedef T type;
80};
81
82template <class T, size_t N>
83struct remove_extent<T[N]> {
84 typedef T type;
85};
86
87template <class T>
88struct is_array : false_type {};
89
90template <class T>
91struct is_array<T[]> : true_type {};
92
93template <class T, size_t N>
94struct is_array<T[N]> : true_type {};
95
96template <class>
97struct is_function : false_type {};
98
99template <class Ret, class... Args>
100struct is_function<Ret(Args...)> : true_type {};
101
102namespace detail {
103
104template <class T>
105struct type_identity {
106 using type = T;
107}; // or use type_identity (since C++20)
108
109template <class T>
110auto try_add_pointer(int) -> type_identity<typename remove_reference<T>::type*>;
111template <class T>
112auto try_add_pointer(...) -> type_identity<T>;
113
114} // namespace detail
115
116template <class T>
117struct add_pointer : decltype(detail::try_add_pointer<T>(0)) {};
118
119template <bool B, class T, class F>
120struct conditional {
121 typedef T type;
122};
123
124template <class T, class F>
125struct conditional<false, T, F> {
126 typedef F type;
127};
128
129template <class T>
130struct remove_cv {
131 typedef T type;
132};
133template <class T>
134struct remove_cv<const T> {
135 typedef T type;
136};
137template <class T>
138struct remove_cv<volatile T> {
139 typedef T type;
140};
141template <class T>
142struct remove_cv<const volatile T> {
143 typedef T type;
144};
145
146template <class T>
147using remove_cv_t = typename remove_cv<T>::type;
148
149template <class T>
150struct decay {
151 private:
152 typedef typename remove_reference<T>::type U;
153
154 public:
155 typedef typename conditional<
156 is_array<U>::value, typename remove_extent<U>::type*,
157 typename conditional<is_function<U>::value, typename add_pointer<U>::type,
158 typename remove_cv<U>::type>::type>::type type;
159};
160
161template <bool B, class T = void>
162struct enable_if {};
163
164template <class T>
165struct enable_if<true, T> {
166 typedef T type;
167};
168
169template <bool B, class T = void>
170using enable_if_t = typename enable_if<B, T>::type;
171
172template <class T, class U>
173struct is_same : false_type {};
174
175template <class T>
176struct is_same<T, T> : true_type {};
177
178template <class T>
179struct is_void : is_same<void, typename remove_cv<T>::type> {};
180
181namespace detail {
182
183template <class T>
184auto try_add_lvalue_reference(int) -> type_identity<T&>;
185template <class T>
186auto try_add_lvalue_reference(...) -> type_identity<T>;
187
188template <class T>
189auto try_add_rvalue_reference(int) -> type_identity<T&&>;
190template <class T>
191auto try_add_rvalue_reference(...) -> type_identity<T>;
192
193} // namespace detail
194
195template <class T>
196struct add_lvalue_reference : decltype(detail::try_add_lvalue_reference<T>(0)) {
197};
198
199template <class T>
200struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {
201};
202
203template <class T>
204typename add_rvalue_reference<T>::type declval() noexcept;
205
206namespace detail {
207
208template <class T>
209auto test_returnable(int)
210 -> decltype(void(static_cast<T (*)()>(nullptr)), true_type{});
211template <class>
212auto test_returnable(...) -> false_type;
213
214template <class From, class To>
215auto test_implicitly_convertible(int)
216 -> decltype(void(declval<void (&)(To)>()(declval<From>())), true_type{});
217template <class, class>
218auto test_implicitly_convertible(...) -> false_type;
219
220} // namespace detail
221
222template <class From, class To>
223struct is_convertible
224 : integral_constant<bool,
225 (decltype(detail::test_returnable<To>(0))::value &&
226 decltype(detail::test_implicitly_convertible<From, To>(
227 0))::value) ||
228 (is_void<From>::value && is_void<To>::value)> {};
229
230template <class From, class To>
231inline constexpr bool is_convertible_v = is_convertible<From, To>::value;
232
233template <class...>
234using void_t = void;
235
236template <class, class T, class... Args>
237struct is_constructible_ : false_type {};
238
239template <class T, class... Args>
240struct is_constructible_<void_t<decltype(T(declval<Args>()...))>, T, Args...>
241 : true_type {};
242
243template <class T, class... Args>
244using is_constructible = is_constructible_<void_t<>, T, Args...>;
245
246template <class T, class... Args>
247inline constexpr bool is_constructible_v = is_constructible<T, Args...>::value;
248
249template <class _Tp>
250struct __uncvref {
251 typedef typename remove_cv<typename remove_reference<_Tp>::type>::type type;
252};
253
254template <class _Tp>
255using __uncvref_t = typename __uncvref<_Tp>::type;
256
257template <bool _Val>
258using _BoolConstant = integral_constant<bool, _Val>;
259
260template <class _Tp, class _Up>
261using _IsSame = _BoolConstant<__is_same(_Tp, _Up)>;
262
263template <class _Tp, class _Up>
264using _IsNotSame = _BoolConstant<!__is_same(_Tp, _Up)>;
265
266template <bool>
267struct _MetaBase;
268template <>
269struct _MetaBase<true> {
270 template <class _Tp, class _Up>
271 using _SelectImpl = _Tp;
272 template <template <class...> class _FirstFn, template <class...> class,
273 class... _Args>
274 using _SelectApplyImpl = _FirstFn<_Args...>;
275 template <class _First, class...>
276 using _FirstImpl = _First;
277 template <class, class _Second, class...>
278 using _SecondImpl = _Second;
279 template <class _Result, class _First, class... _Rest>
280 using _OrImpl =
281 typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>::
282 template _OrImpl<_First, _Rest...>;
283};
284
285template <>
286struct _MetaBase<false> {
287 template <class _Tp, class _Up>
288 using _SelectImpl = _Up;
289 template <template <class...> class, template <class...> class _SecondFn,
290 class... _Args>
291 using _SelectApplyImpl = _SecondFn<_Args...>;
292 template <class _Result, class...>
293 using _OrImpl = _Result;
294};
295
296template <bool _Cond, class _IfRes, class _ElseRes>
297using _If = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
298
299template <class... _Rest>
300using _Or = typename _MetaBase<sizeof...(_Rest) !=
301 0>::template _OrImpl<false_type, _Rest...>;
302
303template <bool _Bp, class _Tp = void>
304using __enable_if_t = typename enable_if<_Bp, _Tp>::type;
305
306template <class...>
307using __expand_to_true = true_type;
308template <class... _Pred>
309__expand_to_true<__enable_if_t<_Pred::value>...> __and_helper(int);
310template <class...>
311false_type __and_helper(...);
312template <class... _Pred>
313using _And = decltype(__and_helper<_Pred...>(0));
314
315template <class _Pred>
316struct _Not : _BoolConstant<!_Pred::value> {};
317
318struct __check_tuple_constructor_fail {
319 static constexpr bool __enable_explicit_default() { return false; }
320 static constexpr bool __enable_implicit_default() { return false; }
321 template <class...>
322 static constexpr bool __enable_explicit() {
323 return false;
324 }
325 template <class...>
326 static constexpr bool __enable_implicit() {
327 return false;
328 }
329};
330
331template <typename, typename _Tp>
332struct __select_2nd {
333 typedef _Tp type;
334};
335template <class _Tp, class _Arg>
336typename __select_2nd<decltype((declval<_Tp>() = declval<_Arg>())),
337 true_type>::type
338__is_assignable_test(int);
339template <class, class>
340false_type __is_assignable_test(...);
341template <class _Tp, class _Arg,
342 bool = is_void<_Tp>::value || is_void<_Arg>::value>
343struct __is_assignable_imp
344 : public decltype((__is_assignable_test<_Tp, _Arg>(0))) {};
345template <class _Tp, class _Arg>
346struct __is_assignable_imp<_Tp, _Arg, true> : public false_type {};
347template <class _Tp, class _Arg>
348struct is_assignable : public __is_assignable_imp<_Tp, _Arg> {};
349
350template <class _Tp>
351struct __libcpp_is_integral : public false_type {};
352template <>
353struct __libcpp_is_integral<bool> : public true_type {};
354template <>
355struct __libcpp_is_integral<char> : public true_type {};
356template <>
357struct __libcpp_is_integral<signed char> : public true_type {};
358template <>
359struct __libcpp_is_integral<unsigned char> : public true_type {};
360template <>
361struct __libcpp_is_integral<wchar_t> : public true_type {};
362template <>
363struct __libcpp_is_integral<short> : public true_type {}; // NOLINT
364template <>
365struct __libcpp_is_integral<unsigned short> : public true_type {}; // NOLINT
366template <>
367struct __libcpp_is_integral<int> : public true_type {};
368template <>
369struct __libcpp_is_integral<unsigned int> : public true_type {};
370template <>
371struct __libcpp_is_integral<long> : public true_type {}; // NOLINT
372template <>
373struct __libcpp_is_integral<unsigned long> : public true_type {}; // NOLINT
374template <>
375struct __libcpp_is_integral<long long> : public true_type {}; // NOLINT
376template <> // NOLINTNEXTLINE
377struct __libcpp_is_integral<unsigned long long> : public true_type {};
378template <class _Tp>
379struct is_integral
380 : public __libcpp_is_integral<typename remove_cv<_Tp>::type> {};
381
382template <class _Tp>
383struct __libcpp_is_floating_point : public false_type {};
384template <>
385struct __libcpp_is_floating_point<float> : public true_type {};
386template <>
387struct __libcpp_is_floating_point<double> : public true_type {};
388template <>
389struct __libcpp_is_floating_point<long double> : public true_type {};
390template <class _Tp>
391struct is_floating_point
392 : public __libcpp_is_floating_point<typename remove_cv<_Tp>::type> {};
393
394template <class _Tp>
395struct is_arithmetic
396 : public integral_constant<bool, is_integral<_Tp>::value ||
397 is_floating_point<_Tp>::value> {};
398
399template <class _Tp>
400struct __libcpp_is_pointer : public false_type {};
401template <class _Tp>
402struct __libcpp_is_pointer<_Tp*> : public true_type {};
403template <class _Tp>
404struct is_pointer : public __libcpp_is_pointer<typename remove_cv<_Tp>::type> {
405};
406
407template <class _Tp>
408struct __libcpp_is_member_pointer : public false_type {};
409template <class _Tp, class _Up>
410struct __libcpp_is_member_pointer<_Tp _Up::*> : public true_type {};
411template <class _Tp>
412struct is_member_pointer
413 : public __libcpp_is_member_pointer<typename remove_cv<_Tp>::type> {};
414
415template <class _Tp>
416struct __libcpp_union : public false_type {};
417template <class _Tp>
418struct is_union : public __libcpp_union<typename remove_cv<_Tp>::type> {};
419
420template <class T>
421struct is_reference : false_type {};
422template <class T>
423struct is_reference<T&> : true_type {};
424template <class T>
425struct is_reference<T&&> : true_type {};
426
427template <class T>
428inline constexpr bool is_reference_v = is_reference<T>::value;
429
430struct __two {
431 char __lx[2];
432};
433
434namespace __is_class_imp {
435template <class _Tp>
436char __test(int _Tp::*);
437template <class _Tp>
438__two __test(...);
439} // namespace __is_class_imp
440template <class _Tp>
441struct is_class
442 : public integral_constant<bool,
443 sizeof(__is_class_imp::__test<_Tp>(0)) == 1 &&
444 !is_union<_Tp>::value> {};
445
446template <class _Tp>
447struct __is_nullptr_t_impl : public false_type {};
448template <>
449struct __is_nullptr_t_impl<nullptr_t> : public true_type {};
450template <class _Tp>
451struct __is_nullptr_t
452 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
453template <class _Tp>
454struct is_null_pointer
455 : public __is_nullptr_t_impl<typename remove_cv<_Tp>::type> {};
456
457template <class _Tp>
458struct is_enum
459 : public integral_constant<
460 bool, !is_void<_Tp>::value && !is_integral<_Tp>::value &&
461 !is_floating_point<_Tp>::value && !is_array<_Tp>::value &&
462 !is_pointer<_Tp>::value && !is_reference<_Tp>::value &&
463 !is_member_pointer<_Tp>::value && !is_union<_Tp>::value &&
464 !is_class<_Tp>::value && !is_function<_Tp>::value> {};
465
466template <class _Tp>
467struct is_scalar
468 : public integral_constant<
469 bool, is_arithmetic<_Tp>::value || is_member_pointer<_Tp>::value ||
470 is_pointer<_Tp>::value || __is_nullptr_t<_Tp>::value ||
471 is_enum<_Tp>::value> {};
472template <>
473struct is_scalar<nullptr_t> : public true_type {};
474
475} // namespace std
476
477#endif // STD_TYPE_TRAITS_H
478)";
479
480static constexpr char AbslTypeTraitsHeader[] = R"(
481#ifndef ABSL_TYPE_TRAITS_H
482#define ABSL_TYPE_TRAITS_H
483
484#include "std_type_traits.h"
485
486namespace absl {
487
488template <typename... Ts>
489struct conjunction : std::true_type {};
490
491template <typename T, typename... Ts>
492struct conjunction<T, Ts...>
493 : std::conditional<T::value, conjunction<Ts...>, T>::type {};
494
495template <typename T>
496struct conjunction<T> : T {};
497
498template <typename T>
499struct negation : std::integral_constant<bool, !T::value> {};
500
501template <bool B, typename T = void>
502using enable_if_t = typename std::enable_if<B, T>::type;
503
504} // namespace absl
505
506#endif // ABSL_TYPE_TRAITS_H
507)";
508
509static constexpr char StdStringHeader[] = R"(
510#ifndef STRING_H
511#define STRING_H
512
513namespace std {
514
515struct string {
516 string(const char*);
517 ~string();
518 bool empty();
519};
520bool operator!=(const string &LHS, const char *RHS);
521
522} // namespace std
523
524#endif // STRING_H
525)";
526
527static constexpr char StdUtilityHeader[] = R"(
528#ifndef UTILITY_H
529#define UTILITY_H
530
531#include "std_type_traits.h"
532
533namespace std {
534
535template <typename T>
536constexpr remove_reference_t<T>&& move(T&& x);
537
538template <typename T>
539void swap(T& a, T& b) noexcept;
540
541} // namespace std
542
543#endif // UTILITY_H
544)";
545
546static constexpr char StdInitializerListHeader[] = R"(
547#ifndef INITIALIZER_LIST_H
548#define INITIALIZER_LIST_H
549
550namespace std {
551
552template <typename T>
553class initializer_list {
554 public:
555 initializer_list() noexcept;
556};
557
558} // namespace std
559
560#endif // INITIALIZER_LIST_H
561)";
562
563static constexpr char StdOptionalHeader[] = R"(
564#include "std_initializer_list.h"
565#include "std_type_traits.h"
566#include "std_utility.h"
567
568namespace std {
569
570struct in_place_t {};
571constexpr in_place_t in_place;
572
573struct nullopt_t {
574 constexpr explicit nullopt_t() {}
575};
576constexpr nullopt_t nullopt;
577
578template <class _Tp>
579struct __optional_destruct_base {
580 constexpr void reset() noexcept;
581};
582
583template <class _Tp>
584struct __optional_storage_base : __optional_destruct_base<_Tp> {
585 constexpr bool has_value() const noexcept;
586};
587
588template <typename _Tp>
589class optional : private __optional_storage_base<_Tp> {
590 using __base = __optional_storage_base<_Tp>;
591
592 public:
593 using value_type = _Tp;
594
595 private:
596 struct _CheckOptionalArgsConstructor {
597 template <class _Up>
598 static constexpr bool __enable_implicit() {
599 return is_constructible_v<_Tp, _Up&&> && is_convertible_v<_Up&&, _Tp>;
600 }
601
602 template <class _Up>
603 static constexpr bool __enable_explicit() {
604 return is_constructible_v<_Tp, _Up&&> && !is_convertible_v<_Up&&, _Tp>;
605 }
606 };
607 template <class _Up>
608 using _CheckOptionalArgsCtor =
609 _If<_IsNotSame<__uncvref_t<_Up>, in_place_t>::value &&
610 _IsNotSame<__uncvref_t<_Up>, optional>::value,
611 _CheckOptionalArgsConstructor, __check_tuple_constructor_fail>;
612 template <class _QualUp>
613 struct _CheckOptionalLikeConstructor {
614 template <class _Up, class _Opt = optional<_Up>>
615 using __check_constructible_from_opt =
616 _Or<is_constructible<_Tp, _Opt&>, is_constructible<_Tp, _Opt const&>,
617 is_constructible<_Tp, _Opt&&>, is_constructible<_Tp, _Opt const&&>,
618 is_convertible<_Opt&, _Tp>, is_convertible<_Opt const&, _Tp>,
619 is_convertible<_Opt&&, _Tp>, is_convertible<_Opt const&&, _Tp>>;
620 template <class _Up, class _QUp = _QualUp>
621 static constexpr bool __enable_implicit() {
622 return is_convertible<_QUp, _Tp>::value &&
623 !__check_constructible_from_opt<_Up>::value;
624 }
625 template <class _Up, class _QUp = _QualUp>
626 static constexpr bool __enable_explicit() {
627 return !is_convertible<_QUp, _Tp>::value &&
628 !__check_constructible_from_opt<_Up>::value;
629 }
630 };
631
632 template <class _Up, class _QualUp>
633 using _CheckOptionalLikeCtor =
634 _If<_And<_IsNotSame<_Up, _Tp>, is_constructible<_Tp, _QualUp>>::value,
635 _CheckOptionalLikeConstructor<_QualUp>,
636 __check_tuple_constructor_fail>;
637
638
639 template <class _Up, class _QualUp>
640 using _CheckOptionalLikeAssign = _If<
641 _And<
642 _IsNotSame<_Up, _Tp>,
643 is_constructible<_Tp, _QualUp>,
644 is_assignable<_Tp&, _QualUp>
645 >::value,
646 _CheckOptionalLikeConstructor<_QualUp>,
647 __check_tuple_constructor_fail
648 >;
649
650 public:
651 constexpr optional() noexcept {}
652 constexpr optional(const optional&) = default;
653 constexpr optional(optional&&) = default;
654 constexpr optional(nullopt_t) noexcept {}
655
656 template <
657 class _InPlaceT, class... _Args,
658 class = enable_if_t<_And<_IsSame<_InPlaceT, in_place_t>,
659 is_constructible<value_type, _Args...>>::value>>
660 constexpr explicit optional(_InPlaceT, _Args&&... __args);
661
662 template <class _Up, class... _Args,
663 class = enable_if_t<is_constructible_v<
664 value_type, initializer_list<_Up>&, _Args...>>>
665 constexpr explicit optional(in_place_t, initializer_list<_Up> __il,
666 _Args&&... __args);
667
668 template <
669 class _Up = value_type,
670 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_implicit<_Up>(),
671 int> = 0>
672 constexpr optional(_Up&& __v);
673
674 template <
675 class _Up,
676 enable_if_t<_CheckOptionalArgsCtor<_Up>::template __enable_explicit<_Up>(),
677 int> = 0>
678 constexpr explicit optional(_Up&& __v);
679
680 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::
681 template __enable_implicit<_Up>(),
682 int> = 0>
683 constexpr optional(const optional<_Up>& __v);
684
685 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up const&>::
686 template __enable_explicit<_Up>(),
687 int> = 0>
688 constexpr explicit optional(const optional<_Up>& __v);
689
690 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
691 template __enable_implicit<_Up>(),
692 int> = 0>
693 constexpr optional(optional<_Up>&& __v);
694
695 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
696 template __enable_explicit<_Up>(),
697 int> = 0>
698 constexpr explicit optional(optional<_Up>&& __v);
699
700 constexpr optional& operator=(nullopt_t) noexcept;
701
702 optional& operator=(const optional&);
703
704 optional& operator=(optional&&);
705
706 template <class _Up = value_type,
707 class = enable_if_t<_And<_IsNotSame<__uncvref_t<_Up>, optional>,
708 _Or<_IsNotSame<__uncvref_t<_Up>, value_type>,
709 _Not<is_scalar<value_type>>>,
710 is_constructible<value_type, _Up>,
711 is_assignable<value_type&, _Up>>::value>>
712 constexpr optional& operator=(_Up&& __v);
713
714 template <class _Up, enable_if_t<_CheckOptionalLikeAssign<_Up, _Up const&>::
715 template __enable_assign<_Up>(),
716 int> = 0>
717 constexpr optional& operator=(const optional<_Up>& __v);
718
719 template <class _Up, enable_if_t<_CheckOptionalLikeCtor<_Up, _Up&&>::
720 template __enable_assign<_Up>(),
721 int> = 0>
722 constexpr optional& operator=(optional<_Up>&& __v);
723
724 const _Tp& operator*() const&;
725 _Tp& operator*() &;
726 const _Tp&& operator*() const&&;
727 _Tp&& operator*() &&;
728
729 const _Tp* operator->() const;
730 _Tp* operator->();
731
732 const _Tp& value() const&;
733 _Tp& value() &;
734 const _Tp&& value() const&&;
735 _Tp&& value() &&;
736
737 template <typename U>
738 constexpr _Tp value_or(U&& v) const&;
739 template <typename U>
740 _Tp value_or(U&& v) &&;
741
742 template <typename... Args>
743 _Tp& emplace(Args&&... args);
744
745 template <typename U, typename... Args>
746 _Tp& emplace(std::initializer_list<U> ilist, Args&&... args);
747
748 using __base::reset;
749
750 constexpr explicit operator bool() const noexcept;
751 using __base::has_value;
752
753 constexpr void swap(optional& __opt) noexcept;
754};
755
756template <typename T>
757constexpr optional<typename std::decay<T>::type> make_optional(T&& v);
758
759template <typename T, typename... Args>
760constexpr optional<T> make_optional(Args&&... args);
761
762template <typename T, typename U, typename... Args>
763constexpr optional<T> make_optional(std::initializer_list<U> il,
764 Args&&... args);
765
766template <typename T, typename U>
767constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs);
768template <typename T, typename U>
769constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs);
770
771template <typename T>
772constexpr bool operator==(const optional<T> &opt, nullopt_t);
773
774// C++20 and later do not define the following overloads because they are
775// provided by rewritten candidates instead.
776#if __cplusplus < 202002L
777template <typename T>
778constexpr bool operator==(nullopt_t, const optional<T> &opt);
779template <typename T>
780constexpr bool operator!=(const optional<T> &opt, nullopt_t);
781template <typename T>
782constexpr bool operator!=(nullopt_t, const optional<T> &opt);
783#endif // __cplusplus < 202002L
784
785template <typename T, typename U>
786constexpr bool operator==(const optional<T> &opt, const U &value);
787template <typename T, typename U>
788constexpr bool operator==(const T &value, const optional<U> &opt);
789template <typename T, typename U>
790constexpr bool operator!=(const optional<T> &opt, const U &value);
791template <typename T, typename U>
792constexpr bool operator!=(const T &value, const optional<U> &opt);
793
794} // namespace std
795)";
796
797static constexpr char AbslOptionalHeader[] = R"(
798#include "absl_type_traits.h"
799#include "std_initializer_list.h"
800#include "std_type_traits.h"
801#include "std_utility.h"
802
803namespace absl {
804
805struct nullopt_t {
806 constexpr explicit nullopt_t() {}
807};
808constexpr nullopt_t nullopt;
809
810struct in_place_t {};
811constexpr in_place_t in_place;
812
813template <typename T>
814class optional;
815
816namespace optional_internal {
817
818template <typename T, typename U>
819struct is_constructible_convertible_from_optional
820 : std::integral_constant<
821 bool, std::is_constructible<T, optional<U>&>::value ||
822 std::is_constructible<T, optional<U>&&>::value ||
823 std::is_constructible<T, const optional<U>&>::value ||
824 std::is_constructible<T, const optional<U>&&>::value ||
825 std::is_convertible<optional<U>&, T>::value ||
826 std::is_convertible<optional<U>&&, T>::value ||
827 std::is_convertible<const optional<U>&, T>::value ||
828 std::is_convertible<const optional<U>&&, T>::value> {};
829
830template <typename T, typename U>
831struct is_constructible_convertible_assignable_from_optional
832 : std::integral_constant<
833 bool, is_constructible_convertible_from_optional<T, U>::value ||
834 std::is_assignable<T&, optional<U>&>::value ||
835 std::is_assignable<T&, optional<U>&&>::value ||
836 std::is_assignable<T&, const optional<U>&>::value ||
837 std::is_assignable<T&, const optional<U>&&>::value> {};
838
839} // namespace optional_internal
840
841template <typename T>
842class optional {
843 public:
844 constexpr optional() noexcept;
845
846 constexpr optional(nullopt_t) noexcept;
847
848 optional(const optional&) = default;
849
850 optional(optional&&) = default;
851
852 template <typename InPlaceT, typename... Args,
853 absl::enable_if_t<absl::conjunction<
854 std::is_same<InPlaceT, in_place_t>,
855 std::is_constructible<T, Args&&...>>::value>* = nullptr>
856 constexpr explicit optional(InPlaceT, Args&&... args);
857
858 template <typename U, typename... Args,
859 typename = typename std::enable_if<std::is_constructible<
860 T, std::initializer_list<U>&, Args&&...>::value>::type>
861 constexpr explicit optional(in_place_t, std::initializer_list<U> il,
862 Args&&... args);
863
864 template <
865 typename U = T,
866 typename std::enable_if<
867 absl::conjunction<absl::negation<std::is_same<
868 in_place_t, typename std::decay<U>::type>>,
869 absl::negation<std::is_same<
870 optional<T>, typename std::decay<U>::type>>,
871 std::is_convertible<U&&, T>,
872 std::is_constructible<T, U&&>>::value,
873 bool>::type = false>
874 constexpr optional(U&& v);
875
876 template <
877 typename U = T,
878 typename std::enable_if<
879 absl::conjunction<absl::negation<std::is_same<
880 in_place_t, typename std::decay<U>::type>>,
881 absl::negation<std::is_same<
882 optional<T>, typename std::decay<U>::type>>,
883 absl::negation<std::is_convertible<U&&, T>>,
884 std::is_constructible<T, U&&>>::value,
885 bool>::type = false>
886 explicit constexpr optional(U&& v);
887
888 template <typename U,
889 typename std::enable_if<
890 absl::conjunction<
891 absl::negation<std::is_same<T, U>>,
892 std::is_constructible<T, const U&>,
893 absl::negation<
894 optional_internal::
895 is_constructible_convertible_from_optional<T, U>>,
896 std::is_convertible<const U&, T>>::value,
897 bool>::type = false>
898 optional(const optional<U>& rhs);
899
900 template <typename U,
901 typename std::enable_if<
902 absl::conjunction<
903 absl::negation<std::is_same<T, U>>,
904 std::is_constructible<T, const U&>,
905 absl::negation<
906 optional_internal::
907 is_constructible_convertible_from_optional<T, U>>,
908 absl::negation<std::is_convertible<const U&, T>>>::value,
909 bool>::type = false>
910 explicit optional(const optional<U>& rhs);
911
912 template <
913 typename U,
914 typename std::enable_if<
915 absl::conjunction<
916 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
917 absl::negation<
918 optional_internal::is_constructible_convertible_from_optional<
919 T, U>>,
920 std::is_convertible<U&&, T>>::value,
921 bool>::type = false>
922 optional(optional<U>&& rhs);
923
924 template <
925 typename U,
926 typename std::enable_if<
927 absl::conjunction<
928 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U&&>,
929 absl::negation<
930 optional_internal::is_constructible_convertible_from_optional<
931 T, U>>,
932 absl::negation<std::is_convertible<U&&, T>>>::value,
933 bool>::type = false>
934 explicit optional(optional<U>&& rhs);
935
936 optional& operator=(nullopt_t) noexcept;
937
938 optional& operator=(const optional& src);
939
940 optional& operator=(optional&& src);
941
942 template <
943 typename U = T,
944 typename = typename std::enable_if<absl::conjunction<
945 absl::negation<
946 std::is_same<optional<T>, typename std::decay<U>::type>>,
947 absl::negation<
948 absl::conjunction<std::is_scalar<T>,
949 std::is_same<T, typename std::decay<U>::type>>>,
950 std::is_constructible<T, U>, std::is_assignable<T&, U>>::value>::type>
951 optional& operator=(U&& v);
952
953 template <
954 typename U,
955 typename = typename std::enable_if<absl::conjunction<
956 absl::negation<std::is_same<T, U>>,
957 std::is_constructible<T, const U&>, std::is_assignable<T&, const U&>,
958 absl::negation<
959 optional_internal::
960 is_constructible_convertible_assignable_from_optional<
961 T, U>>>::value>::type>
962 optional& operator=(const optional<U>& rhs);
963
964 template <typename U,
965 typename = typename std::enable_if<absl::conjunction<
966 absl::negation<std::is_same<T, U>>, std::is_constructible<T, U>,
967 std::is_assignable<T&, U>,
968 absl::negation<
969 optional_internal::
970 is_constructible_convertible_assignable_from_optional<
971 T, U>>>::value>::type>
972 optional& operator=(optional<U>&& rhs);
973
974 const T& operator*() const&;
975 T& operator*() &;
976 const T&& operator*() const&&;
977 T&& operator*() &&;
978
979 const T* operator->() const;
980 T* operator->();
981
982 const T& value() const&;
983 T& value() &;
984 const T&& value() const&&;
985 T&& value() &&;
986
987 template <typename U>
988 constexpr T value_or(U&& v) const&;
989 template <typename U>
990 T value_or(U&& v) &&;
991
992 template <typename... Args>
993 T& emplace(Args&&... args);
994
995 template <typename U, typename... Args>
996 T& emplace(std::initializer_list<U> ilist, Args&&... args);
997
998 void reset() noexcept;
999
1000 constexpr explicit operator bool() const noexcept;
1001 constexpr bool has_value() const noexcept;
1002
1003 void swap(optional& rhs) noexcept;
1004};
1005
1006template <typename T>
1007constexpr optional<typename std::decay<T>::type> make_optional(T&& v);
1008
1009template <typename T, typename... Args>
1010constexpr optional<T> make_optional(Args&&... args);
1011
1012template <typename T, typename U, typename... Args>
1013constexpr optional<T> make_optional(std::initializer_list<U> il,
1014 Args&&... args);
1015
1016template <typename T, typename U>
1017constexpr bool operator==(const optional<T> &lhs, const optional<U> &rhs);
1018template <typename T, typename U>
1019constexpr bool operator!=(const optional<T> &lhs, const optional<U> &rhs);
1020
1021template <typename T>
1022constexpr bool operator==(const optional<T> &opt, nullopt_t);
1023template <typename T>
1024constexpr bool operator==(nullopt_t, const optional<T> &opt);
1025template <typename T>
1026constexpr bool operator!=(const optional<T> &opt, nullopt_t);
1027template <typename T>
1028constexpr bool operator!=(nullopt_t, const optional<T> &opt);
1029
1030template <typename T, typename U>
1031constexpr bool operator==(const optional<T> &opt, const U &value);
1032template <typename T, typename U>
1033constexpr bool operator==(const T &value, const optional<U> &opt);
1034template <typename T, typename U>
1035constexpr bool operator!=(const optional<T> &opt, const U &value);
1036template <typename T, typename U>
1037constexpr bool operator!=(const T &value, const optional<U> &opt);
1038
1039} // namespace absl
1040)";
1041
1042static constexpr char BaseOptionalHeader[] = R"(
1043#include "std_initializer_list.h"
1044#include "std_type_traits.h"
1045#include "std_utility.h"
1046
1047namespace base {
1048
1049struct in_place_t {};
1050constexpr in_place_t in_place;
1051
1052struct nullopt_t {
1053 constexpr explicit nullopt_t() {}
1054};
1055constexpr nullopt_t nullopt;
1056
1057template <typename T>
1058class Optional;
1059
1060namespace internal {
1061
1062template <typename T>
1063using RemoveCvRefT = std::remove_cv_t<std::remove_reference_t<T>>;
1064
1065template <typename T, typename U>
1066struct IsConvertibleFromOptional
1067 : std::integral_constant<
1068 bool, std::is_constructible<T, Optional<U>&>::value ||
1069 std::is_constructible<T, const Optional<U>&>::value ||
1070 std::is_constructible<T, Optional<U>&&>::value ||
1071 std::is_constructible<T, const Optional<U>&&>::value ||
1072 std::is_convertible<Optional<U>&, T>::value ||
1073 std::is_convertible<const Optional<U>&, T>::value ||
1074 std::is_convertible<Optional<U>&&, T>::value ||
1075 std::is_convertible<const Optional<U>&&, T>::value> {};
1076
1077template <typename T, typename U>
1078struct IsAssignableFromOptional
1079 : std::integral_constant<
1080 bool, IsConvertibleFromOptional<T, U>::value ||
1081 std::is_assignable<T&, Optional<U>&>::value ||
1082 std::is_assignable<T&, const Optional<U>&>::value ||
1083 std::is_assignable<T&, Optional<U>&&>::value ||
1084 std::is_assignable<T&, const Optional<U>&&>::value> {};
1085
1086} // namespace internal
1087
1088template <typename T>
1089class Optional {
1090 public:
1091 using value_type = T;
1092
1093 constexpr Optional() = default;
1094 constexpr Optional(const Optional& other) noexcept = default;
1095 constexpr Optional(Optional&& other) noexcept = default;
1096
1097 constexpr Optional(nullopt_t);
1098
1099 template <typename U,
1100 typename std::enable_if<
1101 std::is_constructible<T, const U&>::value &&
1102 !internal::IsConvertibleFromOptional<T, U>::value &&
1103 std::is_convertible<const U&, T>::value,
1104 bool>::type = false>
1105 Optional(const Optional<U>& other) noexcept;
1106
1107 template <typename U,
1108 typename std::enable_if<
1109 std::is_constructible<T, const U&>::value &&
1110 !internal::IsConvertibleFromOptional<T, U>::value &&
1111 !std::is_convertible<const U&, T>::value,
1112 bool>::type = false>
1113 explicit Optional(const Optional<U>& other) noexcept;
1114
1115 template <typename U,
1116 typename std::enable_if<
1117 std::is_constructible<T, U&&>::value &&
1118 !internal::IsConvertibleFromOptional<T, U>::value &&
1119 std::is_convertible<U&&, T>::value,
1120 bool>::type = false>
1121 Optional(Optional<U>&& other) noexcept;
1122
1123 template <typename U,
1124 typename std::enable_if<
1125 std::is_constructible<T, U&&>::value &&
1126 !internal::IsConvertibleFromOptional<T, U>::value &&
1127 !std::is_convertible<U&&, T>::value,
1128 bool>::type = false>
1129 explicit Optional(Optional<U>&& other) noexcept;
1130
1131 template <class... Args>
1132 constexpr explicit Optional(in_place_t, Args&&... args);
1133
1134 template <class U, class... Args,
1135 class = typename std::enable_if<std::is_constructible<
1136 value_type, std::initializer_list<U>&, Args...>::value>::type>
1137 constexpr explicit Optional(in_place_t, std::initializer_list<U> il,
1138 Args&&... args);
1139
1140 template <
1141 typename U = value_type,
1142 typename std::enable_if<
1143 std::is_constructible<T, U&&>::value &&
1144 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
1145 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
1146 std::is_convertible<U&&, T>::value,
1147 bool>::type = false>
1148 constexpr Optional(U&& value);
1149
1150 template <
1151 typename U = value_type,
1152 typename std::enable_if<
1153 std::is_constructible<T, U&&>::value &&
1154 !std::is_same<internal::RemoveCvRefT<U>, in_place_t>::value &&
1155 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
1156 !std::is_convertible<U&&, T>::value,
1157 bool>::type = false>
1158 constexpr explicit Optional(U&& value);
1159
1160 Optional& operator=(const Optional& other) noexcept;
1161
1162 Optional& operator=(Optional&& other) noexcept;
1163
1164 Optional& operator=(nullopt_t);
1165
1166 template <typename U>
1167 typename std::enable_if<
1168 !std::is_same<internal::RemoveCvRefT<U>, Optional<T>>::value &&
1169 std::is_constructible<T, U>::value &&
1170 std::is_assignable<T&, U>::value &&
1171 (!std::is_scalar<T>::value ||
1172 !std::is_same<typename std::decay<U>::type, T>::value),
1173 Optional&>::type
1174 operator=(U&& value) noexcept;
1175
1176 template <typename U>
1177 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value &&
1178 std::is_constructible<T, const U&>::value &&
1179 std::is_assignable<T&, const U&>::value,
1180 Optional&>::type
1181 operator=(const Optional<U>& other) noexcept;
1182
1183 template <typename U>
1184 typename std::enable_if<!internal::IsAssignableFromOptional<T, U>::value &&
1185 std::is_constructible<T, U>::value &&
1186 std::is_assignable<T&, U>::value,
1187 Optional&>::type
1188 operator=(Optional<U>&& other) noexcept;
1189
1190 const T& operator*() const&;
1191 T& operator*() &;
1192 const T&& operator*() const&&;
1193 T&& operator*() &&;
1194
1195 const T* operator->() const;
1196 T* operator->();
1197
1198 const T& value() const&;
1199 T& value() &;
1200 const T&& value() const&&;
1201 T&& value() &&;
1202
1203 template <typename U>
1204 constexpr T value_or(U&& v) const&;
1205 template <typename U>
1206 T value_or(U&& v) &&;
1207
1208 template <typename... Args>
1209 T& emplace(Args&&... args);
1210
1211 template <typename U, typename... Args>
1212 T& emplace(std::initializer_list<U> ilist, Args&&... args);
1213
1214 void reset() noexcept;
1215
1216 constexpr explicit operator bool() const noexcept;
1217 constexpr bool has_value() const noexcept;
1218
1219 void swap(Optional& other);
1220};
1221
1222template <typename T>
1223constexpr Optional<typename std::decay<T>::type> make_optional(T&& v);
1224
1225template <typename T, typename... Args>
1226constexpr Optional<T> make_optional(Args&&... args);
1227
1228template <typename T, typename U, typename... Args>
1229constexpr Optional<T> make_optional(std::initializer_list<U> il,
1230 Args&&... args);
1231
1232template <typename T, typename U>
1233constexpr bool operator==(const Optional<T> &lhs, const Optional<U> &rhs);
1234template <typename T, typename U>
1235constexpr bool operator!=(const Optional<T> &lhs, const Optional<U> &rhs);
1236
1237template <typename T>
1238constexpr bool operator==(const Optional<T> &opt, nullopt_t);
1239template <typename T>
1240constexpr bool operator==(nullopt_t, const Optional<T> &opt);
1241template <typename T>
1242constexpr bool operator!=(const Optional<T> &opt, nullopt_t);
1243template <typename T>
1244constexpr bool operator!=(nullopt_t, const Optional<T> &opt);
1245
1246template <typename T, typename U>
1247constexpr bool operator==(const Optional<T> &opt, const U &value);
1248template <typename T, typename U>
1249constexpr bool operator==(const T &value, const Optional<U> &opt);
1250template <typename T, typename U>
1251constexpr bool operator!=(const Optional<T> &opt, const U &value);
1252template <typename T, typename U>
1253constexpr bool operator!=(const T &value, const Optional<U> &opt);
1254
1255} // namespace base
1256)";
1257
1258/// Replaces all occurrences of `Pattern` in `S` with `Replacement`.
1259static void ReplaceAllOccurrences(std::string &S, const std::string &Pattern,
1260 const std::string &Replacement) {
1261 size_t Pos = 0;
1262 while (true) {
1263 Pos = S.find(str: Pattern, pos: Pos);
1264 if (Pos == std::string::npos)
1265 break;
1266 S.replace(pos: Pos, n: Pattern.size(), str: Replacement);
1267 }
1268}
1269
1270struct OptionalTypeIdentifier {
1271 std::string NamespaceName;
1272 std::string TypeName;
1273};
1274
1275static raw_ostream &operator<<(raw_ostream &OS,
1276 const OptionalTypeIdentifier &TypeId) {
1277 OS << TypeId.NamespaceName << "::" << TypeId.TypeName;
1278 return OS;
1279}
1280
1281class UncheckedOptionalAccessTest
1282 : public ::testing::TestWithParam<OptionalTypeIdentifier> {
1283protected:
1284 void ExpectDiagnosticsFor(std::string SourceCode) {
1285 ExpectDiagnosticsFor(SourceCode, FuncMatcher: ast_matchers::hasName(Name: "target"));
1286 }
1287
1288 void ExpectDiagnosticsForLambda(std::string SourceCode) {
1289 ExpectDiagnosticsFor(
1290 SourceCode, FuncMatcher: ast_matchers::hasDeclContext(
1291 InnerMatcher: ast_matchers::cxxRecordDecl(ast_matchers::isLambda())));
1292 }
1293
1294 template <typename FuncDeclMatcher>
1295 void ExpectDiagnosticsFor(std::string SourceCode,
1296 FuncDeclMatcher FuncMatcher) {
1297 // Run in C++17 and C++20 mode to cover differences in the AST between modes
1298 // (e.g. C++20 can contain `CXXRewrittenBinaryOperator`).
1299 for (const char *CxxMode : {"-std=c++17", "-std=c++20"})
1300 ExpectDiagnosticsFor(SourceCode, FuncMatcher, CxxMode);
1301 }
1302
1303 template <typename FuncDeclMatcher>
1304 void ExpectDiagnosticsFor(std::string SourceCode, FuncDeclMatcher FuncMatcher,
1305 const char *CxxMode) {
1306 ReplaceAllOccurrences(S&: SourceCode, Pattern: "$ns", Replacement: GetParam().NamespaceName);
1307 ReplaceAllOccurrences(S&: SourceCode, Pattern: "$optional", Replacement: GetParam().TypeName);
1308
1309 std::vector<std::pair<std::string, std::string>> Headers;
1310 Headers.emplace_back(args: "cstddef.h", args: CSDtdDefHeader);
1311 Headers.emplace_back(args: "std_initializer_list.h", args: StdInitializerListHeader);
1312 Headers.emplace_back(args: "std_string.h", args: StdStringHeader);
1313 Headers.emplace_back(args: "std_type_traits.h", args: StdTypeTraitsHeader);
1314 Headers.emplace_back(args: "std_utility.h", args: StdUtilityHeader);
1315 Headers.emplace_back(args: "std_optional.h", args: StdOptionalHeader);
1316 Headers.emplace_back(args: "absl_type_traits.h", args: AbslTypeTraitsHeader);
1317 Headers.emplace_back(args: "absl_optional.h", args: AbslOptionalHeader);
1318 Headers.emplace_back(args: "base_optional.h", args: BaseOptionalHeader);
1319 Headers.emplace_back(args: "unchecked_optional_access_test.h", args: R"(
1320 #include "absl_optional.h"
1321 #include "base_optional.h"
1322 #include "std_initializer_list.h"
1323 #include "std_optional.h"
1324 #include "std_string.h"
1325 #include "std_utility.h"
1326
1327 template <typename T>
1328 T Make();
1329 )");
1330 UncheckedOptionalAccessModelOptions Options{
1331 /*IgnoreSmartPointerDereference=*/true};
1332 std::vector<SourceLocation> Diagnostics;
1333 llvm::Error Error = checkDataflow<UncheckedOptionalAccessModel>(
1334 AnalysisInputs<UncheckedOptionalAccessModel>(
1335 SourceCode, std::move(FuncMatcher),
1336 [](ASTContext &Ctx, Environment &Env) {
1337 return UncheckedOptionalAccessModel(Ctx, Env);
1338 })
1339 .withPostVisitCFG(
1340 Arg: [&Diagnostics,
1341 Diagnoser = UncheckedOptionalAccessDiagnoser(Options)](
1342 ASTContext &Ctx, const CFGElement &Elt,
1343 const TransferStateForDiagnostics<NoopLattice>
1344 &State) mutable {
1345 auto EltDiagnostics = Diagnoser(Elt, Ctx, State);
1346 llvm::move(Range&: EltDiagnostics, Out: std::back_inserter(x&: Diagnostics));
1347 })
1348 .withASTBuildArgs(
1349 {"-fsyntax-only", CxxMode, "-Wno-undefined-inline"})
1350 .withASTBuildVirtualMappedFiles(
1351 tooling::FileContentMappings(Headers.begin(), Headers.end())),
1352 /*VerifyResults=*/[&Diagnostics](
1353 const llvm::DenseMap<unsigned, std::string>
1354 &Annotations,
1355 const AnalysisOutputs &AO) {
1356 llvm::DenseSet<unsigned> AnnotationLines;
1357 for (const auto &[Line, _] : Annotations) {
1358 AnnotationLines.insert(V: Line);
1359 }
1360 auto &SrcMgr = AO.ASTCtx.getSourceManager();
1361 llvm::DenseSet<unsigned> DiagnosticLines;
1362 for (SourceLocation &Loc : Diagnostics) {
1363 unsigned Line = SrcMgr.getPresumedLineNumber(Loc);
1364 DiagnosticLines.insert(V: Line);
1365 if (!AnnotationLines.contains(V: Line)) {
1366 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(
1367 new DiagnosticOptions());
1368 TextDiagnostic TD(llvm::errs(), AO.ASTCtx.getLangOpts(),
1369 DiagOpts.get());
1370 TD.emitDiagnostic(
1371 Loc: FullSourceLoc(Loc, SrcMgr), Level: DiagnosticsEngine::Error,
1372 Message: "unexpected diagnostic", Ranges: std::nullopt, FixItHints: std::nullopt);
1373 }
1374 }
1375
1376 EXPECT_THAT(DiagnosticLines, ContainerEq(AnnotationLines));
1377 });
1378 if (Error)
1379 FAIL() << llvm::toString(E: std::move(Error));
1380 }
1381};
1382
1383INSTANTIATE_TEST_SUITE_P(
1384 UncheckedOptionalUseTestInst, UncheckedOptionalAccessTest,
1385 ::testing::Values(OptionalTypeIdentifier{"std", "optional"},
1386 OptionalTypeIdentifier{"absl", "optional"},
1387 OptionalTypeIdentifier{"base", "Optional"}),
1388 [](const ::testing::TestParamInfo<OptionalTypeIdentifier> &Info) {
1389 return Info.param.NamespaceName;
1390 });
1391
1392// Verifies that similarly-named types are ignored.
1393TEST_P(UncheckedOptionalAccessTest, NonTrackedOptionalType) {
1394 ExpectDiagnosticsFor(
1395 SourceCode: R"(
1396 namespace other {
1397 namespace $ns {
1398 template <typename T>
1399 struct $optional {
1400 T value();
1401 };
1402 }
1403
1404 void target($ns::$optional<int> opt) {
1405 opt.value();
1406 }
1407 }
1408 )");
1409}
1410
1411TEST_P(UncheckedOptionalAccessTest, EmptyFunctionBody) {
1412 ExpectDiagnosticsFor(SourceCode: R"(
1413 void target() {
1414 (void)0;
1415 }
1416 )");
1417}
1418
1419TEST_P(UncheckedOptionalAccessTest, UnwrapUsingValueNoCheck) {
1420 ExpectDiagnosticsFor(
1421 SourceCode: R"(
1422 #include "unchecked_optional_access_test.h"
1423
1424 void target($ns::$optional<int> opt) {
1425 opt.value(); // [[unsafe]]
1426 }
1427 )");
1428
1429 ExpectDiagnosticsFor(
1430 SourceCode: R"(
1431 #include "unchecked_optional_access_test.h"
1432
1433 void target($ns::$optional<int> opt) {
1434 std::move(opt).value(); // [[unsafe]]
1435 }
1436 )");
1437}
1438
1439TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorStarNoCheck) {
1440 ExpectDiagnosticsFor(
1441 SourceCode: R"(
1442 #include "unchecked_optional_access_test.h"
1443
1444 void target($ns::$optional<int> opt) {
1445 *opt; // [[unsafe]]
1446 }
1447 )");
1448
1449 ExpectDiagnosticsFor(
1450 SourceCode: R"(
1451 #include "unchecked_optional_access_test.h"
1452
1453 void target($ns::$optional<int> opt) {
1454 *std::move(opt); // [[unsafe]]
1455 }
1456 )");
1457}
1458
1459TEST_P(UncheckedOptionalAccessTest, UnwrapUsingOperatorArrowNoCheck) {
1460 ExpectDiagnosticsFor(
1461 SourceCode: R"(
1462 #include "unchecked_optional_access_test.h"
1463
1464 struct Foo {
1465 void foo();
1466 };
1467
1468 void target($ns::$optional<Foo> opt) {
1469 opt->foo(); // [[unsafe]]
1470 }
1471 )");
1472
1473 ExpectDiagnosticsFor(
1474 SourceCode: R"(
1475 #include "unchecked_optional_access_test.h"
1476
1477 struct Foo {
1478 void foo();
1479 };
1480
1481 void target($ns::$optional<Foo> opt) {
1482 std::move(opt)->foo(); // [[unsafe]]
1483 }
1484 )");
1485}
1486
1487TEST_P(UncheckedOptionalAccessTest, HasValueCheck) {
1488 ExpectDiagnosticsFor(SourceCode: R"(
1489 #include "unchecked_optional_access_test.h"
1490
1491 void target($ns::$optional<int> opt) {
1492 if (opt.has_value()) {
1493 opt.value();
1494 }
1495 }
1496 )");
1497}
1498
1499TEST_P(UncheckedOptionalAccessTest, OperatorBoolCheck) {
1500 ExpectDiagnosticsFor(SourceCode: R"(
1501 #include "unchecked_optional_access_test.h"
1502
1503 void target($ns::$optional<int> opt) {
1504 if (opt) {
1505 opt.value();
1506 }
1507 }
1508 )");
1509}
1510
1511TEST_P(UncheckedOptionalAccessTest, UnwrapFunctionCallResultNoCheck) {
1512 ExpectDiagnosticsFor(
1513 SourceCode: R"(
1514 #include "unchecked_optional_access_test.h"
1515
1516 void target() {
1517 Make<$ns::$optional<int>>().value(); // [[unsafe]]
1518 (void)0;
1519 }
1520 )");
1521
1522 ExpectDiagnosticsFor(
1523 SourceCode: R"(
1524 #include "unchecked_optional_access_test.h"
1525
1526 void target($ns::$optional<int> opt) {
1527 std::move(opt).value(); // [[unsafe]]
1528 }
1529 )");
1530}
1531
1532TEST_P(UncheckedOptionalAccessTest, DefaultConstructor) {
1533 ExpectDiagnosticsFor(
1534 SourceCode: R"(
1535 #include "unchecked_optional_access_test.h"
1536
1537 void target() {
1538 $ns::$optional<int> opt;
1539 opt.value(); // [[unsafe]]
1540 }
1541 )");
1542}
1543
1544TEST_P(UncheckedOptionalAccessTest, NulloptConstructor) {
1545 ExpectDiagnosticsFor(
1546 SourceCode: R"(
1547 #include "unchecked_optional_access_test.h"
1548
1549 void target() {
1550 $ns::$optional<int> opt($ns::nullopt);
1551 opt.value(); // [[unsafe]]
1552 }
1553 )");
1554}
1555
1556TEST_P(UncheckedOptionalAccessTest, NulloptConstructorWithSugaredType) {
1557 ExpectDiagnosticsFor(
1558 SourceCode: R"(
1559 #include "unchecked_optional_access_test.h"
1560 template <typename T>
1561 using wrapper = T;
1562
1563 template <typename T>
1564 wrapper<T> wrap(T);
1565
1566 void target() {
1567 $ns::$optional<int> opt(wrap($ns::nullopt));
1568 opt.value(); // [[unsafe]]
1569 }
1570 )");
1571}
1572
1573TEST_P(UncheckedOptionalAccessTest, InPlaceConstructor) {
1574 ExpectDiagnosticsFor(SourceCode: R"(
1575 #include "unchecked_optional_access_test.h"
1576
1577 void target() {
1578 $ns::$optional<int> opt($ns::in_place, 3);
1579 opt.value();
1580 }
1581 )");
1582
1583 ExpectDiagnosticsFor(SourceCode: R"(
1584 #include "unchecked_optional_access_test.h"
1585
1586 struct Foo {};
1587
1588 void target() {
1589 $ns::$optional<Foo> opt($ns::in_place);
1590 opt.value();
1591 }
1592 )");
1593
1594 ExpectDiagnosticsFor(SourceCode: R"(
1595 #include "unchecked_optional_access_test.h"
1596
1597 struct Foo {
1598 explicit Foo(int, bool);
1599 };
1600
1601 void target() {
1602 $ns::$optional<Foo> opt($ns::in_place, 3, false);
1603 opt.value();
1604 }
1605 )");
1606
1607 ExpectDiagnosticsFor(SourceCode: R"(
1608 #include "unchecked_optional_access_test.h"
1609
1610 struct Foo {
1611 explicit Foo(std::initializer_list<int>);
1612 };
1613
1614 void target() {
1615 $ns::$optional<Foo> opt($ns::in_place, {3});
1616 opt.value();
1617 }
1618 )");
1619}
1620
1621TEST_P(UncheckedOptionalAccessTest, ValueConstructor) {
1622 ExpectDiagnosticsFor(SourceCode: R"(
1623 #include "unchecked_optional_access_test.h"
1624
1625 void target() {
1626 $ns::$optional<int> opt(21);
1627 opt.value();
1628 }
1629 )");
1630
1631 ExpectDiagnosticsFor(SourceCode: R"(
1632 #include "unchecked_optional_access_test.h"
1633
1634 void target() {
1635 $ns::$optional<int> opt = $ns::$optional<int>(21);
1636 opt.value();
1637 }
1638 )");
1639 ExpectDiagnosticsFor(SourceCode: R"(
1640 #include "unchecked_optional_access_test.h"
1641
1642 void target() {
1643 $ns::$optional<$ns::$optional<int>> opt(Make<$ns::$optional<int>>());
1644 opt.value();
1645 }
1646 )");
1647
1648 ExpectDiagnosticsFor(SourceCode: R"(
1649 #include "unchecked_optional_access_test.h"
1650
1651 struct MyString {
1652 MyString(const char*);
1653 };
1654
1655 void target() {
1656 $ns::$optional<MyString> opt("foo");
1657 opt.value();
1658 }
1659 )");
1660
1661 ExpectDiagnosticsFor(SourceCode: R"(
1662 #include "unchecked_optional_access_test.h"
1663
1664 struct Foo {};
1665
1666 struct Bar {
1667 Bar(const Foo&);
1668 };
1669
1670 void target() {
1671 $ns::$optional<Bar> opt(Make<Foo>());
1672 opt.value();
1673 }
1674 )");
1675
1676 ExpectDiagnosticsFor(SourceCode: R"(
1677 #include "unchecked_optional_access_test.h"
1678
1679 struct Foo {
1680 explicit Foo(int);
1681 };
1682
1683 void target() {
1684 $ns::$optional<Foo> opt(3);
1685 opt.value();
1686 }
1687 )");
1688}
1689
1690TEST_P(UncheckedOptionalAccessTest, ConvertibleOptionalConstructor) {
1691 ExpectDiagnosticsFor(
1692 SourceCode: R"(
1693 #include "unchecked_optional_access_test.h"
1694
1695 struct Foo {};
1696
1697 struct Bar {
1698 Bar(const Foo&);
1699 };
1700
1701 void target() {
1702 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>());
1703 opt.value(); // [[unsafe]]
1704 }
1705 )");
1706
1707 ExpectDiagnosticsFor(
1708 SourceCode: R"(
1709 #include "unchecked_optional_access_test.h"
1710
1711 struct Foo {};
1712
1713 struct Bar {
1714 explicit Bar(const Foo&);
1715 };
1716
1717 void target() {
1718 $ns::$optional<Bar> opt(Make<$ns::$optional<Foo>>());
1719 opt.value(); // [[unsafe]]
1720 }
1721 )");
1722
1723 ExpectDiagnosticsFor(
1724 SourceCode: R"(
1725 #include "unchecked_optional_access_test.h"
1726
1727 struct Foo {};
1728
1729 struct Bar {
1730 Bar(const Foo&);
1731 };
1732
1733 void target() {
1734 $ns::$optional<Foo> opt1 = $ns::nullopt;
1735 $ns::$optional<Bar> opt2(opt1);
1736 opt2.value(); // [[unsafe]]
1737 }
1738 )");
1739
1740 ExpectDiagnosticsFor(SourceCode: R"(
1741 #include "unchecked_optional_access_test.h"
1742
1743 struct Foo {};
1744
1745 struct Bar {
1746 Bar(const Foo&);
1747 };
1748
1749 void target() {
1750 $ns::$optional<Foo> opt1(Make<Foo>());
1751 $ns::$optional<Bar> opt2(opt1);
1752 opt2.value();
1753 }
1754 )");
1755
1756 ExpectDiagnosticsFor(SourceCode: R"(
1757 #include "unchecked_optional_access_test.h"
1758
1759 struct Foo {};
1760
1761 struct Bar {
1762 explicit Bar(const Foo&);
1763 };
1764
1765 void target() {
1766 $ns::$optional<Foo> opt1(Make<Foo>());
1767 $ns::$optional<Bar> opt2(opt1);
1768 opt2.value();
1769 }
1770 )");
1771}
1772
1773TEST_P(UncheckedOptionalAccessTest, MakeOptional) {
1774 ExpectDiagnosticsFor(SourceCode: R"(
1775 #include "unchecked_optional_access_test.h"
1776
1777 void target() {
1778 $ns::$optional<int> opt = $ns::make_optional(0);
1779 opt.value();
1780 }
1781 )");
1782
1783 ExpectDiagnosticsFor(SourceCode: R"(
1784 #include "unchecked_optional_access_test.h"
1785
1786 struct Foo {
1787 Foo(int, int);
1788 };
1789
1790 void target() {
1791 $ns::$optional<Foo> opt = $ns::make_optional<Foo>(21, 22);
1792 opt.value();
1793 }
1794 )");
1795
1796 ExpectDiagnosticsFor(SourceCode: R"(
1797 #include "unchecked_optional_access_test.h"
1798
1799 struct Foo {
1800 constexpr Foo(std::initializer_list<char>);
1801 };
1802
1803 void target() {
1804 char a = 'a';
1805 $ns::$optional<Foo> opt = $ns::make_optional<Foo>({a});
1806 opt.value();
1807 }
1808 )");
1809}
1810
1811TEST_P(UncheckedOptionalAccessTest, ValueOr) {
1812 ExpectDiagnosticsFor(SourceCode: R"(
1813 #include "unchecked_optional_access_test.h"
1814
1815 void target() {
1816 $ns::$optional<int> opt;
1817 opt.value_or(0);
1818 (void)0;
1819 }
1820 )");
1821}
1822
1823TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointers) {
1824 ExpectDiagnosticsFor(
1825 SourceCode: R"code(
1826 #include "unchecked_optional_access_test.h"
1827
1828 void target($ns::$optional<int*> opt) {
1829 if (opt.value_or(nullptr) != nullptr) {
1830 opt.value();
1831 } else {
1832 opt.value(); // [[unsafe]]
1833 }
1834 }
1835 )code");
1836}
1837
1838TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonIntegers) {
1839 ExpectDiagnosticsFor(
1840 SourceCode: R"code(
1841 #include "unchecked_optional_access_test.h"
1842
1843 void target($ns::$optional<int> opt) {
1844 if (opt.value_or(0) != 0) {
1845 opt.value();
1846 } else {
1847 opt.value(); // [[unsafe]]
1848 }
1849 }
1850 )code");
1851}
1852
1853TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonStrings) {
1854 ExpectDiagnosticsFor(
1855 SourceCode: R"code(
1856 #include "unchecked_optional_access_test.h"
1857
1858 void target($ns::$optional<std::string> opt) {
1859 if (!opt.value_or("").empty()) {
1860 opt.value();
1861 } else {
1862 opt.value(); // [[unsafe]]
1863 }
1864 }
1865 )code");
1866
1867 ExpectDiagnosticsFor(
1868 SourceCode: R"code(
1869 #include "unchecked_optional_access_test.h"
1870
1871 void target($ns::$optional<std::string> opt) {
1872 if (opt.value_or("") != "") {
1873 opt.value();
1874 } else {
1875 opt.value(); // [[unsafe]]
1876 }
1877 }
1878 )code");
1879}
1880
1881TEST_P(UncheckedOptionalAccessTest, ValueOrComparisonPointerToOptional) {
1882 // FIXME: make `opt` a parameter directly, once we ensure that all `optional`
1883 // values have a `has_value` property.
1884 ExpectDiagnosticsFor(
1885 SourceCode: R"code(
1886 #include "unchecked_optional_access_test.h"
1887
1888 void target($ns::$optional<int> p) {
1889 $ns::$optional<int> *opt = &p;
1890 if (opt->value_or(0) != 0) {
1891 opt->value();
1892 } else {
1893 opt->value(); // [[unsafe]]
1894 }
1895 }
1896 )code");
1897}
1898
1899TEST_P(UncheckedOptionalAccessTest, Emplace) {
1900 ExpectDiagnosticsFor(SourceCode: R"(
1901 #include "unchecked_optional_access_test.h"
1902
1903 void target() {
1904 $ns::$optional<int> opt;
1905 opt.emplace(0);
1906 opt.value();
1907 }
1908 )");
1909
1910 ExpectDiagnosticsFor(SourceCode: R"(
1911 #include "unchecked_optional_access_test.h"
1912
1913 void target($ns::$optional<int> *opt) {
1914 opt->emplace(0);
1915 opt->value();
1916 }
1917 )");
1918
1919 // FIXME: Add tests that call `emplace` in conditional branches:
1920 // ExpectDiagnosticsFor(
1921 // R"(
1922 // #include "unchecked_optional_access_test.h"
1923 //
1924 // void target($ns::$optional<int> opt, bool b) {
1925 // if (b) {
1926 // opt.emplace(0);
1927 // }
1928 // if (b) {
1929 // opt.value();
1930 // } else {
1931 // opt.value(); // [[unsafe]]
1932 // }
1933 // }
1934 // )");
1935}
1936
1937TEST_P(UncheckedOptionalAccessTest, Reset) {
1938 ExpectDiagnosticsFor(
1939 SourceCode: R"(
1940 #include "unchecked_optional_access_test.h"
1941
1942 void target() {
1943 $ns::$optional<int> opt = $ns::make_optional(0);
1944 opt.reset();
1945 opt.value(); // [[unsafe]]
1946 }
1947 )");
1948
1949 ExpectDiagnosticsFor(
1950 SourceCode: R"(
1951 #include "unchecked_optional_access_test.h"
1952
1953 void target($ns::$optional<int> &opt) {
1954 if (opt.has_value()) {
1955 opt.reset();
1956 opt.value(); // [[unsafe]]
1957 }
1958 }
1959 )");
1960
1961 // FIXME: Add tests that call `reset` in conditional branches:
1962 // ExpectDiagnosticsFor(
1963 // R"(
1964 // #include "unchecked_optional_access_test.h"
1965 //
1966 // void target(bool b) {
1967 // $ns::$optional<int> opt = $ns::make_optional(0);
1968 // if (b) {
1969 // opt.reset();
1970 // }
1971 // if (b) {
1972 // opt.value(); // [[unsafe]]
1973 // } else {
1974 // opt.value();
1975 // }
1976 // }
1977 // )");
1978}
1979
1980TEST_P(UncheckedOptionalAccessTest, ValueAssignment) {
1981 ExpectDiagnosticsFor(SourceCode: R"(
1982 #include "unchecked_optional_access_test.h"
1983
1984 struct Foo {};
1985
1986 void target() {
1987 $ns::$optional<Foo> opt;
1988 opt = Foo();
1989 opt.value();
1990 }
1991 )");
1992
1993 ExpectDiagnosticsFor(SourceCode: R"(
1994 #include "unchecked_optional_access_test.h"
1995
1996 struct Foo {};
1997
1998 void target() {
1999 $ns::$optional<Foo> opt;
2000 (opt = Foo()).value();
2001 (void)0;
2002 }
2003 )");
2004
2005 ExpectDiagnosticsFor(SourceCode: R"(
2006 #include "unchecked_optional_access_test.h"
2007
2008 struct MyString {
2009 MyString(const char*);
2010 };
2011
2012 void target() {
2013 $ns::$optional<MyString> opt;
2014 opt = "foo";
2015 opt.value();
2016 }
2017 )");
2018
2019 ExpectDiagnosticsFor(SourceCode: R"(
2020 #include "unchecked_optional_access_test.h"
2021
2022 struct MyString {
2023 MyString(const char*);
2024 };
2025
2026 void target() {
2027 $ns::$optional<MyString> opt;
2028 (opt = "foo").value();
2029 }
2030 )");
2031}
2032
2033TEST_P(UncheckedOptionalAccessTest, OptionalConversionAssignment) {
2034 ExpectDiagnosticsFor(
2035 SourceCode: R"(
2036 #include "unchecked_optional_access_test.h"
2037
2038 struct Foo {};
2039
2040 struct Bar {
2041 Bar(const Foo&);
2042 };
2043
2044 void target() {
2045 $ns::$optional<Foo> opt1 = Foo();
2046 $ns::$optional<Bar> opt2;
2047 opt2 = opt1;
2048 opt2.value();
2049 }
2050 )");
2051
2052 ExpectDiagnosticsFor(
2053 SourceCode: R"(
2054 #include "unchecked_optional_access_test.h"
2055
2056 struct Foo {};
2057
2058 struct Bar {
2059 Bar(const Foo&);
2060 };
2061
2062 void target() {
2063 $ns::$optional<Foo> opt1;
2064 $ns::$optional<Bar> opt2;
2065 if (opt2.has_value()) {
2066 opt2 = opt1;
2067 opt2.value(); // [[unsafe]]
2068 }
2069 }
2070 )");
2071
2072 ExpectDiagnosticsFor(
2073 SourceCode: R"(
2074 #include "unchecked_optional_access_test.h"
2075
2076 struct Foo {};
2077
2078 struct Bar {
2079 Bar(const Foo&);
2080 };
2081
2082 void target() {
2083 $ns::$optional<Foo> opt1 = Foo();
2084 $ns::$optional<Bar> opt2;
2085 (opt2 = opt1).value();
2086 (void)0;
2087 }
2088 )");
2089}
2090
2091TEST_P(UncheckedOptionalAccessTest, NulloptAssignment) {
2092 ExpectDiagnosticsFor(
2093 SourceCode: R"(
2094 #include "unchecked_optional_access_test.h"
2095
2096 void target() {
2097 $ns::$optional<int> opt = 3;
2098 opt = $ns::nullopt;
2099 opt.value(); // [[unsafe]]
2100 }
2101 )");
2102
2103 ExpectDiagnosticsFor(
2104 SourceCode: R"(
2105 #include "unchecked_optional_access_test.h"
2106
2107 void target() {
2108 $ns::$optional<int> opt = 3;
2109 (opt = $ns::nullopt).value(); // [[unsafe]]
2110 }
2111 )");
2112}
2113
2114TEST_P(UncheckedOptionalAccessTest, OptionalSwap) {
2115 ExpectDiagnosticsFor(
2116 SourceCode: R"(
2117 #include "unchecked_optional_access_test.h"
2118
2119 void target() {
2120 $ns::$optional<int> opt1 = $ns::nullopt;
2121 $ns::$optional<int> opt2 = 3;
2122
2123 opt1.swap(opt2);
2124
2125 opt1.value();
2126
2127 opt2.value(); // [[unsafe]]
2128 }
2129 )");
2130
2131 ExpectDiagnosticsFor(
2132 SourceCode: R"(
2133 #include "unchecked_optional_access_test.h"
2134
2135 void target() {
2136 $ns::$optional<int> opt1 = $ns::nullopt;
2137 $ns::$optional<int> opt2 = 3;
2138
2139 opt2.swap(opt1);
2140
2141 opt1.value();
2142
2143 opt2.value(); // [[unsafe]]
2144 }
2145 )");
2146}
2147
2148TEST_P(UncheckedOptionalAccessTest, OptionalReturnedFromFuntionCall) {
2149 ExpectDiagnosticsFor(
2150 SourceCode: R"(
2151 #include "unchecked_optional_access_test.h"
2152
2153 struct S {
2154 $ns::$optional<float> x;
2155 } s;
2156 S getOptional() {
2157 return s;
2158 }
2159
2160 void target() {
2161 getOptional().x = 0;
2162 }
2163 )");
2164}
2165
2166TEST_P(UncheckedOptionalAccessTest, StdSwap) {
2167 ExpectDiagnosticsFor(
2168 SourceCode: R"(
2169 #include "unchecked_optional_access_test.h"
2170
2171 void target() {
2172 $ns::$optional<int> opt1 = $ns::nullopt;
2173 $ns::$optional<int> opt2 = 3;
2174
2175 std::swap(opt1, opt2);
2176
2177 opt1.value();
2178
2179 opt2.value(); // [[unsafe]]
2180 }
2181 )");
2182
2183 ExpectDiagnosticsFor(
2184 SourceCode: R"(
2185 #include "unchecked_optional_access_test.h"
2186
2187 void target() {
2188 $ns::$optional<int> opt1 = $ns::nullopt;
2189 $ns::$optional<int> opt2 = 3;
2190
2191 std::swap(opt2, opt1);
2192
2193 opt1.value();
2194
2195 opt2.value(); // [[unsafe]]
2196 }
2197 )");
2198}
2199
2200TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocLeft) {
2201 ExpectDiagnosticsFor(
2202 SourceCode: R"(
2203 #include "unchecked_optional_access_test.h"
2204
2205 struct L { $ns::$optional<int> hd; L* tl; };
2206
2207 void target() {
2208 $ns::$optional<int> foo = 3;
2209 L bar;
2210
2211 // Any `tl` beyond the first is not modeled.
2212 bar.tl->tl->hd.swap(foo);
2213
2214 bar.tl->tl->hd.value(); // [[unsafe]]
2215 foo.value(); // [[unsafe]]
2216 }
2217 )");
2218}
2219
2220TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledLocRight) {
2221 ExpectDiagnosticsFor(
2222 SourceCode: R"(
2223 #include "unchecked_optional_access_test.h"
2224
2225 struct L { $ns::$optional<int> hd; L* tl; };
2226
2227 void target() {
2228 $ns::$optional<int> foo = 3;
2229 L bar;
2230
2231 // Any `tl` beyond the first is not modeled.
2232 foo.swap(bar.tl->tl->hd);
2233
2234 bar.tl->tl->hd.value(); // [[unsafe]]
2235 foo.value(); // [[unsafe]]
2236 }
2237 )");
2238}
2239
2240TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftSet) {
2241 ExpectDiagnosticsFor(
2242 SourceCode: R"(
2243 #include "unchecked_optional_access_test.h"
2244
2245 struct S { int x; };
2246 struct A { $ns::$optional<S> late; };
2247 struct B { A f3; };
2248 struct C { B f2; };
2249 struct D { C f1; };
2250
2251 void target() {
2252 $ns::$optional<S> foo = S{3};
2253 D bar;
2254
2255 bar.f1.f2.f3.late.swap(foo);
2256
2257 bar.f1.f2.f3.late.value();
2258 foo.value(); // [[unsafe]]
2259 }
2260 )");
2261}
2262
2263TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueLeftUnset) {
2264 ExpectDiagnosticsFor(
2265 SourceCode: R"(
2266 #include "unchecked_optional_access_test.h"
2267
2268 struct S { int x; };
2269 struct A { $ns::$optional<S> late; };
2270 struct B { A f3; };
2271 struct C { B f2; };
2272 struct D { C f1; };
2273
2274 void target() {
2275 $ns::$optional<S> foo;
2276 D bar;
2277
2278 bar.f1.f2.f3.late.swap(foo);
2279
2280 bar.f1.f2.f3.late.value(); // [[unsafe]]
2281 foo.value(); // [[unsafe]]
2282 }
2283 )");
2284}
2285
2286// fixme: use recursion instead of depth.
2287TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightSet) {
2288 ExpectDiagnosticsFor(
2289 SourceCode: R"(
2290 #include "unchecked_optional_access_test.h"
2291
2292 struct S { int x; };
2293 struct A { $ns::$optional<S> late; };
2294 struct B { A f3; };
2295 struct C { B f2; };
2296 struct D { C f1; };
2297
2298 void target() {
2299 $ns::$optional<S> foo = S{3};
2300 D bar;
2301
2302 foo.swap(bar.f1.f2.f3.late);
2303
2304 bar.f1.f2.f3.late.value();
2305 foo.value(); // [[unsafe]]
2306 }
2307 )");
2308}
2309
2310TEST_P(UncheckedOptionalAccessTest, SwapUnmodeledValueRightUnset) {
2311 ExpectDiagnosticsFor(
2312 SourceCode: R"(
2313 #include "unchecked_optional_access_test.h"
2314
2315 struct S { int x; };
2316 struct A { $ns::$optional<S> late; };
2317 struct B { A f3; };
2318 struct C { B f2; };
2319 struct D { C f1; };
2320
2321 void target() {
2322 $ns::$optional<S> foo;
2323 D bar;
2324
2325 foo.swap(bar.f1.f2.f3.late);
2326
2327 bar.f1.f2.f3.late.value(); // [[unsafe]]
2328 foo.value(); // [[unsafe]]
2329 }
2330 )");
2331}
2332
2333TEST_P(UncheckedOptionalAccessTest, UniquePtrToOptional) {
2334 // We suppress diagnostics for optionals in smart pointers (other than
2335 // `optional` itself).
2336 ExpectDiagnosticsFor(
2337 SourceCode: R"(
2338 #include "unchecked_optional_access_test.h"
2339
2340 template <typename T>
2341 struct smart_ptr {
2342 T& operator*() &;
2343 T* operator->();
2344 };
2345
2346 void target() {
2347 smart_ptr<$ns::$optional<bool>> foo;
2348 foo->value();
2349 (*foo).value();
2350 }
2351 )");
2352}
2353
2354TEST_P(UncheckedOptionalAccessTest, UniquePtrToStructWithOptionalField) {
2355 // We suppress diagnostics for optional fields reachable from smart pointers
2356 // (other than `optional` itself) through (exactly) one member access.
2357 ExpectDiagnosticsFor(
2358 SourceCode: R"(
2359 #include "unchecked_optional_access_test.h"
2360
2361 template <typename T>
2362 struct smart_ptr {
2363 T& operator*() &;
2364 T* operator->();
2365 };
2366
2367 struct Foo {
2368 $ns::$optional<int> opt;
2369 };
2370
2371 void target() {
2372 smart_ptr<Foo> foo;
2373 *foo->opt;
2374 *(*foo).opt;
2375 }
2376 )");
2377}
2378
2379TEST_P(UncheckedOptionalAccessTest, CallReturningOptional) {
2380 ExpectDiagnosticsFor(
2381 SourceCode: R"(
2382 #include "unchecked_optional_access_test.h"
2383
2384 $ns::$optional<int> MakeOpt();
2385
2386 void target() {
2387 $ns::$optional<int> opt = 0;
2388 opt = MakeOpt();
2389 opt.value(); // [[unsafe]]
2390 }
2391 )");
2392 ExpectDiagnosticsFor(
2393 SourceCode: R"(
2394 #include "unchecked_optional_access_test.h"
2395
2396 const $ns::$optional<int>& MakeOpt();
2397
2398 void target() {
2399 $ns::$optional<int> opt = 0;
2400 opt = MakeOpt();
2401 opt.value(); // [[unsafe]]
2402 }
2403 )");
2404
2405 ExpectDiagnosticsFor(
2406 SourceCode: R"(
2407 #include "unchecked_optional_access_test.h"
2408
2409 using IntOpt = $ns::$optional<int>;
2410 IntOpt MakeOpt();
2411
2412 void target() {
2413 IntOpt opt = 0;
2414 opt = MakeOpt();
2415 opt.value(); // [[unsafe]]
2416 }
2417 )");
2418
2419 ExpectDiagnosticsFor(
2420 SourceCode: R"(
2421 #include "unchecked_optional_access_test.h"
2422
2423 using IntOpt = $ns::$optional<int>;
2424 const IntOpt& MakeOpt();
2425
2426 void target() {
2427 IntOpt opt = 0;
2428 opt = MakeOpt();
2429 opt.value(); // [[unsafe]]
2430 }
2431 )");
2432}
2433
2434
2435TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftSet) {
2436 ExpectDiagnosticsFor(
2437 SourceCode: R"(
2438 #include "unchecked_optional_access_test.h"
2439
2440 void target() {
2441 $ns::$optional<int> opt1 = 3;
2442 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2443
2444 if (opt1 == opt2) {
2445 opt2.value();
2446 } else {
2447 opt2.value(); // [[unsafe]]
2448 }
2449 }
2450 )");
2451}
2452
2453TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightSet) {
2454 ExpectDiagnosticsFor(
2455 SourceCode: R"(
2456 #include "unchecked_optional_access_test.h"
2457
2458 void target() {
2459 $ns::$optional<int> opt1 = 3;
2460 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2461
2462 if (opt2 == opt1) {
2463 opt2.value();
2464 } else {
2465 opt2.value(); // [[unsafe]]
2466 }
2467 }
2468 )");
2469}
2470
2471TEST_P(UncheckedOptionalAccessTest, EqualityCheckVerifySetAfterEq) {
2472 ExpectDiagnosticsFor(
2473 SourceCode: R"(
2474 #include "unchecked_optional_access_test.h"
2475
2476 void target() {
2477 $ns::$optional<int> opt1 = Make<$ns::$optional<int>>();
2478 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2479
2480 if (opt1 == opt2) {
2481 if (opt1.has_value())
2482 opt2.value();
2483 if (opt2.has_value())
2484 opt1.value();
2485 }
2486 }
2487 )");
2488}
2489
2490TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftUnset) {
2491 ExpectDiagnosticsFor(
2492 SourceCode: R"(
2493 #include "unchecked_optional_access_test.h"
2494
2495 void target() {
2496 $ns::$optional<int> opt1 = $ns::nullopt;
2497 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2498
2499 if (opt1 == opt2) {
2500 opt2.value(); // [[unsafe]]
2501 } else {
2502 opt2.value();
2503 }
2504 }
2505 )");
2506}
2507
2508TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightUnset) {
2509 ExpectDiagnosticsFor(
2510 SourceCode: R"(
2511 #include "unchecked_optional_access_test.h"
2512
2513 void target() {
2514 $ns::$optional<int> opt1 = $ns::nullopt;
2515 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2516
2517 if (opt2 == opt1) {
2518 opt2.value(); // [[unsafe]]
2519 } else {
2520 opt2.value();
2521 }
2522 }
2523 )");
2524}
2525
2526TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightNullopt) {
2527 ExpectDiagnosticsFor(
2528 SourceCode: R"(
2529 #include "unchecked_optional_access_test.h"
2530
2531 void target() {
2532 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2533
2534 if (opt == $ns::nullopt) {
2535 opt.value(); // [[unsafe]]
2536 } else {
2537 opt.value();
2538 }
2539 }
2540 )");
2541}
2542
2543TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftNullopt) {
2544 ExpectDiagnosticsFor(
2545 SourceCode: R"(
2546 #include "unchecked_optional_access_test.h"
2547
2548 void target() {
2549 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2550
2551 if ($ns::nullopt == opt) {
2552 opt.value(); // [[unsafe]]
2553 } else {
2554 opt.value();
2555 }
2556 }
2557 )");
2558}
2559
2560TEST_P(UncheckedOptionalAccessTest, EqualityCheckRightValue) {
2561 ExpectDiagnosticsFor(
2562 SourceCode: R"(
2563 #include "unchecked_optional_access_test.h"
2564
2565 void target() {
2566 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2567
2568 if (opt == 3) {
2569 opt.value();
2570 } else {
2571 opt.value(); // [[unsafe]]
2572 }
2573 }
2574 )");
2575}
2576
2577TEST_P(UncheckedOptionalAccessTest, EqualityCheckLeftValue) {
2578 ExpectDiagnosticsFor(
2579 SourceCode: R"(
2580 #include "unchecked_optional_access_test.h"
2581
2582 void target() {
2583 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2584
2585 if (3 == opt) {
2586 opt.value();
2587 } else {
2588 opt.value(); // [[unsafe]]
2589 }
2590 }
2591 )");
2592}
2593
2594TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftSet) {
2595 ExpectDiagnosticsFor(
2596 SourceCode: R"(
2597 #include "unchecked_optional_access_test.h"
2598
2599 void target() {
2600 $ns::$optional<int> opt1 = 3;
2601 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2602
2603 if (opt1 != opt2) {
2604 opt2.value(); // [[unsafe]]
2605 } else {
2606 opt2.value();
2607 }
2608 }
2609 )");
2610}
2611
2612TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightSet) {
2613 ExpectDiagnosticsFor(
2614 SourceCode: R"(
2615 #include "unchecked_optional_access_test.h"
2616
2617 void target() {
2618 $ns::$optional<int> opt1 = 3;
2619 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2620
2621 if (opt2 != opt1) {
2622 opt2.value(); // [[unsafe]]
2623 } else {
2624 opt2.value();
2625 }
2626 }
2627 )");
2628}
2629
2630TEST_P(UncheckedOptionalAccessTest, InequalityCheckVerifySetAfterEq) {
2631 ExpectDiagnosticsFor(
2632 SourceCode: R"(
2633 #include "unchecked_optional_access_test.h"
2634
2635 void target() {
2636 $ns::$optional<int> opt1 = Make<$ns::$optional<int>>();
2637 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2638
2639 if (opt1 != opt2) {
2640 if (opt1.has_value())
2641 opt2.value(); // [[unsafe]]
2642 if (opt2.has_value())
2643 opt1.value(); // [[unsafe]]
2644 }
2645 }
2646 )");
2647}
2648
2649TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftUnset) {
2650 ExpectDiagnosticsFor(
2651 SourceCode: R"(
2652 #include "unchecked_optional_access_test.h"
2653
2654 void target() {
2655 $ns::$optional<int> opt1 = $ns::nullopt;
2656 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2657
2658 if (opt1 != opt2) {
2659 opt2.value();
2660 } else {
2661 opt2.value(); // [[unsafe]]
2662 }
2663 }
2664 )");
2665}
2666
2667TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightUnset) {
2668 ExpectDiagnosticsFor(
2669 SourceCode: R"(
2670 #include "unchecked_optional_access_test.h"
2671
2672 void target() {
2673 $ns::$optional<int> opt1 = $ns::nullopt;
2674 $ns::$optional<int> opt2 = Make<$ns::$optional<int>>();
2675
2676 if (opt2 != opt1) {
2677 opt2.value();
2678 } else {
2679 opt2.value(); // [[unsafe]]
2680 }
2681 }
2682 )");
2683}
2684
2685TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightNullopt) {
2686 ExpectDiagnosticsFor(
2687 SourceCode: R"(
2688 #include "unchecked_optional_access_test.h"
2689
2690 void target() {
2691 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2692
2693 if (opt != $ns::nullopt) {
2694 opt.value();
2695 } else {
2696 opt.value(); // [[unsafe]]
2697 }
2698 }
2699 )");
2700}
2701
2702TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftNullopt) {
2703 ExpectDiagnosticsFor(
2704 SourceCode: R"(
2705 #include "unchecked_optional_access_test.h"
2706
2707 void target() {
2708 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2709
2710 if ($ns::nullopt != opt) {
2711 opt.value();
2712 } else {
2713 opt.value(); // [[unsafe]]
2714 }
2715 }
2716 )");
2717}
2718
2719TEST_P(UncheckedOptionalAccessTest, InequalityCheckRightValue) {
2720 ExpectDiagnosticsFor(
2721 SourceCode: R"(
2722 #include "unchecked_optional_access_test.h"
2723
2724 void target() {
2725 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2726
2727 if (opt != 3) {
2728 opt.value(); // [[unsafe]]
2729 } else {
2730 opt.value();
2731 }
2732 }
2733 )");
2734}
2735
2736TEST_P(UncheckedOptionalAccessTest, InequalityCheckLeftValue) {
2737 ExpectDiagnosticsFor(
2738 SourceCode: R"(
2739 #include "unchecked_optional_access_test.h"
2740
2741 void target() {
2742 $ns::$optional<int> opt = Make<$ns::$optional<int>>();
2743
2744 if (3 != opt) {
2745 opt.value(); // [[unsafe]]
2746 } else {
2747 opt.value();
2748 }
2749 }
2750 )");
2751}
2752
2753// Verifies that the model sees through aliases.
2754TEST_P(UncheckedOptionalAccessTest, WithAlias) {
2755 ExpectDiagnosticsFor(
2756 SourceCode: R"(
2757 #include "unchecked_optional_access_test.h"
2758
2759 template <typename T>
2760 using MyOptional = $ns::$optional<T>;
2761
2762 void target(MyOptional<int> opt) {
2763 opt.value(); // [[unsafe]]
2764 }
2765 )");
2766}
2767
2768TEST_P(UncheckedOptionalAccessTest, OptionalValueOptional) {
2769 // Basic test that nested values are populated. We nest an optional because
2770 // its easy to use in a test, but the type of the nested value shouldn't
2771 // matter.
2772 ExpectDiagnosticsFor(
2773 SourceCode: R"(
2774 #include "unchecked_optional_access_test.h"
2775
2776 using Foo = $ns::$optional<std::string>;
2777
2778 void target($ns::$optional<Foo> foo) {
2779 if (foo && *foo) {
2780 foo->value();
2781 }
2782 }
2783 )");
2784
2785 // Mutation is supported for nested values.
2786 ExpectDiagnosticsFor(
2787 SourceCode: R"(
2788 #include "unchecked_optional_access_test.h"
2789
2790 using Foo = $ns::$optional<std::string>;
2791
2792 void target($ns::$optional<Foo> foo) {
2793 if (foo && *foo) {
2794 foo->reset();
2795 foo->value(); // [[unsafe]]
2796 }
2797 }
2798 )");
2799}
2800
2801TEST_P(UncheckedOptionalAccessTest, NestedOptionalAssignValue) {
2802 ExpectDiagnosticsFor(
2803 SourceCode: R"(
2804 #include "unchecked_optional_access_test.h"
2805
2806 using OptionalInt = $ns::$optional<int>;
2807
2808 void target($ns::$optional<OptionalInt> opt) {
2809 if (!opt) return;
2810
2811 // Accessing the outer optional is OK now.
2812 *opt;
2813
2814 // But accessing the nested optional is still unsafe because we haven't
2815 // checked it.
2816 **opt; // [[unsafe]]
2817
2818 *opt = 1;
2819
2820 // Accessing the nested optional is safe after assigning a value to it.
2821 **opt;
2822 }
2823 )");
2824}
2825
2826TEST_P(UncheckedOptionalAccessTest, NestedOptionalAssignOptional) {
2827 ExpectDiagnosticsFor(
2828 SourceCode: R"(
2829 #include "unchecked_optional_access_test.h"
2830
2831 using OptionalInt = $ns::$optional<int>;
2832
2833 void target($ns::$optional<OptionalInt> opt) {
2834 if (!opt) return;
2835
2836 // Accessing the outer optional is OK now.
2837 *opt;
2838
2839 // But accessing the nested optional is still unsafe because we haven't
2840 // checked it.
2841 **opt; // [[unsafe]]
2842
2843 // Assign from `optional<short>` so that we trigger conversion assignment
2844 // instead of move assignment.
2845 *opt = $ns::$optional<short>();
2846
2847 // Accessing the nested optional is still unsafe after assigning an empty
2848 // optional to it.
2849 **opt; // [[unsafe]]
2850 }
2851 )");
2852}
2853
2854// Tests that structs can be nested. We use an optional field because its easy
2855// to use in a test, but the type of the field shouldn't matter.
2856TEST_P(UncheckedOptionalAccessTest, OptionalValueStruct) {
2857 ExpectDiagnosticsFor(
2858 SourceCode: R"(
2859 #include "unchecked_optional_access_test.h"
2860
2861 struct Foo {
2862 $ns::$optional<std::string> opt;
2863 };
2864
2865 void target($ns::$optional<Foo> foo) {
2866 if (foo && foo->opt) {
2867 foo->opt.value();
2868 }
2869 }
2870 )");
2871}
2872
2873TEST_P(UncheckedOptionalAccessTest, OptionalValueInitialization) {
2874 ExpectDiagnosticsFor(
2875 SourceCode: R"(
2876 #include "unchecked_optional_access_test.h"
2877
2878 using Foo = $ns::$optional<std::string>;
2879
2880 void target($ns::$optional<Foo> foo, bool b) {
2881 if (!foo.has_value()) return;
2882 if (b) {
2883 if (!foo->has_value()) return;
2884 // We have created `foo.value()`.
2885 foo->value();
2886 } else {
2887 if (!foo->has_value()) return;
2888 // We have created `foo.value()` again, in a different environment.
2889 foo->value();
2890 }
2891 // Now we merge the two values. UncheckedOptionalAccessModel::merge() will
2892 // throw away the "value" property.
2893 foo->value();
2894 }
2895 )");
2896}
2897
2898// This test is aimed at the core model, not the diagnostic. It is a regression
2899// test against a crash when using non-trivial smart pointers, like
2900// `std::unique_ptr`. As such, it doesn't test the access itself, which would be
2901// ignored regardless because of `IgnoreSmartPointerDereference = true`, above.
2902TEST_P(UncheckedOptionalAccessTest, AssignThroughLvalueReferencePtr) {
2903 ExpectDiagnosticsFor(
2904 SourceCode: R"(
2905 #include "unchecked_optional_access_test.h"
2906
2907 template <typename T>
2908 struct smart_ptr {
2909 typename std::add_lvalue_reference<T>::type operator*() &;
2910 };
2911
2912 void target() {
2913 smart_ptr<$ns::$optional<int>> x;
2914 // Verify that this assignment does not crash.
2915 *x = 3;
2916 }
2917 )");
2918}
2919
2920TEST_P(UncheckedOptionalAccessTest, CorrelatedBranches) {
2921 ExpectDiagnosticsFor(SourceCode: R"code(
2922 #include "unchecked_optional_access_test.h"
2923
2924 void target(bool b, $ns::$optional<int> opt) {
2925 if (b || opt.has_value()) {
2926 if (!b) {
2927 opt.value();
2928 }
2929 }
2930 }
2931 )code");
2932
2933 ExpectDiagnosticsFor(SourceCode: R"code(
2934 #include "unchecked_optional_access_test.h"
2935
2936 void target(bool b, $ns::$optional<int> opt) {
2937 if (b && !opt.has_value()) return;
2938 if (b) {
2939 opt.value();
2940 }
2941 }
2942 )code");
2943
2944 ExpectDiagnosticsFor(
2945 SourceCode: R"code(
2946 #include "unchecked_optional_access_test.h"
2947
2948 void target(bool b, $ns::$optional<int> opt) {
2949 if (opt.has_value()) b = true;
2950 if (b) {
2951 opt.value(); // [[unsafe]]
2952 }
2953 }
2954 )code");
2955
2956 ExpectDiagnosticsFor(SourceCode: R"code(
2957 #include "unchecked_optional_access_test.h"
2958
2959 void target(bool b, $ns::$optional<int> opt) {
2960 if (b) return;
2961 if (opt.has_value()) b = true;
2962 if (b) {
2963 opt.value();
2964 }
2965 }
2966 )code");
2967
2968 ExpectDiagnosticsFor(SourceCode: R"(
2969 #include "unchecked_optional_access_test.h"
2970
2971 void target(bool b, $ns::$optional<int> opt) {
2972 if (opt.has_value() == b) {
2973 if (b) {
2974 opt.value();
2975 }
2976 }
2977 }
2978 )");
2979
2980 ExpectDiagnosticsFor(SourceCode: R"(
2981 #include "unchecked_optional_access_test.h"
2982
2983 void target(bool b, $ns::$optional<int> opt) {
2984 if (opt.has_value() != b) {
2985 if (!b) {
2986 opt.value();
2987 }
2988 }
2989 }
2990 )");
2991
2992 ExpectDiagnosticsFor(SourceCode: R"(
2993 #include "unchecked_optional_access_test.h"
2994
2995 void target(bool b) {
2996 $ns::$optional<int> opt1 = $ns::nullopt;
2997 $ns::$optional<int> opt2;
2998 if (b) {
2999 opt2 = $ns::nullopt;
3000 } else {
3001 opt2 = $ns::nullopt;
3002 }
3003 if (opt2.has_value()) {
3004 opt1.value();
3005 }
3006 }
3007 )");
3008}
3009
3010TEST_P(UncheckedOptionalAccessTest, JoinDistinctValues) {
3011 ExpectDiagnosticsFor(
3012 SourceCode: R"code(
3013 #include "unchecked_optional_access_test.h"
3014
3015 void target(bool b) {
3016 $ns::$optional<int> opt;
3017 if (b) {
3018 opt = Make<$ns::$optional<int>>();
3019 } else {
3020 opt = Make<$ns::$optional<int>>();
3021 }
3022 if (opt.has_value()) {
3023 opt.value();
3024 } else {
3025 opt.value(); // [[unsafe]]
3026 }
3027 }
3028 )code");
3029
3030 ExpectDiagnosticsFor(SourceCode: R"code(
3031 #include "unchecked_optional_access_test.h"
3032
3033 void target(bool b) {
3034 $ns::$optional<int> opt;
3035 if (b) {
3036 opt = Make<$ns::$optional<int>>();
3037 if (!opt.has_value()) return;
3038 } else {
3039 opt = Make<$ns::$optional<int>>();
3040 if (!opt.has_value()) return;
3041 }
3042 opt.value();
3043 }
3044 )code");
3045
3046 ExpectDiagnosticsFor(
3047 SourceCode: R"code(
3048 #include "unchecked_optional_access_test.h"
3049
3050 void target(bool b) {
3051 $ns::$optional<int> opt;
3052 if (b) {
3053 opt = Make<$ns::$optional<int>>();
3054 if (!opt.has_value()) return;
3055 } else {
3056 opt = Make<$ns::$optional<int>>();
3057 }
3058 opt.value(); // [[unsafe]]
3059 }
3060 )code");
3061
3062 ExpectDiagnosticsFor(
3063 SourceCode: R"code(
3064 #include "unchecked_optional_access_test.h"
3065
3066 void target(bool b) {
3067 $ns::$optional<int> opt;
3068 if (b) {
3069 opt = 1;
3070 } else {
3071 opt = 2;
3072 }
3073 opt.value();
3074 }
3075 )code");
3076
3077 ExpectDiagnosticsFor(
3078 SourceCode: R"code(
3079 #include "unchecked_optional_access_test.h"
3080
3081 void target(bool b) {
3082 $ns::$optional<int> opt;
3083 if (b) {
3084 opt = 1;
3085 } else {
3086 opt = Make<$ns::$optional<int>>();
3087 }
3088 opt.value(); // [[unsafe]]
3089 }
3090 )code");
3091}
3092
3093TEST_P(UncheckedOptionalAccessTest, AccessValueInLoop) {
3094 ExpectDiagnosticsFor(SourceCode: R"(
3095 #include "unchecked_optional_access_test.h"
3096
3097 void target() {
3098 $ns::$optional<int> opt = 3;
3099 while (Make<bool>()) {
3100 opt.value();
3101 }
3102 }
3103 )");
3104}
3105
3106TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopWithCheckSafe) {
3107 ExpectDiagnosticsFor(SourceCode: R"(
3108 #include "unchecked_optional_access_test.h"
3109
3110 void target() {
3111 $ns::$optional<int> opt = 3;
3112 while (Make<bool>()) {
3113 opt.value();
3114
3115 opt = Make<$ns::$optional<int>>();
3116 if (!opt.has_value()) return;
3117 }
3118 }
3119 )");
3120}
3121
3122TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopNoCheckUnsafe) {
3123 ExpectDiagnosticsFor(
3124 SourceCode: R"(
3125 #include "unchecked_optional_access_test.h"
3126
3127 void target() {
3128 $ns::$optional<int> opt = 3;
3129 while (Make<bool>()) {
3130 opt.value(); // [[unsafe]]
3131
3132 opt = Make<$ns::$optional<int>>();
3133 }
3134 }
3135 )");
3136}
3137
3138TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnsetUnsafe) {
3139 ExpectDiagnosticsFor(
3140 SourceCode: R"(
3141 #include "unchecked_optional_access_test.h"
3142
3143 void target() {
3144 $ns::$optional<int> opt = 3;
3145 while (Make<bool>())
3146 opt = $ns::nullopt;
3147 $ns::$optional<int> opt2 = $ns::nullopt;
3148 if (opt.has_value())
3149 opt2 = $ns::$optional<int>(3);
3150 opt2.value(); // [[unsafe]]
3151 }
3152 )");
3153}
3154
3155TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToSetUnsafe) {
3156 ExpectDiagnosticsFor(
3157 SourceCode: R"(
3158 #include "unchecked_optional_access_test.h"
3159
3160 void target() {
3161 $ns::$optional<int> opt = $ns::nullopt;
3162 while (Make<bool>())
3163 opt = $ns::$optional<int>(3);
3164 $ns::$optional<int> opt2 = $ns::nullopt;
3165 if (!opt.has_value())
3166 opt2 = $ns::$optional<int>(3);
3167 opt2.value(); // [[unsafe]]
3168 }
3169 )");
3170}
3171
3172TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopToUnknownUnsafe) {
3173 ExpectDiagnosticsFor(
3174 SourceCode: R"(
3175 #include "unchecked_optional_access_test.h"
3176
3177 void target() {
3178 $ns::$optional<int> opt = $ns::nullopt;
3179 while (Make<bool>())
3180 opt = Make<$ns::$optional<int>>();
3181 $ns::$optional<int> opt2 = $ns::nullopt;
3182 if (!opt.has_value())
3183 opt2 = $ns::$optional<int>(3);
3184 opt2.value(); // [[unsafe]]
3185 }
3186 )");
3187}
3188
3189TEST_P(UncheckedOptionalAccessTest, ReassignValueInLoopBadConditionUnsafe) {
3190 ExpectDiagnosticsFor(
3191 SourceCode: R"(
3192 #include "unchecked_optional_access_test.h"
3193
3194 void target() {
3195 $ns::$optional<int> opt = 3;
3196 while (Make<bool>()) {
3197 opt.value(); // [[unsafe]]
3198
3199 opt = Make<$ns::$optional<int>>();
3200 if (!opt.has_value()) continue;
3201 }
3202 }
3203 )");
3204}
3205
3206TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromStruct) {
3207 ExpectDiagnosticsFor(SourceCode: R"(
3208 #include "unchecked_optional_access_test.h"
3209
3210 struct kv { $ns::$optional<int> opt; int x; };
3211 int target() {
3212 auto [contents, x] = Make<kv>();
3213 return contents ? *contents : x;
3214 }
3215 )");
3216
3217 ExpectDiagnosticsFor(SourceCode: R"(
3218 #include "unchecked_optional_access_test.h"
3219
3220 template <typename T1, typename T2>
3221 struct pair { T1 fst; T2 snd; };
3222 int target() {
3223 auto [contents, x] = Make<pair<$ns::$optional<int>, int>>();
3224 return contents ? *contents : x;
3225 }
3226 )");
3227}
3228
3229TEST_P(UncheckedOptionalAccessTest, StructuredBindingsFromTupleLikeType) {
3230 ExpectDiagnosticsFor(SourceCode: R"(
3231 #include "unchecked_optional_access_test.h"
3232
3233 namespace std {
3234 template <class> struct tuple_size;
3235 template <size_t, class> struct tuple_element;
3236 template <class...> class tuple;
3237
3238 template <class... T>
3239 struct tuple_size<tuple<T...>> : integral_constant<size_t, sizeof...(T)> {};
3240
3241 template <size_t I, class... T>
3242 struct tuple_element<I, tuple<T...>> {
3243 using type = __type_pack_element<I, T...>;
3244 };
3245
3246 template <class...> class tuple {};
3247 template <size_t I, class... T>
3248 typename tuple_element<I, tuple<T...>>::type get(tuple<T...>);
3249 } // namespace std
3250
3251 std::tuple<$ns::$optional<const char *>, int> get_opt();
3252 void target() {
3253 auto [content, ck] = get_opt();
3254 content ? *content : "";
3255 }
3256 )");
3257}
3258
3259TEST_P(UncheckedOptionalAccessTest, CtorInitializerNullopt) {
3260 using namespace ast_matchers;
3261 ExpectDiagnosticsFor(
3262 SourceCode: R"(
3263 #include "unchecked_optional_access_test.h"
3264
3265 struct Target {
3266 Target(): opt($ns::nullopt) {
3267 opt.value(); // [[unsafe]]
3268 }
3269 $ns::$optional<int> opt;
3270 };
3271 )",
3272 FuncMatcher: cxxConstructorDecl(ofClass(InnerMatcher: hasName(Name: "Target"))));
3273}
3274
3275TEST_P(UncheckedOptionalAccessTest, CtorInitializerValue) {
3276 using namespace ast_matchers;
3277 ExpectDiagnosticsFor(
3278 SourceCode: R"(
3279 #include "unchecked_optional_access_test.h"
3280
3281 struct Target {
3282 Target(): opt(3) {
3283 opt.value();
3284 }
3285 $ns::$optional<int> opt;
3286 };
3287 )",
3288 FuncMatcher: cxxConstructorDecl(ofClass(InnerMatcher: hasName(Name: "Target"))));
3289}
3290
3291// This is regression test, it shouldn't crash.
3292TEST_P(UncheckedOptionalAccessTest, Bitfield) {
3293 using namespace ast_matchers;
3294 ExpectDiagnosticsFor(
3295 SourceCode: R"(
3296 #include "unchecked_optional_access_test.h"
3297 struct Dst {
3298 unsigned int n : 1;
3299 };
3300 void target() {
3301 $ns::$optional<bool> v;
3302 Dst d;
3303 if (v.has_value())
3304 d.n = v.value();
3305 }
3306 )");
3307}
3308
3309TEST_P(UncheckedOptionalAccessTest, LambdaParam) {
3310 ExpectDiagnosticsForLambda(SourceCode: R"(
3311 #include "unchecked_optional_access_test.h"
3312
3313 void target() {
3314 []($ns::$optional<int> opt) {
3315 if (opt.has_value()) {
3316 opt.value();
3317 } else {
3318 opt.value(); // [[unsafe]]
3319 }
3320 }(Make<$ns::$optional<int>>());
3321 }
3322 )");
3323}
3324
3325TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopy) {
3326 ExpectDiagnosticsForLambda(SourceCode: R"(
3327 #include "unchecked_optional_access_test.h"
3328
3329 void target($ns::$optional<int> opt) {
3330 [opt]() {
3331 if (opt.has_value()) {
3332 opt.value();
3333 } else {
3334 opt.value(); // [[unsafe]]
3335 }
3336 }();
3337 }
3338 )");
3339}
3340
3341TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReference) {
3342 ExpectDiagnosticsForLambda(SourceCode: R"(
3343 #include "unchecked_optional_access_test.h"
3344
3345 void target($ns::$optional<int> opt) {
3346 [&opt]() {
3347 if (opt.has_value()) {
3348 opt.value();
3349 } else {
3350 opt.value(); // [[unsafe]]
3351 }
3352 }();
3353 }
3354 )");
3355}
3356
3357TEST_P(UncheckedOptionalAccessTest, LambdaCaptureWithInitializer) {
3358 ExpectDiagnosticsForLambda(SourceCode: R"(
3359 #include "unchecked_optional_access_test.h"
3360
3361 void target($ns::$optional<int> opt) {
3362 [opt2=opt]() {
3363 if (opt2.has_value()) {
3364 opt2.value();
3365 } else {
3366 opt2.value(); // [[unsafe]]
3367 }
3368 }();
3369 }
3370 )");
3371}
3372
3373TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByCopyImplicit) {
3374 ExpectDiagnosticsForLambda(SourceCode: R"(
3375 #include "unchecked_optional_access_test.h"
3376
3377 void target($ns::$optional<int> opt) {
3378 [=]() {
3379 if (opt.has_value()) {
3380 opt.value();
3381 } else {
3382 opt.value(); // [[unsafe]]
3383 }
3384 }();
3385 }
3386 )");
3387}
3388
3389TEST_P(UncheckedOptionalAccessTest, LambdaCaptureByReferenceImplicit) {
3390 ExpectDiagnosticsForLambda(SourceCode: R"(
3391 #include "unchecked_optional_access_test.h"
3392
3393 void target($ns::$optional<int> opt) {
3394 [&]() {
3395 if (opt.has_value()) {
3396 opt.value();
3397 } else {
3398 opt.value(); // [[unsafe]]
3399 }
3400 }();
3401 }
3402 )");
3403}
3404
3405TEST_P(UncheckedOptionalAccessTest, LambdaCaptureThis) {
3406 ExpectDiagnosticsForLambda(SourceCode: R"(
3407 #include "unchecked_optional_access_test.h"
3408
3409 struct Foo {
3410 $ns::$optional<int> opt;
3411
3412 void target() {
3413 [this]() {
3414 if (opt.has_value()) {
3415 opt.value();
3416 } else {
3417 opt.value(); // [[unsafe]]
3418 }
3419 }();
3420 }
3421 };
3422 )");
3423}
3424
3425TEST_P(UncheckedOptionalAccessTest, LambdaCaptureStateNotPropagated) {
3426 // We can't propagate information from the surrounding context.
3427 ExpectDiagnosticsForLambda(SourceCode: R"(
3428 #include "unchecked_optional_access_test.h"
3429
3430 void target($ns::$optional<int> opt) {
3431 if (opt.has_value()) {
3432 [&opt]() {
3433 opt.value(); // [[unsafe]]
3434 }();
3435 }
3436 }
3437 )");
3438}
3439
3440TEST_P(UncheckedOptionalAccessTest, ClassDerivedFromOptional) {
3441 ExpectDiagnosticsFor(SourceCode: R"(
3442 #include "unchecked_optional_access_test.h"
3443
3444 struct Derived : public $ns::$optional<int> {};
3445
3446 void target(Derived opt) {
3447 *opt; // [[unsafe]]
3448 if (opt.has_value())
3449 *opt;
3450
3451 // The same thing, but with a pointer receiver.
3452 Derived *popt = &opt;
3453 **popt; // [[unsafe]]
3454 if (popt->has_value())
3455 **popt;
3456 }
3457 )");
3458}
3459
3460TEST_P(UncheckedOptionalAccessTest, ClassTemplateDerivedFromOptional) {
3461 ExpectDiagnosticsFor(SourceCode: R"(
3462 #include "unchecked_optional_access_test.h"
3463
3464 template <class T>
3465 struct Derived : public $ns::$optional<T> {};
3466
3467 void target(Derived<int> opt) {
3468 *opt; // [[unsafe]]
3469 if (opt.has_value())
3470 *opt;
3471
3472 // The same thing, but with a pointer receiver.
3473 Derived<int> *popt = &opt;
3474 **popt; // [[unsafe]]
3475 if (popt->has_value())
3476 **popt;
3477 }
3478 )");
3479}
3480
3481TEST_P(UncheckedOptionalAccessTest, ClassDerivedPrivatelyFromOptional) {
3482 // Classes that derive privately from optional can themselves still call
3483 // member functions of optional. Check that we model the optional correctly
3484 // in this situation.
3485 ExpectDiagnosticsFor(SourceCode: R"(
3486 #include "unchecked_optional_access_test.h"
3487
3488 struct Derived : private $ns::$optional<int> {
3489 void Method() {
3490 **this; // [[unsafe]]
3491 if (this->has_value())
3492 **this;
3493 }
3494 };
3495 )",
3496 FuncMatcher: ast_matchers::hasName(Name: "Method"));
3497}
3498
3499TEST_P(UncheckedOptionalAccessTest, ClassDerivedFromOptionalValueConstructor) {
3500 ExpectDiagnosticsFor(SourceCode: R"(
3501 #include "unchecked_optional_access_test.h"
3502
3503 struct Derived : public $ns::$optional<int> {
3504 Derived(int);
3505 };
3506
3507 void target(Derived opt) {
3508 *opt; // [[unsafe]]
3509 opt = 1;
3510 *opt;
3511 }
3512 )");
3513}
3514
3515// FIXME: Add support for:
3516// - constructors (copy, move)
3517// - assignment operators (default, copy, move)
3518// - invalidation (passing optional by non-const reference/pointer)
3519

source code of clang/unittests/Analysis/FlowSensitive/UncheckedOptionalAccessModelTest.cpp