[libc++][ranges] Applied [[nodiscard]] to adjacent_transform_view (#205900)

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

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

---------

Co-authored-by: Hristo Hristov <zingam@outlook.com>
This commit is contained in:
Lucas Mellone 2026-07-07 07:41:28 +02:00 committed by GitHub
parent cbf8048aa1
commit 0fc9beeadf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 120 additions and 18 deletions

View file

@ -105,22 +105,22 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr explicit adjacent_transform_view(_View __base, _Fn __fun)
: __inner_(std::move(__base)), __fun_(std::in_place, std::move(__fun)) {}
_LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
requires copy_constructible<_View>
{
return __inner_.base();
}
_LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__inner_).base(); }
_LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() { return __iterator<false>(*this, __inner_.begin()); }
_LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
{
return __iterator<true>(*this, __inner_.begin());
}
_LIBCPP_HIDE_FROM_ABI constexpr auto end() {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
if constexpr (common_range<_InnerView>) {
return __iterator<false>(*this, __inner_.end());
} else {
@ -128,7 +128,7 @@ public:
}
}
_LIBCPP_HIDE_FROM_ABI constexpr auto end() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
requires range<const _InnerView> && regular_invocable<__apply_n<const _Fn&, _Np>, range_reference_t<const _View>>
{
if constexpr (common_range<const _InnerView>) {
@ -138,13 +138,13 @@ public:
}
}
_LIBCPP_HIDE_FROM_ABI constexpr auto size()
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size()
requires sized_range<_InnerView>
{
return __inner_.size();
}
_LIBCPP_HIDE_FROM_ABI constexpr auto size() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto size() const
requires sized_range<const _InnerView>
{
return __inner_.size();
@ -202,7 +202,7 @@ public:
requires _Const && convertible_to<__inner_iterator<false>, __inner_iterator<true>>
: __parent_(__i.__parent_), __inner_(std::move(__i.__inner_)) {}
_LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const
noexcept(__noexcept_dereference(make_index_sequence<_Np>{})) {
return std::apply(
[&](const auto&... __iters) -> decltype(auto) { return std::invoke(*__parent_->__fun_, *__iters...); },
@ -249,7 +249,7 @@ public:
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
requires random_access_range<_Base>
{
return std::apply(
@ -291,25 +291,26 @@ public:
return __x.__inner_ <=> __y.__inner_;
}
_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>
{
return __iterator(*__i.__parent_, __i.__inner_ + __n);
}
_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 __iterator(*__i.__parent_, __i.__inner_ + __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>
{
return __iterator(*__i.__parent_, __i.__inner_ - __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<__inner_iterator<_Const>, __inner_iterator<_Const>>
{
return __x.__inner_ - __y.__inner_;
@ -344,14 +345,14 @@ public:
template <bool _OtherConst>
requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
_LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
return __x.__inner_ - __y.__inner_;
}
template <bool _OtherConst>
requires sized_sentinel_for<__inner_sentinel<_Const>, __inner_iterator<_OtherConst>>
_LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend constexpr range_difference_t<__maybe_const<_OtherConst, _InnerView>>
operator-(const __sentinel& __x, const __iterator<_OtherConst>& __y) {
return __x.__inner_ - __y.__inner_;
}
@ -364,14 +365,14 @@ template <size_t _Np>
struct __fn : __range_adaptor_closure<__fn<_Np>> {
template <class _Range, class _Fn>
requires(_Np == 0 && forward_range<_Range &&>)
_LIBCPP_HIDE_FROM_ABI static constexpr auto
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto
operator()(_Range&&, _Fn&& __fn) noexcept(noexcept(views::zip_transform(std::forward<_Fn>(__fn))))
-> decltype(views::zip_transform(std::forward<_Fn>(__fn))) {
return views::zip_transform(std::forward<_Fn>(__fn));
}
template <class _Range, class _Fn>
_LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr auto operator()(_Range&& __range, _Fn&& __fn) noexcept(
noexcept(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(
std::forward<_Range>(__range), std::forward<_Fn>(__fn))))
-> decltype(adjacent_transform_view<views::all_t<_Range&&>, decay_t<_Fn>, _Np>(

View file

@ -0,0 +1,101 @@
//===----------------------------------------------------------------------===//
//
// 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_transform_view and std::views::adjacent_transform are marked as [[nodiscard]].
#include <functional>
#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>>);
struct Fn {
int operator()(auto...) const;
};
void test() {
auto v = View{} | std::views::adjacent_transform<2>(std::multiplies());
// [range.adjacent.transform.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.transform.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.tranform.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]
int range[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
static_assert(std::ranges::forward_range<decltype(range)>);
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::adjacent_transform<0>(range, Fn{});
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::adjacent_transform<2>(range, std::multiplies());
// expected-warning@+1 {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::adjacent_transform<2>(std::multiplies());
}