Skip to content

Commit 72f380a

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 7063d84 commit 72f380a

File tree

8 files changed

+408
-0
lines changed

8 files changed

+408
-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: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
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/IntrusiveRefCntPtr.h"
12+
#include "llvm/ADT/StringRef.h"
13+
#include <initializer_list>
14+
#include <string>
15+
16+
using namespace clang::ast_matchers;
17+
18+
namespace clang::tidy::boost {
19+
20+
namespace {
21+
/// Base replacer that handles the boost include path and namespace
22+
class BoostReplacer : public UseRangesCheck::Replacer {
23+
public:
24+
BoostReplacer(ArrayRef<ArrayRef<Indexes>> Signature, bool IncludeSystem)
25+
: Signature(Signature), IncludeSystem(IncludeSystem) {}
26+
27+
ArrayRef<ArrayRef<Indexes>> getReplacementSignatures() const final {
28+
return Signature;
29+
}
30+
31+
virtual std::pair<StringRef, StringRef>
32+
getBoostName(const NamedDecl &OriginalName) const = 0;
33+
virtual std::pair<StringRef, StringRef>
34+
getBoostHeader(const NamedDecl &OriginalName) const = 0;
35+
36+
std::string getReplaceName(const NamedDecl &OriginalName) const final {
37+
auto [Namespace, Function] = getBoostName(OriginalName);
38+
return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function)
39+
.str();
40+
}
41+
42+
std::optional<std::string>
43+
getHeaderInclusion(const NamedDecl &OriginalName) const final {
44+
auto [Path, HeaderName] = getBoostHeader(OriginalName);
45+
return ((IncludeSystem ? "<boost/" : "boost/") + Path +
46+
(Path.empty() ? "" : "/") + HeaderName +
47+
(IncludeSystem ? ".hpp>" : ".hpp"))
48+
.str();
49+
}
50+
51+
private:
52+
ArrayRef<ArrayRef<Indexes>> Signature;
53+
bool IncludeSystem;
54+
};
55+
56+
/// Creates replaces where the header file lives in
57+
/// `boost/algorithm/<FUNC_NAME>.hpp and the function is named
58+
/// `boost::range::<FUNC_NAME>`
59+
class BoostRangeAlgorithmReplacer : public BoostReplacer {
60+
public:
61+
using BoostReplacer::BoostReplacer;
62+
std::pair<StringRef, StringRef>
63+
getBoostName(const NamedDecl &OriginalName) const override {
64+
return {"range", OriginalName.getName()};
65+
}
66+
67+
std::pair<StringRef, StringRef>
68+
getBoostHeader(const NamedDecl &OriginalName) const override {
69+
return {"range/algorithm", OriginalName.getName()};
70+
}
71+
};
72+
73+
/// Creates replaces where the header file lives in
74+
/// `boost/algorithm/<CUSTOM_HEADER>.hpp and the function is named
75+
/// `boost::range::<FUNC_NAME>`
76+
class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer {
77+
public:
78+
CustomBoostAlgorithmHeaderReplacer(StringRef HeaderName,
79+
ArrayRef<ArrayRef<Indexes>> Signature,
80+
bool IncludeSystem)
81+
: BoostRangeAlgorithmReplacer(Signature, IncludeSystem),
82+
HeaderName(HeaderName) {}
83+
84+
std::pair<StringRef, StringRef>
85+
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
86+
return {"range/algorithm", HeaderName};
87+
}
88+
89+
private:
90+
StringRef HeaderName;
91+
};
92+
93+
/// Creates replaces where the header file lives in
94+
/// `boost/algorithm/<SUB_HEADER>.hpp and the function is named
95+
/// `boost::algorithm::<FUNC_NAME>`
96+
class BoostAlgorithmReplacer : public BoostReplacer {
97+
public:
98+
BoostAlgorithmReplacer(StringRef SubHeader,
99+
ArrayRef<ArrayRef<Indexes>> Signature,
100+
bool IncludeSystem)
101+
: BoostReplacer(Signature, IncludeSystem),
102+
SubHeader(("algorithm/" + SubHeader).str()) {}
103+
std::pair<StringRef, StringRef>
104+
getBoostName(const NamedDecl &OriginalName) const override {
105+
return {"algorithm", OriginalName.getName()};
106+
}
107+
108+
std::pair<StringRef, StringRef>
109+
getBoostHeader(const NamedDecl &OriginalName) const override {
110+
return {SubHeader, OriginalName.getName()};
111+
}
112+
113+
std::string SubHeader;
114+
};
115+
116+
/// Creates replaces where the header file lives in
117+
/// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp and the function is named
118+
/// `boost::algorithm::<FUNC_NAME>`
119+
class CustomBoostAlgorithmReplacer : public BoostReplacer {
120+
public:
121+
CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName,
122+
ArrayRef<ArrayRef<Indexes>> Signature,
123+
bool IncludeSystem)
124+
: BoostReplacer(Signature, IncludeSystem),
125+
SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {}
126+
std::pair<StringRef, StringRef>
127+
getBoostName(const NamedDecl &OriginalName) const override {
128+
return {"algorithm", OriginalName.getName()};
129+
}
130+
131+
std::pair<StringRef, StringRef>
132+
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
133+
return {SubHeader, HeaderName};
134+
}
135+
136+
std::string SubHeader;
137+
StringRef HeaderName;
138+
};
139+
140+
} // namespace
141+
142+
utils::UseRangesCheck::ReplacerMap UseRangesCheck::GetReplacerMap() const {
143+
144+
ReplacerMap Results;
145+
static const Replacer::Indexes SingleSig[] = {{0}};
146+
static const Replacer::Indexes TwoSig[] = {{0}, {2}};
147+
static const ArrayRef<Replacer::Indexes> Single = {SingleSig};
148+
static const ArrayRef<Replacer::Indexes> Two = {TwoSig};
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", Two, IncludeBoostSystem),
159+
{"includes", "set_union", "set_intersection", "set_difference",
160+
"set_symmetric_difference"});
161+
Add(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
162+
Single, 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+
Two, IncludeBoostSystem),
177+
{"find_end", "merge", "partial_sort_copy", "find_first_of", "search",
178+
"lexicographical_compare", "equal", "mismatch"});
179+
Add(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
180+
"permutation", Single, IncludeBoostSystem),
181+
{"next_permutation", "prev_permutation"});
182+
Add(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
183+
"heap_algorithm", Single, IncludeBoostSystem),
184+
{"push_heap", "pop_heap", "make_heap", "sort_heap"});
185+
Add(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>("cxx11", Single,
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", Single, IncludeBoostSystem),
192+
{"is_sorted_until"});
193+
Add(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>("cxx17", Single,
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+
} // namespace clang::tidy::boost
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
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+
private:
29+
bool IncludeBoostSystem;
30+
};
31+
32+
} // namespace clang::tidy::boost
33+
34+
#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)