[libcxx-commits] [libcxx] [libc++][optional] Applied `[[nodiscard]]` (PR #170045)

Hristo Hristov via libcxx-commits libcxx-commits at lists.llvm.org
Sun Nov 30 23:46:58 PST 2025


https://github.com/H-G-Hristov updated https://github.com/llvm/llvm-project/pull/170045

>From ff99ffae4044fe6ca69b0a14b3ddb20461f31a8d Mon Sep 17 00:00:00 2001
From: Hristo Hristov <hghristov.rmm at gmail.com>
Date: Thu, 20 Nov 2025 05:10:21 +0200
Subject: [PATCH] [libc++][optional] Applied `[[nodiscard]]`

`[[nodiscard]]` should be applied to functions where discarding the return value is most likely a correctness issue.

- https://libcxx.llvm.org/CodingGuidelines.html
- https://wg21.link/optional
---
 libcxx/include/optional                       |  69 ++++----
 .../utilities/optional/nodiscard.verify.cpp   | 156 ++++++++++++++++++
 .../optional.monadic/and_then.pass.cpp        |  16 +-
 .../optional.monadic/or_else.pass.cpp         |   8 +-
 .../optional.monadic/transform.pass.cpp       |  20 +--
 5 files changed, 213 insertions(+), 56 deletions(-)
 create mode 100644 libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp

diff --git a/libcxx/include/optional b/libcxx/include/optional
index 23b21364b1a79..50ff0232b5c22 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -275,7 +275,7 @@ public:
   _LIBCPP_HIDE_FROM_ABI bad_optional_access& operator=(const bad_optional_access&) _NOEXCEPT = default;
   // Get the key function ~bad_optional_access() into the dylib
   ~bad_optional_access() _NOEXCEPT override;
-  const char* what() const _NOEXCEPT override;
+  [[__nodiscard__]] const char* what() const _NOEXCEPT override;
 };
 
 } // namespace std
@@ -378,7 +378,7 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
   using value_type             = _Tp;
   using __base::__base;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return this->__engaged_; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() & noexcept { return this->__val_; }
   _LIBCPP_HIDE_FROM_ABI constexpr const value_type& __get() const& noexcept { return this->__val_; }
@@ -440,7 +440,7 @@ struct __optional_storage_base<_Tp, true> {
 
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept { __value_ = nullptr; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __value_ != nullptr; }
 
   _LIBCPP_HIDE_FROM_ABI constexpr value_type& __get() const& noexcept { return *__value_; }
 
@@ -634,7 +634,7 @@ public:
 #      endif
 
   // [optional.iterators], iterator support
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator begin() noexcept {
     auto& __derived_self = static_cast<optional<_Tp>&>(*this);
     auto __ptr           = [&__derived_self]() {
       if constexpr (is_lvalue_reference_v<_Tp>) {
@@ -653,7 +653,7 @@ public:
 #      endif
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator begin() const noexcept {
     auto& __derived_self = static_cast<const optional<_Tp>&>(*this);
     auto* __ptr          = [&__derived_self]() {
       if constexpr (is_lvalue_reference_v<_Tp>) {
@@ -672,10 +672,10 @@ public:
 #      endif
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator end() noexcept {
     return begin() + (static_cast<optional<_Tp>&>(*this).has_value() ? 1 : 0);
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const_iterator end() const noexcept {
     return begin() + (static_cast<const optional<_Tp>&>(*this).has_value() ? 1 : 0);
   }
 #    endif
@@ -946,22 +946,22 @@ public:
     return std::addressof(this->__get());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& operator*() const& noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
     return this->__get();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& operator*() & noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
     return this->__get();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& operator*() && noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
     return std::move(this->__get());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& operator*() const&& noexcept {
     _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(this->has_value(), "optional operator* called on a disengaged value");
     return std::move(this->__get());
   }
@@ -971,25 +971,25 @@ public:
   using __base::__get;
   using __base::has_value;
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const& value() const& {
     if (!this->has_value())
       std::__throw_bad_optional_access();
     return this->__get();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp& value() & {
     if (!this->has_value())
       std::__throw_bad_optional_access();
     return this->__get();
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& value() && {
     if (!this->has_value())
       std::__throw_bad_optional_access();
     return std::move(this->__get());
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp const&& value() const&& {
     if (!this->has_value())
       std::__throw_bad_optional_access();
     return std::move(this->__get());
@@ -1000,7 +1000,7 @@ public:
     requires(!(is_lvalue_reference_v<_Tp> && is_function_v<__libcpp_remove_reference_t<_Tp>>) &&
              !(is_lvalue_reference_v<_Tp> && is_array_v<__libcpp_remove_reference_t<_Tp>>))
 #    endif
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) const& {
     static_assert(is_copy_constructible_v<_Tp>, "optional<T>::value_or: T must be copy constructible");
     static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
     return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
@@ -1010,7 +1010,7 @@ public:
 #    if _LIBCPP_STD_VER >= 26
     requires(!is_lvalue_reference_v<_Tp>)
 #    endif
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
     static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
     static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
     return this->has_value() ? std::move(this->__get()) : static_cast<_Tp>(std::forward<_Up>(__v));
@@ -1020,7 +1020,7 @@ public:
   template <class _Up = remove_cv_t<_Tp>>
     requires(is_lvalue_reference_v<_Tp> &&
              !(is_function_v<__libcpp_remove_reference_t<_Tp>> || is_array_v<__libcpp_remove_reference_t<_Tp>>))
-  _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Tp value_or(_Up&& __v) && {
     static_assert(is_move_constructible_v<_Tp>, "optional<T>::value_or: T must be move constructible");
     static_assert(is_convertible_v<_Up, _Tp>, "optional<T>::value_or: U must be convertible to T");
     return this->has_value() ? this->__get() : static_cast<_Tp>(std::forward<_Up>(__v));
@@ -1029,7 +1029,7 @@ public:
 
 #    if _LIBCPP_STD_VER >= 23
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) & {
     using _Up = invoke_result_t<_Func, _Tp&>;
     static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                   "Result of f(value()) must be a specialization of std::optional");
@@ -1039,7 +1039,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const& {
     using _Up = invoke_result_t<_Func, const _Tp&>;
     static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                   "Result of f(value()) must be a specialization of std::optional");
@@ -1049,7 +1049,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) && {
     using _Up = invoke_result_t<_Func, _Tp&&>;
     static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                   "Result of f(std::move(value())) must be a specialization of std::optional");
@@ -1059,7 +1059,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto and_then(_Func&& __f) const&& {
     using _Up = invoke_result_t<_Func, const _Tp&&>;
     static_assert(__is_std_optional<remove_cvref_t<_Up>>::value,
                   "Result of f(std::move(value())) must be a specialization of std::optional");
@@ -1069,7 +1069,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) & {
     using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&>>;
     static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
     static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
@@ -1081,7 +1081,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const& {
     using _Up = remove_cv_t<invoke_result_t<_Func, const _Tp&>>;
     static_assert(!is_array_v<_Up>, "Result of f(value()) should not be an Array");
     static_assert(!is_same_v<_Up, in_place_t>, "Result of f(value()) should not be std::in_place_t");
@@ -1093,7 +1093,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) && {
     using _Up = remove_cv_t<invoke_result_t<_Func, _Tp&&>>;
     static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
     static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
@@ -1105,7 +1105,7 @@ public:
   }
 
   template <class _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto transform(_Func&& __f) const&& {
     using _Up = remove_cvref_t<invoke_result_t<_Func, const _Tp&&>>;
     static_assert(!is_array_v<_Up>, "Result of f(std::move(value())) should not be an Array");
     static_assert(!is_same_v<_Up, in_place_t>, "Result of f(std::move(value())) should not be std::in_place_t");
@@ -1117,7 +1117,7 @@ public:
   }
 
   template <invocable _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) const&
     requires is_copy_constructible_v<_Tp>
   {
     static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
@@ -1128,7 +1128,7 @@ public:
   }
 
   template <invocable _Func>
-  _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional or_else(_Func&& __f) &&
     requires is_move_constructible_v<_Tp>
   {
     static_assert(is_same_v<remove_cvref_t<invoke_result_t<_Func>>, optional>,
@@ -1426,8 +1426,8 @@ operator<=>(const optional<_Tp>& __x, const _Up& __v) {
 #    endif // _LIBCPP_STD_VER >= 20
 
 template <class _Tp, enable_if_t< is_move_constructible_v<_Tp> && is_swappable_v<_Tp>, int> = 0>
-inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
-swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
+inline _LIBCPP_HIDE_FROM_ABI
+_LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(optional<_Tp>& __x, optional<_Tp>& __y) noexcept(noexcept(__x.swap(__y))) {
   __x.swap(__y);
 }
 
@@ -1440,17 +1440,18 @@ template <
     __make_optional_barrier_tag = __make_optional_barrier_tag{},
 #    endif
     class _Tp>
-_LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<decay_t<_Tp>> make_optional(_Tp&& __v) {
   return optional<decay_t<_Tp>>(std::forward<_Tp>(__v));
 }
 
 template <class _Tp, class... _Args>
-_LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(_Args&&... __args) {
   return optional<_Tp>(in_place, std::forward<_Args>(__args)...);
 }
 
 template <class _Tp, class _Up, class... _Args>
-_LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp> make_optional(initializer_list<_Up> __il, _Args&&... __args) {
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr optional<_Tp>
+make_optional(initializer_list<_Up> __il, _Args&&... __args) {
   return optional<_Tp>(in_place, __il, std::forward<_Args>(__args)...);
 }
 
@@ -1461,7 +1462,7 @@ struct hash< __enable_hash_helper<optional<_Tp>, remove_const_t<_Tp>> > {
   _LIBCPP_DEPRECATED_IN_CXX17 typedef size_t result_type;
 #    endif
 
-  _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI size_t operator()(const optional<_Tp>& __opt) const {
     return static_cast<bool>(__opt) ? hash<remove_const_t<_Tp>>()(*__opt) : 0;
   }
 };
diff --git a/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp b/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..8e3abb6e273de
--- /dev/null
+++ b/libcxx/test/libcxx/utilities/optional/nodiscard.verify.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++17
+
+// <optional>
+
+// Check that functions are marked [[nodiscard]]
+
+#include <string>
+#include <optional>
+#include <utility>
+
+#include "test_macros.h"
+
+struct LVal {
+  constexpr std::optional<int> operator()(int&) { return 1; }
+  std::optional<int> operator()(const int&)  = delete;
+  std::optional<int> operator()(int&&)       = delete;
+  std::optional<int> operator()(const int&&) = delete;
+};
+
+struct CLVal {
+  std::optional<int> operator()(int&) = delete;
+  constexpr std::optional<int> operator()(const int&) { return 1; }
+  std::optional<int> operator()(int&&)       = delete;
+  std::optional<int> operator()(const int&&) = delete;
+};
+
+struct RVal {
+  std::optional<int> operator()(int&)       = delete;
+  std::optional<int> operator()(const int&) = delete;
+  constexpr std::optional<int> operator()(int&&) { return 1; }
+  std::optional<int> operator()(const int&&) = delete;
+};
+
+struct CRVal {
+  std::optional<int> operator()(int&)       = delete;
+  std::optional<int> operator()(const int&) = delete;
+  std::optional<int> operator()(int&&)      = delete;
+  constexpr std::optional<int> operator()(const int&&) { return 1; }
+};
+
+void test() {
+  std::bad_optional_access ex;
+
+  ex.what(); // expect-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  std::optional<int> opt;
+  const std::optional<int> cOpt;
+
+  opt.has_value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+#if TEST_STD_VER >= 26
+  opt.begin();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOpt.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  opt.end();    // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOpt.end();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
+  *opt;             // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cOpt;            // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::move(opt);  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *std::move(cOpt); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  opt.value();             // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOpt.value();            // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(opt).value();  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(cOpt).value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  opt.value_or(82);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOpt.value_or(82);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(opt).value_or(82);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(cOpt).value_or(82);
+
+#if TEST_STD_VER >= 23
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  opt.and_then(LVal{});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOpt.and_then(CLVal{});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(opt).and_then(RVal{});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(cOpt).and_then(CRVal{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  opt.transform(LVal{});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOpt.transform(CLVal{});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(opt).transform(RVal{});
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(cOpt).transform(CRVal{});
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  opt.or_else([] { return std::optional<int>{82}; });
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(opt).or_else([] { return std::optional<int>{82}; });
+#endif // TEST_STD_VER >= 23
+
+#if TEST_STD_VER >= 26
+  int z = 94;
+  std::optional<int&> optRef{z};
+  const std::optional<int&> cOptRef{z};
+
+  optRef.has_value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  optRef.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  optRef.end();   // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  *optRef;  // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+  *cOptRef; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  optRef.value(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(optRef).value_or(z);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOptRef.value_or(z);
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  optRef.and_then([](int&) { return std::optional<int>{82}; });
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOptRef.and_then([](int&) { return std::optional<int>{82}; });
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  optRef.transform([](int&) { return std::optional<int>{82}; });
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  cOptRef.transform([](int&) { return std::optional<int>{82}; });
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  optRef.or_else([] { return std::optional<int&>{}; });
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::move(optRef).or_else([] { return std::optional<int&>{}; });
+#endif // TEST_STD_VER >= 26
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::make_optional(82);
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::make_optional<int>('h');
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  std::make_optional<std::string>({'z', 'm', 't'});
+
+  std::hash<std::optional<int>> hash;
+
+  hash(opt); //expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp
index 133eed4a606bb..810283a4bc80e 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/and_then.pass.cpp
@@ -231,8 +231,8 @@ constexpr void test_val_types() {
 constexpr void test_sfinae() {
   std::optional<NonConst> opt{};
   auto l = [](auto&& x) { return x.non_const(); };
-  opt.and_then(l);
-  std::move(opt).and_then(l);
+  (void)opt.and_then(l);
+  (void)std::move(opt).and_then(l);
 }
 
 constexpr bool test() {
@@ -245,15 +245,15 @@ constexpr bool test() {
     return std::optional<int>{};
   };
 
-  opt.and_then(never_called);
-  std::move(opt).and_then(never_called);
-  copt.and_then(never_called);
-  std::move(copt).and_then(never_called);
+  (void)opt.and_then(never_called);
+  (void)std::move(opt).and_then(never_called);
+  (void)copt.and_then(never_called);
+  (void)std::move(copt).and_then(never_called);
 
   std::optional<NoCopy> nc;
   const auto& cnc = nc;
-  std::move(cnc).and_then(NoCopy{});
-  std::move(nc).and_then(NoCopy{});
+  (void)std::move(cnc).and_then(NoCopy{});
+  (void)std::move(nc).and_then(NoCopy{});
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
index de0a67c1579ee..0c330e30055a2 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/or_else.pass.cpp
@@ -49,7 +49,7 @@ constexpr bool test() {
     std::optional<int> opt;
     assert(opt.or_else([] { return std::optional<int>{0}; }) == 0);
     opt = 1;
-    opt.or_else([] {
+    (void)opt.or_else([] {
       assert(false);
       return std::optional<int>{};
     });
@@ -57,7 +57,7 @@ constexpr bool test() {
   {
     std::optional<MoveOnly> opt;
     opt = std::move(opt).or_else([] { return std::optional<MoveOnly>{MoveOnly{}}; });
-    std::move(opt).or_else([] {
+    (void)std::move(opt).or_else([] {
       assert(false);
       return std::optional<MoveOnly>{};
     });
@@ -69,7 +69,7 @@ constexpr bool test() {
     assert(opt.or_else([&] { return std::optional<int&>{i}; }) == i);
     int j = 3;
     opt   = j;
-    opt.or_else([] {
+    (void)opt.or_else([] {
       assert(false);
       return std::optional<int&>{};
     });
@@ -81,7 +81,7 @@ constexpr bool test() {
     assert(std::move(opt).or_else([&] { return std::optional<int&>{i}; }) == i);
     int j = 3;
     opt   = j;
-    std::move(opt).or_else([] {
+    (void)std::move(opt).or_else([] {
       assert(false);
       return std::optional<int&>{};
     });
diff --git a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp
index ad2713f2ac5b8..36939f0b6b62e 100644
--- a/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp
+++ b/libcxx/test/std/utilities/optional/optional.monadic/transform.pass.cpp
@@ -171,8 +171,8 @@ struct NonConst {
 constexpr void test_sfinae() {
   std::optional<NonConst> opt{};
   auto l = [](auto&& x) { return x.non_const(); };
-  opt.transform(l);
-  std::move(opt).transform(l);
+  (void)opt.transform(l);
+  (void)std::move(opt).transform(l);
 }
 
 constexpr bool test() {
@@ -186,18 +186,18 @@ constexpr bool test() {
     return 0;
   };
 
-  opt.transform(never_called);
-  std::move(opt).transform(never_called);
-  copt.transform(never_called);
-  std::move(copt).transform(never_called);
+  (void)opt.transform(never_called);
+  (void)std::move(opt).transform(never_called);
+  (void)copt.transform(never_called);
+  (void)std::move(copt).transform(never_called);
 
   std::optional<NoCopy> nc;
   const auto& cnc = nc;
-  std::move(nc).transform(NoCopy{});
-  std::move(cnc).transform(NoCopy{});
+  (void)std::move(nc).transform(NoCopy{});
+  (void)std::move(cnc).transform(NoCopy{});
 
-  std::move(nc).transform(NoMove{});
-  std::move(cnc).transform(NoMove{});
+  (void)std::move(nc).transform(NoMove{});
+  (void)std::move(cnc).transform(NoMove{});
 
   return true;
 }



More information about the libcxx-commits mailing list