11038db6fSNathan James //===--- UseRangesCheck.cpp - clang-tidy ----------------------------------===// 21038db6fSNathan James // 31038db6fSNathan James // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41038db6fSNathan James // See https://llvm.org/LICENSE.txt for license information. 51038db6fSNathan James // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61038db6fSNathan James // 71038db6fSNathan James //===----------------------------------------------------------------------===// 81038db6fSNathan James 91038db6fSNathan James #include "UseRangesCheck.h" 101038db6fSNathan James #include "clang/AST/Decl.h" 111038db6fSNathan James #include "clang/Basic/Diagnostic.h" 121038db6fSNathan James #include "clang/Basic/LLVM.h" 131038db6fSNathan James #include "llvm/ADT/ArrayRef.h" 141038db6fSNathan James #include "llvm/ADT/IntrusiveRefCntPtr.h" 151038db6fSNathan James #include "llvm/ADT/SmallString.h" 161038db6fSNathan James #include "llvm/ADT/SmallVector.h" 171038db6fSNathan James #include "llvm/ADT/StringRef.h" 181038db6fSNathan James #include <initializer_list> 191038db6fSNathan James #include <optional> 201038db6fSNathan James #include <string> 211038db6fSNathan James 221038db6fSNathan James // FixItHint - Let the docs script know that this class does provide fixits 231038db6fSNathan James 241038db6fSNathan James namespace clang::tidy::boost { 251038db6fSNathan James 261038db6fSNathan James namespace { 271038db6fSNathan James /// Base replacer that handles the boost include path and namespace 281038db6fSNathan James class BoostReplacer : public UseRangesCheck::Replacer { 291038db6fSNathan James public: 301038db6fSNathan James BoostReplacer(ArrayRef<UseRangesCheck::Signature> Signatures, 311038db6fSNathan James bool IncludeSystem) 321038db6fSNathan James : Signatures(Signatures), IncludeSystem(IncludeSystem) {} 331038db6fSNathan James 341038db6fSNathan James ArrayRef<UseRangesCheck::Signature> getReplacementSignatures() const final { 351038db6fSNathan James return Signatures; 361038db6fSNathan James } 371038db6fSNathan James 381038db6fSNathan James virtual std::pair<StringRef, StringRef> 391038db6fSNathan James getBoostName(const NamedDecl &OriginalName) const = 0; 401038db6fSNathan James 411038db6fSNathan James virtual std::pair<StringRef, StringRef> 421038db6fSNathan James getBoostHeader(const NamedDecl &OriginalName) const = 0; 431038db6fSNathan James 441038db6fSNathan James std::optional<std::string> 451038db6fSNathan James getReplaceName(const NamedDecl &OriginalName) const final { 461038db6fSNathan James auto [Namespace, Function] = getBoostName(OriginalName); 471038db6fSNathan James return ("boost::" + Namespace + (Namespace.empty() ? "" : "::") + Function) 481038db6fSNathan James .str(); 491038db6fSNathan James } 501038db6fSNathan James 511038db6fSNathan James std::optional<std::string> 521038db6fSNathan James getHeaderInclusion(const NamedDecl &OriginalName) const final { 531038db6fSNathan James auto [Path, HeaderName] = getBoostHeader(OriginalName); 541038db6fSNathan James return ((IncludeSystem ? "<boost/" : "boost/") + Path + 551038db6fSNathan James (Path.empty() ? "" : "/") + HeaderName + 561038db6fSNathan James (IncludeSystem ? ".hpp>" : ".hpp")) 571038db6fSNathan James .str(); 581038db6fSNathan James } 591038db6fSNathan James 601038db6fSNathan James private: 611038db6fSNathan James SmallVector<UseRangesCheck::Signature> Signatures; 621038db6fSNathan James bool IncludeSystem; 631038db6fSNathan James }; 641038db6fSNathan James 651038db6fSNathan James /// Creates replaces where the header file lives in 661038db6fSNathan James /// `boost/algorithm/<FUNC_NAME>.hpp` and the function is named 671038db6fSNathan James /// `boost::range::<FUNC_NAME>` 681038db6fSNathan James class BoostRangeAlgorithmReplacer : public BoostReplacer { 691038db6fSNathan James public: 701038db6fSNathan James using BoostReplacer::BoostReplacer; 711038db6fSNathan James 721038db6fSNathan James std::pair<StringRef, StringRef> 731038db6fSNathan James getBoostName(const NamedDecl &OriginalName) const override { 741038db6fSNathan James return {"range", OriginalName.getName()}; 751038db6fSNathan James } 761038db6fSNathan James 771038db6fSNathan James std::pair<StringRef, StringRef> 781038db6fSNathan James getBoostHeader(const NamedDecl &OriginalName) const override { 791038db6fSNathan James return {"range/algorithm", OriginalName.getName()}; 801038db6fSNathan James } 811038db6fSNathan James }; 821038db6fSNathan James 831038db6fSNathan James /// Creates replaces where the header file lives in 841038db6fSNathan James /// `boost/algorithm/<CUSTOM_HEADER>.hpp` and the function is named 851038db6fSNathan James /// `boost::range::<FUNC_NAME>` 861038db6fSNathan James class CustomBoostAlgorithmHeaderReplacer : public BoostRangeAlgorithmReplacer { 871038db6fSNathan James public: 881038db6fSNathan James CustomBoostAlgorithmHeaderReplacer( 891038db6fSNathan James StringRef HeaderName, ArrayRef<UseRangesCheck::Signature> Signatures, 901038db6fSNathan James bool IncludeSystem) 911038db6fSNathan James : BoostRangeAlgorithmReplacer(Signatures, IncludeSystem), 921038db6fSNathan James HeaderName(HeaderName) {} 931038db6fSNathan James 941038db6fSNathan James std::pair<StringRef, StringRef> 951038db6fSNathan James getBoostHeader(const NamedDecl & /*OriginalName*/) const override { 961038db6fSNathan James return {"range/algorithm", HeaderName}; 971038db6fSNathan James } 981038db6fSNathan James 991038db6fSNathan James private: 1001038db6fSNathan James StringRef HeaderName; 1011038db6fSNathan James }; 1021038db6fSNathan James 1031038db6fSNathan James /// Creates replaces where the header file lives in 1041038db6fSNathan James /// `boost/algorithm/<SUB_HEADER>.hpp` and the function is named 1051038db6fSNathan James /// `boost::algorithm::<FUNC_NAME>` 1061038db6fSNathan James class BoostAlgorithmReplacer : public BoostReplacer { 1071038db6fSNathan James public: 1081038db6fSNathan James BoostAlgorithmReplacer(StringRef SubHeader, 1091038db6fSNathan James ArrayRef<UseRangesCheck::Signature> Signatures, 1101038db6fSNathan James bool IncludeSystem) 1111038db6fSNathan James : BoostReplacer(Signatures, IncludeSystem), 1121038db6fSNathan James SubHeader(("algorithm/" + SubHeader).str()) {} 1131038db6fSNathan James std::pair<StringRef, StringRef> 1141038db6fSNathan James getBoostName(const NamedDecl &OriginalName) const override { 1151038db6fSNathan James return {"algorithm", OriginalName.getName()}; 1161038db6fSNathan James } 1171038db6fSNathan James 1181038db6fSNathan James std::pair<StringRef, StringRef> 1191038db6fSNathan James getBoostHeader(const NamedDecl &OriginalName) const override { 1201038db6fSNathan James return {SubHeader, OriginalName.getName()}; 1211038db6fSNathan James } 1221038db6fSNathan James 1231038db6fSNathan James private: 1241038db6fSNathan James std::string SubHeader; 1251038db6fSNathan James }; 1261038db6fSNathan James 1271038db6fSNathan James /// Creates replaces where the header file lives in 1281038db6fSNathan James /// `boost/algorithm/<SUB_HEADER>/<HEADER_NAME>.hpp` and the function is named 1291038db6fSNathan James /// `boost::algorithm::<FUNC_NAME>` 1301038db6fSNathan James class CustomBoostAlgorithmReplacer : public BoostReplacer { 1311038db6fSNathan James public: 1321038db6fSNathan James CustomBoostAlgorithmReplacer(StringRef SubHeader, StringRef HeaderName, 1331038db6fSNathan James ArrayRef<UseRangesCheck::Signature> Signatures, 1341038db6fSNathan James bool IncludeSystem) 1351038db6fSNathan James : BoostReplacer(Signatures, IncludeSystem), 1361038db6fSNathan James SubHeader(("algorithm/" + SubHeader).str()), HeaderName(HeaderName) {} 1371038db6fSNathan James std::pair<StringRef, StringRef> 1381038db6fSNathan James getBoostName(const NamedDecl &OriginalName) const override { 1391038db6fSNathan James return {"algorithm", OriginalName.getName()}; 1401038db6fSNathan James } 1411038db6fSNathan James 1421038db6fSNathan James std::pair<StringRef, StringRef> 1431038db6fSNathan James getBoostHeader(const NamedDecl & /*OriginalName*/) const override { 1441038db6fSNathan James return {SubHeader, HeaderName}; 1451038db6fSNathan James } 1461038db6fSNathan James 1471038db6fSNathan James private: 1481038db6fSNathan James std::string SubHeader; 1491038db6fSNathan James StringRef HeaderName; 1501038db6fSNathan James }; 1511038db6fSNathan James 1521038db6fSNathan James /// A Replacer that is used for functions that just call a new overload 1531038db6fSNathan James class MakeOverloadReplacer : public UseRangesCheck::Replacer { 1541038db6fSNathan James public: 1551038db6fSNathan James explicit MakeOverloadReplacer(ArrayRef<UseRangesCheck::Signature> Signatures) 1561038db6fSNathan James : Signatures(Signatures) {} 1571038db6fSNathan James 1581038db6fSNathan James ArrayRef<UseRangesCheck::Signature> 1591038db6fSNathan James getReplacementSignatures() const override { 1601038db6fSNathan James return Signatures; 1611038db6fSNathan James } 1621038db6fSNathan James 1631038db6fSNathan James std::optional<std::string> 1641038db6fSNathan James getReplaceName(const NamedDecl & /* OriginalName */) const override { 1651038db6fSNathan James return std::nullopt; 1661038db6fSNathan James } 1671038db6fSNathan James 1681038db6fSNathan James std::optional<std::string> 1691038db6fSNathan James getHeaderInclusion(const NamedDecl & /* OriginalName */) const override { 1701038db6fSNathan James return std::nullopt; 1711038db6fSNathan James } 1721038db6fSNathan James 1731038db6fSNathan James private: 1741038db6fSNathan James SmallVector<UseRangesCheck::Signature> Signatures; 1751038db6fSNathan James }; 1761038db6fSNathan James 1771038db6fSNathan James /// A replacer that replaces functions with an equivalent named function in the 1781038db6fSNathan James /// root boost namespace 1791038db6fSNathan James class FixedBoostReplace : public BoostReplacer { 1801038db6fSNathan James public: 1811038db6fSNathan James FixedBoostReplace(StringRef Header, 1821038db6fSNathan James ArrayRef<UseRangesCheck::Signature> Signatures, 1831038db6fSNathan James bool IncludeBoostSystem) 1841038db6fSNathan James : BoostReplacer(Signatures, IncludeBoostSystem), Header(Header) {} 1851038db6fSNathan James 1861038db6fSNathan James std::pair<StringRef, StringRef> 1871038db6fSNathan James getBoostName(const NamedDecl &OriginalName) const override { 1881038db6fSNathan James return {{}, OriginalName.getName()}; 1891038db6fSNathan James } 1901038db6fSNathan James 1911038db6fSNathan James std::pair<StringRef, StringRef> 1921038db6fSNathan James getBoostHeader(const NamedDecl & /* OriginalName */) const override { 1931038db6fSNathan James return {{}, Header}; 1941038db6fSNathan James } 1951038db6fSNathan James 1961038db6fSNathan James private: 1971038db6fSNathan James StringRef Header; 1981038db6fSNathan James }; 1991038db6fSNathan James 2001038db6fSNathan James } // namespace 2011038db6fSNathan James 2021038db6fSNathan James utils::UseRangesCheck::ReplacerMap UseRangesCheck::getReplacerMap() const { 2031038db6fSNathan James 2041038db6fSNathan James ReplacerMap Results; 2051038db6fSNathan James static const Signature SingleSig = {{0}}; 2061038db6fSNathan James static const Signature TwoSig = {{0}, {2}}; 207*acf92a47SNathan Ridge const auto AddFrom = 2081038db6fSNathan James [&Results](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, 2091038db6fSNathan James std::initializer_list<StringRef> Names, StringRef Prefix) { 2101038db6fSNathan James llvm::SmallString<64> Buffer; 2111038db6fSNathan James for (const auto &Name : Names) { 2121038db6fSNathan James Buffer.assign({"::", Prefix, (Prefix.empty() ? "" : "::"), Name}); 2131038db6fSNathan James Results.try_emplace(Buffer, Replacer); 2141038db6fSNathan James } 2151038db6fSNathan James }; 2161038db6fSNathan James 217*acf92a47SNathan Ridge const auto AddFromStd = 218*acf92a47SNathan Ridge [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, 2191038db6fSNathan James std::initializer_list<StringRef> Names) { 2201038db6fSNathan James AddFrom(Replacer, Names, "std"); 2211038db6fSNathan James }; 2221038db6fSNathan James 223*acf92a47SNathan Ridge const auto AddFromBoost = 224*acf92a47SNathan Ridge [&](llvm::IntrusiveRefCntPtr<UseRangesCheck::Replacer> Replacer, 2251038db6fSNathan James std::initializer_list< 2261038db6fSNathan James std::pair<StringRef, std::initializer_list<StringRef>>> 2271038db6fSNathan James NamespaceAndNames) { 2281038db6fSNathan James for (auto [Namespace, Names] : NamespaceAndNames) 2291038db6fSNathan James AddFrom(Replacer, Names, 2301038db6fSNathan James SmallString<64>{"boost", (Namespace.empty() ? "" : "::"), 2311038db6fSNathan James Namespace}); 2321038db6fSNathan James }; 2331038db6fSNathan James 2341038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>( 2351038db6fSNathan James "set_algorithm", TwoSig, IncludeBoostSystem), 2361038db6fSNathan James {"includes", "set_union", "set_intersection", "set_difference", 2371038db6fSNathan James "set_symmetric_difference"}); 2381038db6fSNathan James 2391038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>( 2401038db6fSNathan James SingleSig, IncludeBoostSystem), 2411038db6fSNathan James {"unique", "lower_bound", "stable_sort", 2421038db6fSNathan James "equal_range", "remove_if", "sort", 2431038db6fSNathan James "random_shuffle", "remove_copy", "stable_partition", 2441038db6fSNathan James "remove_copy_if", "count", "copy_backward", 2451038db6fSNathan James "reverse_copy", "adjacent_find", "remove", 2461038db6fSNathan James "upper_bound", "binary_search", "replace_copy_if", 2471038db6fSNathan James "for_each", "generate", "count_if", 2481038db6fSNathan James "min_element", "reverse", "replace_copy", 2491038db6fSNathan James "fill", "unique_copy", "transform", 2501038db6fSNathan James "copy", "replace", "find", 2511038db6fSNathan James "replace_if", "find_if", "partition", 2521038db6fSNathan James "max_element"}); 2531038db6fSNathan James 2541038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<BoostRangeAlgorithmReplacer>( 2551038db6fSNathan James TwoSig, IncludeBoostSystem), 2561038db6fSNathan James {"find_end", "merge", "partial_sort_copy", "find_first_of", 2571038db6fSNathan James "search", "lexicographical_compare", "equal", "mismatch"}); 2581038db6fSNathan James 2591038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>( 2601038db6fSNathan James "permutation", SingleSig, IncludeBoostSystem), 2611038db6fSNathan James {"next_permutation", "prev_permutation"}); 2621038db6fSNathan James 2631038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmHeaderReplacer>( 2641038db6fSNathan James "heap_algorithm", SingleSig, IncludeBoostSystem), 2651038db6fSNathan James {"push_heap", "pop_heap", "make_heap", "sort_heap"}); 2661038db6fSNathan James 2671038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>( 2681038db6fSNathan James "cxx11", SingleSig, IncludeBoostSystem), 2691038db6fSNathan James {"copy_if", "is_permutation", "is_partitioned", "find_if_not", 2701038db6fSNathan James "partition_copy", "any_of", "iota", "all_of", "partition_point", 2711038db6fSNathan James "is_sorted", "none_of"}); 2721038db6fSNathan James 2731038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<CustomBoostAlgorithmReplacer>( 2741038db6fSNathan James "cxx11", "is_sorted", SingleSig, IncludeBoostSystem), 2751038db6fSNathan James {"is_sorted_until"}); 2761038db6fSNathan James 2771038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<FixedBoostReplace>( 2781038db6fSNathan James "range/numeric", SingleSig, IncludeBoostSystem), 2791038db6fSNathan James {"accumulate", "partial_sum", "adjacent_difference"}); 2801038db6fSNathan James 2811038db6fSNathan James if (getLangOpts().CPlusPlus17) 2821038db6fSNathan James AddFromStd(llvm::makeIntrusiveRefCnt<BoostAlgorithmReplacer>( 2831038db6fSNathan James "cxx17", SingleSig, IncludeBoostSystem), 2841038db6fSNathan James {"reduce"}); 2851038db6fSNathan James 2861038db6fSNathan James AddFromBoost(llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(SingleSig), 2871038db6fSNathan James {{"algorithm", 2881038db6fSNathan James {"reduce", 2891038db6fSNathan James "find_backward", 2901038db6fSNathan James "find_not_backward", 2911038db6fSNathan James "find_if_backward", 2921038db6fSNathan James "find_if_not_backward", 2931038db6fSNathan James "hex", 2941038db6fSNathan James "hex_lower", 2951038db6fSNathan James "unhex", 2961038db6fSNathan James "is_partitioned_until", 2971038db6fSNathan James "is_palindrome", 2981038db6fSNathan James "copy_if", 2991038db6fSNathan James "copy_while", 3001038db6fSNathan James "copy_until", 3011038db6fSNathan James "copy_if_while", 3021038db6fSNathan James "copy_if_until", 3031038db6fSNathan James "is_permutation", 3041038db6fSNathan James "is_partitioned", 3051038db6fSNathan James "one_of", 3061038db6fSNathan James "one_of_equal", 3071038db6fSNathan James "find_if_not", 3081038db6fSNathan James "partition_copy", 3091038db6fSNathan James "any_of", 3101038db6fSNathan James "any_of_equal", 3111038db6fSNathan James "iota", 3121038db6fSNathan James "all_of", 3131038db6fSNathan James "all_of_equal", 3141038db6fSNathan James "partition_point", 3151038db6fSNathan James "is_sorted_until", 3161038db6fSNathan James "is_sorted", 3171038db6fSNathan James "is_increasing", 3181038db6fSNathan James "is_decreasing", 3191038db6fSNathan James "is_strictly_increasing", 3201038db6fSNathan James "is_strictly_decreasing", 3211038db6fSNathan James "none_of", 3221038db6fSNathan James "none_of_equal", 3231038db6fSNathan James "clamp_range"}}}); 3241038db6fSNathan James 3251038db6fSNathan James AddFromBoost( 3261038db6fSNathan James llvm::makeIntrusiveRefCnt<MakeOverloadReplacer>(TwoSig), 3271038db6fSNathan James {{"algorithm", {"apply_permutation", "apply_reverse_permutation"}}}); 3281038db6fSNathan James 3291038db6fSNathan James return Results; 3301038db6fSNathan James } 3311038db6fSNathan James 3321038db6fSNathan James UseRangesCheck::UseRangesCheck(StringRef Name, ClangTidyContext *Context) 3331038db6fSNathan James : utils::UseRangesCheck(Name, Context), 33487ca6386SNathan James IncludeBoostSystem(Options.get("IncludeBoostSystem", true)), 33587ca6386SNathan James UseReversePipe(Options.get("UseReversePipe", false)) {} 3361038db6fSNathan James 3371038db6fSNathan James void UseRangesCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { 3381038db6fSNathan James utils::UseRangesCheck::storeOptions(Opts); 3391038db6fSNathan James Options.store(Opts, "IncludeBoostSystem", IncludeBoostSystem); 34087ca6386SNathan James Options.store(Opts, "UseReversePipe", UseReversePipe); 3411038db6fSNathan James } 34287ca6386SNathan James 3431038db6fSNathan James DiagnosticBuilder UseRangesCheck::createDiag(const CallExpr &Call) { 3441038db6fSNathan James DiagnosticBuilder D = 3451038db6fSNathan James diag(Call.getBeginLoc(), "use a %0 version of this algorithm"); 3461038db6fSNathan James D << (Call.getDirectCallee()->isInStdNamespace() ? "boost" : "ranged"); 3471038db6fSNathan James return D; 3481038db6fSNathan James } 3491038db6fSNathan James ArrayRef<std::pair<StringRef, StringRef>> 3501038db6fSNathan James UseRangesCheck::getFreeBeginEndMethods() const { 3511038db6fSNathan James static const std::pair<StringRef, StringRef> Refs[] = { 3521038db6fSNathan James {"::std::begin", "::std::end"}, 3531038db6fSNathan James {"::std::cbegin", "::std::cend"}, 3541038db6fSNathan James {"::boost::range_adl_barrier::begin", "::boost::range_adl_barrier::end"}, 3551038db6fSNathan James {"::boost::range_adl_barrier::const_begin", 3561038db6fSNathan James "::boost::range_adl_barrier::const_end"}, 3571038db6fSNathan James }; 3581038db6fSNathan James return Refs; 3591038db6fSNathan James } 3601038db6fSNathan James std::optional<UseRangesCheck::ReverseIteratorDescriptor> 3611038db6fSNathan James UseRangesCheck::getReverseDescriptor() const { 3621038db6fSNathan James static const std::pair<StringRef, StringRef> Refs[] = { 3631038db6fSNathan James {"::std::rbegin", "::std::rend"}, 3641038db6fSNathan James {"::std::crbegin", "::std::crend"}, 3651038db6fSNathan James {"::boost::rbegin", "::boost::rend"}, 3661038db6fSNathan James {"::boost::const_rbegin", "::boost::const_rend"}, 3671038db6fSNathan James }; 36887ca6386SNathan James return ReverseIteratorDescriptor{ 36987ca6386SNathan James UseReversePipe ? "boost::adaptors::reversed" : "boost::adaptors::reverse", 37087ca6386SNathan James IncludeBoostSystem ? "<boost/range/adaptor/reversed.hpp>" 3711038db6fSNathan James : "boost/range/adaptor/reversed.hpp", 37287ca6386SNathan James Refs, UseReversePipe}; 3731038db6fSNathan James } 3741038db6fSNathan James } // namespace clang::tidy::boost 375