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

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

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