Skip to content

Commit bb17fb6

Browse files
committed
[clang-tidy] Add new check 'llvm-use-ranges'
1 parent 16deba3 commit bb17fb6

File tree

8 files changed

+320
-1
lines changed

8 files changed

+320
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_clang_library(clangTidyLLVMModule STATIC
1212
PreferStaticOverAnonymousNamespaceCheck.cpp
1313
TwineLocalCheck.cpp
1414
UseNewMLIROpBuilderCheck.cpp
15+
UseRangesCheck.cpp
1516

1617
LINK_LIBS
1718
clangTidy

clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "PreferStaticOverAnonymousNamespaceCheck.h"
2020
#include "TwineLocalCheck.h"
2121
#include "UseNewMLIROpBuilderCheck.h"
22+
#include "UseRangesCheck.h"
2223

2324
namespace clang::tidy {
2425
namespace llvm_check {
@@ -43,6 +44,7 @@ class LLVMModule : public ClangTidyModule {
4344
CheckFactories.registerCheck<TwineLocalCheck>("llvm-twine-local");
4445
CheckFactories.registerCheck<UseNewMlirOpBuilderCheck>(
4546
"llvm-use-new-mlir-op-builder");
47+
CheckFactories.registerCheck<UseRangesCheck>("llvm-use-ranges");
4648
}
4749

4850
ClangTidyOptions getModuleOptions() override {
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
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+
11+
namespace clang::tidy::llvm_check {
12+
13+
namespace {
14+
15+
class StdToLLVMReplacer : public utils::UseRangesCheck::Replacer {
16+
public:
17+
explicit StdToLLVMReplacer(
18+
ArrayRef<utils::UseRangesCheck::Signature> Signatures)
19+
: Signatures(Signatures) {}
20+
21+
ArrayRef<utils::UseRangesCheck::Signature>
22+
getReplacementSignatures() const override {
23+
return Signatures;
24+
}
25+
26+
std::optional<std::string>
27+
getReplaceName(const NamedDecl &OriginalName) const override {
28+
return ("llvm::" + OriginalName.getName()).str();
29+
}
30+
31+
std::optional<std::string>
32+
getHeaderInclusion(const NamedDecl &) const override {
33+
return "llvm/ADT/STLExtras.h";
34+
}
35+
36+
private:
37+
SmallVector<utils::UseRangesCheck::Signature> Signatures;
38+
};
39+
40+
} // namespace
41+
42+
utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
43+
ReplacerMap Results;
44+
45+
static const Signature SingleSig = {{0}};
46+
static const Signature TwoSig = {{0}, {2}};
47+
48+
const auto AddStdToLLVM =
49+
[&Results](llvm::IntrusiveRefCntPtr<Replacer> Replacer,
50+
std::initializer_list<StringRef> Names) {
51+
for (const auto &Name : Names) {
52+
Results.try_emplace(("::std::" + Name).str(), Replacer);
53+
}
54+
};
55+
56+
// Single range algorithms
57+
AddStdToLLVM(llvm::makeIntrusiveRefCnt<StdToLLVMReplacer>(SingleSig),
58+
{"all_of", "any_of", "none_of", "for_each",
59+
"find", "find_if", "find_if_not", "count",
60+
"count_if", "transform", "replace", "remove_if",
61+
"sort", "partition", "is_sorted", "min_element",
62+
"max_element", "binary_search", "lower_bound", "upper_bound",
63+
"unique", "copy", "copy_if", "fill"});
64+
65+
// Two range algorithms
66+
AddStdToLLVM(llvm::makeIntrusiveRefCnt<StdToLLVMReplacer>(TwoSig),
67+
{"equal", "mismatch"});
68+
69+
return Results;
70+
}
71+
72+
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
73+
: utils::UseRangesCheck(Name, Context) {}
74+
75+
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
76+
return diag(Call.getBeginLoc(), "use a llvm range-based algorithm");
77+
}
78+
79+
ArrayRef<std::pair<StringRef, StringRef>>
80+
UseRangesCheck::getFreeBeginEndMethods() const {
81+
static const std::pair<StringRef, StringRef> Refs[] = {
82+
{"::std::begin", "::std::end"},
83+
{"::std::cbegin", "::std::cend"},
84+
{"::std::rbegin", "::std::rend"},
85+
{"::std::crbegin", "::std::crend"},
86+
};
87+
return Refs;
88+
}
89+
90+
} // namespace clang::tidy::llvm_check
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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_LLVM_USERANGESCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_USERANGESCHECK_H
11+
12+
#include "../utils/UseRangesCheck.h"
13+
14+
namespace clang::tidy::llvm_check {
15+
16+
/// Finds calls to STL iterator algorithms that can be replaced with LLVM
17+
/// range-based algorithms from `llvm/ADT/STLExtras.h`.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/llvm/use-ranges.html
21+
class UseRangesCheck : public utils::UseRangesCheck {
22+
public:
23+
UseRangesCheck(StringRef Name, ClangTidyContext *Context);
24+
25+
ReplacerMap getReplacerMap() const override;
26+
DiagnosticBuilder createDiag(const CallExpr &Call) override;
27+
ArrayRef<std::pair<StringRef, StringRef>>
28+
getFreeBeginEndMethods() const override;
29+
};
30+
31+
} // namespace clang::tidy::llvm_check
32+
33+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_USERANGESCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ New checks
137137
Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create<T>`` form
138138
and suggests using ``T::create`` instead.
139139

140+
- New :doc:`llvm-use-ranges
141+
<clang-tidy/checks/llvm/use-ranges>` check.
142+
143+
Detects calls to standard library iterator algorithms that could be replaced
144+
with LLVM range-based algorithms from ``llvm/ADT/STLExtras.h``.
145+
140146
- New :doc:`misc-override-with-different-visibility
141147
<clang-tidy/checks/misc/override-with-different-visibility>` check.
142148

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,13 @@ Clang-Tidy Checks
248248
:doc:`linuxkernel-must-check-errs <linuxkernel/must-check-errs>`,
249249
:doc:`llvm-header-guard <llvm/header-guard>`,
250250
:doc:`llvm-include-order <llvm/include-order>`, "Yes"
251-
:doc:`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>`, "Yes"
252251
:doc:`llvm-namespace-comment <llvm/namespace-comment>`,
253252
:doc:`llvm-prefer-isa-or-dyn-cast-in-conditionals <llvm/prefer-isa-or-dyn-cast-in-conditionals>`, "Yes"
254253
:doc:`llvm-prefer-register-over-unsigned <llvm/prefer-register-over-unsigned>`, "Yes"
255254
:doc:`llvm-prefer-static-over-anonymous-namespace <llvm/prefer-static-over-anonymous-namespace>`,
256255
:doc:`llvm-twine-local <llvm/twine-local>`, "Yes"
256+
:doc:`llvm-use-new-mlir-op-builder <llvm/use-new-mlir-op-builder>`, "Yes"
257+
:doc:`llvm-use-ranges <llvm/use-ranges>`, "Yes"
257258
:doc:`llvmlibc-callee-namespace <llvmlibc/callee-namespace>`,
258259
:doc:`llvmlibc-implementation-in-namespace <llvmlibc/implementation-in-namespace>`,
259260
:doc:`llvmlibc-inline-function-decl <llvmlibc/inline-function-decl>`, "Yes"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
.. title:: clang-tidy - llvm-use-ranges
2+
3+
llvm-use-ranges
4+
===============
5+
6+
Detects calls to standard library iterator algorithms that could be replaced
7+
with LLVM range-based algorithms from ``llvm/ADT/STLExtras.h``.
8+
9+
Example
10+
-------
11+
12+
.. code-block:: c++
13+
14+
auto it = std::find(vec.begin(), vec.end(), value);
15+
bool all = std::all_of(vec.begin(), vec.end(),
16+
[](int x) { return x > 0; });
17+
18+
Transforms to:
19+
20+
.. code-block:: c++
21+
22+
auto it = llvm::find(vec, value);
23+
bool all = llvm::all_of(vec, [](int x) { return x > 0; });
24+
25+
Supported algorithms
26+
--------------------
27+
28+
Calls to the following ``std`` library algorithms are checked:
29+
30+
``std::all_of``,
31+
``std::any_of``,
32+
``std::binary_search``,
33+
``std::copy``,
34+
``std::copy_if``,
35+
``std::count``,
36+
``std::count_if``,
37+
``std::equal``,
38+
``std::fill``,
39+
``std::find``,
40+
``std::find_if``,
41+
``std::find_if_not``,
42+
``std::for_each``,
43+
``std::is_sorted``,
44+
``std::lower_bound``,
45+
``std::max_element``,
46+
``std::min_element``,
47+
``std::mismatch``,
48+
``std::none_of``,
49+
``std::partition``,
50+
``std::remove_if``,
51+
``std::replace``,
52+
``std::sort``,
53+
``std::transform``,
54+
``std::unique``,
55+
``std::upper_bound``.
56+
57+
The check will add the necessary ``#include "llvm/ADT/STLExtras.h"`` directive
58+
when applying fixes.
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
// RUN: %check_clang_tidy %s llvm-use-ranges %t
2+
3+
// Test that the header is included
4+
// CHECK-FIXES: #include "llvm/ADT/STLExtras.h"
5+
6+
namespace std {
7+
8+
template <typename T> class vector {
9+
public:
10+
using iterator = T *;
11+
using const_iterator = const T *;
12+
13+
iterator begin();
14+
iterator end();
15+
const_iterator begin() const;
16+
const_iterator end() const;
17+
const_iterator cbegin() const;
18+
const_iterator cend() const;
19+
};
20+
21+
template <typename T> T* begin(T (&arr)[5]);
22+
template <typename T> T* end(T (&arr)[5]);
23+
24+
template <class InputIt, class T>
25+
InputIt find(InputIt first, InputIt last, const T &value);
26+
27+
template <class RandomIt>
28+
void sort(RandomIt first, RandomIt last);
29+
30+
template <class InputIt, class UnaryPredicate>
31+
bool all_of(InputIt first, InputIt last, UnaryPredicate p);
32+
33+
template <class InputIt, class UnaryFunction>
34+
UnaryFunction for_each(InputIt first, InputIt last, UnaryFunction f);
35+
36+
template <class ForwardIt, class T>
37+
ForwardIt remove(ForwardIt first, ForwardIt last, const T& value);
38+
39+
template <class ForwardIt>
40+
ForwardIt min_element(ForwardIt first, ForwardIt last);
41+
42+
template <class InputIt1, class InputIt2>
43+
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
44+
45+
template <class InputIt1, class InputIt2>
46+
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2);
47+
48+
template <class InputIt, class OutputIt>
49+
OutputIt copy(InputIt first, InputIt last, OutputIt d_first);
50+
51+
template <class ForwardIt, class T>
52+
void fill(ForwardIt first, ForwardIt last, const T& value);
53+
54+
template <class BidirIt>
55+
void reverse(BidirIt first, BidirIt last);
56+
57+
template <class ForwardIt>
58+
ForwardIt unique(ForwardIt first, ForwardIt last);
59+
60+
template <class ForwardIt>
61+
bool is_sorted(ForwardIt first, ForwardIt last);
62+
63+
} // namespace std
64+
65+
bool is_even(int x);
66+
void double_ref(int& x);
67+
68+
void test_positive() {
69+
std::vector<int> vec;
70+
int arr[5] = {1, 2, 3, 4, 5};
71+
72+
auto it1 = std::find(vec.begin(), vec.end(), 3);
73+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use a llvm range-based algorithm
74+
// CHECK-FIXES: auto it1 = llvm::find(vec, 3);
75+
76+
auto it2 = std::find(std::begin(arr), std::end(arr), 3);
77+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use a llvm range-based algorithm
78+
// CHECK-FIXES: auto it2 = llvm::find(arr, 3);
79+
80+
std::sort(vec.begin(), vec.end());
81+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a llvm range-based algorithm
82+
// CHECK-FIXES: llvm::sort(vec);
83+
84+
bool all = std::all_of(vec.begin(), vec.end(), is_even);
85+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: use a llvm range-based algorithm
86+
// CHECK-FIXES: bool all = llvm::all_of(vec, is_even);
87+
88+
std::for_each(vec.begin(), vec.end(), double_ref);
89+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a llvm range-based algorithm
90+
// CHECK-FIXES: llvm::for_each(vec, double_ref);
91+
92+
auto min_it = std::min_element(vec.begin(), vec.end());
93+
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use a llvm range-based algorithm
94+
// CHECK-FIXES: auto min_it = llvm::min_element(vec);
95+
96+
std::vector<int> vec2;
97+
bool eq = std::equal(vec.begin(), vec.end(), vec2.begin(), vec2.end());
98+
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: use a llvm range-based algorithm
99+
// CHECK-FIXES: bool eq = llvm::equal(vec, vec2);
100+
101+
std::copy(vec.begin(), vec.end(), vec2.begin());
102+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a llvm range-based algorithm
103+
// CHECK-FIXES: llvm::copy(vec, vec2.begin());
104+
105+
std::fill(vec.begin(), vec.end(), 0);
106+
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a llvm range-based algorithm
107+
// CHECK-FIXES: llvm::fill(vec, 0);
108+
109+
auto last = std::unique(vec.begin(), vec.end());
110+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: use a llvm range-based algorithm
111+
// CHECK-FIXES: auto last = llvm::unique(vec);
112+
113+
bool sorted = std::is_sorted(vec.begin(), vec.end());
114+
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: use a llvm range-based algorithm
115+
// CHECK-FIXES: bool sorted = llvm::is_sorted(vec);
116+
}
117+
118+
void test_negative() {
119+
std::vector<int> v;
120+
121+
//non-begin/end iterators
122+
auto it1 = std::find(v.begin() + 1, v.end(), 2);
123+
auto it2 = std::find(v.begin(), v.end() - 1, 2);
124+
125+
// Using different containers (3-arg equal)
126+
std::vector<int> v2;
127+
bool eq = std::equal(v.begin(), v.end(), v2.begin());
128+
}

0 commit comments

Comments
 (0)