[libc++] Add [[nodiscard]] to std::prev and std::next (#109550)

Add `[[nodiscard]]` attribute to `std::prev` and `std::next`. Those are
potential pitfalls for users who might think they mutate the iterator.

Fixes #109452
This commit is contained in:
Marc Auberer 2024-09-28 22:56:11 +02:00 committed by GitHub
parent 6022a3a05f
commit e9c0c6604e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 33 additions and 19 deletions

View File

@ -25,7 +25,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
next(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n = 1) {
// Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
// Note that this check duplicates the similar check in `std::advance`.
@ -43,25 +43,26 @@ next(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n =
namespace ranges {
struct __next {
template <input_or_output_iterator _Ip>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x) const {
++__x;
return __x;
}
template <input_or_output_iterator _Ip>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, iter_difference_t<_Ip> __n) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, iter_difference_t<_Ip> __n) const {
ranges::advance(__x, __n);
return __x;
}
template <input_or_output_iterator _Ip, sentinel_for<_Ip> _Sp>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, _Sp __bound_sentinel) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, _Sp __bound_sentinel) const {
ranges::advance(__x, __bound_sentinel);
return __x;
}
template <input_or_output_iterator _Ip, sentinel_for<_Ip> _Sp>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, iter_difference_t<_Ip> __n, _Sp __bound_sentinel) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip
operator()(_Ip __x, iter_difference_t<_Ip> __n, _Sp __bound_sentinel) const {
ranges::advance(__x, __n, __bound_sentinel);
return __x;
}

View File

@ -25,7 +25,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> = 0>
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX17 _InputIter
prev(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n = 1) {
// Calling `advance` with a negative value on a non-bidirectional iterator is a no-op in the current implementation.
// Note that this check duplicates the similar check in `std::advance`.
@ -42,19 +42,20 @@ prev(_InputIter __x, typename iterator_traits<_InputIter>::difference_type __n =
namespace ranges {
struct __prev {
template <bidirectional_iterator _Ip>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x) const {
--__x;
return __x;
}
template <bidirectional_iterator _Ip>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, iter_difference_t<_Ip> __n) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, iter_difference_t<_Ip> __n) const {
ranges::advance(__x, -__n);
return __x;
}
template <bidirectional_iterator _Ip>
_LIBCPP_HIDE_FROM_ABI constexpr _Ip operator()(_Ip __x, iter_difference_t<_Ip> __n, _Ip __bound_iter) const {
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr _Ip
operator()(_Ip __x, iter_difference_t<_Ip> __n, _Ip __bound_iter) const {
ranges::advance(__x, -__n, __bound_iter);
return __x;
}

View File

@ -15,12 +15,24 @@
#include <iterator>
#include <vector>
#include "test_macros.h"
void test() {
std::vector<int> container;
int c_array[] = {1, 2, 3};
std::initializer_list<int> initializer_list;
std::empty(container); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::empty(c_array); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::empty(initializer_list); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::empty(container); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::empty(c_array); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::empty(initializer_list); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::prev(c_array); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::next(c_array); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 20
std::ranges::prev(c_array); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::prev(container.end(), 2); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::next(container.end(), 2, container.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::next(c_array); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::next(container.begin(), 2); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::ranges::next(container.end(), 1, container.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
}

View File

@ -23,8 +23,8 @@
int main(int, char**) {
int a[] = {1, 2, 3};
forward_iterator<int *> it(a+1);
std::next(it, 1); // should work fine
std::next(it, 0); // should work fine
(void)std::next(it, 1); // should work fine
(void)std::next(it, 0); // should work fine
TEST_LIBCPP_ASSERT_FAILURE(std::next(it, -1), "Attempt to next(it, n) with negative n on a non-bidirectional iterator");
return 0;

View File

@ -24,13 +24,13 @@ int main(int, char**) {
int a[] = {1, 2, 3};
bidirectional_iterator<int *> bidi(a+1);
std::prev(bidi, -1); // should work fine
std::prev(bidi, 0); // should work fine
std::prev(bidi, 1); // should work fine
(void)std::prev(bidi, -1); // should work fine
(void)std::prev(bidi, 0); // should work fine
(void)std::prev(bidi, 1); // should work fine
forward_iterator<int *> it(a+1);
std::prev(it, -1); // should work fine
std::prev(it, 0); // should work fine
(void)std::prev(it, -1); // should work fine
(void)std::prev(it, 0); // should work fine
TEST_LIBCPP_ASSERT_FAILURE(std::prev(it, 1), "Attempt to prev(it, n) with a positive n on a non-bidirectional iterator");
return 0;