[libc++][ranges] Applied [[nodiscard]] to adjacent_view (#205206)

[[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.adjacent

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

---------

Co-authored-by: A. Jiang <de34@live.cn>
Co-authored-by: Hristo Hristov <zingam@outlook.com>
This commit is contained in:
Lucas Mellone 2026-06-29 13:46:49 +02:00 committed by GitHub
parent acd0f6fa1e
commit cf1f0e9d82
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 110 additions and 18 deletions

View file

@ -81,26 +81,26 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_view(_View __base) : __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 auto begin()
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin()
requires(!__simple_view<_View>)
{
return __iterator<false>(ranges::begin(__base_), ranges::end(__base_));
}
_LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
requires range<const _View> // LWG4482 This is under-constrained.
{
return __iterator<true>(ranges::begin(__base_), ranges::end(__base_));
}
_LIBCPP_HIDE_FROM_ABI constexpr auto end()
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end()
requires(!__simple_view<_View>)
{
if constexpr (common_range<_View>) {
@ -110,7 +110,7 @@ public:
}
}
_LIBCPP_HIDE_FROM_ABI constexpr auto end() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
requires range<const _View> // LWG4482 This is under-constrained.
{
if constexpr (common_range<const _View>) {
@ -120,7 +120,7 @@ public:
}
}
_LIBCPP_HIDE_FROM_ABI constexpr auto size()
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
requires sized_range<_View>
{
using _ST = decltype(ranges::size(__base_));
@ -130,7 +130,7 @@ public:
return static_cast<_ST>(__sz);
}
_LIBCPP_HIDE_FROM_ABI constexpr auto size() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
requires sized_range<const _View>
{
using _ST = decltype(ranges::size(__base_));
@ -205,7 +205,7 @@ public:
requires _Const && convertible_to<iterator_t<_View>, iterator_t<const _View>>
: __iterator(std::move(__i), make_index_sequence<_Np>{}) {}
_LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator*() const {
return std::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
}
@ -257,7 +257,7 @@ public:
return *this;
}
_LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator[](difference_type __n) const
requires random_access_range<_Base>
{
return std::__tuple_transform([&](auto& __i) -> decltype(auto) { return __i[__n]; }, __current_);
@ -297,7 +297,7 @@ public:
return __x.__current_.back() <=> __y.__current_.back();
}
_LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
requires random_access_range<_Base>
{
auto __r = __i;
@ -305,13 +305,13 @@ public:
return __r;
}
_LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
requires random_access_range<_Base>
{
return __i + __n;
}
_LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
requires random_access_range<_Base>
{
auto __r = __i;
@ -319,7 +319,8 @@ public:
return __r;
}
_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_.back() - __y.__current_.back();
@ -366,14 +367,14 @@ public:
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_.back() - __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& __y, const __iterator<_OtherConst>& __x) {
return __y.__end_ - __x.__current_.back();
}
@ -389,12 +390,12 @@ template <size_t _Np>
struct __fn : __range_adaptor_closure<__fn<_Np>> {
template <class _Range>
requires(_Np == 0 && forward_range<_Range &&>)
_LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&&) noexcept {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&&) noexcept {
return empty_view<tuple<>>{};
}
template <class _Ranges>
_LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ranges&& __range) noexcept(
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Ranges&& __range) noexcept(
noexcept(adjacent_view<views::all_t<_Ranges&&>, _Np>(std::forward<_Ranges>(__range))))
-> decltype(adjacent_view<views::all_t<_Ranges&&>, _Np>(std::forward<_Ranges>(__range))) {
return adjacent_view<views::all_t<_Ranges&&>, _Np>(std::forward<_Ranges>(__range));

View file

@ -0,0 +1,91 @@
//===----------------------------------------------------------------------===//
//
// 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++23
// Test the libc++ extension that std::ranges::adjacent_view and std::views::adjacent are marked as [[nodiscard]].
#include <ranges>
#include <utility>
struct View : std::ranges::view_interface<View> {
int* begin();
const int* begin() const;
volatile int* end();
const volatile int* end() const;
};
static_assert(!std::ranges::common_range<View>);
static_assert(!std::same_as<std::ranges::iterator_t<View>, std::ranges::iterator_t<const View>>);
static_assert(!std::same_as<std::ranges::sentinel_t<View>, std::ranges::sentinel_t<const View>>);
void test() {
int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
auto v = View{} | std::views::adjacent<2>;
// [range.adjacent.view]
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(v).base();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::move(v).base();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
v.begin();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(v).begin();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
v.end();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(v).end();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
v.size();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(v).size();
// [range.adjacent.iterator]
auto it = v.begin();
auto c_it = std::as_const(v).begin();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
*c_it;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
c_it[0];
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
it + 0;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
0 + it;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
it - 0;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
it - it;
// [range.adjacent.sentinel]
auto st = v.end();
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
it - st;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
st - it;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
st - c_it;
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
c_it - st;
// [range.adjacent.overview]
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::adjacent<0>(range);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::adjacent<2>(range);
}