[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