[clang-tidy] Allow specifying pipe syntax for use-ranges checks (#98696)

Add `UseReversePipe` option to (boost|modernize)-use-ranges checks. This
controls whether to create a reverse view using function syntax
(`reverse(Range)`) or pipe syntax (`Range | reverse`)
This commit is contained in:
Nathan James 2024-07-15 20:22:15 +01:00 committed by GitHub
parent 31d4c97506
commit 87ca6386f9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 358 additions and 237 deletions

View File

@ -331,12 +331,15 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
: utils::UseRangesCheck(Name, Context),
IncludeBoostSystem(Options.get("IncludeBoostSystem", true)) {}
IncludeBoostSystem(Options.get("IncludeBoostSystem", true)),
UseReversePipe(Options.get("UseReversePipe", false)) {}
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
utils::UseRangesCheck::storeOptions(Opts);
Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
Options.store(Opts, "UseReversePipe", UseReversePipe);
}
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
DiagnosticBuilder D =
diag(Call.getBeginLoc(), "use a %0 version of this algorithm");
@ -362,10 +365,10 @@ UseRangesCheck::getReverseDescriptor() const {
{"::boost::rbegin", "::boost::rend"},
{"::boost::const_rbegin", "::boost::const_rend"},
};
return ReverseIteratorDescriptor{"boost::adaptors::reverse",
IncludeBoostSystem
? "<boost/range/adaptor/reversed.hpp>"
: "boost/range/adaptor/reversed.hpp",
Refs};
return ReverseIteratorDescriptor{
UseReversePipe ? "boost::adaptors::reversed" : "boost::adaptors::reverse",
IncludeBoostSystem ? "<boost/range/adaptor/reversed.hpp>"
: "boost/range/adaptor/reversed.hpp",
Refs, UseReversePipe};
}
} // namespace clang::tidy::boost

View File

@ -36,6 +36,7 @@ public:
private:
bool IncludeBoostSystem;
bool UseReversePipe;
};
} // namespace clang::tidy::boost

View File

@ -166,6 +166,15 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
return Result;
}
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
: utils::UseRangesCheck(Name, Context),
UseReversePipe(Options.get("UseReversePipe", false)) {}
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
utils::UseRangesCheck::storeOptions(Opts);
Options.store(Opts, "UseReversePipe", UseReversePipe);
}
bool UseRangesCheck::isLanguageVersionSupported(
const LangOptions &LangOpts) const {
return LangOpts.CPlusPlus20;
@ -180,6 +189,8 @@ std::optional<UseRangesCheck::ReverseIteratorDescriptor>
UseRangesCheck::getReverseDescriptor() const {
static const std::pair<StringRef, StringRef> Refs[] = {
{"::std::rbegin", "::std::rend"}, {"::std::crbegin", "::std::crend"}};
return ReverseIteratorDescriptor{"std::views::reverse", "<ranges>", Refs};
return ReverseIteratorDescriptor{UseReversePipe ? "std::views::reverse"
: "std::ranges::reverse_view",
"<ranges>", Refs, UseReversePipe};
}
} // namespace clang::tidy::modernize

View File

@ -20,7 +20,9 @@ namespace clang::tidy::modernize {
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-ranges.html
class UseRangesCheck : public utils::UseRangesCheck {
public:
using utils::UseRangesCheck::UseRangesCheck;
UseRangesCheck(StringRef CheckName, ClangTidyContext *Context);
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
ReplacerMap getReplacerMap() const override;
@ -31,6 +33,9 @@ public:
getReverseDescriptor() const override;
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
private:
bool UseReversePipe;
};
} // namespace clang::tidy::modernize

View File

@ -242,16 +242,20 @@ void UseRangesCheck::check(const MatchFinder::MatchResult &Result) {
Diag << Inserter.createIncludeInsertion(
Result.SourceManager->getFileID(Call->getBeginLoc()),
*ReverseDescriptor->ReverseHeader);
StringRef ArgText = Lexer::getSourceText(
CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
Result.Context->getSourceManager(), Result.Context->getLangOpts());
SmallString<128> ReplaceText;
if (ReverseDescriptor->IsPipeSyntax)
ReplaceText.assign(
{ArgText, " | ", ReverseDescriptor->ReverseAdaptorName});
else
ReplaceText.assign(
{ReverseDescriptor->ReverseAdaptorName, "(", ArgText, ")"});
Diag << FixItHint::CreateReplacement(
Call->getArg(Replace == Indexes::Second ? Second : First)
->getSourceRange(),
SmallString<128>{
ReverseDescriptor->ReverseAdaptorName, "(",
Lexer::getSourceText(
CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
Result.Context->getSourceManager(),
Result.Context->getLangOpts()),
")"});
ReplaceText);
}
ToRemove.push_back(Replace == Indexes::Second ? First : Second);
}

View File

@ -38,6 +38,7 @@ public:
StringRef ReverseAdaptorName;
std::optional<StringRef> ReverseHeader;
ArrayRef<std::pair<StringRef, StringRef>> FreeReverseNames;
bool IsPipeSyntax = false;
};
class Replacer : public llvm::RefCountedBase<Replacer> {

View File

@ -154,8 +154,8 @@ Transforms to:
.. code-block:: c++
auto AreSame = std::equal(boost::adaptors::reverse(Items1),
boost::adaptors::reverse(Items2));
auto AreSame = boost::range::equal(boost::adaptors::reverse(Items1),
boost::adaptors::reverse(Items2));
Options
-------
@ -170,3 +170,18 @@ Options
If `true` (default value) the boost headers are included as system headers
with angle brackets (`#include <boost.hpp>`), otherwise quotes are used
(`#include "boost.hpp"`).
.. option:: UseReversePipe
When `true` (default `false`), fixes which involve reverse ranges will use the
pipe adaptor syntax instead of the function syntax.
.. code-block:: c++
std::find(Items.rbegin(), Items.rend(), 0);
Transforms to:
.. code-block:: c++
boost::range::find(Items | boost::adaptors::reversed, 0);

View File

@ -116,8 +116,8 @@ Transforms to:
.. code-block:: c++
auto AreSame = std::equal(std::views::reverse(Items1),
std::views::reverse(Items2));
auto AreSame = std::ranges::equal(std::ranges::reverse_view(Items1),
std::ranges::reverse_view(Items2));
Options
-------
@ -127,3 +127,17 @@ Options
A string specifying which include-style is used, `llvm` or `google`. Default
is `llvm`.
.. option:: UseReversePipe
When `true` (default `false`), fixes which involve reverse ranges will use the
pipe adaptor syntax instead of the function syntax.
.. code-block:: c++
std::find(Items.rbegin(), Items.rend(), 0);
Transforms to:
.. code-block:: c++
std::ranges::find(Items | std::views::reverse, 0);

View File

@ -0,0 +1,29 @@
#ifndef USE_RANGES_FAKE_BOOST_H
#define USE_RANGES_FAKE_BOOST_H
namespace boost {
namespace range_adl_barrier {
template <typename T> void *begin(T &);
template <typename T> void *end(T &);
template <typename T> void *const_begin(const T &);
template <typename T> void *const_end(const T &);
} // namespace range_adl_barrier
using namespace range_adl_barrier;
template <typename T> void *rbegin(T &);
template <typename T> void *rend(T &);
template <typename T> void *const_rbegin(T &);
template <typename T> void *const_rend(T &);
namespace algorithm {
template <class InputIterator, class T, class BinaryOperation>
T reduce(InputIterator first, InputIterator last, T init, BinaryOperation bOp) {
return init;
}
} // namespace algorithm
} // namespace boost
#endif // USE_RANGES_FAKE_BOOST_H

View File

@ -0,0 +1,99 @@
#ifndef USE_RANGES_FAKE_STD_H
#define USE_RANGES_FAKE_STD_H
namespace std {
template <typename T> class vector {
public:
using iterator = T *;
using const_iterator = const T *;
using reverse_iterator = T*;
using reverse_const_iterator = const T*;
constexpr const_iterator begin() const;
constexpr const_iterator end() const;
constexpr const_iterator cbegin() const;
constexpr const_iterator cend() const;
constexpr iterator begin();
constexpr iterator end();
constexpr reverse_const_iterator rbegin() const;
constexpr reverse_const_iterator rend() const;
constexpr reverse_const_iterator crbegin() const;
constexpr reverse_const_iterator crend() const;
constexpr reverse_iterator rbegin();
constexpr reverse_iterator rend();
};
template <typename Container> constexpr auto begin(const Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto begin(Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto end(const Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto end(Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto cbegin(const Container &Cont) {
return Cont.cbegin();
}
template <typename Container> constexpr auto cend(const Container &Cont) {
return Cont.cend();
}
// Find
template< class InputIt, class T >
InputIt find(InputIt first, InputIt last, const T& value);
template <typename Iter> void reverse(Iter begin, Iter end);
template <class InputIt1, class InputIt2>
bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
template <class ForwardIt1, class ForwardIt2>
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
ForwardIt2 last2);
template <class BidirIt>
bool next_permutation(BidirIt first, BidirIt last);
inline namespace inline_test{
template <class ForwardIt1, class ForwardIt2>
bool equal(ForwardIt1 first1, ForwardIt1 last1,
ForwardIt2 first2, ForwardIt2 last2);
template <class RandomIt>
void push_heap(RandomIt first, RandomIt last);
template <class InputIt, class OutputIt, class UnaryPred>
OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPred pred);
template <class ForwardIt>
ForwardIt is_sorted_until(ForwardIt first, ForwardIt last);
template <class InputIt>
void reduce(InputIt first, InputIt last);
template <class InputIt, class T>
T reduce(InputIt first, InputIt last, T init);
template <class InputIt, class T, class BinaryOp>
T reduce(InputIt first, InputIt last, T init, BinaryOp op) {
// Need a definition to suppress undefined_internal_type when invoked with lambda
return init;
}
template <class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init);
} // namespace inline_test
} // namespace std
#endif // USE_RANGES_FAKE_STD_H

View File

@ -0,0 +1,18 @@
// RUN: %check_clang_tidy -std=c++14 %s boost-use-ranges %t -check-suffixes=,PIPE \
// RUN: -config="{CheckOptions: { \
// RUN: boost-use-ranges.UseReversePipe: true }}" -- -I %S/Inputs/use-ranges/
// RUN: %check_clang_tidy -std=c++14 %s boost-use-ranges %t -check-suffixes=,NOPIPE -- -I %S/Inputs/use-ranges/
// CHECK-FIXES: #include <boost/algorithm/cxx11/is_sorted.hpp>
// CHECK-FIXES: #include <boost/range/adaptor/reversed.hpp>
#include "fake_std.h"
void stdLib() {
std::vector<int> I;
std::is_sorted_until(I.rbegin(), I.rend());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
// CHECK-FIXES-NOPIPE: boost::algorithm::is_sorted_until(boost::adaptors::reverse(I));
// CHECK-FIXES-PIPE: boost::algorithm::is_sorted_until(I | boost::adaptors::reversed);
}

View File

@ -1,5 +1,5 @@
// RUN: %check_clang_tidy -std=c++14 %s boost-use-ranges %t
// RUN: %check_clang_tidy -std=c++17 %s boost-use-ranges %t -check-suffixes=,CPP17
// RUN: %check_clang_tidy -std=c++14 %s boost-use-ranges %t -- -- -I %S/Inputs/use-ranges/
// RUN: %check_clang_tidy -std=c++17 %s boost-use-ranges %t -check-suffixes=,CPP17 -- -I %S/Inputs/use-ranges/
// CHECK-FIXES: #include <boost/range/algorithm/find.hpp>
// CHECK-FIXES: #include <boost/range/algorithm/reverse.hpp>
@ -13,111 +13,8 @@
// CHECK-FIXES: #include <boost/range/adaptor/reversed.hpp>
// CHECK-FIXES: #include <boost/range/numeric.hpp>
namespace std {
template <typename T> class vector {
public:
using iterator = T *;
using const_iterator = const T *;
constexpr const_iterator begin() const;
constexpr const_iterator end() const;
constexpr const_iterator cbegin() const;
constexpr const_iterator cend() const;
constexpr iterator begin();
constexpr iterator end();
};
template <typename Container> constexpr auto begin(const Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto begin(Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto end(const Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto end(Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto cbegin(const Container &Cont) {
return Cont.cbegin();
}
template <typename Container> constexpr auto cend(const Container &Cont) {
return Cont.cend();
}
// Find
template< class InputIt, class T >
InputIt find(InputIt first, InputIt last, const T& value);
template <typename Iter> void reverse(Iter begin, Iter end);
template <class InputIt1, class InputIt2>
bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
template <class ForwardIt1, class ForwardIt2>
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
ForwardIt2 last2);
template <class BidirIt>
bool next_permutation(BidirIt first, BidirIt last);
template <class ForwardIt1, class ForwardIt2>
bool equal(ForwardIt1 first1, ForwardIt1 last1,
ForwardIt2 first2, ForwardIt2 last2);
template <class RandomIt>
void push_heap(RandomIt first, RandomIt last);
template <class InputIt, class OutputIt, class UnaryPred>
OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPred pred);
template <class ForwardIt>
ForwardIt is_sorted_until(ForwardIt first, ForwardIt last);
template <class InputIt>
void reduce(InputIt first, InputIt last);
template <class InputIt, class T>
T reduce(InputIt first, InputIt last, T init);
template <class InputIt, class T, class BinaryOp>
T reduce(InputIt first, InputIt last, T init, BinaryOp op) {
// Need a definition to suppress undefined_internal_type when invoked with lambda
return init;
}
template <class InputIt, class T>
T accumulate(InputIt first, InputIt last, T init);
} // namespace std
namespace boost {
namespace range_adl_barrier {
template <typename T> void *begin(T &);
template <typename T> void *end(T &);
template <typename T> void *const_begin(const T &);
template <typename T> void *const_end(const T &);
} // namespace range_adl_barrier
using namespace range_adl_barrier;
template <typename T> void *rbegin(T &);
template <typename T> void *rend(T &);
template <typename T> void *const_rbegin(T &);
template <typename T> void *const_rend(T &);
namespace algorithm {
template <class InputIterator, class T, class BinaryOperation>
T reduce(InputIterator first, InputIterator last, T init, BinaryOperation bOp) {
return init;
}
} // namespace algorithm
} // namespace boost
#include "fake_boost.h"
#include "fake_std.h"
bool returnTrue(int val) {
return true;

View File

@ -0,0 +1,111 @@
#ifndef USE_RANGES_FAKE_STD_H
#define USE_RANGES_FAKE_STD_H
namespace std {
template <typename T> class vector {
public:
using iterator = T *;
using const_iterator = const T *;
using reverse_iterator = T*;
using reverse_const_iterator = const T*;
constexpr const_iterator begin() const;
constexpr const_iterator end() const;
constexpr const_iterator cbegin() const;
constexpr const_iterator cend() const;
constexpr iterator begin();
constexpr iterator end();
constexpr reverse_const_iterator rbegin() const;
constexpr reverse_const_iterator rend() const;
constexpr reverse_const_iterator crbegin() const;
constexpr reverse_const_iterator crend() const;
constexpr reverse_iterator rbegin();
constexpr reverse_iterator rend();
};
template <typename Container> constexpr auto begin(const Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto begin(Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto end(const Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto end(Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto cbegin(const Container &Cont) {
return Cont.cbegin();
}
template <typename Container> constexpr auto cend(const Container &Cont) {
return Cont.cend();
}
template <typename Container> constexpr auto rbegin(const Container &Cont) {
return Cont.rbegin();
}
template <typename Container> constexpr auto rbegin(Container &Cont) {
return Cont.rbegin();
}
template <typename Container> constexpr auto rend(const Container &Cont) {
return Cont.rend();
}
template <typename Container> constexpr auto rend(Container &Cont) {
return Cont.rend();
}
template <typename Container> constexpr auto crbegin(const Container &Cont) {
return Cont.crbegin();
}
template <typename Container> constexpr auto crend(const Container &Cont) {
return Cont.crend();
}
// Find
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
// Reverse
template <typename Iter> void reverse(Iter begin, Iter end);
// Includes
template <class InputIt1, class InputIt2>
bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
// IsPermutation
template <class ForwardIt1, class ForwardIt2>
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2);
template <class ForwardIt1, class ForwardIt2>
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
ForwardIt2 last2);
// Equal
template <class InputIt1, class InputIt2>
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2);
template <class InputIt1, class InputIt2>
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
template <class InputIt1, class InputIt2, class BinaryPred>
bool equal(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, BinaryPred p) {
// Need a definition to suppress undefined_internal_type when invoked with lambda
return true;
}
template <class ForwardIt, class T>
void iota(ForwardIt first, ForwardIt last, T value);
} // namespace std
#endif // USE_RANGES_FAKE_STD_H

View File

@ -0,0 +1,18 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t -check-suffixes=,PIPE \
// RUN: -config="{CheckOptions: { \
// RUN: modernize-use-ranges.UseReversePipe: true }}" -- -I %S/Inputs/use-ranges/
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t -check-suffixes=,NOPIPE -- -I %S/Inputs/use-ranges/
// CHECK-FIXES: #include <algorithm>
// CHECK-FIXES: #include <ranges>
#include "fake_std.h"
void stdLib() {
std::vector<int> I;
std::find(I.rbegin(), I.rend(), 0);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
// CHECK-FIXES-NOPIPE: std::ranges::find(std::ranges::reverse_view(I), 0);
// CHECK-FIXES-PIPE: std::ranges::find(I | std::views::reverse, 0);
}

View File

@ -1,116 +1,11 @@
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t
// RUN: %check_clang_tidy -std=c++23 %s modernize-use-ranges %t -check-suffixes=,CPP23
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t -- -- -I %S/Inputs/use-ranges/
// RUN: %check_clang_tidy -std=c++23 %s modernize-use-ranges %t -check-suffixes=,CPP23 -- -I %S/Inputs/use-ranges/
// CHECK-FIXES: #include <algorithm>
// CHECK-FIXES-CPP23: #include <numeric>
// CHECK-FIXES: #include <ranges>
namespace std {
template <typename T> class vector {
public:
using iterator = T *;
using const_iterator = const T *;
using reverse_iterator = T*;
using reverse_const_iterator = const T*;
constexpr const_iterator begin() const;
constexpr const_iterator end() const;
constexpr const_iterator cbegin() const;
constexpr const_iterator cend() const;
constexpr iterator begin();
constexpr iterator end();
constexpr reverse_const_iterator rbegin() const;
constexpr reverse_const_iterator rend() const;
constexpr reverse_const_iterator crbegin() const;
constexpr reverse_const_iterator crend() const;
constexpr reverse_iterator rbegin();
constexpr reverse_iterator rend();
};
template <typename Container> constexpr auto begin(const Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto begin(Container &Cont) {
return Cont.begin();
}
template <typename Container> constexpr auto end(const Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto end(Container &Cont) {
return Cont.end();
}
template <typename Container> constexpr auto cbegin(const Container &Cont) {
return Cont.cbegin();
}
template <typename Container> constexpr auto cend(const Container &Cont) {
return Cont.cend();
}
template <typename Container> constexpr auto rbegin(const Container &Cont) {
return Cont.rbegin();
}
template <typename Container> constexpr auto rbegin(Container &Cont) {
return Cont.rbegin();
}
template <typename Container> constexpr auto rend(const Container &Cont) {
return Cont.rend();
}
template <typename Container> constexpr auto rend(Container &Cont) {
return Cont.rend();
}
template <typename Container> constexpr auto crbegin(const Container &Cont) {
return Cont.crbegin();
}
template <typename Container> constexpr auto crend(const Container &Cont) {
return Cont.crend();
}
// Find
template< class InputIt, class T >
InputIt find( InputIt first, InputIt last, const T& value );
// Reverse
template <typename Iter> void reverse(Iter begin, Iter end);
// Includes
template <class InputIt1, class InputIt2>
bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
// IsPermutation
template <class ForwardIt1, class ForwardIt2>
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2);
template <class ForwardIt1, class ForwardIt2>
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
ForwardIt2 last2);
// Equal
template <class InputIt1, class InputIt2>
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2);
template <class InputIt1, class InputIt2>
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
template <class InputIt1, class InputIt2, class BinaryPred>
bool equal(InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2, BinaryPred p) {
// Need a definition to suppress undefined_internal_type when invoked with lambda
return true;
}
template <class ForwardIt, class T>
void iota(ForwardIt first, ForwardIt last, T value);
} // namespace std
#include "fake_std.h"
void Positives() {
std::vector<int> I, J;
@ -179,15 +74,15 @@ void Reverse(){
std::vector<int> I, J;
std::find(I.rbegin(), I.rend(), 0);
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
// CHECK-FIXES: std::ranges::find(std::views::reverse(I), 0);
// CHECK-FIXES: std::ranges::find(std::ranges::reverse_view(I), 0);
std::equal(std::rbegin(I), std::rend(I), J.begin(), J.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
// CHECK-FIXES: std::ranges::equal(std::views::reverse(I), J);
// CHECK-FIXES: std::ranges::equal(std::ranges::reverse_view(I), J);
std::equal(I.begin(), I.end(), std::crbegin(J), std::crend(J));
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
// CHECK-FIXES: std::ranges::equal(I, std::views::reverse(J));
// CHECK-FIXES: std::ranges::equal(I, std::ranges::reverse_view(J));
}
void Negatives() {