[libcxx-commits] [libcxx] [libc++][ranges] Applied `[[nodiscard]]` to Range access (PR #173550)
via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jan 1 22:43:24 PST 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Hristo Hristov (H-G-Hristov)
<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.access
Towards #<!-- -->172124
---
Full diff: https://github.com/llvm/llvm-project/pull/173550.diff
2 Files Affected:
- (modified) libcxx/include/__ranges/data.h (+2-2)
- (added) libcxx/test/libcxx/ranges/range.adaptors/range.access/nodiscard.verify.cpp (+336)
``````````diff
diff --git a/libcxx/include/__ranges/data.h b/libcxx/include/__ranges/data.h
index 50db3cffeeed8..cb3b826fceb23 100644
--- a/libcxx/include/__ranges/data.h
+++ b/libcxx/include/__ranges/data.h
@@ -51,12 +51,12 @@ concept __ranges_begin_invocable = !__member_data<_Tp> && __can_borrow<_Tp> && r
struct __fn {
template <__member_data _Tp>
- _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const noexcept(noexcept(__t.data())) {
return __t.data();
}
template <__ranges_begin_invocable _Tp>
- _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp&& __t) const
noexcept(noexcept(std::to_address(ranges::begin(__t)))) {
return std::to_address(ranges::begin(__t));
}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.access/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.access/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..bd1c1ecbd4c6e
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.access/nodiscard.verify.cpp
@@ -0,0 +1,336 @@
+//===----------------------------------------------------------------------===//
+//
+// 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++20
+
+// Check that functions are marked [[nodiscard]]
+
+#include <cstddef>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+// begin()
+
+struct NonMemberBegin {};
+int* begin(NonMemberBegin);
+
+// end()
+
+struct NonMemberBeginEnd {};
+int* begin(NonMemberBeginEnd);
+int* end(NonMemberBeginEnd);
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<NonMemberBeginEnd> = true;
+
+// cbegin()
+
+struct MemberConstBegin {
+ int* begin() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<MemberConstBegin> = true;
+
+// cend()
+
+struct MemberConstBeginConstEnd {
+ int* begin() const;
+ int* end() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<MemberConstBeginConstEnd> = true;
+
+// rbegin()
+
+struct MemberRBegin {
+ int* rbegin();
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<MemberRBegin> = true;
+
+struct NonMemberRBegin {};
+int* rbegin(NonMemberRBegin);
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<NonMemberRBegin> = true;
+
+// crbegin()
+
+struct MemberConstRBegin {
+ int* rbegin() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<MemberConstRBegin> = true;
+
+// rend()
+
+struct MemberREnd {
+ int* rbegin();
+ int* rend();
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<MemberREnd> = true;
+
+struct NonMemberREnd {};
+int* rbegin(NonMemberREnd);
+int* rend(NonMemberREnd);
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<NonMemberREnd> = true;
+
+// crend()
+
+struct MemberConstREnd {
+ int* rbegin() const;
+ int* rend() const;
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<MemberConstREnd> = true;
+
+// size()
+
+struct NonMemberSize {};
+std::size_t size(NonMemberSize) { return 0; }
+
+struct DisableSizedRange {
+ int* begin();
+ int* end();
+ std::size_t size() { return 0; }
+};
+
+template <>
+inline constexpr bool std::ranges::disable_sized_range<DisableSizedRange> = true;
+
+// data()
+
+struct BorrowedRange {
+ int* begin() const { return nullptr; }
+};
+template <>
+inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange> = true;
+
+void test() {
+ // [range.access.begin]
+
+ {
+ extern int uArr[];
+ int arr[]{94, 82, 47};
+
+ struct MemberBegin {
+ int* begin() { return nullptr; };
+ } member;
+
+ NonMemberBegin nonMember;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::begin(uArr);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::begin(arr);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::begin(member);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::begin(nonMember);
+ }
+
+ // [range.access.end]
+
+ {
+ int arr[]{94, 82, 47};
+
+ struct MemberBeginEnd {
+ int* begin() { return nullptr; };
+ int* end() { return nullptr; };
+ } member;
+
+ NonMemberBeginEnd nonMember;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::end(arr);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::end(member);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::end(nonMember);
+ }
+
+ // [range.access.cbegin]
+
+ {
+ MemberConstBegin obj;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::cbegin(std::as_const(obj));
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::cbegin(std::move(obj));
+ }
+
+ // [range.access.cend]
+
+ {
+ MemberConstBeginConstEnd obj;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::cend(std::as_const(obj));
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::cend(std::move(obj));
+ }
+
+ // [range.access.rbegin]
+
+ {
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::rbegin(MemberRBegin{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::rbegin(NonMemberRBegin{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::rbegin(NonMemberBeginEnd{});
+ }
+
+ // [range.access.rend]
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::rend(MemberREnd{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::rend(NonMemberREnd{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::rend(NonMemberBeginEnd{});
+
+ // [range.access.crbegin]
+
+ {
+ MemberConstRBegin obj;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::crbegin(std::as_const(obj));
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::crbegin(std::move(obj));
+ }
+
+ // [range.access.crend]
+ {
+ MemberConstREnd obj;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::crend(std::as_const(obj));
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::crend(std::move(obj));
+ }
+
+ // [range.prim.size]
+ {
+ int arr[]{94, 82, 49};
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::size(std::move(arr));
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::size(arr);
+
+ struct MemberSize {
+ std::size_t size() const { return 0; };
+ };
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::size(MemberSize{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::size(NonMemberSize{});
+
+ DisableSizedRange ds;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::size(ds);
+ }
+
+ // [range.prim.ssize]
+ {
+ struct UnsignedReturnTypeSize {
+ constexpr unsigned short size() { return 0; }
+ };
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::ssize(UnsignedReturnTypeSize{});
+ }
+
+ // [range.prim.size.hint]
+
+ // [range.prim.empty]
+
+ {
+ struct MemberEmptyRange {
+ int* begin();
+ bool empty() const { return true; };
+ };
+
+ struct MemberSizeRange {
+ int* begin() { return nullptr; };
+ std::size_t size() const { return 0; }
+ };
+
+ struct BeginEndComparableRange {
+ struct Sentinel {
+ constexpr bool operator==(int*) const { return true; }
+ };
+
+ int* begin() { return nullptr; };
+ Sentinel end() { return {}; };
+ };
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::empty(MemberEmptyRange{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::empty(MemberSizeRange{});
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::empty(BeginEndComparableRange{});
+ }
+
+ // [range.prim.data]
+
+ {
+ struct MemberDataRange {
+ int* begin();
+ int* data() { return nullptr; }
+ } memberDataRange;
+
+ struct MemberBeginRange {
+ int* begin() { return nullptr; }
+ } range;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::data(memberDataRange);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::data(range);
+ }
+
+ // [range.prim.cdata]
+
+ {
+ struct MemberConstBeginRange {
+ int* begin() const { return nullptr; }
+ } range;
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::cdata(range);
+
+ // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+ std::ranges::cdata(BorrowedRange{});
+ }
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/173550
More information about the libcxx-commits
mailing list