
These algorithms take 3 iterators for the range and we are only interested in the first and last iterator argument. The ranges versions of these take a range and an iterator(defined to be inside the range) so the transformation is pretty similar `algo(I.begin, other, I.end,...)` -> `ranges::algo(I, other,...)`
206 lines
6.1 KiB
C++
206 lines
6.1 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 "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",
|
|
};
|
|
|
|
static constexpr const char *SinglePivotRangeNames[] = {"rotate", "rotate_copy",
|
|
"inplace_merge"};
|
|
|
|
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}};
|
|
|
|
// template<typename Iter> Func(Iter first, Iter pivot, Iter last,...).
|
|
static const Signature SinglePivotRange = {{0, 2}};
|
|
|
|
static const Signature SingleRangeFunc[] = {SingleRangeArgs};
|
|
|
|
static const Signature TwoRangeFunc[] = {TwoRangeArgs};
|
|
|
|
static const Signature SinglePivotFunc[] = {SinglePivotRange};
|
|
|
|
static const std::pair<ArrayRef<Signature>, ArrayRef<const char *>>
|
|
AlgorithmNames[] = {{SingleRangeFunc, SingleRangeNames},
|
|
{TwoRangeFunc, TwoRangeNames},
|
|
{SinglePivotFunc, SinglePivotRangeNames}};
|
|
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;
|
|
}
|
|
|
|
UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context)
|
|
: utils::UseRangesCheck(Name, Context),
|
|
UseReversePipe(Options.get("UseReversePipe", false)) {}
|
|
|
|
void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
|
utils::UseRangesCheck::storeOptions(Opts);
|
|
Options.store(Opts, "UseReversePipe", UseReversePipe);
|
|
}
|
|
|
|
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{UseReversePipe ? "std::views::reverse"
|
|
: "std::ranges::reverse_view",
|
|
"<ranges>", Refs, UseReversePipe};
|
|
}
|
|
} // namespace clang::tidy::modernize
|