375 lines
14 KiB
C++
375 lines
14 KiB
C++
//===--- 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}};
|
|
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);
|
|
}
|
|
};
|
|
|
|
const auto AddFromStd =
|
|
[&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer,
|
|
std::initializer_list<StringRef> Names) {
|
|
AddFrom(Replacer, Names, "std");
|
|
};
|
|
|
|
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)),
|
|
UseReversePipe(Options.get("UseReversePipe", false)) {}
|
|
|
|
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|
utils::UseRangesCheck::storeOptions(Opts);
|
|
Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem);
|
|
Options.store(Opts, "UseReversePipe", UseReversePipe);
|
|
}
|
|
|
|
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{
|
|
UseReversePipe ? "boost::adaptors::reversed" : "boost::adaptors::reverse",
|
|
IncludeBoostSystem ? "<boost/range/adaptor/reversed.hpp>"
|
|
: "boost/range/adaptor/reversed.hpp",
|
|
Refs, UseReversePipe};
|
|
}
|
|
} // namespace clang::tidy::boost
|