Skip to content

Commit d1f1f72

Browse files
committed
Add a boost-use-ranges check
Akin to the modernize-use-ranges check but good for users of older toolchains who can't use c++20 ranges and rely on boost instead
1 parent 065d343 commit d1f1f72

File tree

8 files changed

+610
-0
lines changed

8 files changed

+610
-0
lines changed

clang-tools-extra/clang-tidy/boost/BoostTidyModule.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "../ClangTidy.h"
1010
#include "../ClangTidyModule.h"
1111
#include "../ClangTidyModuleRegistry.h"
12+
#include "UseRangesCheck.h"
1213
#include "UseToStringCheck.h"
1314
using namespace clang::ast_matchers;
1415

@@ -18,6 +19,7 @@ namespace boost {
1819
class BoostModule : public ClangTidyModule {
1920
public:
2021
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
22+
CheckFactories.registerCheck<UseRangesCheck>("boost-use-ranges");
2123
CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
2224
}
2325
};

clang-tools-extra/clang-tidy/boost/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
55

66
add_clang_library(clangTidyBoostModule
77
BoostTidyModule.cpp
8+
UseRangesCheck.cpp
89
UseToStringCheck.cpp
910

1011
LINK_LIBS
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
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 "clang/Basic/Diagnostic.h"
12+
#include "clang/Basic/LLVM.h"
13+
#include "llvm/ADT/ArrayRef.h"
14+
#include "llvm/ADT/IntrusiveRefCntPtr.h"
15+
#include "llvm/ADT/SmallString.h"
16+
#include "llvm/ADT/SmallVector.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include <initializer_list>
19+
#include <optional>
20+
#include <string>
21+
22+
// FixItHint - Let the docs script know that this class does provide fixits
23+
24+
namespace clang::tidy::boost {
25+
26+
namespace {
27+
/// Base replacer that handles the boost include path and namespace
28+
class BoostReplacer : public UseRangesCheck::Replacer {
29+
public:
30+
BoostReplacer(ArrayRef<UseRangesCheck::Signature> Signatures,
31+
bool IncludeSystem)
32+
: Signatures(Signatures), IncludeSystem(IncludeSystem) {}
33+
34+
ArrayRef<UseRangesCheck::Signature> getReplacementSignatures() const final {
35+
return Signatures;
36+
}
37+
38+
virtual std::pair<StringRef, StringRef>
39+
getBoostName(const NamedDecl &OriginalName) const = 0;
40+
41+
virtual std::pair<StringRef, StringRef>
42+
getBoostHeader(const NamedDecl &OriginalName) const = 0;
43+
44+
std::optional<std::string>
45+
getReplaceName(const NamedDecl &OriginalName) const final {
46+
auto [Namespace, Function] = getBoostName(OriginalName);
47+
return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function)
48+
.str();
49+
}
50+
51+
std::optional<std::string>
52+
getHeaderInclusion(const NamedDecl &OriginalName) const final {
53+
auto [Path, HeaderName] = getBoostHeader(OriginalName);
54+
return ((IncludeSystem ? "<boost/" : "boost/") + Path +
55+
(Path.empty() ? "" : "/") + HeaderName +
56+
(IncludeSystem ? ".hpp>" : ".hpp"))
57+
.str();
58+
}
59+
60+
private:
61+
SmallVector<UseRangesCheck::Signature> Signatures;
62+
bool IncludeSystem;
63+
};
64+
65+
/// Creates replaces where the header file lives in
66+
/// `boost/algorithm/<FUNC_NAME>.hpp` and the function is named
67+
/// `boost::range::<FUNC_NAME>`
68+
class BoostRangeAlgorithmReplacer : public BoostReplacer {
69+
public:
70+
using BoostReplacer::BoostReplacer;
71+
72+
std::pair<StringRef, StringRef>
73+
getBoostName(const NamedDecl &OriginalName) const override {
74+
return {"range", OriginalName.getName()};
75+
}
76+
77+
std::pair<StringRef, StringRef>
78+
getBoostHeader(const NamedDecl &OriginalName) const override {
79+
return {"range/algorithm", OriginalName.getName()};
80+
}
81+
};
82+
83+
/// Creates replaces where the header file lives in
84+
/// `boost/algorithm/<CUSTOM_HEADER>.hpp` and the function is named
85+
/// `boost::range::<FUNC_NAME>`
86+
class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer {
87+
public:
88+
CustomBoostAlgorithmHeaderReplacer(
89+
StringRef HeaderName, ArrayRef<UseRangesCheck::Signature> Signatures,
90+
bool IncludeSystem)
91+
: BoostRangeAlgorithmReplacer(Signatures, IncludeSystem),
92+
HeaderName(HeaderName) {}
93+
94+
std::pair<StringRef, StringRef>
95+
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
96+
return {"range/algorithm", HeaderName};
97+
}
98+
99+
private:
100+
StringRef HeaderName;
101+
};
102+
103+
/// Creates replaces where the header file lives in
104+
/// `boost/algorithm/<SUB_HEADER>.hpp` and the function is named
105+
/// `boost::algorithm::<FUNC_NAME>`
106+
class BoostAlgorithmReplacer : public BoostReplacer {
107+
public:
108+
BoostAlgorithmReplacer(StringRef SubHeader,
109+
ArrayRef<UseRangesCheck::Signature> Signatures,
110+
bool IncludeSystem)
111+
: BoostReplacer(Signatures, IncludeSystem),
112+
SubHeader(("algorithm/" + SubHeader).str()) {}
113+
std::pair<StringRef, StringRef>
114+
getBoostName(const NamedDecl &OriginalName) const override {
115+
return {"algorithm", OriginalName.getName()};
116+
}
117+
118+
std::pair<StringRef, StringRef>
119+
getBoostHeader(const NamedDecl &OriginalName) const override {
120+
return {SubHeader, OriginalName.getName()};
121+
}
122+
123+
private:
124+
std::string SubHeader;
125+
};
126+
127+
/// Creates replaces where the header file lives in
128+
/// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp` and the function is named
129+
/// `boost::algorithm::<FUNC_NAME>`
130+
class CustomBoostAlgorithmReplacer : public BoostReplacer {
131+
public:
132+
CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName,
133+
ArrayRef<UseRangesCheck::Signature> Signatures,
134+
bool IncludeSystem)
135+
: BoostReplacer(Signatures, IncludeSystem),
136+
SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {}
137+
std::pair<StringRef, StringRef>
138+
getBoostName(const NamedDecl &OriginalName) const override {
139+
return {"algorithm", OriginalName.getName()};
140+
}
141+
142+
std::pair<StringRef, StringRef>
143+
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
144+
return {SubHeader, HeaderName};
145+
}
146+
147+
private:
148+
std::string SubHeader;
149+
StringRef HeaderName;
150+
};
151+
152+
/// A Replacer that is used for functions that just call a new overload
153+
class MakeOverloadReplacer : public UseRangesCheck::Replacer {
154+
public:
155+
explicit MakeOverloadReplacer(ArrayRef<UseRangesCheck::Signature> Signatures)
156+
: Signatures(Signatures) {}
157+
158+
ArrayRef<UseRangesCheck::Signature>
159+
getReplacementSignatures() const override {
160+
return Signatures;
161+
}
162+
163+
std::optional<std::string>
164+
getReplaceName(const NamedDecl & /* OriginalName */) const override {
165+
return std::nullopt;
166+
}
167+
168+
std::optional<std::string>
169+
getHeaderInclusion(const NamedDecl & /* OriginalName */) const override {
170+
return std::nullopt;
171+
}
172+
173+
private:
174+
SmallVector<UseRangesCheck::Signature> Signatures;
175+
};
176+
177+
} // namespace
178+
179+
utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
180+
181+
ReplacerMap Results;
182+
static const Signature SingleSig = {{0}};
183+
static const Signature TwoSig = {{0}, {2}};
184+
static const auto AddFrom =
185+
[&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
186+
std::initializer_list<StringRef> Names, StringRef Prefix) {
187+
llvm::SmallString<64> Buffer;
188+
for (const auto &Name : Names) {
189+
Buffer.assign({"::", Prefix, (Prefix.empty() ? "" : "::"), Name});
190+
Results.try_emplace(Buffer, Replacer);
191+
}
192+
};
193+
194+
static const auto AddFromStd =
195+
[](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
196+
std::initializer_list<StringRef> Names) {
197+
AddFrom(Replacer, Names, "std");
198+
};
199+
200+
static const auto AddFromBoost =
201+
[](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
202+
std::initializer_list<
203+
std::pair<StringRef, std::initializer_list<StringRef>>>
204+
NamespaceAndNames) {
205+
for (auto [Namespace, Names] : NamespaceAndNames) {
206+
AddFrom(Replacer, Names,
207+
SmallString<64>{"boost", (Namespace.empty() ? "" : "::"),
208+
Namespace});
209+
}
210+
};
211+
212+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
213+
"set_algorithm", TwoSig, IncludeBoostSystem),
214+
{"includes", "set_union", "set_intersection", "set_difference",
215+
"set_symmetric_difference"});
216+
217+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
218+
SingleSig, IncludeBoostSystem),
219+
{"unique", "lower_bound", "stable_sort",
220+
"equal_range", "remove_if", "sort",
221+
"random_shuffle", "remove_copy", "stable_partition",
222+
"remove_copy_if", "count", "copy_backward",
223+
"reverse_copy", "adjacent_find", "remove",
224+
"upper_bound", "binary_search", "replace_copy_if",
225+
"for_each", "generate", "count_if",
226+
"min_element", "reverse", "replace_copy",
227+
"fill", "unique_copy", "transform",
228+
"copy", "replace", "find",
229+
"replace_if", "find_if", "partition",
230+
"max_element"});
231+
232+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
233+
TwoSig, IncludeBoostSystem),
234+
{"find_end", "merge", "partial_sort_copy", "find_first_of",
235+
"search", "lexicographical_compare", "equal", "mismatch"});
236+
237+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
238+
"permutation", SingleSig, IncludeBoostSystem),
239+
{"next_permutation", "prev_permutation"});
240+
241+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
242+
"heap_algorithm", SingleSig, IncludeBoostSystem),
243+
{"push_heap", "pop_heap", "make_heap", "sort_heap"});
244+
245+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
246+
"cxx11", SingleSig, IncludeBoostSystem),
247+
{"copy_if", "is_permutation", "is_partitioned", "find_if_not",
248+
"partition_copy", "any_of", "iota", "all_of", "partition_point",
249+
"is_sorted", "none_of"});
250+
251+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmReplacer>(
252+
"cxx11", "is_sorted", SingleSig, IncludeBoostSystem),
253+
{"is_sorted_until"});
254+
255+
if (getLangOpts().CPlusPlus17)
256+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
257+
"cxx17", SingleSig, IncludeBoostSystem),
258+
{"reduce"});
259+
260+
AddFromBoost(llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(SingleSig),
261+
{{"algorithm",
262+
{"reduce",
263+
"find_backward",
264+
"find_not_backward",
265+
"find_if_backward",
266+
"find_if_not_backward",
267+
"hex",
268+
"hex_lower",
269+
"unhex",
270+
"is_partitioned_until",
271+
"is_palindrome",
272+
"copy_if",
273+
"copy_while",
274+
"copy_until",
275+
"copy_if_while",
276+
"copy_if_until",
277+
"is_permutation",
278+
"is_partitioned",
279+
"one_of",
280+
"one_of_equal",
281+
"find_if_not",
282+
"partition_copy",
283+
"any_of",
284+
"any_of_equal",
285+
"iota",
286+
"all_of",
287+
"all_of_equal",
288+
"partition_point",
289+
"is_sorted_until",
290+
"is_sorted",
291+
"is_increasing",
292+
"is_decreasing",
293+
"is_strictly_increasing",
294+
"is_strictly_decreasing",
295+
"none_of",
296+
"none_of_equal",
297+
"clamp_range"}}});
298+
299+
AddFromBoost(
300+
llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(TwoSig),
301+
{{"algorithm", {"apply_permutation", "apply_reverse_permutation"}}});
302+
303+
return Results;
304+
}
305+
306+
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
307+
: utils::UseRangesCheck(Name, Context),
308+
IncludeBoostSystem(Options.get("IncludeBoostSystem", true)) {}
309+
310+
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
311+
utils::UseRangesCheck::storeOptions(Opts);
312+
Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
313+
}
314+
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
315+
DiagnosticBuilder D =
316+
diag(Call.getBeginLoc(), "use a %0 version of this algorithm");
317+
D << (Call.getDirectCallee()->isInStdNamespace() ? "boost" : "ranged");
318+
return D;
319+
}
320+
ArrayRef<std::pair<StringRef, StringRef>>
321+
UseRangesCheck::getFreeBeginEndMethods() const {
322+
static const std::pair<StringRef, StringRef> Refs[] = {
323+
{"::std::begin", "::std::end"},
324+
{"::std::cbegin", "::std::cend"},
325+
{"::boost::range_adl_barrier::begin", "::boost::range_adl_barrier::end"},
326+
{"::boost::range_adl_barrier::const_begin",
327+
"::boost::range_adl_barrier::const_end"},
328+
};
329+
return Refs;
330+
}
331+
} // namespace clang::tidy::boost
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
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+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
11+
12+
#include "../utils/UseRangesCheck.h"
13+
14+
namespace clang::tidy::boost {
15+
16+
/// Detects calls to standard library iterator algorithms that could be
17+
/// replaced with a boost ranges version instead
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/boost/use-ranges.html
21+
class UseRangesCheck : public utils::UseRangesCheck {
22+
public:
23+
UseRangesCheck(StringRef Name, ClangTidyContext *Context);
24+
25+
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
26+
27+
ReplacerMap getReplacerMap() const override;
28+
29+
DiagnosticBuilder createDiag(const CallExpr &Call) override;
30+
31+
ArrayRef<std::pair<StringRef, StringRef>>
32+
getFreeBeginEndMethods() const override;
33+
34+
private:
35+
bool IncludeBoostSystem;
36+
};
37+
38+
} // namespace clang::tidy::boost
39+
40+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H

0 commit comments

Comments
 (0)