1 | //===--- UseRangesCheck.cpp - clang-tidy ----------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #include "UseRangesCheck.h" |
10 | #include "clang/AST/Decl.h" |
11 | #include "llvm/ADT/ArrayRef.h" |
12 | #include "llvm/ADT/IntrusiveRefCntPtr.h" |
13 | #include "llvm/ADT/SmallVector.h" |
14 | #include "llvm/ADT/StringRef.h" |
15 | #include <initializer_list> |
16 | |
17 | // FixItHint - Let the docs script know that this class does provide fixits |
18 | |
19 | namespace clang::tidy::modernize { |
20 | |
21 | static constexpr const char *SingleRangeNames[] = { |
22 | "all_of" , |
23 | "any_of" , |
24 | "none_of" , |
25 | "for_each" , |
26 | "find" , |
27 | "find_if" , |
28 | "find_if_not" , |
29 | "adjacent_find" , |
30 | "copy" , |
31 | "copy_if" , |
32 | "copy_backward" , |
33 | "move" , |
34 | "move_backward" , |
35 | "fill" , |
36 | "transform" , |
37 | "replace" , |
38 | "replace_if" , |
39 | "generate" , |
40 | "remove" , |
41 | "remove_if" , |
42 | "remove_copy" , |
43 | "remove_copy_if" , |
44 | "unique" , |
45 | "unique_copy" , |
46 | "sample" , |
47 | "partition_point" , |
48 | "lower_bound" , |
49 | "upper_bound" , |
50 | "equal_range" , |
51 | "binary_search" , |
52 | "push_heap" , |
53 | "pop_heap" , |
54 | "make_heap" , |
55 | "sort_heap" , |
56 | "next_permutation" , |
57 | "prev_permutation" , |
58 | "reverse" , |
59 | "reverse_copy" , |
60 | "shift_left" , |
61 | "shift_right" , |
62 | "is_partitioned" , |
63 | "partition" , |
64 | "partition_copy" , |
65 | "stable_partition" , |
66 | "sort" , |
67 | "stable_sort" , |
68 | "is_sorted" , |
69 | "is_sorted_until" , |
70 | "is_heap" , |
71 | "is_heap_until" , |
72 | "max_element" , |
73 | "min_element" , |
74 | "minmax_element" , |
75 | "uninitialized_copy" , |
76 | "uninitialized_fill" , |
77 | "uninitialized_move" , |
78 | "uninitialized_default_construct" , |
79 | "uninitialized_value_construct" , |
80 | "destroy" , |
81 | }; |
82 | |
83 | static constexpr const char *TwoRangeNames[] = { |
84 | "equal" , |
85 | "mismatch" , |
86 | "partial_sort_copy" , |
87 | "includes" , |
88 | "set_union" , |
89 | "set_intersection" , |
90 | "set_difference" , |
91 | "set_symmetric_difference" , |
92 | "merge" , |
93 | "lexicographical_compare" , |
94 | "find_end" , |
95 | "search" , |
96 | "is_permutation" , |
97 | }; |
98 | |
99 | static constexpr const char *SinglePivotRangeNames[] = {"rotate" , "rotate_copy" , |
100 | "inplace_merge" }; |
101 | |
102 | namespace { |
103 | class StdReplacer : public utils::UseRangesCheck::Replacer { |
104 | public: |
105 | explicit StdReplacer(SmallVector<UseRangesCheck::Signature> Signatures) |
106 | : Signatures(std::move(Signatures)) {} |
107 | std::optional<std::string> |
108 | getReplaceName(const NamedDecl &OriginalName) const override { |
109 | return ("std::ranges::" + OriginalName.getName()).str(); |
110 | } |
111 | ArrayRef<UseRangesCheck::Signature> |
112 | getReplacementSignatures() const override { |
113 | return Signatures; |
114 | } |
115 | |
116 | private: |
117 | SmallVector<UseRangesCheck::Signature> Signatures; |
118 | }; |
119 | |
120 | class StdAlgorithmReplacer : public StdReplacer { |
121 | using StdReplacer::StdReplacer; |
122 | std::optional<std::string> |
123 | (const NamedDecl & /*OriginalName*/) const override { |
124 | return "<algorithm>" ; |
125 | } |
126 | }; |
127 | |
128 | class StdNumericReplacer : public StdReplacer { |
129 | using StdReplacer::StdReplacer; |
130 | std::optional<std::string> |
131 | (const NamedDecl & /*OriginalName*/) const override { |
132 | return "<numeric>" ; |
133 | } |
134 | }; |
135 | } // namespace |
136 | |
137 | utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const { |
138 | |
139 | utils::UseRangesCheck::ReplacerMap Result; |
140 | |
141 | // template<typename Iter> Func(Iter first, Iter last,...). |
142 | static const Signature SingleRangeArgs = {{.BeginArg: 0}}; |
143 | // template<typename Iter1, typename Iter2> |
144 | // Func(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2,...). |
145 | static const Signature TwoRangeArgs = {{.BeginArg: 0}, {.BeginArg: 2}}; |
146 | |
147 | // template<typename Iter> Func(Iter first, Iter pivot, Iter last,...). |
148 | static const Signature SinglePivotRange = {{.BeginArg: 0, .EndArg: 2}}; |
149 | |
150 | static const Signature SingleRangeFunc[] = {SingleRangeArgs}; |
151 | |
152 | static const Signature TwoRangeFunc[] = {TwoRangeArgs}; |
153 | |
154 | static const Signature SinglePivotFunc[] = {SinglePivotRange}; |
155 | |
156 | static const std::pair<ArrayRef<Signature>, ArrayRef<const char *>> |
157 | AlgorithmNames[] = {{SingleRangeFunc, SingleRangeNames}, |
158 | {TwoRangeFunc, TwoRangeNames}, |
159 | {SinglePivotFunc, SinglePivotRangeNames}}; |
160 | SmallString<64> Buff; |
161 | for (const auto &[Signatures, Values] : AlgorithmNames) { |
162 | auto Replacer = llvm::makeIntrusiveRefCnt<StdAlgorithmReplacer>( |
163 | A: SmallVector<UseRangesCheck::Signature>{Signatures}); |
164 | for (const auto &Name : Values) { |
165 | Buff.assign(Refs: {"::std::" , Name}); |
166 | Result.try_emplace(Key: Buff, Args&: Replacer); |
167 | } |
168 | } |
169 | if (getLangOpts().CPlusPlus23) |
170 | Result.try_emplace( |
171 | Key: "::std::iota" , |
172 | Args: llvm::makeIntrusiveRefCnt<StdNumericReplacer>( |
173 | A: SmallVector<UseRangesCheck::Signature>{std::begin(arr: SingleRangeFunc), |
174 | std::end(arr: SingleRangeFunc)})); |
175 | return Result; |
176 | } |
177 | |
178 | UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context) |
179 | : utils::UseRangesCheck(Name, Context), |
180 | UseReversePipe(Options.get(LocalName: "UseReversePipe" , Default: false)) {} |
181 | |
182 | void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { |
183 | utils::UseRangesCheck::storeOptions(Options&: Opts); |
184 | Options.store(Options&: Opts, LocalName: "UseReversePipe" , Value: UseReversePipe); |
185 | } |
186 | |
187 | bool UseRangesCheck::isLanguageVersionSupported( |
188 | const LangOptions &LangOpts) const { |
189 | return LangOpts.CPlusPlus20; |
190 | } |
191 | ArrayRef<std::pair<StringRef, StringRef>> |
192 | UseRangesCheck::getFreeBeginEndMethods() const { |
193 | static const std::pair<StringRef, StringRef> Refs[] = { |
194 | {"::std::begin" , "::std::end" }, {"::std::cbegin" , "::std::cend" }}; |
195 | return Refs; |
196 | } |
197 | std::optional<UseRangesCheck::ReverseIteratorDescriptor> |
198 | UseRangesCheck::getReverseDescriptor() const { |
199 | static const std::pair<StringRef, StringRef> Refs[] = { |
200 | {"::std::rbegin" , "::std::rend" }, {"::std::crbegin" , "::std::crend" }}; |
201 | return ReverseIteratorDescriptor{.ReverseAdaptorName: UseReversePipe ? "std::views::reverse" |
202 | : "std::ranges::reverse_view" , |
203 | .ReverseHeader: "<ranges>" , .FreeReverseNames: Refs, .IsPipeSyntax: UseReversePipe}; |
204 | } |
205 | } // namespace clang::tidy::modernize |
206 | |