//===--- 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 #include #include // 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 Signatures, bool IncludeSystem) : Signatures(Signatures), IncludeSystem(IncludeSystem) {} ArrayRef getReplacementSignatures() const final { return Signatures; } virtual std::pair getBoostName(const NamedDecl &OriginalName) const = 0; virtual std::pair getBoostHeader(const NamedDecl &OriginalName) const = 0; std::optional getReplaceName(const NamedDecl &OriginalName) const final { auto [Namespace, Function] = getBoostName(OriginalName); return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function) .str(); } std::optional getHeaderInclusion(const NamedDecl &OriginalName) const final { auto [Path, HeaderName] = getBoostHeader(OriginalName); return ((IncludeSystem ? "" : ".hpp")) .str(); } private: SmallVector Signatures; bool IncludeSystem; }; /// Creates replaces where the header file lives in /// `boost/algorithm/.hpp` and the function is named /// `boost::range::` class BoostRangeAlgorithmReplacer : public BoostReplacer { public: using BoostReplacer::BoostReplacer; std::pair getBoostName(const NamedDecl &OriginalName) const override { return {"range", OriginalName.getName()}; } std::pair getBoostHeader(const NamedDecl &OriginalName) const override { return {"range/algorithm", OriginalName.getName()}; } }; /// Creates replaces where the header file lives in /// `boost/algorithm/.hpp` and the function is named /// `boost::range::` class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer { public: CustomBoostAlgorithmHeaderReplacer( StringRef HeaderName, ArrayRef Signatures, bool IncludeSystem) : BoostRangeAlgorithmReplacer(Signatures, IncludeSystem), HeaderName(HeaderName) {} std::pair getBoostHeader(const NamedDecl & /*OriginalName*/) const override { return {"range/algorithm", HeaderName}; } private: StringRef HeaderName; }; /// Creates replaces where the header file lives in /// `boost/algorithm/.hpp` and the function is named /// `boost::algorithm::` class BoostAlgorithmReplacer : public BoostReplacer { public: BoostAlgorithmReplacer(StringRef SubHeader, ArrayRef Signatures, bool IncludeSystem) : BoostReplacer(Signatures, IncludeSystem), SubHeader(("algorithm/" + SubHeader).str()) {} std::pair getBoostName(const NamedDecl &OriginalName) const override { return {"algorithm", OriginalName.getName()}; } std::pair getBoostHeader(const NamedDecl &OriginalName) const override { return {SubHeader, OriginalName.getName()}; } private: std::string SubHeader; }; /// Creates replaces where the header file lives in /// `boost/algorithm//.hpp` and the function is named /// `boost::algorithm::` class CustomBoostAlgorithmReplacer : public BoostReplacer { public: CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName, ArrayRef Signatures, bool IncludeSystem) : BoostReplacer(Signatures, IncludeSystem), SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {} std::pair getBoostName(const NamedDecl &OriginalName) const override { return {"algorithm", OriginalName.getName()}; } std::pair 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 Signatures) : Signatures(Signatures) {} ArrayRef getReplacementSignatures() const override { return Signatures; } std::optional getReplaceName(const NamedDecl & /* OriginalName */) const override { return std::nullopt; } std::optional getHeaderInclusion(const NamedDecl & /* OriginalName */) const override { return std::nullopt; } private: SmallVector 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 Signatures, bool IncludeBoostSystem) : BoostReplacer(Signatures, IncludeBoostSystem), Header(Header) {} std::pair getBoostName(const NamedDecl &OriginalName) const override { return {{}, OriginalName.getName()}; } std::pair 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 Replacer, std::initializer_list 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 Replacer, std::initializer_list Names) { AddFrom(Replacer, Names, "std"); }; const auto AddFromBoost = [&](llvm::IntrusiveRefCntPtr Replacer, std::initializer_list< std::pair>> NamespaceAndNames) { for (auto [Namespace, Names] : NamespaceAndNames) AddFrom(Replacer, Names, SmallString<64>{"boost", (Namespace.empty() ? "" : "::"), Namespace}); }; AddFromStd(llvm::makeIntrusiveRefCnt( "set_algorithm", TwoSig, IncludeBoostSystem), {"includes", "set_union", "set_intersection", "set_difference", "set_symmetric_difference"}); AddFromStd(llvm::makeIntrusiveRefCnt( 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( TwoSig, IncludeBoostSystem), {"find_end", "merge", "partial_sort_copy", "find_first_of", "search", "lexicographical_compare", "equal", "mismatch"}); AddFromStd(llvm::makeIntrusiveRefCnt( "permutation", SingleSig, IncludeBoostSystem), {"next_permutation", "prev_permutation"}); AddFromStd(llvm::makeIntrusiveRefCnt( "heap_algorithm", SingleSig, IncludeBoostSystem), {"push_heap", "pop_heap", "make_heap", "sort_heap"}); AddFromStd(llvm::makeIntrusiveRefCnt( "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( "cxx11", "is_sorted", SingleSig, IncludeBoostSystem), {"is_sorted_until"}); AddFromStd(llvm::makeIntrusiveRefCnt( "range/numeric", SingleSig, IncludeBoostSystem), {"accumulate", "partial_sum", "adjacent_difference"}); if (getLangOpts().CPlusPlus17) AddFromStd(llvm::makeIntrusiveRefCnt( "cxx17", SingleSig, IncludeBoostSystem), {"reduce"}); AddFromBoost(llvm::makeIntrusiveRefCnt(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(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> UseRangesCheck::getFreeBeginEndMethods() const { static const std::pair 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::getReverseDescriptor() const { static const std::pair 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", Refs, UseReversePipe}; } } // namespace clang::tidy::boost