Skip to content

Commit 77b2c68

Browse files
authored
[clang-tidy] Add support for std::rotate(_copy) and inplace_merge to modernize-use-ranges (#99057)
These algorithms take 3 iterators for the range and we are only interested in the first and last iterator argument. The ranges versions of these take a range and an iterator(defined to be inside the range) so the transformation is pretty similar `algo(I.begin, other, I.end,...)` -> `ranges::algo(I, other,...)`
1 parent ec9d62f commit 77b2c68

File tree

4 files changed

+27
-1
lines changed

4 files changed

+27
-1
lines changed

clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ static constexpr const char *TwoRangeNames[] = {
9696
"is_permutation",
9797
};
9898

99+
static constexpr const char *SinglePivotRangeNames[] = {"rotate", "rotate_copy",
100+
"inplace_merge"};
101+
99102
namespace {
100103
class StdReplacer : public utils::UseRangesCheck::Replacer {
101104
public:
@@ -141,13 +144,19 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
141144
// Func(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2,...).
142145
static const Signature TwoRangeArgs = {{0}, {2}};
143146

147+
// template<typename Iter> Func(Iter first, Iter pivot, Iter last,...).
148+
static const Signature SinglePivotRange = {{0, 2}};
149+
144150
static const Signature SingleRangeFunc[] = {SingleRangeArgs};
145151

146152
static const Signature TwoRangeFunc[] = {TwoRangeArgs};
147153

154+
static const Signature SinglePivotFunc[] = {SinglePivotRange};
155+
148156
static const std::pair<ArrayRef<Signature>, ArrayRef<const char *>>
149157
AlgorithmNames[] = {{SingleRangeFunc, SingleRangeNames},
150-
{TwoRangeFunc, TwoRangeNames}};
158+
{TwoRangeFunc, TwoRangeNames},
159+
{SinglePivotFunc, SinglePivotRangeNames}};
151160
SmallString<64> Buff;
152161
for (const auto &[Signatures, Values] : AlgorithmNames) {
153162
auto Replacer = llvm::makeIntrusiveRefCnt<StdAlgorithmReplacer>(

clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ Calls to the following std library algorithms are checked:
4646
``std::for_each``,
4747
``std::generate``,
4848
``std::includes``,
49+
``std::inplace_merge``,
4950
``std::iota``,
5051
``std::is_heap_until``,
5152
``std::is_heap``,
@@ -79,6 +80,8 @@ Calls to the following std library algorithms are checked:
7980
``std::replace``,
8081
``std::reverse_copy``,
8182
``std::reverse``,
83+
``std::rotate``,
84+
``std::rotate_copy``,
8285
``std::sample``,
8386
``std::search``,
8487
``std::set_difference``,

clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ bool equal(InputIt1 first1, InputIt1 last1,
106106
template <class ForwardIt, class T>
107107
void iota(ForwardIt first, ForwardIt last, T value);
108108

109+
template <class ForwardIt>
110+
ForwardIt rotate(ForwardIt first, ForwardIt middle, ForwardIt last);
111+
109112
} // namespace std
110113

111114
#endif // USE_RANGES_FAKE_STD_H

clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ void Positives() {
5757
// CHECK-MESSAGES-CPP23: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
5858
// CHECK-FIXES-CPP23: std::ranges::iota(I, 0);
5959

60+
std::rotate(I.begin(), I.begin() + 2, I.end());
61+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
62+
// CHECK-FIXES: std::ranges::rotate(I, I.begin() + 2);
63+
6064
using std::find;
6165
namespace my_std = std;
6266

@@ -100,4 +104,11 @@ void Negatives() {
100104
std::equal(I.begin(), I.end(), J.end(), J.end());
101105
std::equal(std::rbegin(I), std::rend(I), std::rend(J), std::rbegin(J));
102106
std::equal(I.begin(), J.end(), I.begin(), I.end());
107+
108+
// std::rotate expects the full range in the 1st and 3rd argument.
109+
// Anyone writing this code has probably written a bug, but this isn't the
110+
// purpose of this check.
111+
std::rotate(I.begin(), I.end(), I.begin() + 2);
112+
// Pathological, but probably shouldn't diagnose this
113+
std::rotate(I.begin(), I.end(), I.end() + 0);
103114
}

0 commit comments

Comments
 (0)