[llvm-branch-commits] [clang-tools-extra] [clang-tidy] `use-ranges`: preserve output results (PR #196037)
Daniil Dudkin via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun May 31 03:15:04 PDT 2026
https://github.com/unterumarmung updated https://github.com/llvm/llvm-project/pull/196037
>From 2c051e50ad856d1c5418b1299101f0c63fddf3ce Mon Sep 17 00:00:00 2001
From: Daniil Dudkin <unterumarmung at yandex.ru>
Date: Wed, 6 May 2026 11:33:15 +0300
Subject: [PATCH] [clang-tidy] `use-ranges`: preserve output results
Preserve used output iterator results for output algorithm replacements by appending .out where the ranges algorithm returns an algorithm result object.
Fix #110223
Assisted by Codex.
---
.../clang-tidy/modernize/UseRangesCheck.cpp | 46 ++++----
clang-tools-extra/docs/ReleaseNotes.rst | 3 +
.../checks/modernize/use-ranges.rst | 2 +
.../modernize/Inputs/use-ranges/fake_std.h | 59 ++++++++++
.../checkers/modernize/use-ranges.cpp | 104 ++++++++++++++++++
5 files changed, 190 insertions(+), 24 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
index e36ce3adeb15f..e25e444cc3819 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
@@ -27,19 +27,10 @@ static constexpr const char *SingleRangeNames[] = {
"find_if",
"find_if_not",
"adjacent_find",
- "copy",
- "copy_if",
- "copy_backward",
- "move",
- "move_backward",
"fill",
- "transform",
"replace",
"replace_if",
"generate",
- "remove_copy",
- "remove_copy_if",
- "unique_copy",
"sample",
"partition_point",
"lower_bound",
@@ -53,7 +44,6 @@ static constexpr const char *SingleRangeNames[] = {
"next_permutation",
"prev_permutation",
"reverse",
- "reverse_copy",
"shift_left",
"shift_right",
"is_partitioned",
@@ -67,9 +57,7 @@ static constexpr const char *SingleRangeNames[] = {
"max_element",
"min_element",
"minmax_element",
- "uninitialized_copy",
"uninitialized_fill",
- "uninitialized_move",
"uninitialized_default_construct",
"uninitialized_value_construct",
"destroy",
@@ -78,27 +66,32 @@ static constexpr const char *SingleRangeNames[] = {
static constexpr const char *SingleRangeBeginResultNames[] = {
"remove", "remove_if", "stable_partition", "partition", "unique"};
+static constexpr const char *SingleRangeOutResultNames[] = {
+ "copy", "copy_if", "copy_backward", "move",
+ "move_backward", "remove_copy", "remove_copy_if", "reverse_copy",
+ "transform", "unique_copy", "uninitialized_copy", "uninitialized_move",
+};
+
static constexpr const char *TwoRangeNames[] = {
- "equal",
- "mismatch",
+ "equal", "mismatch", "includes", "lexicographical_compare",
+ "find_end", "search", "is_permutation",
+};
+
+static constexpr const char *TwoRangeOutResultNames[] = {
+ "merge",
"partial_sort_copy",
- "includes",
- "set_union",
- "set_intersection",
"set_difference",
+ "set_intersection",
"set_symmetric_difference",
- "merge",
- "lexicographical_compare",
- "find_end",
- "search",
- "is_permutation",
+ "set_union",
};
-static constexpr const char *SinglePivotRangeNames[] = {"rotate_copy",
- "inplace_merge"};
+static constexpr const char *SinglePivotRangeNames[] = {"inplace_merge"};
static constexpr const char *SinglePivotRangeBeginResultNames[] = {"rotate"};
+static constexpr const char *SinglePivotRangeOutResultNames[] = {"rotate_copy"};
+
namespace {
class StdReplacer : public utils::UseRangesCheck::Replacer {
public:
@@ -164,6 +157,8 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
const ResultPolicy DefaultPolicy;
const ResultPolicy BeginResultPolicy = {
PolicyKind::AppendAccessorForUsedResult, ".begin()"};
+ const ResultPolicy OutResultPolicy = {PolicyKind::AppendAccessorForUsedResult,
+ ".out"};
struct AlgorithmGroup {
ArrayRef<Signature> Signatures;
@@ -173,9 +168,12 @@ utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
const AlgorithmGroup AlgorithmNames[] = {
{SingleRangeFunc, SingleRangeNames, DefaultPolicy},
{SingleRangeFunc, SingleRangeBeginResultNames, BeginResultPolicy},
+ {SingleRangeFunc, SingleRangeOutResultNames, OutResultPolicy},
{TwoRangeFunc, TwoRangeNames, DefaultPolicy},
+ {TwoRangeFunc, TwoRangeOutResultNames, OutResultPolicy},
{SinglePivotFunc, SinglePivotRangeNames, DefaultPolicy},
{SinglePivotFunc, SinglePivotRangeBeginResultNames, BeginResultPolicy},
+ {SinglePivotFunc, SinglePivotRangeOutResultNames, OutResultPolicy},
};
SmallString<64> Buff;
for (const auto &[Signatures, Values, Policy] : AlgorithmNames) {
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 5092a695bc2df..81837d8544261 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -577,6 +577,9 @@ Changes in existing checks
``std::remove_if``, ``std::partition``, ``std::stable_partition``, and
``std::rotate`` calls with their ``std::ranges`` counterparts.
+ - Preserved used output iterator results when replacing output algorithms
+ such as ``std::copy``.
+
- Improved :doc:`modernize-use-std-format
<clang-tidy/checks/modernize/use-std-format>` check:
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst
index 8d1a12490ceb3..6c2c15b0445d2 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-ranges.rst
@@ -13,6 +13,7 @@ Example
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
auto NewEnd = std::unique(Items.begin(), Items.end());
+ auto Out = std::copy(Items.begin(), Items.end(), Output);
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(),
std::begin(Items2), std::end(Items2));
@@ -23,6 +24,7 @@ Transforms to:
auto Iter1 = std::ranges::find(Items, 0);
auto NewEnd = std::ranges::unique(Items).begin();
+ auto Out = std::ranges::copy(Items, Output).out;
auto AreSame = std::ranges::equal(Items1, Items2);
Supported algorithms
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h
index be5e963870fc4..be7de8983e7d9 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/Inputs/use-ranges/fake_std.h
@@ -56,8 +56,27 @@ template <typename Container> constexpr auto crend(const Container &Cont) {
template <class InputIt, class T>
InputIt find(InputIt first, InputIt last, const T &value);
+// Copy
+template <class InputIt, class OutputIt>
+OutputIt copy(InputIt first, InputIt last, OutputIt d_first);
+template <class InputIt, class OutputIt, class UnaryPred>
+OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first,
+ UnaryPred pred) {
+ return d_first;
+}
+template <class BidirIt1, class BidirIt2>
+BidirIt2 copy_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last);
+
+// Move
+template <class InputIt, class OutputIt>
+OutputIt move(InputIt first, InputIt last, OutputIt d_first);
+template <class BidirIt1, class BidirIt2>
+BidirIt2 move_backward(BidirIt1 first, BidirIt1 last, BidirIt2 d_last);
+
// Reverse
template <typename Iter> void reverse(Iter begin, Iter end);
+template <class BidirIt, class OutputIt>
+OutputIt reverse_copy(BidirIt first, BidirIt last, OutputIt d_first);
// Includes
template <class InputIt1, class InputIt2>
@@ -102,6 +121,14 @@ template <class ForwardIt, class UnaryPred>
ForwardIt remove_if(ForwardIt first, ForwardIt last, UnaryPred pred) {
return first;
}
+template <class InputIt, class OutputIt, class T>
+OutputIt remove_copy(InputIt first, InputIt last, OutputIt d_first,
+ const T &value);
+template <class InputIt, class OutputIt, class UnaryPred>
+OutputIt remove_copy_if(InputIt first, InputIt last, OutputIt d_first,
+ UnaryPred pred) {
+ return d_first;
+}
template <class ForwardIt, class UnaryPred>
ForwardIt partition(ForwardIt first, ForwardIt last, UnaryPred pred) {
@@ -114,6 +141,38 @@ BidirIt stable_partition(BidirIt first, BidirIt last, UnaryPred pred) {
template <class ForwardIt>
ForwardIt rotate(ForwardIt first, ForwardIt middle, ForwardIt last);
+template <class InputIt, class OutputIt>
+OutputIt unique_copy(InputIt first, InputIt last, OutputIt d_first);
+template <class InputIt, class OutputIt, class UnaryOp>
+OutputIt transform(InputIt first, InputIt last, OutputIt d_first, UnaryOp op) {
+ return d_first;
+}
+template <class InputIt1, class InputIt2, class OutputIt>
+OutputIt merge(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+template <class InputIt1, class InputIt2, class OutputIt>
+OutputIt set_union(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+template <class InputIt1, class InputIt2, class OutputIt>
+OutputIt set_intersection(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+template <class InputIt1, class InputIt2, class OutputIt>
+OutputIt set_difference(InputIt1 first1, InputIt1 last1, InputIt2 first2,
+ InputIt2 last2, OutputIt d_first);
+template <class InputIt1, class InputIt2, class OutputIt>
+OutputIt set_symmetric_difference(InputIt1 first1, InputIt1 last1,
+ InputIt2 first2, InputIt2 last2,
+ OutputIt d_first);
+template <class InputIt, class RandomIt>
+RandomIt partial_sort_copy(InputIt first, InputIt last, RandomIt d_first,
+ RandomIt d_last);
+template <class InputIt, class ForwardIt>
+ForwardIt uninitialized_copy(InputIt first, InputIt last, ForwardIt d_first);
+template <class InputIt, class ForwardIt>
+ForwardIt uninitialized_move(InputIt first, InputIt last, ForwardIt d_first);
+template <class ForwardIt, class OutputIt>
+OutputIt rotate_copy(ForwardIt first, ForwardIt middle, ForwardIt last,
+ OutputIt d_first);
} // namespace _V1
} // namespace std
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp
index 1724b9311e57e..96b5e8ba160f8 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-ranges.cpp
@@ -82,6 +82,110 @@ void Positives() {
// CHECK-FIXES: auto StablePartitionPoint =
// CHECK-FIXES-NEXT: std::ranges::stable_partition(I, [](int N) { return N == 0; }).begin();
+ auto Output = std::copy(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto Output = std::ranges::copy(I, J.begin()).out;
+
+ std::copy(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: std::ranges::copy(I, J.begin());
+
+ auto CopyIfOutput =
+ std::copy_if(I.begin(), I.end(), J.begin(), [](int N) { return N == 0; });
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto CopyIfOutput =
+ // CHECK-FIXES-NEXT: std::ranges::copy_if(I, J.begin(), [](int N) { return N == 0; }).out;
+
+ auto CopyBackwardOutput = std::copy_backward(I.begin(), I.end(), J.end());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto CopyBackwardOutput = std::ranges::copy_backward(I, J.end()).out;
+
+ auto MoveOutput = std::move(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto MoveOutput = std::ranges::move(I, J.begin()).out;
+
+ auto MoveBackwardOutput = std::move_backward(I.begin(), I.end(), J.end());
+ // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto MoveBackwardOutput = std::ranges::move_backward(I, J.end()).out;
+
+ auto RemoveCopyOutput = std::remove_copy(I.begin(), I.end(), J.begin(), 0);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto RemoveCopyOutput = std::ranges::remove_copy(I, J.begin(), 0).out;
+
+ auto RemoveCopyIfOutput = std::remove_copy_if(
+ I.begin(), I.end(), J.begin(), [](int N) { return N == 0; });
+ // CHECK-MESSAGES: :[[@LINE-2]]:29: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto RemoveCopyIfOutput = std::ranges::remove_copy_if(
+ // CHECK-FIXES-NEXT: I, J.begin(), [](int N) { return N == 0; }).out;
+
+ auto ReverseCopyOutput = std::reverse_copy(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto ReverseCopyOutput = std::ranges::reverse_copy(I, J.begin()).out;
+
+ auto TransformOutput =
+ std::transform(I.begin(), I.end(), J.begin(), [](int N) { return N; });
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto TransformOutput =
+ // CHECK-FIXES-NEXT: std::ranges::transform(I, J.begin(), [](int N) { return N; }).out;
+
+ auto UniqueCopyOutput = std::unique_copy(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto UniqueCopyOutput = std::ranges::unique_copy(I, J.begin()).out;
+
+ auto UninitializedCopyOutput =
+ std::uninitialized_copy(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto UninitializedCopyOutput =
+ // CHECK-FIXES-NEXT: std::ranges::uninitialized_copy(I, J.begin()).out;
+
+ auto UninitializedMoveOutput =
+ std::uninitialized_move(I.begin(), I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto UninitializedMoveOutput =
+ // CHECK-FIXES-NEXT: std::ranges::uninitialized_move(I, J.begin()).out;
+
+ auto MergeOutput =
+ std::merge(I.begin(), I.end(), J.begin(), J.end(), I.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto MergeOutput =
+ // CHECK-FIXES-NEXT: std::ranges::merge(I, J, I.begin()).out;
+
+ auto SetUnionOutput =
+ std::set_union(I.begin(), I.end(), J.begin(), J.end(), I.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto SetUnionOutput =
+ // CHECK-FIXES-NEXT: std::ranges::set_union(I, J, I.begin()).out;
+
+ auto SetIntersectionOutput =
+ std::set_intersection(I.begin(), I.end(), J.begin(), J.end(), I.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto SetIntersectionOutput =
+ // CHECK-FIXES-NEXT: std::ranges::set_intersection(I, J, I.begin()).out;
+
+ auto SetDifferenceOutput =
+ std::set_difference(I.begin(), I.end(), J.begin(), J.end(), I.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto SetDifferenceOutput =
+ // CHECK-FIXES-NEXT: std::ranges::set_difference(I, J, I.begin()).out;
+
+ auto SetSymmetricDifferenceOutput = std::set_symmetric_difference(
+ I.begin(), I.end(), J.begin(), J.end(), I.begin());
+ // CHECK-MESSAGES: :[[@LINE-2]]:39: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto SetSymmetricDifferenceOutput = std::ranges::set_symmetric_difference(
+ // CHECK-FIXES-NEXT: I, J, I.begin()).out;
+
+ auto PartialSortCopyOutput =
+ std::partial_sort_copy(I.begin(), I.end(), J.begin(), J.end());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto PartialSortCopyOutput =
+ // CHECK-FIXES-NEXT: std::ranges::partial_sort_copy(I, J).out;
+
+ auto RotateCopyOutput =
+ std::rotate_copy(I.begin(), I.begin() + 2, I.end(), J.begin());
+ // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: use a ranges version of this algorithm
+ // CHECK-FIXES: auto RotateCopyOutput =
+ // CHECK-FIXES-NEXT: std::ranges::rotate_copy(I, I.begin() + 2, J.begin()).out;
+
std::includes(I.begin(), I.end(), I.begin(), I.end());
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
// CHECK-FIXES: std::ranges::includes(I, I);
More information about the llvm-branch-commits
mailing list