Skip to content

Commit 071f243

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 56d4524 commit 071f243

File tree

8 files changed

+609
-0
lines changed

8 files changed

+609
-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: 330 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
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+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
212+
"set_algorithm", TwoSig, IncludeBoostSystem),
213+
{"includes", "set_union", "set_intersection", "set_difference",
214+
"set_symmetric_difference"});
215+
216+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
217+
SingleSig, IncludeBoostSystem),
218+
{"unique", "lower_bound", "stable_sort",
219+
"equal_range", "remove_if", "sort",
220+
"random_shuffle", "remove_copy", "stable_partition",
221+
"remove_copy_if", "count", "copy_backward",
222+
"reverse_copy", "adjacent_find", "remove",
223+
"upper_bound", "binary_search", "replace_copy_if",
224+
"for_each", "generate", "count_if",
225+
"min_element", "reverse", "replace_copy",
226+
"fill", "unique_copy", "transform",
227+
"copy", "replace", "find",
228+
"replace_if", "find_if", "partition",
229+
"max_element"});
230+
231+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
232+
TwoSig, IncludeBoostSystem),
233+
{"find_end", "merge", "partial_sort_copy", "find_first_of",
234+
"search", "lexicographical_compare", "equal", "mismatch"});
235+
236+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
237+
"permutation", SingleSig, IncludeBoostSystem),
238+
{"next_permutation", "prev_permutation"});
239+
240+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
241+
"heap_algorithm", SingleSig, IncludeBoostSystem),
242+
{"push_heap", "pop_heap", "make_heap", "sort_heap"});
243+
244+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
245+
"cxx11", SingleSig, IncludeBoostSystem),
246+
{"copy_if", "is_permutation", "is_partitioned", "find_if_not",
247+
"partition_copy", "any_of", "iota", "all_of", "partition_point",
248+
"is_sorted", "none_of"});
249+
250+
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmReplacer>(
251+
"cxx11", "is_sorted", SingleSig, IncludeBoostSystem),
252+
{"is_sorted_until"});
253+
254+
if (getLangOpts().CPlusPlus17)
255+
AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
256+
"cxx17", SingleSig, IncludeBoostSystem),
257+
{"reduce"});
258+
259+
AddFromBoost(llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(SingleSig),
260+
{{"algorithm",
261+
{"reduce",
262+
"find_backward",
263+
"find_not_backward",
264+
"find_if_backward",
265+
"find_if_not_backward",
266+
"hex",
267+
"hex_lower",
268+
"unhex",
269+
"is_partitioned_until",
270+
"is_palindrome",
271+
"copy_if",
272+
"copy_while",
273+
"copy_until",
274+
"copy_if_while",
275+
"copy_if_until",
276+
"is_permutation",
277+
"is_partitioned",
278+
"one_of",
279+
"one_of_equal",
280+
"find_if_not",
281+
"partition_copy",
282+
"any_of",
283+
"any_of_equal",
284+
"iota",
285+
"all_of",
286+
"all_of_equal",
287+
"partition_point",
288+
"is_sorted_until",
289+
"is_sorted",
290+
"is_increasing",
291+
"is_decreasing",
292+
"is_strictly_increasing",
293+
"is_strictly_decreasing",
294+
"none_of",
295+
"none_of_equal",
296+
"clamp_range"}}});
297+
298+
AddFromBoost(
299+
llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(TwoSig),
300+
{{"algorithm", {"apply_permutation", "apply_reverse_permutation"}}});
301+
302+
return Results;
303+
}
304+
305+
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
306+
: utils::UseRangesCheck(Name, Context),
307+
IncludeBoostSystem(Options.get("IncludeBoostSystem", true)) {}
308+
309+
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
310+
utils::UseRangesCheck::storeOptions(Opts);
311+
Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
312+
}
313+
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
314+
DiagnosticBuilder D =
315+
diag(Call.getBeginLoc(), "use a %0 version of this algorithm");
316+
D << (Call.getDirectCallee()->isInStdNamespace() ? "boost" : "ranged");
317+
return D;
318+
}
319+
ArrayRef<std::pair<StringRef, StringRef>>
320+
UseRangesCheck::getFreeBeginEndMethods() const {
321+
static const std::pair<StringRef, StringRef> Refs[] = {
322+
{"::std::begin", "::std::end"},
323+
{"::std::cbegin", "::std::cend"},
324+
{"::boost::range_adl_barrier::begin", "::boost::range_adl_barrier::end"},
325+
{"::boost::range_adl_barrier::const_begin",
326+
"::boost::range_adl_barrier::const_end"},
327+
};
328+
return Refs;
329+
}
330+
} // 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)