Skip to content

Commit a8beb1e

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 ad55d54 commit a8beb1e

File tree

8 files changed

+423
-0
lines changed

8 files changed

+423
-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: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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/StringRef.h"
14+
#include <initializer_list>
15+
#include <string>
16+
17+
using namespace clang::ast_matchers;
18+
19+
namespace clang::tidy::boost {
20+
21+
namespace {
22+
/// Base replacer that handles the boost include path and namespace
23+
class BoostReplacer : public UseRangesCheck::Replacer {
24+
public:
25+
BoostReplacer(ArrayRef<UseRangesCheck::Signature> Signatures,
26+
bool IncludeSystem)
27+
: Signature(Signatures), IncludeSystem(IncludeSystem) {}
28+
29+
ArrayRef<UseRangesCheck::Signature> getReplacementSignatures() const final {
30+
return Signature;
31+
}
32+
33+
virtual std::pair<StringRef, StringRef>
34+
getBoostName(const NamedDecl &OriginalName) const = 0;
35+
virtual std::pair<StringRef, StringRef>
36+
getBoostHeader(const NamedDecl &OriginalName) const = 0;
37+
38+
std::string getReplaceName(const NamedDecl &OriginalName) const final {
39+
auto [Namespace, Function] = getBoostName(OriginalName);
40+
return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function)
41+
.str();
42+
}
43+
44+
std::optional<std::string>
45+
getHeaderInclusion(const NamedDecl &OriginalName) const final {
46+
auto [Path, HeaderName] = getBoostHeader(OriginalName);
47+
return ((IncludeSystem ? "<boost/" : "boost/") + Path +
48+
(Path.empty() ? "" : "/") + HeaderName +
49+
(IncludeSystem ? ".hpp>" : ".hpp"))
50+
.str();
51+
}
52+
53+
private:
54+
SmallVector<UseRangesCheck::Signature> Signature;
55+
bool IncludeSystem;
56+
};
57+
58+
/// Creates replaces where the header file lives in
59+
/// `boost/algorithm/<FUNC_NAME>.hpp and the function is named
60+
/// `boost::range::<FUNC_NAME>`
61+
class BoostRangeAlgorithmReplacer : public BoostReplacer {
62+
public:
63+
using BoostReplacer::BoostReplacer;
64+
std::pair<StringRef, StringRef>
65+
getBoostName(const NamedDecl &OriginalName) const override {
66+
return {"range", OriginalName.getName()};
67+
}
68+
69+
std::pair<StringRef, StringRef>
70+
getBoostHeader(const NamedDecl &OriginalName) const override {
71+
return {"range/algorithm", OriginalName.getName()};
72+
}
73+
};
74+
75+
/// Creates replaces where the header file lives in
76+
/// `boost/algorithm/<CUSTOM_HEADER>.hpp and the function is named
77+
/// `boost::range::<FUNC_NAME>`
78+
class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer {
79+
public:
80+
CustomBoostAlgorithmHeaderReplacer(
81+
StringRef HeaderName, ArrayRef<UseRangesCheck::Signature> Signature,
82+
bool IncludeSystem)
83+
: BoostRangeAlgorithmReplacer(Signature, IncludeSystem),
84+
HeaderName(HeaderName) {}
85+
86+
std::pair<StringRef, StringRef>
87+
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
88+
return {"range/algorithm", HeaderName};
89+
}
90+
91+
private:
92+
StringRef HeaderName;
93+
};
94+
95+
/// Creates replaces where the header file lives in
96+
/// `boost/algorithm/<SUB_HEADER>.hpp and the function is named
97+
/// `boost::algorithm::<FUNC_NAME>`
98+
class BoostAlgorithmReplacer : public BoostReplacer {
99+
public:
100+
BoostAlgorithmReplacer(StringRef SubHeader,
101+
ArrayRef<UseRangesCheck::Signature> Signature,
102+
bool IncludeSystem)
103+
: BoostReplacer(Signature, IncludeSystem),
104+
SubHeader(("algorithm/" + SubHeader).str()) {}
105+
std::pair<StringRef, StringRef>
106+
getBoostName(const NamedDecl &OriginalName) const override {
107+
return {"algorithm", OriginalName.getName()};
108+
}
109+
110+
std::pair<StringRef, StringRef>
111+
getBoostHeader(const NamedDecl &OriginalName) const override {
112+
return {SubHeader, OriginalName.getName()};
113+
}
114+
115+
std::string SubHeader;
116+
};
117+
118+
/// Creates replaces where the header file lives in
119+
/// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp and the function is named
120+
/// `boost::algorithm::<FUNC_NAME>`
121+
class CustomBoostAlgorithmReplacer : public BoostReplacer {
122+
public:
123+
CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName,
124+
ArrayRef<UseRangesCheck::Signature> Signature,
125+
bool IncludeSystem)
126+
: BoostReplacer(Signature, IncludeSystem),
127+
SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {}
128+
std::pair<StringRef, StringRef>
129+
getBoostName(const NamedDecl &OriginalName) const override {
130+
return {"algorithm", OriginalName.getName()};
131+
}
132+
133+
std::pair<StringRef, StringRef>
134+
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
135+
return {SubHeader, HeaderName};
136+
}
137+
138+
std::string SubHeader;
139+
StringRef HeaderName;
140+
};
141+
142+
} // namespace
143+
144+
utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
145+
146+
ReplacerMap Results;
147+
static const Signature SingleSig = {{0}};
148+
static const Signature TwoSig = {{0}, {2}};
149+
static const auto Add =
150+
[&Results](llvm::IntrusiveRefCntPtr<BoostReplacer> Replacer,
151+
std::initializer_list<StringRef> Names) {
152+
for (const auto &Name : Names) {
153+
Results.try_emplace(("::std::" + Name).str(), Replacer);
154+
}
155+
};
156+
157+
Add(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
158+
"set_algorithm", TwoSig, IncludeBoostSystem),
159+
{"includes", "set_union", "set_intersection", "set_difference",
160+
"set_symmetric_difference"});
161+
Add(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
162+
SingleSig, IncludeBoostSystem),
163+
{"unique", "lower_bound", "stable_sort",
164+
"equal_range", "remove_if", "sort",
165+
"random_shuffle", "remove_copy", "stable_partition",
166+
"remove_copy_if", "count", "copy_backward",
167+
"reverse_copy", "adjacent_find", "remove",
168+
"upper_bound", "binary_search", "replace_copy_if",
169+
"for_each", "generate", "count_if",
170+
"min_element", "reverse", "replace_copy",
171+
"fill", "unique_copy", "transform",
172+
"copy", "replace", "find",
173+
"replace_if", "find_if", "partition",
174+
"max_element"});
175+
Add(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
176+
TwoSig, IncludeBoostSystem),
177+
{"find_end", "merge", "partial_sort_copy", "find_first_of", "search",
178+
"lexicographical_compare", "equal", "mismatch"});
179+
Add(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
180+
"permutation", SingleSig, IncludeBoostSystem),
181+
{"next_permutation", "prev_permutation"});
182+
Add(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
183+
"heap_algorithm", SingleSig, IncludeBoostSystem),
184+
{"push_heap", "pop_heap", "make_heap", "sort_heap"});
185+
Add(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>("cxx11", SingleSig,
186+
IncludeBoostSystem),
187+
{"copy_if", "is_permutation", "is_partitioned", "find_if_not",
188+
"partition_copy", "any_of", "iota", "all_of", "partition_point",
189+
"is_sorted", "none_of"});
190+
Add(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmReplacer>(
191+
"cxx11", "is_sorted", SingleSig, IncludeBoostSystem),
192+
{"is_sorted_until"});
193+
Add(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>("cxx17", SingleSig,
194+
IncludeBoostSystem),
195+
{"reduce"});
196+
197+
return Results;
198+
}
199+
200+
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
201+
: utils::UseRangesCheck(Name, Context),
202+
IncludeBoostSystem(Options.get("IncludeBoostSystem", true)) {}
203+
204+
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
205+
utils::UseRangesCheck::storeOptions(Opts);
206+
Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
207+
}
208+
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
209+
return diag(Call.getBeginLoc(), "use a boost version of this algorithm");
210+
}
211+
} // namespace clang::tidy::boost
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
/// FIXME: Write a short description.
17+
///
18+
/// For the user-facing documentation see:
19+
/// http://clang.llvm.org/extra/clang-tidy/checks/boost/use-ranges.html
20+
class UseRangesCheck : public utils::UseRangesCheck {
21+
public:
22+
UseRangesCheck(StringRef Name, ClangTidyContext *Context);
23+
24+
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
25+
26+
ReplacerMap getReplacerMap() const override;
27+
28+
DiagnosticBuilder createDiag(const CallExpr &Call) override;
29+
30+
private:
31+
bool IncludeBoostSystem;
32+
};
33+
34+
} // namespace clang::tidy::boost
35+
36+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,12 @@ Improvements to clang-tidy
131131
New checks
132132
^^^^^^^^^^
133133

134+
- New :doc:`boost-use-ranges
135+
<clang-tidy/checks/boost/use-ranges>` check.
136+
137+
Detects calls to standard library iterator algorithms that could be replaced
138+
with a boost ranges version instead
139+
134140
- New :doc:`bugprone-crtp-constructor-accessibility
135141
<clang-tidy/checks/bugprone/crtp-constructor-accessibility>` check.
136142

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
.. title:: clang-tidy - boost-use-ranges
2+
3+
boost-use-ranges
4+
================
5+
6+
Detects calls to standard library iterator algorithms that could be replaced
7+
with a ranges version instead
8+
9+
Example
10+
-------
11+
12+
.. code-block:: c++
13+
14+
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
15+
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(), std::begin(Items2),
16+
std::end(Items2));
17+
18+
19+
transforms to:
20+
21+
.. code-block:: c++
22+
23+
auto Iter1 = boost::range::find(Items, 0);
24+
auto AreSame = boost::range::equal(Items1, Items2);

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ Clang-Tidy Checks
7575
:doc:`android-cloexec-pipe2 <android/cloexec-pipe2>`, "Yes"
7676
:doc:`android-cloexec-socket <android/cloexec-socket>`, "Yes"
7777
:doc:`android-comparison-in-temp-failure-retry <android/comparison-in-temp-failure-retry>`,
78+
:doc:`boost-use-ranges <boost/use-ranges>`, "Yes"
7879
:doc:`boost-use-to-string <boost/use-to-string>`, "Yes"
7980
:doc:`bugprone-argument-comment <bugprone/argument-comment>`, "Yes"
8081
:doc:`bugprone-assert-side-effect <bugprone/assert-side-effect>`,

0 commit comments

Comments
 (0)