[clang-tidy] Add checks to convert std library iterator algorithms into c++20 or boost ranges (#97764)
Added modernize-use-ranges Added boost-use-ranges
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include "../ClangTidy.h"
|
||||
#include "../ClangTidyModule.h"
|
||||
#include "../ClangTidyModuleRegistry.h"
|
||||
#include "UseRangesCheck.h"
|
||||
#include "UseToStringCheck.h"
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
@@ -18,6 +19,7 @@ namespace boost {
|
||||
class BoostModule : public ClangTidyModule {
|
||||
public:
|
||||
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
|
||||
CheckFactories.registerCheck<UseRangesCheck>("boost-use-ranges");
|
||||
CheckFactories.registerCheck<UseToStringCheck>("boost-use-to-string");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS
|
||||
|
||||
add_clang_library(clangTidyBoostModule
|
||||
BoostTidyModule.cpp
|
||||
UseRangesCheck.cpp
|
||||
UseToStringCheck.cpp
|
||||
|
||||
LINK_LIBS
|
||||
|
||||
371
clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
Normal file
371
clang-tools-extra/clang-tidy/boost/UseRangesCheck.cpp
Normal file
@@ -0,0 +1,371 @@
|
||||
//===--- UseRangesCheck.cpp - clang-tidy ----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "UseRangesCheck.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
// FixItHint - Let the docs script know that this class does provide fixits
|
||||
|
||||
namespace clang::tidy::boost {
|
||||
|
||||
namespace {
|
||||
/// Base replacer that handles the boost include path and namespace
|
||||
class BoostReplacer : public UseRangesCheck::Replacer {
|
||||
public:
|
||||
BoostReplacer(ArrayRef<UseRangesCheck::Signature> Signatures,
|
||||
bool IncludeSystem)
|
||||
: Signatures(Signatures), IncludeSystem(IncludeSystem) {}
|
||||
|
||||
ArrayRef<UseRangesCheck::Signature> getReplacementSignatures() const final {
|
||||
return Signatures;
|
||||
}
|
||||
|
||||
virtual std::pair<StringRef, StringRef>
|
||||
getBoostName(const NamedDecl &OriginalName) const = 0;
|
||||
|
||||
virtual std::pair<StringRef, StringRef>
|
||||
getBoostHeader(const NamedDecl &OriginalName) const = 0;
|
||||
|
||||
std::optional<std::string>
|
||||
getReplaceName(const NamedDecl &OriginalName) const final {
|
||||
auto [Namespace, Function] = getBoostName(OriginalName);
|
||||
return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function)
|
||||
.str();
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
getHeaderInclusion(const NamedDecl &OriginalName) const final {
|
||||
auto [Path, HeaderName] = getBoostHeader(OriginalName);
|
||||
return ((IncludeSystem ? "<boost/" : "boost/") + Path +
|
||||
(Path.empty() ? "" : "/") + HeaderName +
|
||||
(IncludeSystem ? ".hpp>" : ".hpp"))
|
||||
.str();
|
||||
}
|
||||
|
||||
private:
|
||||
SmallVector<UseRangesCheck::Signature> Signatures;
|
||||
bool IncludeSystem;
|
||||
};
|
||||
|
||||
/// Creates replaces where the header file lives in
|
||||
/// `boost/algorithm/<FUNC_NAME>.hpp` and the function is named
|
||||
/// `boost::range::<FUNC_NAME>`
|
||||
class BoostRangeAlgorithmReplacer : public BoostReplacer {
|
||||
public:
|
||||
using BoostReplacer::BoostReplacer;
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostName(const NamedDecl &OriginalName) const override {
|
||||
return {"range", OriginalName.getName()};
|
||||
}
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostHeader(const NamedDecl &OriginalName) const override {
|
||||
return {"range/algorithm", OriginalName.getName()};
|
||||
}
|
||||
};
|
||||
|
||||
/// Creates replaces where the header file lives in
|
||||
/// `boost/algorithm/<CUSTOM_HEADER>.hpp` and the function is named
|
||||
/// `boost::range::<FUNC_NAME>`
|
||||
class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer {
|
||||
public:
|
||||
CustomBoostAlgorithmHeaderReplacer(
|
||||
StringRef HeaderName, ArrayRef<UseRangesCheck::Signature> Signatures,
|
||||
bool IncludeSystem)
|
||||
: BoostRangeAlgorithmReplacer(Signatures, IncludeSystem),
|
||||
HeaderName(HeaderName) {}
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
|
||||
return {"range/algorithm", HeaderName};
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef HeaderName;
|
||||
};
|
||||
|
||||
/// Creates replaces where the header file lives in
|
||||
/// `boost/algorithm/<SUB_HEADER>.hpp` and the function is named
|
||||
/// `boost::algorithm::<FUNC_NAME>`
|
||||
class BoostAlgorithmReplacer : public BoostReplacer {
|
||||
public:
|
||||
BoostAlgorithmReplacer(StringRef SubHeader,
|
||||
ArrayRef<UseRangesCheck::Signature> Signatures,
|
||||
bool IncludeSystem)
|
||||
: BoostReplacer(Signatures, IncludeSystem),
|
||||
SubHeader(("algorithm/" + SubHeader).str()) {}
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostName(const NamedDecl &OriginalName) const override {
|
||||
return {"algorithm", OriginalName.getName()};
|
||||
}
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostHeader(const NamedDecl &OriginalName) const override {
|
||||
return {SubHeader, OriginalName.getName()};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string SubHeader;
|
||||
};
|
||||
|
||||
/// Creates replaces where the header file lives in
|
||||
/// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp` and the function is named
|
||||
/// `boost::algorithm::<FUNC_NAME>`
|
||||
class CustomBoostAlgorithmReplacer : public BoostReplacer {
|
||||
public:
|
||||
CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName,
|
||||
ArrayRef<UseRangesCheck::Signature> Signatures,
|
||||
bool IncludeSystem)
|
||||
: BoostReplacer(Signatures, IncludeSystem),
|
||||
SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {}
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostName(const NamedDecl &OriginalName) const override {
|
||||
return {"algorithm", OriginalName.getName()};
|
||||
}
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostHeader(const NamedDecl & /*OriginalName*/) const override {
|
||||
return {SubHeader, HeaderName};
|
||||
}
|
||||
|
||||
private:
|
||||
std::string SubHeader;
|
||||
StringRef HeaderName;
|
||||
};
|
||||
|
||||
/// A Replacer that is used for functions that just call a new overload
|
||||
class MakeOverloadReplacer : public UseRangesCheck::Replacer {
|
||||
public:
|
||||
explicit MakeOverloadReplacer(ArrayRef<UseRangesCheck::Signature> Signatures)
|
||||
: Signatures(Signatures) {}
|
||||
|
||||
ArrayRef<UseRangesCheck::Signature>
|
||||
getReplacementSignatures() const override {
|
||||
return Signatures;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
getReplaceName(const NamedDecl & /* OriginalName */) const override {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
getHeaderInclusion(const NamedDecl & /* OriginalName */) const override {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
SmallVector<UseRangesCheck::Signature> Signatures;
|
||||
};
|
||||
|
||||
/// A replacer that replaces functions with an equivalent named function in the
|
||||
/// root boost namespace
|
||||
class FixedBoostReplace : public BoostReplacer {
|
||||
public:
|
||||
FixedBoostReplace(StringRef Header,
|
||||
ArrayRef<UseRangesCheck::Signature> Signatures,
|
||||
bool IncludeBoostSystem)
|
||||
: BoostReplacer(Signatures, IncludeBoostSystem), Header(Header) {}
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostName(const NamedDecl &OriginalName) const override {
|
||||
return {{}, OriginalName.getName()};
|
||||
}
|
||||
|
||||
std::pair<StringRef, StringRef>
|
||||
getBoostHeader(const NamedDecl & /* OriginalName */) const override {
|
||||
return {{}, Header};
|
||||
}
|
||||
|
||||
private:
|
||||
StringRef Header;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
|
||||
|
||||
ReplacerMap Results;
|
||||
static const Signature SingleSig = {{0}};
|
||||
static const Signature TwoSig = {{0}, {2}};
|
||||
static const auto AddFrom =
|
||||
[&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
|
||||
std::initializer_list<StringRef> Names, StringRef Prefix) {
|
||||
llvm::SmallString<64> Buffer;
|
||||
for (const auto &Name : Names) {
|
||||
Buffer.assign({"::", Prefix, (Prefix.empty() ? "" : "::"), Name});
|
||||
Results.try_emplace(Buffer, Replacer);
|
||||
}
|
||||
};
|
||||
|
||||
static const auto AddFromStd =
|
||||
[](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
|
||||
std::initializer_list<StringRef> Names) {
|
||||
AddFrom(Replacer, Names, "std");
|
||||
};
|
||||
|
||||
static const auto AddFromBoost =
|
||||
[](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
|
||||
std::initializer_list<
|
||||
std::pair<StringRef, std::initializer_list<StringRef>>>
|
||||
NamespaceAndNames) {
|
||||
for (auto [Namespace, Names] : NamespaceAndNames)
|
||||
AddFrom(Replacer, Names,
|
||||
SmallString<64>{"boost", (Namespace.empty() ? "" : "::"),
|
||||
Namespace});
|
||||
};
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
|
||||
"set_algorithm", TwoSig, IncludeBoostSystem),
|
||||
{"includes", "set_union", "set_intersection", "set_difference",
|
||||
"set_symmetric_difference"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
|
||||
SingleSig, IncludeBoostSystem),
|
||||
{"unique", "lower_bound", "stable_sort",
|
||||
"equal_range", "remove_if", "sort",
|
||||
"random_shuffle", "remove_copy", "stable_partition",
|
||||
"remove_copy_if", "count", "copy_backward",
|
||||
"reverse_copy", "adjacent_find", "remove",
|
||||
"upper_bound", "binary_search", "replace_copy_if",
|
||||
"for_each", "generate", "count_if",
|
||||
"min_element", "reverse", "replace_copy",
|
||||
"fill", "unique_copy", "transform",
|
||||
"copy", "replace", "find",
|
||||
"replace_if", "find_if", "partition",
|
||||
"max_element"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>(
|
||||
TwoSig, IncludeBoostSystem),
|
||||
{"find_end", "merge", "partial_sort_copy", "find_first_of",
|
||||
"search", "lexicographical_compare", "equal", "mismatch"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
|
||||
"permutation", SingleSig, IncludeBoostSystem),
|
||||
{"next_permutation", "prev_permutation"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>(
|
||||
"heap_algorithm", SingleSig, IncludeBoostSystem),
|
||||
{"push_heap", "pop_heap", "make_heap", "sort_heap"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
|
||||
"cxx11", SingleSig, IncludeBoostSystem),
|
||||
{"copy_if", "is_permutation", "is_partitioned", "find_if_not",
|
||||
"partition_copy", "any_of", "iota", "all_of", "partition_point",
|
||||
"is_sorted", "none_of"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmReplacer>(
|
||||
"cxx11", "is_sorted", SingleSig, IncludeBoostSystem),
|
||||
{"is_sorted_until"});
|
||||
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<FixedBoostReplace>(
|
||||
"range/numeric", SingleSig, IncludeBoostSystem),
|
||||
{"accumulate", "partial_sum", "adjacent_difference"});
|
||||
|
||||
if (getLangOpts().CPlusPlus17)
|
||||
AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>(
|
||||
"cxx17", SingleSig, IncludeBoostSystem),
|
||||
{"reduce"});
|
||||
|
||||
AddFromBoost(llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(SingleSig),
|
||||
{{"algorithm",
|
||||
{"reduce",
|
||||
"find_backward",
|
||||
"find_not_backward",
|
||||
"find_if_backward",
|
||||
"find_if_not_backward",
|
||||
"hex",
|
||||
"hex_lower",
|
||||
"unhex",
|
||||
"is_partitioned_until",
|
||||
"is_palindrome",
|
||||
"copy_if",
|
||||
"copy_while",
|
||||
"copy_until",
|
||||
"copy_if_while",
|
||||
"copy_if_until",
|
||||
"is_permutation",
|
||||
"is_partitioned",
|
||||
"one_of",
|
||||
"one_of_equal",
|
||||
"find_if_not",
|
||||
"partition_copy",
|
||||
"any_of",
|
||||
"any_of_equal",
|
||||
"iota",
|
||||
"all_of",
|
||||
"all_of_equal",
|
||||
"partition_point",
|
||||
"is_sorted_until",
|
||||
"is_sorted",
|
||||
"is_increasing",
|
||||
"is_decreasing",
|
||||
"is_strictly_increasing",
|
||||
"is_strictly_decreasing",
|
||||
"none_of",
|
||||
"none_of_equal",
|
||||
"clamp_range"}}});
|
||||
|
||||
AddFromBoost(
|
||||
llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(TwoSig),
|
||||
{{"algorithm", {"apply_permutation", "apply_reverse_permutation"}}});
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: utils::UseRangesCheck(Name, Context),
|
||||
IncludeBoostSystem(Options.get("IncludeBoostSystem", true)) {}
|
||||
|
||||
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||
utils::UseRangesCheck::storeOptions(Opts);
|
||||
Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
|
||||
}
|
||||
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
|
||||
DiagnosticBuilder D =
|
||||
diag(Call.getBeginLoc(), "use a %0 version of this algorithm");
|
||||
D << (Call.getDirectCallee()->isInStdNamespace() ? "boost" : "ranged");
|
||||
return D;
|
||||
}
|
||||
ArrayRef<std::pair<StringRef, StringRef>>
|
||||
UseRangesCheck::getFreeBeginEndMethods() const {
|
||||
static const std::pair<StringRef, StringRef> Refs[] = {
|
||||
{"::std::begin", "::std::end"},
|
||||
{"::std::cbegin", "::std::cend"},
|
||||
{"::boost::range_adl_barrier::begin", "::boost::range_adl_barrier::end"},
|
||||
{"::boost::range_adl_barrier::const_begin",
|
||||
"::boost::range_adl_barrier::const_end"},
|
||||
};
|
||||
return Refs;
|
||||
}
|
||||
std::optional<UseRangesCheck::ReverseIteratorDescriptor>
|
||||
UseRangesCheck::getReverseDescriptor() const {
|
||||
static const std::pair<StringRef, StringRef> Refs[] = {
|
||||
{"::std::rbegin", "::std::rend"},
|
||||
{"::std::crbegin", "::std::crend"},
|
||||
{"::boost::rbegin", "::boost::rend"},
|
||||
{"::boost::const_rbegin", "::boost::const_rend"},
|
||||
};
|
||||
return ReverseIteratorDescriptor{"boost::adaptors::reverse",
|
||||
IncludeBoostSystem
|
||||
? "<boost/range/adaptor/reversed.hpp>"
|
||||
: "boost/range/adaptor/reversed.hpp",
|
||||
Refs};
|
||||
}
|
||||
} // namespace clang::tidy::boost
|
||||
43
clang-tools-extra/clang-tidy/boost/UseRangesCheck.h
Normal file
43
clang-tools-extra/clang-tidy/boost/UseRangesCheck.h
Normal file
@@ -0,0 +1,43 @@
|
||||
//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
|
||||
|
||||
#include "../utils/UseRangesCheck.h"
|
||||
|
||||
namespace clang::tidy::boost {
|
||||
|
||||
/// Detects calls to standard library iterator algorithms that could be
|
||||
/// replaced with a boost ranges version instead
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/boost/use-ranges.html
|
||||
class UseRangesCheck : public utils::UseRangesCheck {
|
||||
public:
|
||||
UseRangesCheck(StringRef Name, ClangTidyContext *Context);
|
||||
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
|
||||
|
||||
ReplacerMap getReplacerMap() const override;
|
||||
|
||||
DiagnosticBuilder createDiag(const CallExpr &Call) override;
|
||||
|
||||
ArrayRef<std::pair<StringRef, StringRef>>
|
||||
getFreeBeginEndMethods() const override;
|
||||
|
||||
std::optional<ReverseIteratorDescriptor>
|
||||
getReverseDescriptor() const override;
|
||||
|
||||
private:
|
||||
bool IncludeBoostSystem;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::boost
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BOOST_USERANGESCHECK_H
|
||||
@@ -40,6 +40,7 @@ add_clang_library(clangTidyModernizeModule
|
||||
UseNoexceptCheck.cpp
|
||||
UseNullptrCheck.cpp
|
||||
UseOverrideCheck.cpp
|
||||
UseRangesCheck.cpp
|
||||
UseStartsEndsWithCheck.cpp
|
||||
UseStdFormatCheck.cpp
|
||||
UseStdNumbersCheck.cpp
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "UseNoexceptCheck.h"
|
||||
#include "UseNullptrCheck.h"
|
||||
#include "UseOverrideCheck.h"
|
||||
#include "UseRangesCheck.h"
|
||||
#include "UseStartsEndsWithCheck.h"
|
||||
#include "UseStdFormatCheck.h"
|
||||
#include "UseStdNumbersCheck.h"
|
||||
@@ -75,6 +76,7 @@ public:
|
||||
CheckFactories.registerCheck<PassByValueCheck>("modernize-pass-by-value");
|
||||
CheckFactories.registerCheck<UseDesignatedInitializersCheck>(
|
||||
"modernize-use-designated-initializers");
|
||||
CheckFactories.registerCheck<UseRangesCheck>("modernize-use-ranges");
|
||||
CheckFactories.registerCheck<UseStartsEndsWithCheck>(
|
||||
"modernize-use-starts-ends-with");
|
||||
CheckFactories.registerCheck<UseStdFormatCheck>("modernize-use-std-format");
|
||||
|
||||
185
clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
Normal file
185
clang-tools-extra/clang-tidy/modernize/UseRangesCheck.cpp
Normal file
@@ -0,0 +1,185 @@
|
||||
//===--- UseRangesCheck.cpp - clang-tidy ----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "UseRangesCheck.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <initializer_list>
|
||||
|
||||
// FixItHint - Let the docs script know that this class does provide fixits
|
||||
|
||||
namespace clang::tidy::modernize {
|
||||
|
||||
static constexpr const char *SingleRangeNames[] = {
|
||||
"all_of",
|
||||
"any_of",
|
||||
"none_of",
|
||||
"for_each",
|
||||
"find",
|
||||
"find_if",
|
||||
"find_if_not",
|
||||
"adjacent_find",
|
||||
"copy",
|
||||
"copy_if",
|
||||
"copy_backward",
|
||||
"move",
|
||||
"move_backward",
|
||||
"fill",
|
||||
"transform",
|
||||
"replace",
|
||||
"replace_if",
|
||||
"generate",
|
||||
"remove",
|
||||
"remove_if",
|
||||
"remove_copy",
|
||||
"remove_copy_if",
|
||||
"unique",
|
||||
"unique_copy",
|
||||
"sample",
|
||||
"partition_point",
|
||||
"lower_bound",
|
||||
"upper_bound",
|
||||
"equal_range",
|
||||
"binary_search",
|
||||
"push_heap",
|
||||
"pop_heap",
|
||||
"make_heap",
|
||||
"sort_heap",
|
||||
"next_permutation",
|
||||
"prev_permutation",
|
||||
"reverse",
|
||||
"reverse_copy",
|
||||
"shift_left",
|
||||
"shift_right",
|
||||
"is_partitioned",
|
||||
"partition",
|
||||
"partition_copy",
|
||||
"stable_partition",
|
||||
"sort",
|
||||
"stable_sort",
|
||||
"is_sorted",
|
||||
"is_sorted_until",
|
||||
"is_heap",
|
||||
"is_heap_until",
|
||||
"max_element",
|
||||
"min_element",
|
||||
"minmax_element",
|
||||
"uninitialized_copy",
|
||||
"uninitialized_fill",
|
||||
"uninitialized_move",
|
||||
"uninitialized_default_construct",
|
||||
"uninitialized_value_construct",
|
||||
"destroy",
|
||||
};
|
||||
|
||||
static constexpr const char *TwoRangeNames[] = {
|
||||
"equal",
|
||||
"mismatch",
|
||||
"partial_sort_copy",
|
||||
"includes",
|
||||
"set_union",
|
||||
"set_intersection",
|
||||
"set_difference",
|
||||
"set_symmetric_difference",
|
||||
"merge",
|
||||
"lexicographical_compare",
|
||||
"find_end",
|
||||
"search",
|
||||
"is_permutation",
|
||||
};
|
||||
|
||||
namespace {
|
||||
class StdReplacer : public utils::UseRangesCheck::Replacer {
|
||||
public:
|
||||
explicit StdReplacer(SmallVector<UseRangesCheck::Signature> Signatures)
|
||||
: Signatures(std::move(Signatures)) {}
|
||||
std::optional<std::string>
|
||||
getReplaceName(const NamedDecl &OriginalName) const override {
|
||||
return ("std::ranges::" + OriginalName.getName()).str();
|
||||
}
|
||||
ArrayRef<UseRangesCheck::Signature>
|
||||
getReplacementSignatures() const override {
|
||||
return Signatures;
|
||||
}
|
||||
|
||||
private:
|
||||
SmallVector<UseRangesCheck::Signature> Signatures;
|
||||
};
|
||||
|
||||
class StdAlgorithmReplacer : public StdReplacer {
|
||||
using StdReplacer::StdReplacer;
|
||||
std::optional<std::string>
|
||||
getHeaderInclusion(const NamedDecl & /*OriginalName*/) const override {
|
||||
return "<algorithm>";
|
||||
}
|
||||
};
|
||||
|
||||
class StdNumericReplacer : public StdReplacer {
|
||||
using StdReplacer::StdReplacer;
|
||||
std::optional<std::string>
|
||||
getHeaderInclusion(const NamedDecl & /*OriginalName*/) const override {
|
||||
return "<numeric>";
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const {
|
||||
|
||||
utils::UseRangesCheck::ReplacerMap Result;
|
||||
|
||||
// template<typename Iter> Func(Iter first, Iter last,...).
|
||||
static const Signature SingleRangeArgs = {{0}};
|
||||
// template<typename Iter1, typename Iter2>
|
||||
// Func(Iter1 first1, Iter1 last1, Iter2 first2, Iter2 last2,...).
|
||||
static const Signature TwoRangeArgs = {{0}, {2}};
|
||||
|
||||
static const Signature SingleRangeFunc[] = {SingleRangeArgs};
|
||||
|
||||
static const Signature TwoRangeFunc[] = {TwoRangeArgs};
|
||||
|
||||
static const std::pair<ArrayRef<Signature>, ArrayRef<const char *>>
|
||||
AlgorithmNames[] = {{SingleRangeFunc, SingleRangeNames},
|
||||
{TwoRangeFunc, TwoRangeNames}};
|
||||
SmallString<64> Buff;
|
||||
for (const auto &[Signatures, Values] : AlgorithmNames) {
|
||||
auto Replacer = llvm::makeIntrusiveRefCnt<StdAlgorithmReplacer>(
|
||||
SmallVector<UseRangesCheck::Signature>{Signatures});
|
||||
for (const auto &Name : Values) {
|
||||
Buff.assign({"::std::", Name});
|
||||
Result.try_emplace(Buff, Replacer);
|
||||
}
|
||||
}
|
||||
if (getLangOpts().CPlusPlus23)
|
||||
Result.try_emplace(
|
||||
"::std::iota",
|
||||
llvm::makeIntrusiveRefCnt<StdNumericReplacer>(
|
||||
SmallVector<UseRangesCheck::Signature>{std::begin(SingleRangeFunc),
|
||||
std::end(SingleRangeFunc)}));
|
||||
return Result;
|
||||
}
|
||||
|
||||
bool UseRangesCheck::isLanguageVersionSupported(
|
||||
const LangOptions &LangOpts) const {
|
||||
return LangOpts.CPlusPlus20;
|
||||
}
|
||||
ArrayRef<std::pair<StringRef, StringRef>>
|
||||
UseRangesCheck::getFreeBeginEndMethods() const {
|
||||
static const std::pair<StringRef, StringRef> Refs[] = {
|
||||
{"::std::begin", "::std::end"}, {"::std::cbegin", "::std::cend"}};
|
||||
return Refs;
|
||||
}
|
||||
std::optional<UseRangesCheck::ReverseIteratorDescriptor>
|
||||
UseRangesCheck::getReverseDescriptor() const {
|
||||
static const std::pair<StringRef, StringRef> Refs[] = {
|
||||
{"::std::rbegin", "::std::rend"}, {"::std::crbegin", "::std::crend"}};
|
||||
return ReverseIteratorDescriptor{"std::views::reverse", "<ranges>", Refs};
|
||||
}
|
||||
} // namespace clang::tidy::modernize
|
||||
38
clang-tools-extra/clang-tidy/modernize/UseRangesCheck.h
Normal file
38
clang-tools-extra/clang-tidy/modernize/UseRangesCheck.h
Normal file
@@ -0,0 +1,38 @@
|
||||
//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USERANGESCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USERANGESCHECK_H
|
||||
|
||||
#include "../utils/UseRangesCheck.h"
|
||||
|
||||
namespace clang::tidy::modernize {
|
||||
|
||||
/// Detects calls to standard library iterator algorithms that could be
|
||||
/// replaced with a ranges version instead
|
||||
///
|
||||
/// For the user-facing documentation see:
|
||||
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-ranges.html
|
||||
class UseRangesCheck : public utils::UseRangesCheck {
|
||||
public:
|
||||
using utils::UseRangesCheck::UseRangesCheck;
|
||||
|
||||
ReplacerMap getReplacerMap() const override;
|
||||
|
||||
ArrayRef<std::pair<StringRef, StringRef>>
|
||||
getFreeBeginEndMethods() const override;
|
||||
|
||||
std::optional<ReverseIteratorDescriptor>
|
||||
getReverseDescriptor() const override;
|
||||
|
||||
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::modernize
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USERANGESCHECK_H
|
||||
@@ -25,6 +25,7 @@ add_clang_library(clangTidyUtils
|
||||
RenamerClangTidyCheck.cpp
|
||||
TransformerClangTidyCheck.cpp
|
||||
TypeTraits.cpp
|
||||
UseRangesCheck.cpp
|
||||
UsingInserter.cpp
|
||||
|
||||
LINK_LIBS
|
||||
|
||||
306
clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp
Normal file
306
clang-tools-extra/clang-tidy/utils/UseRangesCheck.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
//===--- UseRangesCheck.cpp - clang-tidy ----------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "UseRangesCheck.h"
|
||||
#include "Matchers.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/ASTMatchers/ASTMatchFinder.h"
|
||||
#include "clang/ASTMatchers/ASTMatchers.h"
|
||||
#include "clang/ASTMatchers/ASTMatchersInternal.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/SourceManager.h"
|
||||
#include "clang/Lex/Lexer.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/ADT/SmallBitVector.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include "llvm/ADT/Twine.h"
|
||||
#include "llvm/Support/raw_ostream.h"
|
||||
#include <cassert>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
using namespace clang::ast_matchers;
|
||||
|
||||
static constexpr const char BoundCall[] = "CallExpr";
|
||||
static constexpr const char FuncDecl[] = "FuncDecl";
|
||||
static constexpr const char ArgName[] = "ArgName";
|
||||
|
||||
namespace clang::tidy::utils {
|
||||
|
||||
static bool operator==(const UseRangesCheck::Indexes &L,
|
||||
const UseRangesCheck::Indexes &R) {
|
||||
return std::tie(L.BeginArg, L.EndArg, L.ReplaceArg) ==
|
||||
std::tie(R.BeginArg, R.EndArg, R.ReplaceArg);
|
||||
}
|
||||
|
||||
static std::string getFullPrefix(ArrayRef<UseRangesCheck::Indexes> Signature) {
|
||||
std::string Output;
|
||||
llvm::raw_string_ostream OS(Output);
|
||||
for (const UseRangesCheck::Indexes &Item : Signature)
|
||||
OS << Item.BeginArg << ":" << Item.EndArg << ":"
|
||||
<< (Item.ReplaceArg == Item.First ? '0' : '1');
|
||||
return Output;
|
||||
}
|
||||
|
||||
static llvm::hash_code hash_value(const UseRangesCheck::Indexes &Indexes) {
|
||||
return llvm::hash_combine(Indexes.BeginArg, Indexes.EndArg,
|
||||
Indexes.ReplaceArg);
|
||||
}
|
||||
|
||||
static llvm::hash_code hash_value(const UseRangesCheck::Signature &Sig) {
|
||||
return llvm::hash_combine_range(Sig.begin(), Sig.end());
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
AST_MATCHER(Expr, hasSideEffects) {
|
||||
return Node.HasSideEffects(Finder->getASTContext());
|
||||
}
|
||||
} // namespace
|
||||
|
||||
static auto
|
||||
makeExprMatcher(ast_matchers::internal::Matcher<Expr> ArgumentMatcher,
|
||||
ArrayRef<StringRef> MethodNames,
|
||||
ArrayRef<StringRef> FreeNames) {
|
||||
return expr(
|
||||
anyOf(cxxMemberCallExpr(argumentCountIs(0),
|
||||
callee(cxxMethodDecl(hasAnyName(MethodNames))),
|
||||
on(ArgumentMatcher)),
|
||||
callExpr(argumentCountIs(1), hasArgument(0, ArgumentMatcher),
|
||||
hasDeclaration(functionDecl(hasAnyName(FreeNames))))));
|
||||
}
|
||||
|
||||
static ast_matchers::internal::Matcher<CallExpr>
|
||||
makeMatcherPair(StringRef State, const UseRangesCheck::Indexes &Indexes,
|
||||
ArrayRef<StringRef> BeginFreeNames,
|
||||
ArrayRef<StringRef> EndFreeNames,
|
||||
const std::optional<UseRangesCheck::ReverseIteratorDescriptor>
|
||||
&ReverseDescriptor) {
|
||||
std::string ArgBound = (ArgName + llvm::Twine(Indexes.BeginArg)).str();
|
||||
SmallString<64> ID = {BoundCall, State};
|
||||
ast_matchers::internal::Matcher<CallExpr> ArgumentMatcher = allOf(
|
||||
hasArgument(Indexes.BeginArg,
|
||||
makeExprMatcher(expr(unless(hasSideEffects())).bind(ArgBound),
|
||||
{"begin", "cbegin"}, BeginFreeNames)),
|
||||
hasArgument(Indexes.EndArg,
|
||||
makeExprMatcher(
|
||||
expr(matchers::isStatementIdenticalToBoundNode(ArgBound)),
|
||||
{"end", "cend"}, EndFreeNames)));
|
||||
if (ReverseDescriptor) {
|
||||
ArgBound.push_back('R');
|
||||
SmallVector<StringRef> RBegin{
|
||||
llvm::make_first_range(ReverseDescriptor->FreeReverseNames)};
|
||||
SmallVector<StringRef> REnd{
|
||||
llvm::make_second_range(ReverseDescriptor->FreeReverseNames)};
|
||||
ArgumentMatcher = anyOf(
|
||||
ArgumentMatcher,
|
||||
allOf(hasArgument(
|
||||
Indexes.BeginArg,
|
||||
makeExprMatcher(expr(unless(hasSideEffects())).bind(ArgBound),
|
||||
{"rbegin", "crbegin"}, RBegin)),
|
||||
hasArgument(
|
||||
Indexes.EndArg,
|
||||
makeExprMatcher(
|
||||
expr(matchers::isStatementIdenticalToBoundNode(ArgBound)),
|
||||
{"rend", "crend"}, REnd))));
|
||||
}
|
||||
return callExpr(argumentCountAtLeast(
|
||||
std::max(Indexes.BeginArg, Indexes.EndArg) + 1),
|
||||
ArgumentMatcher)
|
||||
.bind(ID);
|
||||
}
|
||||
|
||||
void UseRangesCheck::registerMatchers(MatchFinder *Finder) {
|
||||
Replaces = getReplacerMap();
|
||||
ReverseDescriptor = getReverseDescriptor();
|
||||
auto BeginEndNames = getFreeBeginEndMethods();
|
||||
llvm::SmallVector<StringRef, 4> BeginNames{
|
||||
llvm::make_first_range(BeginEndNames)};
|
||||
llvm::SmallVector<StringRef, 4> EndNames{
|
||||
llvm::make_second_range(BeginEndNames)};
|
||||
llvm::DenseSet<ArrayRef<Signature>> Seen;
|
||||
for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
|
||||
const ArrayRef<Signature> &Signatures =
|
||||
I->getValue()->getReplacementSignatures();
|
||||
if (!Seen.insert(Signatures).second)
|
||||
continue;
|
||||
assert(!Signatures.empty() &&
|
||||
llvm::all_of(Signatures, [](auto Index) { return !Index.empty(); }));
|
||||
std::vector<StringRef> Names(1, I->getKey());
|
||||
for (auto J = std::next(I); J != E; ++J)
|
||||
if (J->getValue()->getReplacementSignatures() == Signatures)
|
||||
Names.push_back(J->getKey());
|
||||
|
||||
std::vector<ast_matchers::internal::DynTypedMatcher> TotalMatchers;
|
||||
// As we match on the first matched signature, we need to sort the
|
||||
// signatures in order of length(longest to shortest). This way any
|
||||
// signature that is a subset of another signature will be matched after the
|
||||
// other.
|
||||
SmallVector<Signature> SigVec(Signatures);
|
||||
llvm::sort(SigVec, [](auto &L, auto &R) { return R.size() < L.size(); });
|
||||
for (const auto &Signature : SigVec) {
|
||||
std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
|
||||
for (const auto &ArgPair : Signature)
|
||||
Matchers.push_back(makeMatcherPair(getFullPrefix(Signature), ArgPair,
|
||||
BeginNames, EndNames,
|
||||
ReverseDescriptor));
|
||||
TotalMatchers.push_back(
|
||||
ast_matchers::internal::DynTypedMatcher::constructVariadic(
|
||||
ast_matchers::internal::DynTypedMatcher::VO_AllOf,
|
||||
ASTNodeKind::getFromNodeKind<CallExpr>(), std::move(Matchers)));
|
||||
}
|
||||
Finder->addMatcher(
|
||||
callExpr(
|
||||
callee(functionDecl(hasAnyName(std::move(Names))).bind(FuncDecl)),
|
||||
ast_matchers::internal::DynTypedMatcher::constructVariadic(
|
||||
ast_matchers::internal::DynTypedMatcher::VO_AnyOf,
|
||||
ASTNodeKind::getFromNodeKind<CallExpr>(),
|
||||
std::move(TotalMatchers))
|
||||
.convertTo<CallExpr>()),
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
static void removeFunctionArgs(DiagnosticBuilder &Diag, const CallExpr &Call,
|
||||
ArrayRef<unsigned> Indexes,
|
||||
const ASTContext &Ctx) {
|
||||
llvm::SmallVector<unsigned> Sorted(Indexes);
|
||||
llvm::sort(Sorted);
|
||||
// Keep track of commas removed
|
||||
llvm::SmallBitVector Commas(Call.getNumArgs());
|
||||
// The first comma is actually the '(' which we can't remove
|
||||
Commas[0] = true;
|
||||
for (unsigned Index : Sorted) {
|
||||
const Expr *Arg = Call.getArg(Index);
|
||||
if (Commas[Index]) {
|
||||
if (Index >= Commas.size()) {
|
||||
Diag << FixItHint::CreateRemoval(Arg->getSourceRange());
|
||||
} else {
|
||||
// Remove the next comma
|
||||
Commas[Index + 1] = true;
|
||||
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
|
||||
{Arg->getBeginLoc(),
|
||||
Lexer::getLocForEndOfToken(
|
||||
Arg->getEndLoc(), 0, Ctx.getSourceManager(), Ctx.getLangOpts())
|
||||
.getLocWithOffset(1)}));
|
||||
}
|
||||
} else {
|
||||
Diag << FixItHint::CreateRemoval(CharSourceRange::getTokenRange(
|
||||
Arg->getBeginLoc().getLocWithOffset(-1), Arg->getEndLoc()));
|
||||
Commas[Index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void UseRangesCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
const auto *Function = Result.Nodes.getNodeAs<FunctionDecl>(FuncDecl);
|
||||
std::string Qualified = "::" + Function->getQualifiedNameAsString();
|
||||
auto Iter = Replaces.find(Qualified);
|
||||
assert(Iter != Replaces.end());
|
||||
SmallString<64> Buffer;
|
||||
for (const Signature &Sig : Iter->getValue()->getReplacementSignatures()) {
|
||||
Buffer.assign({BoundCall, getFullPrefix(Sig)});
|
||||
const auto *Call = Result.Nodes.getNodeAs<CallExpr>(Buffer);
|
||||
if (!Call)
|
||||
continue;
|
||||
auto Diag = createDiag(*Call);
|
||||
if (auto ReplaceName = Iter->getValue()->getReplaceName(*Function))
|
||||
Diag << FixItHint::CreateReplacement(Call->getCallee()->getSourceRange(),
|
||||
*ReplaceName);
|
||||
if (auto Include = Iter->getValue()->getHeaderInclusion(*Function))
|
||||
Diag << Inserter.createIncludeInsertion(
|
||||
Result.SourceManager->getFileID(Call->getBeginLoc()), *Include);
|
||||
llvm::SmallVector<unsigned, 3> ToRemove;
|
||||
for (const auto &[First, Second, Replace] : Sig) {
|
||||
auto ArgNode = ArgName + std::to_string(First);
|
||||
if (const auto *ArgExpr = Result.Nodes.getNodeAs<Expr>(ArgNode)) {
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
Call->getArg(Replace == Indexes::Second ? Second : First)
|
||||
->getSourceRange(),
|
||||
Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
|
||||
Result.Context->getSourceManager(),
|
||||
Result.Context->getLangOpts()));
|
||||
} else {
|
||||
assert(ReverseDescriptor && "Couldn't find forward argument");
|
||||
ArgNode.push_back('R');
|
||||
ArgExpr = Result.Nodes.getNodeAs<Expr>(ArgNode);
|
||||
assert(ArgExpr && "Couldn't find forward or reverse argument");
|
||||
if (ReverseDescriptor->ReverseHeader)
|
||||
Diag << Inserter.createIncludeInsertion(
|
||||
Result.SourceManager->getFileID(Call->getBeginLoc()),
|
||||
*ReverseDescriptor->ReverseHeader);
|
||||
Diag << FixItHint::CreateReplacement(
|
||||
Call->getArg(Replace == Indexes::Second ? Second : First)
|
||||
->getSourceRange(),
|
||||
SmallString<128>{
|
||||
ReverseDescriptor->ReverseAdaptorName, "(",
|
||||
Lexer::getSourceText(
|
||||
CharSourceRange::getTokenRange(ArgExpr->getSourceRange()),
|
||||
Result.Context->getSourceManager(),
|
||||
Result.Context->getLangOpts()),
|
||||
")"});
|
||||
}
|
||||
ToRemove.push_back(Replace == Indexes::Second ? First : Second);
|
||||
}
|
||||
removeFunctionArgs(Diag, *Call, ToRemove, *Result.Context);
|
||||
return;
|
||||
}
|
||||
llvm_unreachable("No valid signature found");
|
||||
}
|
||||
|
||||
bool UseRangesCheck::isLanguageVersionSupported(
|
||||
const LangOptions &LangOpts) const {
|
||||
return LangOpts.CPlusPlus11;
|
||||
}
|
||||
|
||||
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
|
||||
: ClangTidyCheck(Name, Context),
|
||||
Inserter(Options.getLocalOrGlobal("IncludeStyle",
|
||||
utils::IncludeSorter::IS_LLVM),
|
||||
areDiagsSelfContained()) {}
|
||||
|
||||
void UseRangesCheck::registerPPCallbacks(const SourceManager &,
|
||||
Preprocessor *PP, Preprocessor *) {
|
||||
Inserter.registerPreprocessor(PP);
|
||||
}
|
||||
|
||||
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "IncludeStyle", Inserter.getStyle());
|
||||
}
|
||||
|
||||
std::optional<std::string>
|
||||
UseRangesCheck::Replacer::getHeaderInclusion(const NamedDecl &) const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) {
|
||||
return diag(Call.getBeginLoc(), "use a ranges version of this algorithm");
|
||||
}
|
||||
|
||||
std::optional<UseRangesCheck::ReverseIteratorDescriptor>
|
||||
UseRangesCheck::getReverseDescriptor() const {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
ArrayRef<std::pair<StringRef, StringRef>>
|
||||
UseRangesCheck::getFreeBeginEndMethods() const {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<TraversalKind> UseRangesCheck::getCheckTraversalKind() const {
|
||||
return TK_IgnoreUnlessSpelledInSource;
|
||||
}
|
||||
} // namespace clang::tidy::utils
|
||||
94
clang-tools-extra/clang-tidy/utils/UseRangesCheck.h
Normal file
94
clang-tools-extra/clang-tidy/utils/UseRangesCheck.h
Normal file
@@ -0,0 +1,94 @@
|
||||
//===--- UseRangesCheck.h - clang-tidy --------------------------*- C++ -*-===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_USERANGESCHECK_H
|
||||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_USERANGESCHECK_H
|
||||
|
||||
#include "../ClangTidyCheck.h"
|
||||
#include "IncludeInserter.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/Basic/Diagnostic.h"
|
||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||
#include "llvm/ADT/StringMap.h"
|
||||
#include "llvm/ADT/StringRef.h"
|
||||
#include <optional>
|
||||
|
||||
namespace clang::tidy::utils {
|
||||
|
||||
/// Base class for handling converting std iterator algorithms to a range
|
||||
/// equivalent.
|
||||
class UseRangesCheck : public ClangTidyCheck {
|
||||
public:
|
||||
struct Indexes {
|
||||
enum Replace { First, Second };
|
||||
unsigned BeginArg;
|
||||
unsigned EndArg = BeginArg + 1;
|
||||
Replace ReplaceArg = First;
|
||||
};
|
||||
|
||||
using Signature = SmallVector<Indexes, 2>;
|
||||
|
||||
struct ReverseIteratorDescriptor {
|
||||
StringRef ReverseAdaptorName;
|
||||
std::optional<StringRef> ReverseHeader;
|
||||
ArrayRef<std::pair<StringRef, StringRef>> FreeReverseNames;
|
||||
};
|
||||
|
||||
class Replacer : public llvm::RefCountedBase<Replacer> {
|
||||
public:
|
||||
/// Gets the name to replace a function with, return std::nullopt for a
|
||||
/// replacement where we just call a different overload.
|
||||
virtual std::optional<std::string>
|
||||
getReplaceName(const NamedDecl &OriginalName) const = 0;
|
||||
|
||||
/// Gets the header needed to access the replaced function
|
||||
/// Return std::nullopt if no new header is needed.
|
||||
virtual std::optional<std::string>
|
||||
getHeaderInclusion(const NamedDecl &OriginalName) const;
|
||||
|
||||
/// Gets an array of all the possible overloads for a function with indexes
|
||||
/// where begin and end arguments are.
|
||||
virtual ArrayRef<Signature> getReplacementSignatures() const = 0;
|
||||
virtual ~Replacer() = default;
|
||||
};
|
||||
|
||||
using ReplacerMap = llvm::StringMap<llvm::IntrusiveRefCntPtr<Replacer>>;
|
||||
|
||||
UseRangesCheck(StringRef Name, ClangTidyContext *Context);
|
||||
/// Gets a map of function to replace and methods to create the replacements
|
||||
virtual ReplacerMap getReplacerMap() const = 0;
|
||||
/// Create a diagnostic for the CallExpr
|
||||
/// Override this to support custom diagnostic messages
|
||||
virtual DiagnosticBuilder createDiag(const CallExpr &Call);
|
||||
|
||||
virtual std::optional<ReverseIteratorDescriptor> getReverseDescriptor() const;
|
||||
|
||||
/// Gets the fully qualified names of begin and end functions.
|
||||
/// The functions must take the container as their one and only argument
|
||||
/// `::std::begin` and `::std::end` are a common example
|
||||
virtual ArrayRef<std::pair<StringRef, StringRef>>
|
||||
getFreeBeginEndMethods() const;
|
||||
|
||||
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP,
|
||||
Preprocessor *ModuleExpanderPP) final;
|
||||
void registerMatchers(ast_matchers::MatchFinder *Finder) final;
|
||||
void check(const ast_matchers::MatchFinder::MatchResult &Result) final;
|
||||
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
|
||||
void storeOptions(ClangTidyOptions::OptionMap &Options) override;
|
||||
std::optional<TraversalKind> getCheckTraversalKind() const override;
|
||||
|
||||
private:
|
||||
ReplacerMap Replaces;
|
||||
std::optional<ReverseIteratorDescriptor> ReverseDescriptor;
|
||||
IncludeInserter Inserter;
|
||||
};
|
||||
|
||||
} // namespace clang::tidy::utils
|
||||
|
||||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_USERANGESCHECK_H
|
||||
@@ -131,6 +131,12 @@ Improvements to clang-tidy
|
||||
New checks
|
||||
^^^^^^^^^^
|
||||
|
||||
- New :doc:`boost-use-ranges
|
||||
<clang-tidy/checks/boost/use-ranges>` check.
|
||||
|
||||
Detects calls to standard library iterator algorithms that could be replaced
|
||||
with a Boost ranges version instead.
|
||||
|
||||
- New :doc:`bugprone-crtp-constructor-accessibility
|
||||
<clang-tidy/checks/bugprone/crtp-constructor-accessibility>` check.
|
||||
|
||||
@@ -174,6 +180,12 @@ New checks
|
||||
Finds initializer lists for aggregate types that could be
|
||||
written as designated initializers instead.
|
||||
|
||||
- New :doc:`modernize-use-ranges
|
||||
<clang-tidy/checks/modernize/use-ranges>` check.
|
||||
|
||||
Detects calls to standard library iterator algorithms that could be replaced
|
||||
with a ranges version instead.
|
||||
|
||||
- New :doc:`modernize-use-std-format
|
||||
<clang-tidy/checks/modernize/use-std-format>` check.
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
.. title:: clang-tidy - boost-use-ranges
|
||||
|
||||
boost-use-ranges
|
||||
================
|
||||
|
||||
Detects calls to standard library iterator algorithms that could be replaced
|
||||
with a Boost ranges version instead.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
|
||||
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(), std::begin(Items2),
|
||||
std::end(Items2));
|
||||
|
||||
|
||||
transforms to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto Iter1 = boost::range::find(Items, 0);
|
||||
auto AreSame = boost::range::equal(Items1, Items2);
|
||||
|
||||
Calls to the following std library algorithms are checked:
|
||||
``includes``,``set_union``,``set_intersection``,``set_difference``,
|
||||
``set_symmetric_difference``,``unique``,``lower_bound``,``stable_sort``,
|
||||
``equal_range``,``remove_if``,``sort``,``random_shuffle``,``remove_copy``,
|
||||
``stable_partition``,``remove_copy_if``,``count``,``copy_backward``,
|
||||
``reverse_copy``,``adjacent_find``,``remove``,``upper_bound``,``binary_search``,
|
||||
``replace_copy_if``,``for_each``,``generate``,``count_if``,``min_element``,
|
||||
``reverse``,``replace_copy``,``fill``,``unique_copy``,``transform``,``copy``,
|
||||
``replace``,``find``,``replace_if``,``find_if``,``partition``,``max_element``,
|
||||
``find_end``,``merge``,``partial_sort_copy``,``find_first_of``,``search``,
|
||||
``lexicographical_compare``,``equal``,``mismatch``,``next_permutation``,
|
||||
``prev_permutation``,``push_heap``,``pop_heap``,``make_heap``,``sort_heap``,
|
||||
``copy_if``,``is_permutation``,``is_partitioned``,``find_if_not``,
|
||||
``partition_copy``,``any_of``,``iota``,``all_of``,``partition_point``,
|
||||
``is_sorted``,``none_of``,``is_sorted_until``,``reduce``,``accumulate``,
|
||||
``parital_sum``,``adjacent_difference``.
|
||||
|
||||
The check will also look for the following functions from the
|
||||
``boost::algorithm`` namespace:
|
||||
``reduce``,``find_backward``,``find_not_backward``,``find_if_backward``,
|
||||
``find_if_not_backward``,``hex``,``hex_lower``,``unhex``,
|
||||
``is_partitioned_until``,``is_palindrome``,``copy_if``,``copy_while``,
|
||||
``copy_until``,``copy_if_while``,``copy_if_until``,``is_permutation``,
|
||||
``is_partitioned``,``one_of``,``one_of_equal``,``find_if_not``,
|
||||
``partition_copy``,``any_of``,``any_of_equal``,``iota``,``all_of``,
|
||||
``all_of_equal``,``partition_point``,``is_sorted_until``,``is_sorted``,
|
||||
``is_increasing``,``is_decreasing``,``is_strictly_increasing``,
|
||||
``is_strictly_decreasing``,``none_of``,``none_of_equal``,``clamp_range``,
|
||||
``apply_permutation``,``apply_reverse_permutation``.
|
||||
|
||||
Reverse Iteration
|
||||
-----------------
|
||||
|
||||
If calls are made using reverse iterators on containers, The code will be
|
||||
fixed using the ``boost::adaptors::reverse`` adaptor.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
|
||||
std::crbegin(Items2), std::crend(Items2));
|
||||
|
||||
transformst to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto AreSame = std::equal(boost::adaptors::reverse(Items1),
|
||||
boost::adaptors::reverse(Items2));
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: IncludeStyle
|
||||
|
||||
A string specifying which include-style is used, `llvm` or `google`. Default
|
||||
is `llvm`.
|
||||
|
||||
.. option:: IncludeBoostSystem
|
||||
|
||||
If `true` (default value) the boost headers are included as system headers
|
||||
with angle brackets (`#include <boost.hpp>`), otherwise quotes are used
|
||||
(`#include "boost.hpp"`).
|
||||
@@ -75,6 +75,7 @@ Clang-Tidy Checks
|
||||
:doc:`android-cloexec-pipe2 <android/cloexec-pipe2>`, "Yes"
|
||||
:doc:`android-cloexec-socket <android/cloexec-socket>`, "Yes"
|
||||
:doc:`android-comparison-in-temp-failure-retry <android/comparison-in-temp-failure-retry>`,
|
||||
:doc:`boost-use-ranges <boost/use-ranges>`, "Yes"
|
||||
:doc:`boost-use-to-string <boost/use-to-string>`, "Yes"
|
||||
:doc:`bugprone-argument-comment <bugprone/argument-comment>`, "Yes"
|
||||
:doc:`bugprone-assert-side-effect <bugprone/assert-side-effect>`,
|
||||
@@ -301,6 +302,7 @@ Clang-Tidy Checks
|
||||
:doc:`modernize-use-noexcept <modernize/use-noexcept>`, "Yes"
|
||||
:doc:`modernize-use-nullptr <modernize/use-nullptr>`, "Yes"
|
||||
:doc:`modernize-use-override <modernize/use-override>`, "Yes"
|
||||
:doc:`modernize-use-ranges <modernize/use-ranges>`, "Yes"
|
||||
:doc:`modernize-use-starts-ends-with <modernize/use-starts-ends-with>`, "Yes"
|
||||
:doc:`modernize-use-std-format <modernize/use-std-format>`, "Yes"
|
||||
:doc:`modernize-use-std-numbers <modernize/use-std-numbers>`, "Yes"
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
.. title:: clang-tidy - modernize-use-ranges
|
||||
|
||||
modernize-use-ranges
|
||||
====================
|
||||
|
||||
Detects calls to standard library iterator algorithms that could be replaced
|
||||
with a ranges version instead.
|
||||
|
||||
Example
|
||||
-------
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto Iter1 = std::find(Items.begin(), Items.end(), 0);
|
||||
auto AreSame = std::equal(Items1.cbegin(), Items1.cend(),
|
||||
std::begin(Items2), std::end(Items2));
|
||||
|
||||
|
||||
transforms to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto Iter1 = std::ranges::find(Items, 0);
|
||||
auto AreSame = std::ranges::equal(Items1, Items2);
|
||||
|
||||
Calls to the following std library algorithms are checked:
|
||||
``::std::all_of``,``::std::any_of``,``::std::none_of``,``::std::for_each``,
|
||||
``::std::find``,``::std::find_if``,``::std::find_if_not``,
|
||||
``::std::adjacent_find``,``::std::copy``,``::std::copy_if``,
|
||||
``::std::copy_backward``,``::std::move``,``::std::move_backward``,
|
||||
``::std::fill``,``::std::transform``,``::std::replace``,``::std::replace_if``,
|
||||
``::std::generate``,``::std::remove``,``::std::remove_if``,
|
||||
``::std::remove_copy``,``::std::remove_copy_if``,``::std::unique``,
|
||||
``::std::unique_copy``,``::std::sample``,``::std::partition_point``,
|
||||
``::std::lower_bound``,``::std::upper_bound``,``::std::equal_range``,
|
||||
``::std::binary_search``,``::std::push_heap``,``::std::pop_heap``,
|
||||
``::std::make_heap``,``::std::sort_heap``,``::std::next_permutation``,
|
||||
``::std::prev_permutation``,``::std::iota``,``::std::reverse``,
|
||||
``::std::reverse_copy``,``::std::shift_left``,``::std::shift_right``,
|
||||
``::std::is_partitioned``,``::std::partition``,``::std::partition_copy``,
|
||||
``::std::stable_partition``,``::std::sort``,``::std::stable_sort``,
|
||||
``::std::is_sorted``,``::std::is_sorted_until``,``::std::is_heap``,
|
||||
``::std::is_heap_until``,``::std::max_element``,``::std::min_element``,
|
||||
``::std::minmax_element``,``::std::uninitialized_copy``,
|
||||
``::std::uninitialized_fill``,``::std::uninitialized_move``,
|
||||
``::std::uninitialized_default_construct``,
|
||||
``::std::uninitialized_value_construct``,``::std::destroy``,
|
||||
``::std::partial_sort_copy``,``::std::includes``,
|
||||
``::std::set_union``,``::std::set_intersection``,``::std::set_difference``,
|
||||
``::std::set_symmetric_difference``,``::std::merge``,
|
||||
``::std::lexicographical_compare``,``::std::find_end``,``::std::search``,
|
||||
``::std::is_permutation``,``::std::equal``,``::std::mismatch``.
|
||||
|
||||
Reverse Iteration
|
||||
-----------------
|
||||
|
||||
If calls are made using reverse iterators on containers, The code will be
|
||||
fixed using the ``std::views::reverse`` adaptor.
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto AreSame = std::equal(Items1.rbegin(), Items1.rend(),
|
||||
std::crbegin(Items2), std::crend(Items2));
|
||||
|
||||
transformst to:
|
||||
|
||||
.. code-block:: c++
|
||||
|
||||
auto AreSame = std::equal(std::views::reverse(Items1),
|
||||
std::views::reverse(Items2));
|
||||
|
||||
Options
|
||||
-------
|
||||
|
||||
.. option:: IncludeStyle
|
||||
|
||||
A string specifying which include-style is used, `llvm` or `google`. Default
|
||||
is `llvm`.
|
||||
|
||||
194
clang-tools-extra/test/clang-tidy/checkers/boost/use-ranges.cpp
Normal file
194
clang-tools-extra/test/clang-tidy/checkers/boost/use-ranges.cpp
Normal file
@@ -0,0 +1,194 @@
|
||||
// RUN: %check_clang_tidy -std=c++14 %s boost-use-ranges %t
|
||||
// RUN: %check_clang_tidy -std=c++17 %s boost-use-ranges %t -check-suffixes=,CPP17
|
||||
|
||||
// CHECK-FIXES: #include <boost/range/algorithm/find.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/algorithm/reverse.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/algorithm/set_algorithm.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/algorithm/equal.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/algorithm/permutation.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/algorithm/heap_algorithm.hpp>
|
||||
// CHECK-FIXES: #include <boost/algorithm/cxx11/copy_if.hpp>
|
||||
// CHECK-FIXES: #include <boost/algorithm/cxx11/is_sorted.hpp>
|
||||
// CHECK-FIXES-CPP17: #include <boost/algorithm/cxx17/reduce.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/adaptor/reversed.hpp>
|
||||
// CHECK-FIXES: #include <boost/range/numeric.hpp>
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename T> class vector {
|
||||
public:
|
||||
using iterator = T *;
|
||||
using const_iterator = const T *;
|
||||
constexpr const_iterator begin() const;
|
||||
constexpr const_iterator end() const;
|
||||
constexpr const_iterator cbegin() const;
|
||||
constexpr const_iterator cend() const;
|
||||
constexpr iterator begin();
|
||||
constexpr iterator end();
|
||||
};
|
||||
|
||||
template <typename Container> constexpr auto begin(const Container &Cont) {
|
||||
return Cont.begin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto begin(Container &Cont) {
|
||||
return Cont.begin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto end(const Container &Cont) {
|
||||
return Cont.end();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto end(Container &Cont) {
|
||||
return Cont.end();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto cbegin(const Container &Cont) {
|
||||
return Cont.cbegin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto cend(const Container &Cont) {
|
||||
return Cont.cend();
|
||||
}
|
||||
// Find
|
||||
template< class InputIt, class T >
|
||||
InputIt find(InputIt first, InputIt last, const T& value);
|
||||
|
||||
template <typename Iter> void reverse(Iter begin, Iter end);
|
||||
|
||||
template <class InputIt1, class InputIt2>
|
||||
bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
|
||||
|
||||
template <class ForwardIt1, class ForwardIt2>
|
||||
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
|
||||
ForwardIt2 last2);
|
||||
|
||||
template <class BidirIt>
|
||||
bool next_permutation(BidirIt first, BidirIt last);
|
||||
|
||||
template <class ForwardIt1, class ForwardIt2>
|
||||
bool equal(ForwardIt1 first1, ForwardIt1 last1,
|
||||
ForwardIt2 first2, ForwardIt2 last2);
|
||||
|
||||
template <class RandomIt>
|
||||
void push_heap(RandomIt first, RandomIt last);
|
||||
|
||||
template <class InputIt, class OutputIt, class UnaryPred>
|
||||
OutputIt copy_if(InputIt first, InputIt last, OutputIt d_first, UnaryPred pred);
|
||||
|
||||
template <class ForwardIt>
|
||||
ForwardIt is_sorted_until(ForwardIt first, ForwardIt last);
|
||||
|
||||
template <class InputIt>
|
||||
void reduce(InputIt first, InputIt last);
|
||||
|
||||
template <class InputIt, class T>
|
||||
T reduce(InputIt first, InputIt last, T init);
|
||||
|
||||
template <class InputIt, class T, class BinaryOp>
|
||||
T reduce(InputIt first, InputIt last, T init, BinaryOp op) {
|
||||
// Need a definition to suppress undefined_internal_type when invoked with lambda
|
||||
return init;
|
||||
}
|
||||
|
||||
template <class InputIt, class T>
|
||||
T accumulate(InputIt first, InputIt last, T init);
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace boost {
|
||||
namespace range_adl_barrier {
|
||||
template <typename T> void *begin(T &);
|
||||
template <typename T> void *end(T &);
|
||||
template <typename T> void *const_begin(const T &);
|
||||
template <typename T> void *const_end(const T &);
|
||||
} // namespace range_adl_barrier
|
||||
using namespace range_adl_barrier;
|
||||
|
||||
template <typename T> void *rbegin(T &);
|
||||
template <typename T> void *rend(T &);
|
||||
|
||||
template <typename T> void *const_rbegin(T &);
|
||||
template <typename T> void *const_rend(T &);
|
||||
namespace algorithm {
|
||||
|
||||
template <class InputIterator, class T, class BinaryOperation>
|
||||
T reduce(InputIterator first, InputIterator last, T init, BinaryOperation bOp) {
|
||||
return init;
|
||||
}
|
||||
} // namespace algorithm
|
||||
} // namespace boost
|
||||
|
||||
bool returnTrue(int val) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void stdLib() {
|
||||
std::vector<int> I, J;
|
||||
std::find(I.begin(), I.end(), 0);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::find(I, 0);
|
||||
|
||||
std::reverse(I.cbegin(), I.cend());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::reverse(I);
|
||||
|
||||
std::includes(I.begin(), I.end(), std::begin(J), std::end(J));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::includes(I, J);
|
||||
|
||||
std::equal(std::cbegin(I), std::cend(I), J.begin(), J.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::equal(I, J);
|
||||
|
||||
std::next_permutation(I.begin(), I.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::next_permutation(I);
|
||||
|
||||
std::push_heap(I.begin(), I.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::push_heap(I);
|
||||
|
||||
std::copy_if(I.begin(), I.end(), J.begin(), &returnTrue);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::algorithm::copy_if(I, J.begin(), &returnTrue);
|
||||
|
||||
std::is_sorted_until(I.begin(), I.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::algorithm::is_sorted_until(I);
|
||||
|
||||
std::reduce(I.begin(), I.end());
|
||||
// CHECK-MESSAGES-CPP17: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES-CPP17: boost::algorithm::reduce(I);
|
||||
|
||||
std::reduce(I.begin(), I.end(), 2);
|
||||
// CHECK-MESSAGES-CPP17: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES-CPP17: boost::algorithm::reduce(I, 2);
|
||||
|
||||
std::reduce(I.begin(), I.end(), 0, [](int a, int b){ return a + b; });
|
||||
// CHECK-MESSAGES-CPP17: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES-CPP17: boost::algorithm::reduce(I, 0, [](int a, int b){ return a + b; });
|
||||
|
||||
std::equal(boost::rbegin(I), boost::rend(I), J.begin(), J.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::range::equal(boost::adaptors::reverse(I), J);
|
||||
|
||||
std::accumulate(I.begin(), I.end(), 0);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a boost version of this algorithm
|
||||
// CHECK-FIXES: boost::accumulate(I, 0);
|
||||
}
|
||||
|
||||
void boostLib() {
|
||||
std::vector<int> I;
|
||||
boost::algorithm::reduce(I.begin(), I.end(), 0, [](int a, int b){ return a + b; });
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranged version of this algorithm
|
||||
// CHECK-FIXES: boost::algorithm::reduce(I, 0, [](int a, int b){ return a + b; });
|
||||
|
||||
boost::algorithm::reduce(boost::begin(I), boost::end(I), 1, [](int a, int b){ return a + b; });
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranged version of this algorithm
|
||||
// CHECK-FIXES: boost::algorithm::reduce(I, 1, [](int a, int b){ return a + b; });
|
||||
|
||||
boost::algorithm::reduce(boost::const_begin(I), boost::const_end(I), 2, [](int a, int b){ return a + b; });
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranged version of this algorithm
|
||||
// CHECK-FIXES: boost::algorithm::reduce(I, 2, [](int a, int b){ return a + b; });
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
// RUN: %check_clang_tidy -std=c++20 %s modernize-use-ranges %t
|
||||
// RUN: %check_clang_tidy -std=c++23 %s modernize-use-ranges %t -check-suffixes=,CPP23
|
||||
|
||||
// CHECK-FIXES: #include <algorithm>
|
||||
// CHECK-FIXES-CPP23: #include <numeric>
|
||||
// CHECK-FIXES: #include <ranges>
|
||||
|
||||
namespace std {
|
||||
|
||||
template <typename T> class vector {
|
||||
public:
|
||||
using iterator = T *;
|
||||
using const_iterator = const T *;
|
||||
using reverse_iterator = T*;
|
||||
using reverse_const_iterator = const T*;
|
||||
|
||||
constexpr const_iterator begin() const;
|
||||
constexpr const_iterator end() const;
|
||||
constexpr const_iterator cbegin() const;
|
||||
constexpr const_iterator cend() const;
|
||||
constexpr iterator begin();
|
||||
constexpr iterator end();
|
||||
constexpr reverse_const_iterator rbegin() const;
|
||||
constexpr reverse_const_iterator rend() const;
|
||||
constexpr reverse_const_iterator crbegin() const;
|
||||
constexpr reverse_const_iterator crend() const;
|
||||
constexpr reverse_iterator rbegin();
|
||||
constexpr reverse_iterator rend();
|
||||
};
|
||||
|
||||
template <typename Container> constexpr auto begin(const Container &Cont) {
|
||||
return Cont.begin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto begin(Container &Cont) {
|
||||
return Cont.begin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto end(const Container &Cont) {
|
||||
return Cont.end();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto end(Container &Cont) {
|
||||
return Cont.end();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto cbegin(const Container &Cont) {
|
||||
return Cont.cbegin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto cend(const Container &Cont) {
|
||||
return Cont.cend();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto rbegin(const Container &Cont) {
|
||||
return Cont.rbegin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto rbegin(Container &Cont) {
|
||||
return Cont.rbegin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto rend(const Container &Cont) {
|
||||
return Cont.rend();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto rend(Container &Cont) {
|
||||
return Cont.rend();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto crbegin(const Container &Cont) {
|
||||
return Cont.crbegin();
|
||||
}
|
||||
|
||||
template <typename Container> constexpr auto crend(const Container &Cont) {
|
||||
return Cont.crend();
|
||||
}
|
||||
// Find
|
||||
template< class InputIt, class T >
|
||||
InputIt find( InputIt first, InputIt last, const T& value );
|
||||
|
||||
// Reverse
|
||||
template <typename Iter> void reverse(Iter begin, Iter end);
|
||||
|
||||
// Includes
|
||||
template <class InputIt1, class InputIt2>
|
||||
bool includes(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
|
||||
|
||||
// IsPermutation
|
||||
template <class ForwardIt1, class ForwardIt2>
|
||||
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2);
|
||||
template <class ForwardIt1, class ForwardIt2>
|
||||
bool is_permutation(ForwardIt1 first1, ForwardIt1 last1, ForwardIt2 first2,
|
||||
ForwardIt2 last2);
|
||||
|
||||
// Equal
|
||||
template <class InputIt1, class InputIt2>
|
||||
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2);
|
||||
|
||||
template <class InputIt1, class InputIt2>
|
||||
bool equal(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2);
|
||||
|
||||
template <class InputIt1, class InputIt2, class BinaryPred>
|
||||
bool equal(InputIt1 first1, InputIt1 last1,
|
||||
InputIt2 first2, InputIt2 last2, BinaryPred p) {
|
||||
// Need a definition to suppress undefined_internal_type when invoked with lambda
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class ForwardIt, class T>
|
||||
void iota(ForwardIt first, ForwardIt last, T value);
|
||||
|
||||
} // namespace std
|
||||
|
||||
void Positives() {
|
||||
std::vector<int> I, J;
|
||||
std::find(I.begin(), I.end(), 0);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 0);
|
||||
|
||||
std::find(I.cbegin(), I.cend(), 1);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 1);
|
||||
|
||||
std::find(std::begin(I), std::end(I), 2);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 2);
|
||||
|
||||
std::find(std::cbegin(I), std::cend(I), 3);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 3);
|
||||
|
||||
std::find(std::cbegin(I), I.cend(), 4);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 4);
|
||||
|
||||
std::reverse(I.begin(), I.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::reverse(I);
|
||||
|
||||
std::includes(I.begin(), I.end(), I.begin(), I.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::includes(I, I);
|
||||
|
||||
std::includes(I.begin(), I.end(), J.begin(), J.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::includes(I, J);
|
||||
|
||||
std::is_permutation(I.begin(), I.end(), J.begin(), J.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::is_permutation(I, J);
|
||||
|
||||
std::equal(I.begin(), I.end(), J.begin(), J.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::equal(I, J);
|
||||
|
||||
std::equal(I.begin(), I.end(), J.begin(), J.end(), [](int a, int b){ return a == b; });
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::equal(I, J, [](int a, int b){ return a == b; });
|
||||
|
||||
std::iota(I.begin(), I.end(), 0);
|
||||
// CHECK-MESSAGES-CPP23: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES-CPP23: std::ranges::iota(I, 0);
|
||||
|
||||
using std::find;
|
||||
namespace my_std = std;
|
||||
|
||||
// Potentially these could be updated to better qualify the replaced function name
|
||||
find(I.begin(), I.end(), 5);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 5);
|
||||
|
||||
my_std::find(I.begin(), I.end(), 6);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(I, 6);
|
||||
}
|
||||
|
||||
void Reverse(){
|
||||
std::vector<int> I, J;
|
||||
std::find(I.rbegin(), I.rend(), 0);
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::find(std::views::reverse(I), 0);
|
||||
|
||||
std::equal(std::rbegin(I), std::rend(I), J.begin(), J.end());
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::equal(std::views::reverse(I), J);
|
||||
|
||||
std::equal(I.begin(), I.end(), std::crbegin(J), std::crend(J));
|
||||
// CHECK-MESSAGES: :[[@LINE-1]]:3: warning: use a ranges version of this algorithm
|
||||
// CHECK-FIXES: std::ranges::equal(I, std::views::reverse(J));
|
||||
}
|
||||
|
||||
void Negatives() {
|
||||
std::vector<int> I, J;
|
||||
std::find(I.begin(), J.end(), 0);
|
||||
std::find(I.begin(), I.begin(), 0);
|
||||
std::find(I.end(), I.begin(), 0);
|
||||
|
||||
|
||||
// Need both ranges for this one
|
||||
std::is_permutation(I.begin(), I.end(), J.begin());
|
||||
|
||||
// We only have one valid match here and the ranges::equal function needs 2 complete ranges
|
||||
std::equal(I.begin(), I.end(), J.begin());
|
||||
std::equal(I.begin(), I.end(), J.end(), J.end());
|
||||
std::equal(std::rbegin(I), std::rend(I), std::rend(J), std::rbegin(J));
|
||||
std::equal(I.begin(), J.end(), I.begin(), I.end());
|
||||
}
|
||||
Reference in New Issue
Block a user