https://github.com/unterumarmung created https://github.com/llvm/llvm-project/pull/196037
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. >From 62094f15fe5212278c83ac8ed3d1dcfa854e8935 Mon Sep 17 00:00:00 2001 From: Daniil Dudkin <[email protected]> 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 9742b71df4e64..2d1eed13dd3ad 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -442,6 +442,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 23ca215268c0c..0ed44d99c35c0 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> @@ -98,6 +117,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) { @@ -110,6 +137,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 c48db04c483b8..29d847e8a0653 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 @@ -76,6 +76,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); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
