[libcxx-commits] [libcxx] [libc++][ranges] Applied [[nodiscard]] to `transform_view` (PR #204014)

via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jun 19 07:01:37 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Lucas (lknknm)

<details>
<summary>Changes</summary>

[[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/range.transform

Towards https://github.com/llvm/llvm-project/issues/172124

---
Full diff: https://github.com/llvm/llvm-project/pull/204014.diff


3 Files Affected:

- (modified) libcxx/include/__ranges/transform_view.h (+32-21) 
- (removed) libcxx/test/libcxx/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp (-23) 
- (added) libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp (+242) 


``````````diff
diff --git a/libcxx/include/__ranges/transform_view.h b/libcxx/include/__ranges/transform_view.h
index 98b38dd9b2aa1..25254cc684bd9 100644
--- a/libcxx/include/__ranges/transform_view.h
+++ b/libcxx/include/__ranges/transform_view.h
@@ -88,43 +88,53 @@ class _LIBCPP_ABI_LLVM18_NO_UNIQUE_ADDRESS transform_view : public view_interfac
   _LIBCPP_HIDE_FROM_ABI constexpr _LIBCPP_EXPLICIT_SINCE_CXX23 transform_view(_View __base, _Fn __func)
       : __func_(std::in_place, std::move(__func)), __base_(std::move(__base)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
     requires copy_constructible<_View>
   {
     return __base_;
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator<false> begin() { return __iterator<false>{*this, ranges::begin(__base_)}; }
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator<true> begin() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __iterator<false> begin() {
+    return __iterator<false>{*this, ranges::begin(__base_)};
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __iterator<true> begin() const
     requires range<const _View> && __regular_invocable_with_range_ref<const _Fn&, const _View>
   {
     return __iterator<true>(*this, ranges::begin(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr __sentinel<false> end() { return __sentinel<false>(ranges::end(__base_)); }
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator<false> end()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __sentinel<false> end() {
+    return __sentinel<false>(ranges::end(__base_));
+  }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __iterator<false> end()
     requires common_range<_View>
   {
     return __iterator<false>(*this, ranges::end(__base_));
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr __sentinel<true> end() const
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __sentinel<true> end() const
     requires range<const _View> && __regular_invocable_with_range_ref<const _Fn&, const _View>
   {
     return __sentinel<true>(ranges::end(__base_));
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr __iterator<true> end() const
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr __iterator<true> end() const
     requires common_range<const _View> && __regular_invocable_with_range_ref<const _Fn&, const _View>
   {
     return __iterator<true>(*this, ranges::end(__base_));
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size()
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
     requires sized_range<_View>
   {
     return ranges::size(__base_);
   }
-  _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
     requires sized_range<const _View>
   {
     return ranges::size(__base_);
@@ -209,11 +219,11 @@ class transform_view<_View, _Fn>::__iterator
     requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>>
       : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr const iterator_t<_Base>& base() const& noexcept { return __current_; }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr iterator_t<_Base> base() && { return std::move(__current_); }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
       noexcept(noexcept(std::invoke(*__parent_->__func_, *__current_))) {
     return std::invoke(*__parent_->__func_, *__current_);
   }
@@ -262,7 +272,7 @@ class transform_view<_View, _Fn>::__iterator
     return *this;
   }
 
-  _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator[](difference_type __n) const
       noexcept(noexcept(std::invoke(*__parent_->__func_, __current_[__n])))
     requires random_access_range<_Base>
   {
@@ -305,25 +315,26 @@ class transform_view<_View, _Fn>::__iterator
     return __x.__current_ <=> __y.__current_;
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(__iterator __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator{*__i.__parent_, __i.__current_ + __n};
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, __iterator __i)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, __iterator __i)
     requires random_access_range<_Base>
   {
     return __iterator{*__i.__parent_, __i.__current_ + __n};
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator __i, difference_type __n)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(__iterator __i, difference_type __n)
     requires random_access_range<_Base>
   {
     return __iterator{*__i.__parent_, __i.__current_ - __n};
   }
 
-  _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr difference_type
+  operator-(const __iterator& __x, const __iterator& __y)
     requires sized_sentinel_for<iterator_t<_Base>, iterator_t<_Base>>
   {
     return __x.__current_ - __y.__current_;
@@ -361,7 +372,7 @@ class transform_view<_View, _Fn>::__sentinel {
     requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
       : __end_(std::move(__i.__end_)) {}
 
-  _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; }
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr sentinel_t<_Base> base() const { return __end_; }
 
   template <bool _OtherConst>
     requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
@@ -371,14 +382,14 @@ class transform_view<_View, _Fn>::__sentinel {
 
   template <bool _OtherConst>
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
   operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
     return __x.__current_ - __y.__end_;
   }
 
   template <bool _OtherConst>
     requires sized_sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
-  _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _View>>
   operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
     return __x.__end_ - __y.__current_;
   }
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp
deleted file mode 100644
index 0a5fbf72a83ee..0000000000000
--- a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/adaptor.nodiscard.verify.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-// UNSUPPORTED: c++03, c++11, c++14, c++17
-
-// Test the libc++ extension that std::views::transform is marked as [[nodiscard]].
-
-#include <ranges>
-
-void test() {
-  int range[] = {1, 2, 3};
-  auto f = [](int i) { return i; };
-
-  std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::transform(range, f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  range | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-  std::views::all | std::views::transform(f); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..20534abc3708a
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.transform/nodiscard.verify.cpp
@@ -0,0 +1,242 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// Test the libc++ extension that std::views::transform is marked as [[nodiscard]].
+
+#include <ranges>
+#include <functional>
+
+struct TestView : std::ranges::view_interface<TestView> {
+  int* begin();
+  char* begin() const;
+  const int* end();
+  const char* end() const;
+};
+
+void test() {
+  int range[] = {1, 2, 3};
+  auto f      = [](int i) { return i; };
+
+  auto identity_view     = TestView{} | std::views::transform(std::identity{});
+  auto transformed_range = range | std::views::transform(f);
+
+  const auto const_identity_view     = TestView{} | std::views::transform(std::identity{});
+  const auto const_transformed_range = range | std::views::transform(f);
+
+  {
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    identity_view.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_identity_view.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    transformed_range.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_transformed_range.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    identity_view.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_identity_view.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    transformed_range.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_transformed_range.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::transform(f);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::transform(range, f);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    range | std::views::transform(f);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::views::all | std::views::transform(f);
+  }
+
+  //===---------------------------------------------------------------------------------------===//
+  //=== ADL-based begin() / end() ===//
+
+  using std::begin, std::end, std::size;
+
+  {
+    // ===== Non-const views =====
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    begin(identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    begin(transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    end(identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    end(transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    size(identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    size(transformed_range);
+  }
+
+  {
+    // ===== Const views =====
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    begin(const_identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    begin(const_transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    end(const_identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    end(const_transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    size(const_identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    size(const_transformed_range);
+  }
+
+  //===---------------------------------------------------------------------------------------===//
+  //=== std::ranges CPO begin() / end() ===//
+
+  {
+    // ===== Non-const views =====
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::begin(identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::begin(transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::end(identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::end(transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::size(identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::size(transformed_range);
+  }
+
+  {
+    // ===== Const views =====
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::begin(const_identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::begin(const_transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::end(const_identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::end(const_transformed_range);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::size(const_identity_view);
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    std::ranges::size(const_transformed_range);
+  }
+
+  //===---------------------------------------------------------------------------------------===//
+  //=== Iterator-specific operations ===//
+  {
+    // ===== Non-const views =====
+    auto it = identity_view.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it.base();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it + 1;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - 1;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it - it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    it[0];
+  }
+
+  {
+    // ===== Const views =====
+    const auto const_it = identity_view.begin();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_it.base();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_it + 1;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    1 + const_it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_it - 1;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_it - const_it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    *const_it;
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_it[0];
+  }
+
+  //===---------------------------------------------------------------------------------------===//
+  //=== Sentinel-specific operations ===//
+  {
+    // ===== Non-const sentinels =====
+    auto transformed_range_sent = transformed_range.end();
+    auto identity_view_sent     = identity_view.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    transformed_range_sent.base();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    identity_view_sent.base();
+  }
+
+  {
+    // ===== Const sentinels =====
+    const auto const_transformed_range_sent = transformed_range.end();
+    const auto const_identity_view_sent     = identity_view.end();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_transformed_range_sent.base();
+
+    // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+    const_identity_view_sent.base();
+  }
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/204014


More information about the libcxx-commits mailing list